├── BSDmakefile ├── Makefile ├── README.md └── dhcp-client.c /BSDmakefile: -------------------------------------------------------------------------------- 1 | SRCS= dhcp-client.c 2 | PROG= dhcp-client 3 | LDADD= -lpcap 4 | WARNS?= 3 5 | MAN= 6 | 7 | .include 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LDLIBS = -lpcap 2 | 3 | dhcp-client: dhcp-client.c 4 | 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | dhcp-client 2 | =========== 3 | 4 | A simple DHCP client written in 500 lines of C code. 5 | 6 | Uses pcap library to read/write packets on the network interface. 7 | This program sends out DHCP DISCOVER packet on the given interface and 8 | waits for DHCP OFFER. 9 | 10 | It works on Linux and FreeBSD. 11 | -------------------------------------------------------------------------------- /dhcp-client.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple DHCP Client 3 | * License : BSD 4 | * Author : Samuel Jacob (samueldotj@gmail.com) 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #ifndef __linux__ 12 | #include 13 | #endif 14 | #include 15 | #include 16 | 17 | #define __FAVOR_BSD 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | typedef u_int32_t ip4_t; 28 | 29 | #define DHCP_CHADDR_LEN 16 30 | #define DHCP_SNAME_LEN 64 31 | #define DHCP_FILE_LEN 128 32 | 33 | /* 34 | * http://www.tcpipguide.com/free/t_DHCPMessageFormat.htm 35 | */ 36 | typedef struct dhcp 37 | { 38 | u_int8_t opcode; 39 | u_int8_t htype; 40 | u_int8_t hlen; 41 | u_int8_t hops; 42 | u_int32_t xid; 43 | u_int16_t secs; 44 | u_int16_t flags; 45 | ip4_t ciaddr; 46 | ip4_t yiaddr; 47 | ip4_t siaddr; 48 | ip4_t giaddr; 49 | u_int8_t chaddr[DHCP_CHADDR_LEN]; 50 | char bp_sname[DHCP_SNAME_LEN]; 51 | char bp_file[DHCP_FILE_LEN]; 52 | uint32_t magic_cookie; 53 | u_int8_t bp_options[0]; 54 | } dhcp_t; 55 | 56 | #define DHCP_BOOTREQUEST 1 57 | #define DHCP_BOOTREPLY 2 58 | 59 | #define DHCP_HARDWARE_TYPE_10_EHTHERNET 1 60 | 61 | #define MESSAGE_TYPE_PAD 0 62 | #define MESSAGE_TYPE_REQ_SUBNET_MASK 1 63 | #define MESSAGE_TYPE_ROUTER 3 64 | #define MESSAGE_TYPE_DNS 6 65 | #define MESSAGE_TYPE_DOMAIN_NAME 15 66 | #define MESSAGE_TYPE_REQ_IP 50 67 | #define MESSAGE_TYPE_DHCP 53 68 | #define MESSAGE_TYPE_PARAMETER_REQ_LIST 55 69 | #define MESSAGE_TYPE_END 255 70 | 71 | #define DHCP_OPTION_DISCOVER 1 72 | #define DHCP_OPTION_OFFER 2 73 | #define DHCP_OPTION_REQUEST 3 74 | #define DHCP_OPTION_PACK 4 75 | 76 | typedef enum { 77 | VERBOSE_LEVEL_NONE, 78 | VERBOSE_LEVEL_ERROR, 79 | VERBOSE_LEVEL_INFO, 80 | VERBOSE_LEVEL_DEBUG, 81 | }verbose_level_t; 82 | 83 | #define PRINT(verbose_level, fmt, args...) \ 84 | do{ \ 85 | if( verbose_level <= program_verbose_level ) { \ 86 | if ( verbose_level == VERBOSE_LEVEL_DEBUG ) { \ 87 | printf("%s:%d:%s::", __FILE__, __LINE__, __FUNCTION__); \ 88 | } \ 89 | printf(fmt, ##args); \ 90 | printf("\n"); \ 91 | } \ 92 | }while(0) 93 | 94 | #define DHCP_SERVER_PORT 67 95 | #define DHCP_CLIENT_PORT 68 96 | 97 | #define DHCP_MAGIC_COOKIE 0x63825363 98 | 99 | verbose_level_t program_verbose_level = VERBOSE_LEVEL_DEBUG; 100 | pcap_t *pcap_handle; 101 | u_int32_t ip; 102 | 103 | /* 104 | * Print the Given ethernet packet in hexa format - Just for debugging 105 | */ 106 | static void 107 | print_packet(const u_int8_t *data, int len) 108 | { 109 | int i; 110 | 111 | for (i = 0; i < len; i++) 112 | { 113 | if (i % 0x10 == 0) 114 | printf("\n %04x :: ", i); 115 | printf("%02x ", data[i]); 116 | } 117 | } 118 | 119 | /* 120 | * Get MAC address of given link(dev_name) 121 | */ 122 | static int 123 | get_mac_address(char *dev_name, u_int8_t *mac) 124 | { 125 | #ifdef __linux__ 126 | struct ifreq s; 127 | int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); 128 | int result; 129 | 130 | strcpy(s.ifr_name, dev_name); 131 | result = ioctl(fd, SIOCGIFHWADDR, &s); 132 | close(fd); 133 | if (result != 0) 134 | return -1; 135 | 136 | memcpy((void *)mac, s.ifr_addr.sa_data, 6); 137 | return 0; 138 | #else 139 | struct ifaddrs *ifap, *p; 140 | 141 | if (getifaddrs(&ifap) != 0) 142 | return -1; 143 | 144 | for (p = ifap; p; p = p->ifa_next) 145 | { 146 | /* Check the device name */ 147 | if ((strcmp(p->ifa_name, dev_name) == 0) && 148 | (p->ifa_addr->sa_family == AF_LINK)) 149 | { 150 | struct sockaddr_dl* sdp; 151 | 152 | sdp = (struct sockaddr_dl*) p->ifa_addr; 153 | memcpy((void *)mac, sdp->sdl_data + sdp->sdl_nlen, 6); 154 | break; 155 | } 156 | } 157 | freeifaddrs(ifap); 158 | #endif 159 | 160 | return 0; 161 | } 162 | 163 | /* 164 | * Return checksum for the given data. 165 | * Copied from FreeBSD 166 | */ 167 | static unsigned short 168 | in_cksum(unsigned short *addr, int len) 169 | { 170 | register int sum = 0; 171 | u_short answer = 0; 172 | register u_short *w = addr; 173 | register int nleft = len; 174 | /* 175 | * Our algorithm is simple, using a 32 bit accumulator (sum), we add 176 | * sequential 16 bit words to it, and at the end, fold back all the 177 | * carry bits from the top 16 bits into the lower 16 bits. 178 | */ 179 | while (nleft > 1) 180 | { 181 | sum += *w++; 182 | nleft -= 2; 183 | } 184 | /* mop up an odd byte, if necessary */ 185 | if (nleft == 1) 186 | { 187 | *(u_char *)(&answer) = *(u_char *) w; 188 | sum += answer; 189 | } 190 | /* add back carry outs from top 16 bits to low 16 bits */ 191 | sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ 192 | sum += (sum >> 16); /* add carry */ 193 | answer = ~sum; /* truncate to 16 bits */ 194 | return (answer); 195 | } 196 | 197 | /* 198 | * This function will be called for any incoming DHCP responses 199 | */ 200 | static void 201 | dhcp_input(dhcp_t *dhcp) 202 | { 203 | if (dhcp->opcode != DHCP_OPTION_OFFER) 204 | return; 205 | 206 | /* Get the IP address given by the server */ 207 | ip = ntohl(dhcp->yiaddr); 208 | 209 | /* We are done - lets break the loop */ 210 | pcap_breakloop(pcap_handle); 211 | } 212 | 213 | /* 214 | * UDP packet handler 215 | */ 216 | static void 217 | udp_input(struct udphdr * udp_packet) 218 | { 219 | /* Check if there is a response from DHCP server by checking the source Port */ 220 | if (ntohs(udp_packet->uh_sport) == DHCP_SERVER_PORT) 221 | dhcp_input((dhcp_t *)((char *)udp_packet + sizeof(struct udphdr))); 222 | } 223 | 224 | /* 225 | * IP Packet handler 226 | */ 227 | static void 228 | ip_input(struct ip * ip_packet) 229 | { 230 | /* Care only about UDP - since DHCP sits over UDP */ 231 | if (ip_packet->ip_p == IPPROTO_UDP) 232 | udp_input((struct udphdr *)((char *)ip_packet + sizeof(struct ip))); 233 | } 234 | 235 | /* 236 | * Ethernet packet handler 237 | */ 238 | static void 239 | ether_input(u_char *args, const struct pcap_pkthdr *header, const u_char *frame) 240 | { 241 | struct ether_header *eframe = (struct ether_header *)frame; 242 | 243 | PRINT(VERBOSE_LEVEL_DEBUG, "Received a frame with length of [%d]", header->len); 244 | 245 | if (program_verbose_level == VERBOSE_LEVEL_DEBUG) 246 | print_packet(frame, header->len); 247 | 248 | if (htons(eframe->ether_type) == ETHERTYPE_IP) 249 | ip_input((struct ip *)(frame + sizeof(struct ether_header))); 250 | } 251 | 252 | /* 253 | * Ethernet output handler - Fills appropriate bytes in ethernet header 254 | */ 255 | static void 256 | ether_output(u_char *frame, u_int8_t *mac, int len) 257 | { 258 | int result; 259 | struct ether_header *eframe = (struct ether_header *)frame; 260 | 261 | memcpy(eframe->ether_shost, mac, ETHER_ADDR_LEN); 262 | memset(eframe->ether_dhost, -1, ETHER_ADDR_LEN); 263 | eframe->ether_type = htons(ETHERTYPE_IP); 264 | 265 | len = len + sizeof(struct ether_header); 266 | 267 | /* Send the packet on wire */ 268 | result = pcap_inject(pcap_handle, frame, len); 269 | PRINT(VERBOSE_LEVEL_DEBUG, "Send %d bytes\n", result); 270 | if (result <= 0) 271 | pcap_perror(pcap_handle, "ERROR:"); 272 | } 273 | 274 | /* 275 | * IP Output handler - Fills appropriate bytes in IP header 276 | */ 277 | static void 278 | ip_output(struct ip *ip_header, int *len) 279 | { 280 | *len += sizeof(struct ip); 281 | 282 | ip_header->ip_hl = 5; 283 | ip_header->ip_v = IPVERSION; 284 | ip_header->ip_tos = 0x10; 285 | ip_header->ip_len = htons(*len); 286 | ip_header->ip_id = htons(0xffff); 287 | ip_header->ip_off = 0; 288 | ip_header->ip_ttl = 16; 289 | ip_header->ip_p = IPPROTO_UDP; 290 | ip_header->ip_sum = 0; 291 | ip_header->ip_src.s_addr = 0; 292 | ip_header->ip_dst.s_addr = 0xFFFFFFFF; 293 | 294 | ip_header->ip_sum = in_cksum((unsigned short *) ip_header, sizeof(struct ip)); 295 | } 296 | 297 | /* 298 | * UDP output - Fills appropriate bytes in UDP header 299 | */ 300 | static void 301 | udp_output(struct udphdr *udp_header, int *len) 302 | { 303 | if (*len & 1) 304 | *len += 1; 305 | *len += sizeof(struct udphdr); 306 | 307 | udp_header->uh_sport = htons(DHCP_CLIENT_PORT); 308 | udp_header->uh_dport = htons(DHCP_SERVER_PORT); 309 | udp_header->uh_ulen = htons(*len); 310 | udp_header->uh_sum = 0; 311 | } 312 | 313 | /* 314 | * DHCP output - Just fills DHCP_BOOTREQUEST 315 | */ 316 | static void 317 | dhcp_output(dhcp_t *dhcp, u_int8_t *mac, int *len) 318 | { 319 | *len += sizeof(dhcp_t); 320 | memset(dhcp, 0, sizeof(dhcp_t)); 321 | 322 | dhcp->opcode = DHCP_BOOTREQUEST; 323 | dhcp->htype = DHCP_HARDWARE_TYPE_10_EHTHERNET; 324 | dhcp->hlen = 6; 325 | memcpy(dhcp->chaddr, mac, DHCP_CHADDR_LEN); 326 | 327 | dhcp->magic_cookie = htonl(DHCP_MAGIC_COOKIE); 328 | } 329 | 330 | /* 331 | * Adds DHCP option to the bytestream 332 | */ 333 | static int 334 | fill_dhcp_option(u_int8_t *packet, u_int8_t code, u_int8_t *data, u_int8_t len) 335 | { 336 | packet[0] = code; 337 | packet[1] = len; 338 | memcpy(&packet[2], data, len); 339 | 340 | return len + (sizeof(u_int8_t) * 2); 341 | } 342 | 343 | /* 344 | * Fill DHCP options 345 | */ 346 | static int 347 | fill_dhcp_discovery_options(dhcp_t *dhcp) 348 | { 349 | int len = 0; 350 | u_int32_t req_ip; 351 | u_int8_t parameter_req_list[] = {MESSAGE_TYPE_REQ_SUBNET_MASK, MESSAGE_TYPE_ROUTER, MESSAGE_TYPE_DNS, MESSAGE_TYPE_DOMAIN_NAME}; 352 | u_int8_t option; 353 | 354 | option = DHCP_OPTION_DISCOVER; 355 | len += fill_dhcp_option(&dhcp->bp_options[len], MESSAGE_TYPE_DHCP, &option, sizeof(option)); 356 | req_ip = htonl(0xc0a8010a); 357 | len += fill_dhcp_option(&dhcp->bp_options[len], MESSAGE_TYPE_REQ_IP, (u_int8_t *)&req_ip, sizeof(req_ip)); 358 | len += fill_dhcp_option(&dhcp->bp_options[len], MESSAGE_TYPE_PARAMETER_REQ_LIST, (u_int8_t *)¶meter_req_list, sizeof(parameter_req_list)); 359 | option = 0; 360 | len += fill_dhcp_option(&dhcp->bp_options[len], MESSAGE_TYPE_END, &option, sizeof(option)); 361 | 362 | return len; 363 | } 364 | 365 | /* 366 | * Send DHCP DISCOVERY packet 367 | */ 368 | static int 369 | dhcp_discovery(u_int8_t *mac) 370 | { 371 | int len = 0; 372 | u_char packet[4096]; 373 | struct udphdr *udp_header; 374 | struct ip *ip_header; 375 | dhcp_t *dhcp; 376 | 377 | PRINT(VERBOSE_LEVEL_INFO, "Sending DHCP_DISCOVERY"); 378 | 379 | ip_header = (struct ip *)(packet + sizeof(struct ether_header)); 380 | udp_header = (struct udphdr *)(((char *)ip_header) + sizeof(struct ip)); 381 | dhcp = (dhcp_t *)(((char *)udp_header) + sizeof(struct udphdr)); 382 | 383 | len = fill_dhcp_discovery_options(dhcp); 384 | dhcp_output(dhcp, mac, &len); 385 | udp_output(udp_header, &len); 386 | ip_output(ip_header, &len); 387 | ether_output(packet, mac, len); 388 | 389 | return 0; 390 | } 391 | 392 | int 393 | main(int argc, char *argv[]) 394 | { 395 | int result; 396 | char errbuf[PCAP_ERRBUF_SIZE]; 397 | char *dev; 398 | u_int8_t mac[6]; 399 | 400 | if (argc < 2 || (strcmp(argv[1], "-h") == 0)) 401 | { 402 | printf("Usage: %s \n", argv[0]); 403 | return 0; 404 | } 405 | dev = argv[1]; 406 | 407 | /* Get the MAC address of the interface */ 408 | result = get_mac_address(dev, mac); 409 | if (result != 0) 410 | { 411 | PRINT(VERBOSE_LEVEL_ERROR, "Unable to get MAC address for %s", dev); 412 | return -1; 413 | } 414 | PRINT(VERBOSE_LEVEL_INFO, "%s MAC : %02X:%02X:%02X:%02X:%02X:%02X", 415 | dev, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 416 | 417 | /* Open the device and get pcap handle for it */ 418 | pcap_handle = pcap_open_live(dev, BUFSIZ, 0, 10, errbuf); 419 | if (pcap_handle == NULL) 420 | { 421 | PRINT(VERBOSE_LEVEL_ERROR, "Couldn't open device %s: %s", dev, errbuf); 422 | return -1; 423 | } 424 | 425 | /* Send DHCP DISCOVERY packet */ 426 | result = dhcp_discovery(mac); 427 | if (result) 428 | { 429 | PRINT(VERBOSE_LEVEL_ERROR, "Couldn't send DHCP DISCOVERY on device %s: %s", dev, errbuf); 430 | goto done; 431 | } 432 | 433 | ip = 0; 434 | PRINT(VERBOSE_LEVEL_INFO, "Waiting for DHCP_OFFER"); 435 | /* Listen till the DHCP OFFER comes */ 436 | pcap_loop(pcap_handle, -1, ether_input, NULL); 437 | printf("Got IP %u.%u.%u.%u\n", ip >> 24, ((ip << 8) >> 24), (ip << 16) >> 24, (ip << 24) >> 24); 438 | 439 | done: 440 | pcap_close(pcap_handle); 441 | 442 | return result; 443 | } 444 | 445 | --------------------------------------------------------------------------------