├── Build.sh ├── Makefile ├── README.md ├── dhcp.h ├── dhcpf.c ├── dhcpf.go ├── dhcpf.prints ├── fp.h ├── oui └── udpip.h /Build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sayf() { 4 | TEXT="$@" 5 | printf "$TEXT " 6 | awk "BEGIN { for (j=length(\"$TEXT\"); j<60; j++) printf \".\" }" 7 | printf " " 8 | } 9 | 10 | PROGNAME="dhcpf" 11 | TMP=".dhcpf-tmp" 12 | 13 | if [ "$1" = "clean" ]; then 14 | rm -f "$PROGNAME" 15 | exit 0 16 | fi 17 | 18 | sayf "Checking directory permissions" 19 | touch "$TMP" 2>/dev/null 20 | if [ -f "$TMP" ]; then 21 | echo "OK" 22 | else 23 | echo "FAIL (can't create)" 24 | echo 25 | echo "Please fix directory permissions and try again." 26 | echo 27 | exit 1 28 | fi 29 | 30 | REQFILES="dhcpf.c dhcp.h udpip.h fp.h dhcpf.prints oui" 31 | for i in $REQFILES; do 32 | sayf "Checking file '$i'" 33 | if [ -s $i ]; then 34 | echo "Found" 35 | else 36 | echo "NOT FOUND!" 37 | echo 38 | echo "It seems that the source code or some part of it is missing." 39 | echo 40 | exit 1 41 | fi 42 | done 43 | 44 | sayf "Checking for working GCC" 45 | rm -f "$TMP" "$TMP.log" "$TMP.c" 46 | echo "int main() { return 0; }" > "$TMP.c" 47 | gcc "$TMP.c" -o "$TMP" &> "$TMP.log" 48 | if [ -x "$TMP" ]; then 49 | echo "Works" 50 | else 51 | echo "FAIL" 52 | echo 53 | echo "Your compiler can't produce executables. You need a functioning install" 54 | echo "of GCC and libc to continue." 55 | echo 56 | echo "Verbose output from compilation attempt:" 57 | cat "$TMP.log" 58 | echo 59 | rm -f "$TMP" "$TMP.log" "$TMP.c" 60 | exit 1 61 | fi 62 | 63 | sayf "Checking for working libpcap" 64 | rm -f "$TMP" "$TMP.log" "$TMP.c" 65 | echo -e "#include \nint main() { char buf[PCAP_ERRBUF_SIZE]; pcap_lookupdev(buf); return 0; }" > "$TMP.c" 66 | gcc "$TMP.c" -o "$TMP" -lpcap &> "$TMP.log" 67 | 68 | if [ -x "$TMP" ]; then 69 | echo "Works" 70 | else 71 | echo "FAIL" 72 | echo 73 | echo "You need a functioning installation of libpcap with development headers." 74 | echo "You can download it from here:" 75 | echo 76 | echo "http://www.tcpdump.org/#latest-release" 77 | echo 78 | echo "Verbose output from an attempt to compile sample code:" 79 | cat "$TMP.log" 80 | echo 81 | rm -f "$TMP" "$TMP.log" "$TMP.c" 82 | exit 1 83 | fi 84 | 85 | echo 86 | sayf "Building $PROGNAME" 87 | rm -f "$PROGNAME" "$TMP" "$TMP.c" 88 | if [ "$1" = "debug" ]; then 89 | gcc dhcpf.c -o "$PROGNAME" -lpcap -Wall -D_DEBUG &> "$TMP.log" 90 | else 91 | gcc dhcpf.c -o "$PROGNAME" -lpcap &> "$TMP.log" 92 | fi 93 | if [ -x "$PROGNAME" ]; then 94 | echo "Done" 95 | else 96 | echo "FAIL" 97 | echo 98 | echo "Well, something went wrong." 99 | echo "Here is the output from compilation attempt:" 100 | echo 101 | cat "$TMP.log" 102 | echo 103 | echo "You may want to inform about this." 104 | echo 105 | exit 1 106 | fi 107 | rm -f "$TMP.log" 108 | 109 | echo 110 | echo "That's it! If you encounter any problems or have some suggestions" 111 | echo "please contact the author at " 112 | echo 113 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | @./Build.sh 3 | 4 | debug: 5 | @./Build.sh debug 6 | 7 | clean: 8 | @./Build.sh clean 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | dhcpf 2 | ===== 3 | It is possible to precisely recognize the operating system on the basis of unique combination 4 | of DHCP options in host requests. This is an example of implementation. 5 | 6 | Signatures 7 | ---------- 8 | This tool is somewhat useless without rich and reliable signature database. I will be very grateful for any kind of help in the database development. 9 | 10 | Contact 11 | ------- 12 | To send questions and comments, just send an e-mail at [marcin@ulikowski.pl](mailto:marcin@ulikowski.pl) 13 | 14 | * LinkedIn: [Marcin Ulikowski](https://pl.linkedin.com/in/elceef) 15 | * Twitter: [@elceef](https://twitter.com/elceef) 16 | 17 | Demo 18 | ---- 19 | ``` 20 | elceef@cerebellum:~/dhcpf% sudo ./dhcpf eth1 21 | === dhcpf 0.7c: passive DHCP fingerprinting === 22 | 23 | Successfully loaded 29 DHCP-prints. 24 | 25 | Discover from Motorola_03:e3:1d (40:fc:89:03:e3:1d) 26 | system = Android 2.2 (Motorola) 27 | 28 | Request from Motorola_03:e3:1d (40:fc:89:03:e3:1d) 29 | system = Android 2.2 (Motorola) 30 | req_ipaddr = 10.1.1.223 31 | 32 | Request from UnknownOUI_58:ab:d5 (d8:31:cf:58:ab:d5) 33 | system = Android 2.3 (Samsung) 34 | req_ipaddr = 10.1.1.209 35 | 36 | Request from FujitsuS_d2:38:de (00:30:05:d2:38:de) 37 | system = Windows XP/Vista/7 (generic) 38 | hostname = DRI-Stacja 39 | req_ipaddr = 10.1.1.115 40 | 41 | Discover from CiscoLin_db:d5:36 (00:0e:08:db:d5:36) 42 | system = Linksys SipuraSPA 43 | hostname = SipuraSPA 44 | req_ipaddr = 10.2.2.4 45 | option82 = remote_id 70:72:cf:59:0d:35 circuit_id vlan 504 unit 1 port 1 46 | 47 | Request from CiscoLin_db:d5:36 (00:0e:08:db:d5:36) 48 | system = Linksys SipuraSPA 49 | hostname = SipuraSPA 50 | req_ipaddr = 10.2.2.4 51 | option82 = remote_id 70:72:cf:59:0d:35 circuit_id vlan 504 unit 1 port 1 52 | ``` 53 | -------------------------------------------------------------------------------- /dhcp.h: -------------------------------------------------------------------------------- 1 | // BOOTP (RFC951) message types 2 | #define DHCP_BOOTREQUEST 1 3 | #define DHCP_BOOTREPLY 2 4 | 5 | // DHCP message types 6 | #define DHCP_DISCOVER 1 7 | #define DHCP_OFFER 2 8 | #define DHCP_REQUEST 3 9 | #define DHCP_DECLINE 4 10 | #define DHCP_ACK 5 11 | #define DHCP_NAK 6 12 | #define DHCP_RELEASE 7 13 | #define DHCP_INFORM 8 14 | 15 | // DHCP options 16 | #define DHCP_OPTION_PAD 0 17 | #define DHCP_OPTION_SUBNET_MASK 1 18 | #define DHCP_OPTION_TIME_OFFSET 2 19 | #define DHCP_OPTION_ROUTERS 3 20 | #define DHCP_OPTION_TIME_SERVERS 4 21 | #define DHCP_OPTION_NAME_SERVERS 5 22 | #define DHCP_OPTION_DOMAIN_NAME_SERVERS 6 23 | #define DHCP_OPTION_LOG_SERVERS 7 24 | #define DHCP_OPTION_COOKIE_SERVERS 8 25 | #define DHCP_OPTION_LPR_SERVERS 9 26 | #define DHCP_OPTION_IMPRESS_SERVERS 10 27 | #define DHCP_OPTION_RESOURCE_LOCATION_SERVERS 11 28 | #define DHCP_OPTION_HOST_NAME 12 29 | #define DHCP_OPTION_BOOT_SIZE 13 30 | #define DHCP_OPTION_MERIT_DUMP 14 31 | #define DHCP_OPTION_DOMAIN_NAME 15 32 | #define DHCP_OPTION_SWAP_SERVER 16 33 | #define DHCP_OPTION_ROOT_PATH 17 34 | #define DHCP_OPTION_EXTENSIONS_PATH 18 35 | #define DHCP_OPTION_IP_FORWARDING 19 36 | #define DHCP_OPTION_NON_LOCAL_SOURCE_ROUTING 20 37 | #define DHCP_OPTION_POLICY_FILTER 21 38 | #define DHCP_OPTION_MAX_DGRAM_REASSEMBLY 22 39 | #define DHCP_OPTION_DEFAULT_IP_TTL 23 40 | #define DHCP_OPTION_PATH_MTU_AGING_TIMEOUT 24 41 | #define DHCP_OPTION_PATH_MTU_PLATEAU_TABLE 25 42 | #define DHCP_OPTION_INTERFACE_MTU 26 43 | #define DHCP_OPTION_ALL_SUBNETS_LOCAL 27 44 | #define DHCP_OPTION_BROADCAST_ADDRESS 28 45 | #define DHCP_OPTION_PERFORM_MASK_DISCOVERY 29 46 | #define DHCP_OPTION_MASK_SUPPLIER 30 47 | #define DHCP_OPTION_ROUTER_DISCOVERY 31 48 | #define DHCP_OPTION_ROUTER_SOLICITATION_ADDRESS 32 49 | #define DHCP_OPTION_STATIC_ROUTES 33 50 | #define DHCP_OPTION_TRAILER_ENCAPSULATION 34 51 | #define DHCP_OPTION_ARP_CACHE_TIMEOUT 35 52 | #define DHCP_OPTION_IEEE802_3_ENCAPSULATION 36 53 | #define DHCP_OPTION_DEFAULT_TCP_TTL 37 54 | #define DHCP_OPTION_TCP_KEEPALIVE_INTERVAL 38 55 | #define DHCP_OPTION_TCP_KEEPALIVE_GARBAGE 39 56 | #define DHCP_OPTION_NIS_DOMAIN 40 57 | #define DHCP_OPTION_NIS_SERVERS 41 58 | #define DHCP_OPTION_NTP_SERVERS 42 59 | #define DHCP_OPTION_VENDOR_ENCAPSULATED_OPTIONS 43 60 | #define DHCP_OPTION_NETBIOS_NAME_SERVERS 44 61 | #define DHCP_OPTION_NETBIOS_DD_SERVER 45 62 | #define DHCP_OPTION_NETBIOS_NODE_TYPE 46 63 | #define DHCP_OPTION_NETBIOS_SCOPE 47 64 | #define DHCP_OPTION_FONT_SERVERS 48 65 | #define DHCP_OPTION_X_DISPLAY_MANAGER 49 66 | #define DHCP_OPTION_DHCP_REQUESTED_ADDRESS 50 67 | #define DHCP_OPTION_DHCP_LEASE_TIME 51 68 | #define DHCP_OPTION_DHCP_OPTION_OVERLOAD 52 69 | #define DHCP_OPTION_DHCP_MESSAGE_TYPE 53 70 | #define DHCP_OPTION_DHCP_SERVER_IDENTIFIER 54 71 | #define DHCP_OPTION_DHCP_PARAMETER_REQUEST_LIST 55 72 | #define DHCP_OPTION_DHCP_MESSAGE 56 73 | #define DHCP_OPTION_DHCP_MAX_MESSAGE_SIZE 57 74 | #define DHCP_OPTION_DHCP_RENEWAL_TIME 58 75 | #define DHCP_OPTION_DHCP_REBINDING_TIME 59 76 | #define DHCP_OPTION_VENDOR_CLASS_IDENTIFIER 60 77 | #define DHCP_OPTION_DHCP_CLIENT_IDENTIFIER 61 78 | #define DHCP_OPTION_NWIP_DOMAIN_NAME 62 79 | #define DHCP_OPTION_NWIP_SUBOPTIONS 63 80 | #define DHCP_OPTION_USER_CLASS 77 81 | #define DHCP_OPTION_FQDN 81 82 | #define DHCP_OPTION_DHCP_AGENT_OPTIONS 82 83 | #define DHCP_OPTION_END 255 84 | 85 | #define DHCP_OPTION82_CIRCUIT_ID 1 86 | #define DHCP_OPTION82_REMOTE_ID 2 87 | 88 | 89 | // PORTS 90 | #define DHCP_CLIENT_PORT 68 91 | #define DHCP_SERVER_PORT 67 92 | 93 | // LENGTHS 94 | #define DHCP_CHADDR_LEN 16 95 | #define DHCP_SNAME_LEN 64 96 | #define DHCP_FILE_LEN 128 97 | #define DHCP_OPTIONS_LEN 512 98 | #define DHCP_MIN_OPTIONS_LEN 68 99 | 100 | struct __attribute__((packed)) dhcpmsg { 101 | unsigned char op; // Message opcode/type 102 | unsigned char htype; // Hardware addr type 103 | unsigned char hlen; // Hardware addr length 104 | unsigned char hops; // Number of relay agent hops from client 105 | unsigned int xid; // Transaction ID 106 | unsigned short secs; // Seconds since client started looking 107 | unsigned short flags; // Flag bits 108 | unsigned int ciaddr; // Client IP address (if already in use) 109 | unsigned int yiaddr; // Client IP address 110 | unsigned int siaddr; // IP address of next server to talk to 111 | unsigned int giaddr; // DHCP relay agent IP address 112 | unsigned char chaddr[DHCP_CHADDR_LEN]; // Client hardware address 113 | char sname[DHCP_SNAME_LEN]; // Server name 114 | char file[DHCP_FILE_LEN]; // Boot filename 115 | unsigned int cookie; // DHCP option cookie 116 | unsigned char options[0]; // Optional parameters (actual length dependent on MTU). 117 | }; 118 | -------------------------------------------------------------------------------- /dhcpf.c: -------------------------------------------------------------------------------- 1 | /* 2 | dhcpf 3 | (c) Marcin Ulikowski 4 | 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, write to the Free Software 18 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "dhcp.h" 32 | #include "udpip.h" 33 | #include "fp.h" 34 | 35 | #define VERSION "0.7c" 36 | 37 | struct fprint fp[FP_MAX_PRINTS]; 38 | unsigned short fpcnt = 0; 39 | 40 | unsigned int udpc = 0; 41 | unsigned short fhlen = 0; // frame header length 42 | 43 | 44 | char* opt2bin(char *opt) 45 | { 46 | 47 | char *pch; 48 | int pcnt = 0; 49 | pch = strtok(opt, ","); 50 | 51 | while (pch != NULL) 52 | { 53 | 54 | opt[pcnt++] = atoi(pch); 55 | pch = strtok(NULL, ","); 56 | 57 | } 58 | 59 | opt[pcnt++] = 0; 60 | 61 | return opt; 62 | 63 | } 64 | 65 | 66 | char* bin2opt(unsigned char *bin) 67 | { 68 | 69 | static char opt[255]; 70 | char *o; 71 | unsigned char i; 72 | 73 | bzero(opt, sizeof(opt)); 74 | 75 | for (i = 0; i < strlen(bin); i++) 76 | { 77 | 78 | o = strdup(opt); 79 | snprintf(opt, sizeof(opt), "%s%s%u", o, (i>0)?",":"", *(bin+i)); 80 | free(o); 81 | 82 | } 83 | 84 | return opt; 85 | 86 | } 87 | 88 | 89 | char* bin2mac(unsigned char *chaddr, unsigned char hlen) 90 | { 91 | 92 | static char mac[255]; 93 | char *o; 94 | unsigned char i; 95 | 96 | bzero(mac, sizeof(mac)); 97 | 98 | for (i = 0; i < hlen; i++) 99 | { 100 | 101 | o = strdup(mac); 102 | snprintf(mac, sizeof(mac), "%s%s%02x", o, (i>0)?":":"", chaddr[i]); 103 | free(o); 104 | 105 | } 106 | 107 | return mac; 108 | 109 | } 110 | 111 | 112 | unsigned char round_ttl(unsigned char ttl) 113 | { 114 | 115 | if (ttl <= 32) return 32; 116 | else 117 | if (ttl <= 64 && ttl > 32) return 64; 118 | else 119 | if (ttl <= 128 && ttl > 64) return 128; 120 | else 121 | if (ttl > 128) return 255; 122 | 123 | } 124 | 125 | 126 | // super fast strcmp() for fixed length string 127 | 128 | char* strcmp6(char *heystack, char *needle) 129 | { 130 | 131 | int heys = heystack[0] << 24 | heystack[1] << 16 | heystack[2] << 8 | heystack[3]; 132 | short tack = heystack[4] << 8 | heystack[5]; 133 | int nee = needle[0] << 24 | needle[1] << 16 | needle[2] << 8 | needle[3]; 134 | short dle = needle[4] << 8 | needle[5]; 135 | 136 | if ( heys == nee && tack == dle ) 137 | return heystack; 138 | 139 | return 0; 140 | 141 | } 142 | 143 | 144 | char* find_ether_oui(unsigned char *mac) 145 | { 146 | 147 | FILE *o; 148 | static unsigned char oui[9]; 149 | unsigned char buf[32]; 150 | unsigned char eth[7]; 151 | 152 | if (o = fopen("oui", "r")) 153 | { 154 | 155 | snprintf(eth, sizeof(eth), "%02X%02X%02X", *mac, *(mac+1), *(mac+2)); 156 | 157 | bzero(oui, sizeof(oui)); 158 | 159 | while (!feof(o)) 160 | { 161 | 162 | fgets(buf, sizeof(buf), o); 163 | 164 | if (strcmp6(buf, eth)) 165 | { 166 | 167 | memcpy(oui, buf+7, sizeof(oui)-1); 168 | fclose(o); 169 | 170 | return (char*)oui; 171 | 172 | } 173 | 174 | } 175 | 176 | fclose(o); 177 | 178 | } 179 | 180 | return (char*)"UnknownOUI"; 181 | 182 | } 183 | 184 | 185 | //TODO: 186 | int load_dhcp_prints(void) 187 | { 188 | 189 | FILE *fpd; 190 | char line[FP_LINE_LEN]; 191 | char osname[FP_OSNAME_LEN]; 192 | char vendor[FP_VENDOR_LEN]; 193 | char opts[FP_OPTS_LEN]; 194 | char opt55[FP_OPT55_LEN]; 195 | int ttl, i = 0; 196 | 197 | if (!(fpd = fopen(FP_DATABASE, "r"))) 198 | fprintf(stderr, "Error: Unable to open %s - %s\n", FP_DATABASE, strerror(errno)); 199 | 200 | while (i++ < FP_MAX_PRINTS) 201 | { 202 | 203 | fgets(line, FP_LINE_LEN, fpd); 204 | 205 | if (feof(fpd)) 206 | break; 207 | 208 | if (line[0] == '#' || line[0] == '\n' || line[0] == '!') 209 | continue; 210 | 211 | line[strlen(line)-1] = 0; 212 | 213 | if (sscanf(line, "%d|%[0-9,*]|%[0-9,*]|%[^|]|%[^|]", &ttl, opts, opt55, vendor, osname) != 5) 214 | fprintf(stderr, "Error: Syntax mismatch on line %u in %s\n", i, FP_DATABASE); 215 | 216 | if (!strcmp(vendor, "(null)")) 217 | bzero(vendor, sizeof(vendor)); 218 | 219 | fp[fpcnt].ttl = ttl; 220 | fp[fpcnt].opts = (!strcmp(opts, "*")) ? strdup(opts) : strdup(opt2bin(opts)); 221 | fp[fpcnt].opt55 = (!strcmp(opt55, "*")) ? strdup(opt55) : strdup(opt2bin(opt55)); 222 | fp[fpcnt].vendor = strdup(vendor); 223 | fp[fpcnt].osname = strdup(osname); 224 | 225 | fpcnt++; 226 | 227 | } 228 | 229 | fclose(fpd); 230 | 231 | return fpcnt; 232 | 233 | } 234 | 235 | 236 | char* osfp(unsigned char ttl, unsigned char *opts, unsigned char *opt55, unsigned char *vendor) 237 | { 238 | 239 | unsigned short i; 240 | 241 | for (i = 0; i < fpcnt; i++) 242 | { 243 | 244 | if (!strcmp(fp[i].opt55, opt55) || !strcmp(fp[i].opt55, "*")) 245 | if (!strcmp(fp[i].opts, opts) || !strcmp(fp[i].opts, "*")) 246 | if (!strcmp(fp[i].vendor, vendor)) 247 | if (fp[i].ttl == ttl || !fp[i].ttl) 248 | return fp[i].osname; 249 | 250 | } 251 | 252 | static char rawsig[255]; 253 | char *o, *o55; 254 | 255 | o = strdup(bin2opt(opts)); 256 | o55 = strdup(bin2opt(opt55)); 257 | snprintf(rawsig, sizeof(rawsig), "%u|%s|%s|%s|Unknown", ttl, o, o55, (strlen(vendor))?vendor:"(null)"); 258 | free(o); 259 | free(o55); 260 | 261 | return rawsig; 262 | 263 | } 264 | 265 | 266 | char* long2ip(unsigned int ip) 267 | { 268 | 269 | unsigned char *ia; 270 | static char dotip[16]; 271 | ia = (unsigned char *)&ip; 272 | snprintf(dotip, sizeof(dotip), "%u.%u.%u.%u", ia[0], ia[1], ia[2], ia[3]); 273 | 274 | return (char*)dotip; 275 | 276 | } 277 | 278 | 279 | void catch_signal() 280 | { 281 | 282 | printf("\nSignal received. Shutdown.\nProcessed %u DHCP requests.\n\nQuestions? Complaints? You can reach the author at \n", udpc); 283 | exit(0); 284 | 285 | } 286 | 287 | 288 | void set_fhlen(unsigned int dlink_type) 289 | { 290 | 291 | switch (dlink_type) 292 | { 293 | 294 | case DLT_RAW: 295 | fhlen = 0; break; 296 | 297 | case DLT_SLIP: 298 | fhlen = 16; break; 299 | 300 | case DLT_EN10MB: 301 | fhlen = 14; break; 302 | 303 | case DLT_NULL: 304 | case DLT_PPP: 305 | fhlen = 4; break; 306 | 307 | case DLT_LOOP: 308 | #ifdef DLT_PPP_SERIAL 309 | case DLT_PPP_SERIAL: 310 | #endif 311 | #ifdef DLT_PPP_ETHER 312 | case DLT_PPP_ETHER: 313 | #endif 314 | fhlen = 8; break; 315 | 316 | #ifdef DLT_PFLOG 317 | case DLT_PFLOG: 318 | fhlen = 28; break; 319 | #endif 320 | 321 | #ifdef DLT_LINUX_SLL 322 | case DLT_LINUX_SLL: 323 | fhlen = 16; break; 324 | #endif 325 | 326 | default: 327 | fprintf(stderr, "Warning: unable to find device link type!\n"); 328 | break; 329 | 330 | } 331 | 332 | } 333 | 334 | 335 | void process_datagram(unsigned char *args, struct pcap_pkthdr *header, unsigned char *datagram) 336 | { 337 | 338 | unsigned char options[255], option55[255], vendor[255], hostname[255], opt82_remote_id[255]; 339 | unsigned char dhcptype; 340 | unsigned int reqip = 0, optc = 0; 341 | unsigned short vlanid = 0, opt82_circuit_vlan = 0; 342 | unsigned short ethtype; 343 | unsigned int i, j, k; 344 | unsigned char *p, *p82; 345 | unsigned char opt, len, opt82, len82; 346 | unsigned char opt82_circuit_unit = 0, opt82_circuit_port = 0; 347 | 348 | unsigned char *ethp = (datagram + 12); 349 | memcpy(ðtype, ethp, 2); 350 | #if BYTE_ORDER == LITTLE_ENDIAN 351 | ethtype = ntohs(ethtype); 352 | #endif 353 | 354 | if (ethtype == 0x8100) 355 | { 356 | 357 | fhlen += 4; // 802.1Q VLAN 358 | ethp += 2; 359 | memcpy(&vlanid, ethp, 2); 360 | vlanid &= 0xFF0F; // remove PCP & CFI 361 | #if BYTE_ORDER == LITTLE_ENDIAN 362 | vlanid = ntohs(vlanid); 363 | #endif 364 | 365 | } 366 | 367 | struct iphdr *ip = (struct iphdr *)(datagram + fhlen); 368 | struct udphdr *udp = (struct udphdr *)(datagram + sizeof(struct iphdr) + fhlen); 369 | struct dhcpmsg *dhcp = (struct dhcpmsg *)(datagram + sizeof(struct iphdr) + sizeof(struct udphdr) + fhlen); 370 | 371 | if (ethtype == 0x8100) // 802.1Q VLAN 372 | fhlen -= 4; 373 | 374 | #if BYTE_ORDER == LITTLE_ENDIAN 375 | udp->len = htons(udp->len); 376 | #endif 377 | 378 | p = (unsigned char *)(dhcp->options); 379 | 380 | bzero(hostname, sizeof(hostname)); 381 | bzero(option55, sizeof(option55)); 382 | bzero(options, sizeof(options)); 383 | bzero(vendor, sizeof(vendor)); 384 | bzero(opt82_remote_id, sizeof(opt82_remote_id)); 385 | 386 | for (i = 0; i < (udp->len - sizeof(struct udphdr) - sizeof(struct dhcpmsg)); i++) 387 | { 388 | 389 | memcpy(&opt, p, 1); 390 | memcpy(&len, p+1, 1); 391 | p+=2; 392 | 393 | // omit pad, end and option82! 394 | if (opt != DHCP_OPTION_PAD && opt != DHCP_OPTION_END && opt != DHCP_OPTION_DHCP_AGENT_OPTIONS) 395 | options[optc++] = opt; 396 | 397 | switch (opt) 398 | { 399 | 400 | case DHCP_OPTION_DHCP_MESSAGE_TYPE: 401 | dhcptype = *p; 402 | break; 403 | 404 | case DHCP_OPTION_DHCP_REQUESTED_ADDRESS: 405 | memcpy(&reqip, p, sizeof(reqip)); 406 | break; 407 | 408 | case DHCP_OPTION_DHCP_PARAMETER_REQUEST_LIST: // 55 409 | for (j = 0; j < len; j++) 410 | option55[j] = *(p+j); 411 | break; 412 | 413 | case DHCP_OPTION_VENDOR_CLASS_IDENTIFIER: 414 | memcpy(&vendor, p, len); 415 | break; 416 | 417 | case DHCP_OPTION_HOST_NAME: 418 | memcpy(hostname, p, len); 419 | break; 420 | 421 | case DHCP_OPTION_DHCP_AGENT_OPTIONS: // 82 422 | p82 = p; 423 | 424 | for (j = 0; j < len; j++) 425 | { 426 | 427 | memcpy(&opt82, p82, 1); 428 | memcpy(&len82, p82+1, 1); 429 | p82+=2; 430 | 431 | switch (opt82) 432 | { 433 | 434 | case DHCP_OPTION82_REMOTE_ID: 435 | memcpy(opt82_remote_id, p82+2, len82-2); 436 | break; 437 | 438 | case DHCP_OPTION82_CIRCUIT_ID: 439 | // make sure we are not dealing with some custom string 440 | if (*p82 == 0 && *(p82+1) == 4) 441 | { 442 | 443 | memcpy(&opt82_circuit_vlan, p82+2, 2); 444 | #if BYTE_ORDER == LITTLE_ENDIAN 445 | opt82_circuit_vlan = htons(opt82_circuit_vlan); 446 | #endif 447 | memcpy(&opt82_circuit_unit, p82+4, 1); 448 | memcpy(&opt82_circuit_port, p82+5, 1); 449 | 450 | } 451 | break; 452 | 453 | default: 454 | #ifdef _DEBUG 455 | printf("subopt82=%u len=%u val=", opt82, len82); 456 | 457 | for (k = 0; k < len82; k++) 458 | printf("%02x", *(p82+k)); 459 | 460 | putchar('|'); 461 | 462 | for (k = 0; k < len82; k++) 463 | { 464 | 465 | if (*(p82+k) >= 32 && *(p82+k) < 128) 466 | putchar(*(p82+k)); 467 | else 468 | putchar('.'); 469 | 470 | } 471 | 472 | putchar(' '); 473 | #endif 474 | break; 475 | } 476 | 477 | p82 += len82; 478 | j += len82 + 1; 479 | 480 | } 481 | break; 482 | 483 | case DHCP_OPTION_PAD: 484 | case DHCP_OPTION_END: 485 | break; 486 | 487 | default: 488 | #ifdef _DEBUG 489 | printf("opt=%u len=%u val=", opt, len); 490 | 491 | for (j = 0; j < len; j++) 492 | printf("%02x", *(p+j)); 493 | 494 | putchar('|'); 495 | 496 | for (j = 0; j < len; j++) 497 | { 498 | 499 | if (*(p+j) >= 32 && *(p+j) < 128) 500 | putchar(*(p+j)); 501 | else 502 | putchar('.'); 503 | 504 | } 505 | 506 | putchar(' '); 507 | #endif 508 | break; 509 | 510 | } 511 | 512 | p += len; 513 | i += len + 1; 514 | 515 | } // options 516 | 517 | switch (dhcptype) 518 | { 519 | 520 | case DHCP_DISCOVER: printf("Discover"); break; 521 | case DHCP_REQUEST: printf("Request"); break; 522 | case DHCP_INFORM: printf("Inform"); break; 523 | default: return; break; 524 | 525 | } 526 | 527 | printf(" from %s_%s (%s)", find_ether_oui(dhcp->chaddr), bin2mac(dhcp->chaddr, dhcp->hlen)+9, bin2mac(dhcp->chaddr, dhcp->hlen)); 528 | 529 | if (vlanid) 530 | printf(" @ VLAN %u\n", vlanid); 531 | else 532 | putchar('\n'); 533 | 534 | printf(" system\t= %s\n", osfp(round_ttl(ip->ttl), options, option55, vendor)); 535 | 536 | if (strlen(hostname)) 537 | printf(" hostname\t= %s\n", hostname); 538 | if (reqip) 539 | printf(" req ipaddr\t= %s\n", long2ip(reqip)); 540 | if (strlen(opt82_remote_id)) 541 | printf(" option82\t= remote_id %s circuit_id vlan %u unit %u port %u\n", bin2mac(opt82_remote_id, 6), opt82_circuit_vlan, opt82_circuit_unit, opt82_circuit_port); 542 | 543 | putchar('\n'); 544 | 545 | fflush(stdout); 546 | 547 | udpc++; 548 | 549 | } 550 | 551 | 552 | int main(int argc, char *argv[]) 553 | { 554 | 555 | char errbuff[PCAP_ERRBUF_SIZE]; 556 | struct bpf_program filter; 557 | pcap_t *pt; 558 | struct passwd *nobody; 559 | 560 | signal(SIGHUP, &catch_signal); 561 | signal(SIGINT, &catch_signal); 562 | signal(SIGTERM, &catch_signal); 563 | 564 | printf("=== dhcpf " VERSION ": passive DHCP fingerprinting ===\n\n"); 565 | 566 | if (argc != 2) 567 | { 568 | 569 | fprintf(stderr, "Error: Missing argument. Interface not specified\n"); 570 | exit(1); 571 | 572 | } 573 | 574 | if (geteuid()) 575 | { 576 | 577 | fprintf(stderr, "Error: You need to have root privileges\n"); 578 | exit(1); 579 | 580 | } 581 | 582 | printf("Successfully loaded %d DHCP-prints.\n\n", load_dhcp_prints()); 583 | 584 | if (!(pt = pcap_open_live(argv[1], 1500, 1, 1, errbuff))) 585 | fprintf(stderr, "Error: Unable to open device %s\n", argv[1]); 586 | 587 | // drop priv 588 | nobody = getpwnam("nobody"); 589 | 590 | if (nobody && nobody->pw_uid) 591 | setuid(nobody->pw_uid); 592 | else 593 | fprintf(stderr, "Warning: Unable to drop privileges!\n"); 594 | 595 | set_fhlen(pcap_datalink(pt)); 596 | 597 | pcap_compile(pt, &filter, "udp[9] == 1 and src port 68 and dst port 67 and greater 267", 1, 0); 598 | 599 | pcap_setfilter(pt, &filter); 600 | 601 | pcap_loop(pt, -1, (pcap_handler)&process_datagram, 0); 602 | 603 | pcap_close(pt); 604 | 605 | return 0; 606 | 607 | } 608 | -------------------------------------------------------------------------------- /dhcpf.go: -------------------------------------------------------------------------------- 1 | // dhcpf 2 | // (c) Marcin Ulikowski 3 | 4 | package main 5 | 6 | import ( 7 | "bufio" 8 | "bytes" 9 | "encoding/binary" 10 | "fmt" 11 | "github.com/google/gopacket" 12 | "github.com/google/gopacket/layers" 13 | "github.com/google/gopacket/pcap" 14 | "log" 15 | "os" 16 | "strings" 17 | "time" 18 | ) 19 | 20 | var ( 21 | device string 22 | snapshotLen int32 = 1024 23 | promiscuous bool = false 24 | err error 25 | timeout time.Duration = 30 * time.Second 26 | handle *pcap.Handle 27 | ) 28 | 29 | type dhcpFprint struct { 30 | TTL uint8 31 | opts []uint8 32 | opt55 []uint8 33 | vendor string 34 | osname string 35 | } 36 | 37 | type dhcpMsg struct { 38 | Op uint8 39 | Htype uint8 40 | Hlen uint8 41 | Hops uint8 42 | Xid uint32 43 | Secs uint16 44 | Flags uint16 45 | Ciaddr uint32 46 | Yiaddr uint32 47 | Siaddr uint32 48 | Giaddr uint32 49 | Chaddr [16]byte 50 | Sname [64]byte 51 | File [128]byte 52 | Cookie uint32 53 | //Options uint8 54 | } 55 | 56 | var fp []dhcpFprint 57 | 58 | func main() { 59 | fmt.Printf("=== dhcpf: passive DHCP fingerprinting ===\n\n") 60 | if len(os.Args) < 2 { 61 | fmt.Printf("Usage: %s \n", os.Args[0]) 62 | os.Exit(1) 63 | } 64 | 65 | loadSignatures() 66 | 67 | if len(fp) > 0 { 68 | fmt.Printf("Successfully loaded %d DHCP-prints\n\n", len(fp)) 69 | } else { 70 | fmt.Printf("Warning: No valid fingerprints found!\n\n") 71 | //os.Exit(1) 72 | } 73 | 74 | device = os.Args[1] 75 | 76 | handle, err = pcap.OpenLive(device, snapshotLen, promiscuous, timeout) 77 | if err != nil { 78 | handle, err = pcap.OpenOffline(device) 79 | } 80 | if err != nil { 81 | log.Fatal(err) 82 | } 83 | defer handle.Close() 84 | 85 | var filter string = "udp[9] == 1 and src port 68 and dst port 67 and greater 267" 86 | err = handle.SetBPFFilter(filter) 87 | if err != nil { 88 | log.Fatal(err) 89 | } 90 | 91 | packetSource := gopacket.NewPacketSource(handle, handle.LinkType()) 92 | for packet := range packetSource.Packets() { 93 | parsePacket(packet) 94 | } 95 | } 96 | 97 | func loadSignatures() { 98 | file, err := os.Open("dhcpf.prints") 99 | if err != nil { 100 | log.Fatal(err) 101 | } 102 | defer file.Close() 103 | 104 | scanner := bufio.NewScanner(file) 105 | scanner.Split(bufio.ScanLines) 106 | for scanner.Scan() { 107 | if strings.HasPrefix(scanner.Text(), "!") == false && 108 | scanner.Text() != "" { 109 | s := strings.Split(scanner.Text(), "|") 110 | var ttl uint8 111 | fmt.Sscanf(s[0], "%d", &ttl) 112 | var opts []uint8 113 | for _, v := range strings.Split(s[1], ",") { 114 | var x uint8 115 | fmt.Sscanf(v, "%d", &x) 116 | opts = append(opts, x) 117 | } 118 | var opt55 []uint8 119 | for _, v := range strings.Split(s[2], ",") { 120 | var x uint8 121 | fmt.Sscanf(v, "%d", &x) 122 | opt55 = append(opt55, x) 123 | } 124 | var vendor string 125 | if s[3] != "(null)" { 126 | vendor = s[3] 127 | } 128 | osname := s[4] 129 | 130 | sig := dhcpFprint{ttl, opts, opt55, vendor, osname} 131 | fp = append(fp, sig) 132 | } 133 | } 134 | 135 | if err := scanner.Err(); err != nil { 136 | log.Fatal(err) 137 | } 138 | 139 | //for i := 0; i < len(fp); i++ { 140 | // fmt.Println(fp[i]) 141 | //} 142 | } 143 | 144 | func roundTTL(ttl uint8) int { 145 | if ttl <= 32 { 146 | return 32 147 | } else if ttl <= 64 && ttl > 32 { 148 | return 64 149 | } else if ttl <= 128 && ttl > 64 { 150 | return 128 151 | } else if ttl > 128 { 152 | return 255 153 | } 154 | return 0 // shouldn't happen :) 155 | } 156 | 157 | func matchSystem(ttl uint8, opts []uint8, opt55 []uint8, vendor string) string { 158 | for i := 0; i < len(fp); i++ { 159 | if fp[i].TTL == ttl || fp[i].TTL == 0 { 160 | if bytes.Equal(fp[i].opts, opts) || fp[i].opts[0] == 0 { 161 | if bytes.Equal(fp[i].opt55, opt55) || fp[i].opt55[0] == 0 { 162 | if fp[i].vendor == vendor || fp[i].vendor == "*" { 163 | return fp[i].osname 164 | } 165 | } 166 | } 167 | } 168 | } 169 | var sopts []string 170 | for i := 0; i < len(opts); i++ { 171 | sopts = append(sopts, fmt.Sprintf("%d", opts[i])) 172 | } 173 | var sopt55 []string 174 | for i := 0; i < len(opt55); i++ { 175 | sopt55 = append(sopt55, fmt.Sprintf("%d", opt55[i])) 176 | } 177 | return fmt.Sprintf("%d|%s|%s|%s|UNKNOWN", ttl, strings.Join(sopts, ","), strings.Join(sopt55, ","), vendor) 178 | } 179 | 180 | func formatMAC(mac []byte) string { 181 | var m []string 182 | for i := 0; i < len(mac); i++ { 183 | m = append(m, fmt.Sprintf("%x", mac[i])) 184 | } 185 | return strings.Join(m, ":") 186 | } 187 | 188 | func dhcpType(code int) string { 189 | switch code { 190 | case 1: // DISCOVER 191 | return "DHCP Discover" 192 | case 3: // REQUEST 193 | return "DHCP Request" 194 | case 8: // INFORM 195 | return "DHCP Inform" 196 | } 197 | return "DHCP Unknown" 198 | } 199 | 200 | func parsePacket(packet gopacket.Packet) { 201 | var srcMAC []byte 202 | ethernetLayer := packet.Layer(layers.LayerTypeEthernet) 203 | if ethernetLayer != nil { 204 | ethernetPacket, _ := ethernetLayer.(*layers.Ethernet) 205 | srcMAC = ethernetPacket.SrcMAC 206 | } 207 | 208 | var TTL uint8 = 0 209 | ipLayer := packet.Layer(layers.LayerTypeIPv4) 210 | if ipLayer != nil { 211 | ip, _ := ipLayer.(*layers.IPv4) 212 | TTL = uint8(ip.TTL) 213 | } 214 | 215 | applicationLayer := packet.ApplicationLayer() 216 | if applicationLayer != nil { 217 | var message int 218 | var vendor string 219 | var opts []uint8 220 | var opt55 []uint8 221 | var dhcpHeader dhcpMsg 222 | 223 | buf := bytes.NewBuffer(applicationLayer.Payload()) 224 | //fmt.Println(buf) 225 | err = binary.Read(buf, binary.BigEndian, &dhcpHeader) 226 | if err != nil { 227 | fmt.Println("binary.Read failed:", err) 228 | } 229 | var options []byte = applicationLayer.Payload()[binary.Size(dhcpHeader):] 230 | //fmt.Println(options) 231 | 232 | for i := 0; i < len(options); i++ { 233 | opt := int(options[i]) 234 | len := int(options[i+1]) 235 | 236 | if opt != 255 && opt != 0 && opt != 82 { 237 | opts = append(opts, uint8(opt)) 238 | } 239 | 240 | switch opt { 241 | case 53: // DHCP_MESSAGE_TYPE 242 | message = int(options[i+1]) 243 | 244 | case 55: // DHCP_PARAMETER_REQUEST_LIST 245 | for j := 0; j < len; j++ { 246 | opt55 = append(opt55, uint8(options[i+j+2])) 247 | } 248 | 249 | case 60: // VENDOR_CLASS_IDENTIFIER 250 | vendor = string(options[i+2 : i+2+len]) 251 | } 252 | 253 | i += len + 1 254 | } 255 | 256 | //fmt.Println(opts, opt55, vendor) 257 | log.Printf("%s %s %s\n", dhcpType(message), formatMAC(srcMAC), matchSystem(TTL, opts, opt55, vendor)) 258 | } 259 | 260 | if err := packet.ErrorLayer(); err != nil { 261 | fmt.Println("Error decoding some part of the packet:", err) 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /dhcpf.prints: -------------------------------------------------------------------------------- 1 | ! ================= 2 | ! = DHCP DISCOVER = 3 | ! ================= 4 | 255|53,55,57,61,51,12|1,3,6,15,119,252|(null)|Apple iPad 5 | !128|53,116,61,50,12,60,55,43|1,15,3,6,44,46,47,31,33,249,43|MSFT 5.0|Windows XP SP3 6 | 64|53,57,60,12,55|1,33,3,6,15,28,51,58,59|dhcpcd-5.2.10:Linux-2.6.39.4-00002-g9254cf3:armv7l:cardhu|Android (Linux 2.6.39) 7 | 64|53,61,57,60,12,55|1,33,3,6,15,28,51,58,59|dhcpcd-5.2.10:Linux-3.0.21-g6c11713:armv7l:protou|Android (Linux 3.0.21) 8 | 128|53,12,81,55|1,28,2,3,15,6,119,12,44,47,26,121,42,121,249,252,42|(null)|Linux 3.5 (xubuntu) 9 | !128|53,12,60,125,55|1,3,6,120,125,114|A580_IP|Gigaset A580 VoIP 10 | 128|53,50,60,12,61,55|1,3,6,15|MSFT 98|D-Link DI-624+ 11 | 12 | ! confirmed below 13 | 128|53,60,12,125,55|1,3,6,15,120,114,125|A510_IP|Gigaset A510 VoIP 14 | 64|53,57,60,55|1,121,33,3,6,28,51,58,59|dhcpcd 4.0.15|Android 2.2 (Motorola) 15 | 64|53,57,60,12,55|1,33,3,6,15,28,51,58,59|dhcpcd-5.2.10|Android 4.1 (Cyanogenmod) 16 | 255|53,61,50,12,55|1,3,42,6,7,15,58,59,66|(null)|Linksys SipuraSPA 17 | 32|53,61,55,12|1,121,3,33,6,42|(null)|Mikrotik RouterOS 18 | 255|53,61,12,50,55|1,3,12,23,6,15,44,47|(null)|Kyocera Network Printer 19 | 128|53,50,61,55,57|12,6,15,1,3,28,120|(null)|Symbian S60 20 | 21 | 22 | ! ================ 23 | ! = DHCP REQUEST = 24 | ! ================ 25 | 255|53,55,57,61,50,54,12|1,3,6,15,119,252|(null)|Apple iPad 26 | !128|53,61,50,12,81,60,55|1,15,3,6,44,46,47,31,33,121,249,43|MSFT 5.0|Windows 7 27 | 128|53,51,50,12,55,57,61|1,2,3,15,6,12,44|(null)|Mac OS X 10.x (TimeCapsule) 28 | !128|53,61,50,54,12,81,60,55,43|1,15,3,6,44,46,47,31,33,249,43|MSFT 5.0|Windows XP SP3 29 | 64|53,50,54,57,60,12,55|1,33,3,6,15,28,51,58,59|dhcpcd-5.2.10:Linux-2.6.39.4-00002-g9254cf3:armv7l:cardhu|Android (Linux 2.6.39) 30 | 128|53,50,55|1,28,2,121,15,6,12,40,41,42,26,119,3,121,249,252,42|(null)|Linux 3.6 (Fuduntu) 31 | 64|53,61,50,54,57,60,12,55|1,33,3,6,15,28,51,58,59|dhcpcd-5.2.10:Linux-3.0.21-g6c11713:armv7l:protou|Android (Linux 3.0.21) 32 | 64|53,61,50,57,60,12,55|1,33,3,6,15,28,51,58,59|dhcpcd-5.2.10:Linux-3.0.21-g6c11713:armv7l:protou|Android (Linux 3.0.21) 33 | 128|53,54,50,12,81,55|1,28,2,3,15,6,119,12,44,47,26,121,42,121,249,252,42|(null)|Linux 3.5 (xubuntu) 34 | !64|53,77,61,50,12,60,55|1,15,3,6,44,46,47,31,33,249,43|MSFT 5.0|Windows Server 2003 SP2 35 | 36 | ! confirmed below 37 | 128|53,50,54,60,12,125,55|1,3,6,15,120,114,125|A510_IP|Gigaset A510 VoIP 38 | 128|53,50,12,60,125,55|1,3,6,120,125,114|A580_IP|Gigaset A580 VoIP 39 | 64|53,61,50,57,60,55|1,121,33,3,6,15,28,51,58,59,119|dhcpcd 4.0.15|Android 2.3 (Samsung) 40 | 64|53,50,54,57,60,55|1,121,33,3,6,28,51,58,59|dhcpcd 4.0.15|Android 2.2 (Motorola) 41 | 64|53,50,54,57,60,12,55|1,33,3,6,15,28,51,58,59|dhcpcd-5.2.10|Android 4.1 (Cyanogenmod) 42 | 64|53,50,57,60,12,55|1,33,3,6,15,28,51,58,59|dhcpcd-5.2.10|Android 4.1 (Cyanogenmod) 43 | 255|53,61,50,54,12,55|1,3,42,6,7,15,58,59,66|(null)|Linksys SipuraSPA 44 | 64|53,51,50,60,55,12,81|1,3,6,7,12,15,18,23,26,44,46,51,54,58,59,78,79,81|SAMSUNG Network Printer|Samsung Network Printer 45 | 32|53,54,50,61,55,12|1,121,3,33,6,42|(null)|Mikrotik RouterOS 46 | 255|53,61,12,50,54,55,81|1,3,12,23,6,15,44,47|(null)|Kyocera Network Printer 47 | 128|53,50,54,61,55,57|12,6,15,1,3,28,120|(null)|Symbian S60 48 | 49 | 50 | ! =============== 51 | ! = DHCP INFORM = 52 | ! =============== 53 | !128|53,61,12,60,55|1,15,3,6,44,46,47,31,33,121,249,43,252|MSFT 5.0|Windows 7 54 | 55 | 56 | ! ====================== 57 | ! = GENERIC SIGNATURES = 58 | ! ====================== 59 | 128|*|*|MSFT 5.0|Windows XP/Vista/7 (generic) 60 | -------------------------------------------------------------------------------- /fp.h: -------------------------------------------------------------------------------- 1 | #define FP_MAX_PRINTS 2000 2 | #define FP_LINE_LEN 1020 3 | #define FP_OPTS_LEN 255 4 | #define FP_OPT55_LEN 255 5 | #define FP_VENDOR_LEN 255 6 | #define FP_OSNAME_LEN 255 7 | #define FP_DATABASE "dhcpf.prints" 8 | 9 | struct fprint { 10 | unsigned char ttl; 11 | char *opts, 12 | *opt55, 13 | *vendor, 14 | *osname; 15 | }; 16 | -------------------------------------------------------------------------------- /udpip.h: -------------------------------------------------------------------------------- 1 | struct iphdr { 2 | unsigned char ihl:4, /* header len */ 3 | version:4; 4 | unsigned char tos; /* type of service */ 5 | unsigned short len, /* total length */ 6 | id, /* identification */ 7 | frag_off; /* fragment offset + DF/MF */ 8 | unsigned char ttl, /* time to live */ 9 | protocol; /* protocol */ 10 | unsigned short cksum; /* checksum */ 11 | unsigned int saddr, /* source */ 12 | daddr; /* destination */ 13 | }; 14 | 15 | 16 | struct udphdr { 17 | unsigned short sport, /* source port */ 18 | dport, /* destination port */ 19 | len, /* length of entire datagram (header+data) */ 20 | cksum; /* checksum */ 21 | }; 22 | 23 | --------------------------------------------------------------------------------