Changeset 3232 in MondoRescue for branches/3.2/mindi-busybox/networking/udhcp/dhcpc.c
- Timestamp:
- Jan 1, 2014, 12:47:38 AM (10 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/3.2/mindi-busybox/networking/udhcp/dhcpc.c
r2725 r3232 26 26 #include "dhcpc.h" 27 27 28 #include <asm/types.h> 29 #if (defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1) || defined(_NEWLIB_VERSION) 30 # include <netpacket/packet.h> 31 # include <net/ethernet.h> 32 #else 33 # include <linux/if_packet.h> 34 # include <linux/if_ether.h> 28 #include <netinet/if_ether.h> 29 #include <linux/filter.h> 30 #include <linux/if_packet.h> 31 32 /* "struct client_config_t client_config" is in bb_common_bufsiz1 */ 33 34 35 #if ENABLE_LONG_OPTS 36 static const char udhcpc_longopts[] ALIGN1 = 37 "clientid-none\0" No_argument "C" 38 "vendorclass\0" Required_argument "V" 39 "hostname\0" Required_argument "H" 40 "fqdn\0" Required_argument "F" 41 "interface\0" Required_argument "i" 42 "now\0" No_argument "n" 43 "pidfile\0" Required_argument "p" 44 "quit\0" No_argument "q" 45 "release\0" No_argument "R" 46 "request\0" Required_argument "r" 47 "script\0" Required_argument "s" 48 "timeout\0" Required_argument "T" 49 "retries\0" Required_argument "t" 50 "tryagain\0" Required_argument "A" 51 "syslog\0" No_argument "S" 52 "request-option\0" Required_argument "O" 53 "no-default-options\0" No_argument "o" 54 "foreground\0" No_argument "f" 55 "background\0" No_argument "b" 56 "broadcast\0" No_argument "B" 57 IF_FEATURE_UDHCPC_ARPING("arping\0" No_argument "a") 58 IF_FEATURE_UDHCP_PORT("client-port\0" Required_argument "P") 59 ; 35 60 #endif 36 #include <linux/filter.h> 37 38 /* struct client_config_t client_config is in bb_common_bufsiz1 */ 61 /* Must match getopt32 option string order */ 62 enum { 63 OPT_C = 1 << 0, 64 OPT_V = 1 << 1, 65 OPT_H = 1 << 2, 66 OPT_h = 1 << 3, 67 OPT_F = 1 << 4, 68 OPT_i = 1 << 5, 69 OPT_n = 1 << 6, 70 OPT_p = 1 << 7, 71 OPT_q = 1 << 8, 72 OPT_R = 1 << 9, 73 OPT_r = 1 << 10, 74 OPT_s = 1 << 11, 75 OPT_T = 1 << 12, 76 OPT_t = 1 << 13, 77 OPT_S = 1 << 14, 78 OPT_A = 1 << 15, 79 OPT_O = 1 << 16, 80 OPT_o = 1 << 17, 81 OPT_x = 1 << 18, 82 OPT_f = 1 << 19, 83 OPT_B = 1 << 20, 84 /* The rest has variable bit positions, need to be clever */ 85 OPTBIT_B = 20, 86 USE_FOR_MMU( OPTBIT_b,) 87 IF_FEATURE_UDHCPC_ARPING(OPTBIT_a,) 88 IF_FEATURE_UDHCP_PORT( OPTBIT_P,) 89 USE_FOR_MMU( OPT_b = 1 << OPTBIT_b,) 90 IF_FEATURE_UDHCPC_ARPING(OPT_a = 1 << OPTBIT_a,) 91 IF_FEATURE_UDHCP_PORT( OPT_P = 1 << OPTBIT_P,) 92 }; 39 93 40 94 … … 46 100 [OPTION_IP_PAIR ] = sizeof("255.255.255.255 ") * 2, 47 101 [OPTION_STATIC_ROUTES ] = sizeof("255.255.255.255/32 255.255.255.255 "), 102 [OPTION_6RD ] = sizeof("32 128 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 255.255.255.255 "), 48 103 [OPTION_STRING ] = 1, 104 [OPTION_STRING_HOST ] = 1, 49 105 #if ENABLE_FEATURE_UDHCP_RFC3397 50 106 [OPTION_DNS_STRING ] = 1, /* unused */ … … 81 137 } 82 138 139 /* Check if a given label represents a valid DNS label 140 * Return pointer to the first character after the label upon success, 141 * NULL otherwise. 142 * See RFC1035, 2.3.1 143 */ 144 /* We don't need to be particularly anal. For example, allowing _, hyphen 145 * at the end, or leading and trailing dots would be ok, since it 146 * can't be used for attacks. (Leading hyphen can be, if someone uses 147 * cmd "$hostname" 148 * in the script: then hostname may be treated as an option) 149 */ 150 static const char *valid_domain_label(const char *label) 151 { 152 unsigned char ch; 153 unsigned pos = 0; 154 155 for (;;) { 156 ch = *label; 157 if ((ch|0x20) < 'a' || (ch|0x20) > 'z') { 158 if (pos == 0) { 159 /* label must begin with letter */ 160 return NULL; 161 } 162 if (ch < '0' || ch > '9') { 163 if (ch == '\0' || ch == '.') 164 return label; 165 /* DNS allows only '-', but we are more permissive */ 166 if (ch != '-' && ch != '_') 167 return NULL; 168 } 169 } 170 label++; 171 pos++; 172 //Do we want this? 173 //if (pos > 63) /* NS_MAXLABEL; labels must be 63 chars or less */ 174 // return NULL; 175 } 176 } 177 178 /* Check if a given name represents a valid DNS name */ 179 /* See RFC1035, 2.3.1 */ 180 static int good_hostname(const char *name) 181 { 182 //const char *start = name; 183 184 for (;;) { 185 name = valid_domain_label(name); 186 if (!name) 187 return 0; 188 if (!name[0]) 189 return 1; 190 //Do we want this? 191 //return ((name - start) < 1025); /* NS_MAXDNAME */ 192 name++; 193 } 194 } 195 83 196 /* Create "opt_name=opt_value" string */ 84 197 static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_optflag *optflag, const char *opt_name) … … 88 201 char *dest, *ret; 89 202 90 /* option points to OPT_DATA, need to go back andget OPT_LEN */91 len = option[ OPT_LEN - OPT_DATA];203 /* option points to OPT_DATA, need to go back to get OPT_LEN */ 204 len = option[-OPT_DATA + OPT_LEN]; 92 205 93 206 type = optflag->flags & OPTION_TYPE_MASK; 94 207 optlen = dhcp_option_lengths[type]; 95 upper_length = len_of_option_as_string[type] * ((unsigned)len / (unsigned)optlen); 208 upper_length = len_of_option_as_string[type] 209 * ((unsigned)(len + optlen - 1) / (unsigned)optlen); 96 210 97 211 dest = ret = xmalloc(upper_length + strlen(opt_name) + 2); … … 99 213 100 214 while (len >= optlen) { 101 unsigned ip_ofs = 0;102 103 215 switch (type) { 216 case OPTION_IP: 104 217 case OPTION_IP_PAIR: 105 218 dest += sprint_nip(dest, "", option); 106 *dest++ = '/'; 107 ip_ofs = 4; 108 /* fall through */ 109 case OPTION_IP: 110 dest += sprint_nip(dest, "", option + ip_ofs); 219 if (type == OPTION_IP) 220 break; 221 dest += sprint_nip(dest, "/", option + 4); 111 222 break; 112 223 // case OPTION_BOOLEAN: … … 130 241 break; 131 242 } 243 /* Note: options which use 'return' instead of 'break' 244 * (for example, OPTION_STRING) skip the code which handles 245 * the case of list of options. 246 */ 132 247 case OPTION_STRING: 248 case OPTION_STRING_HOST: 133 249 memcpy(dest, option, len); 134 250 dest[len] = '\0'; 135 return ret; /* Short circuit this case */ 251 if (type == OPTION_STRING_HOST && !good_hostname(dest)) 252 safe_strncpy(dest, "bad", len); 253 return ret; 136 254 case OPTION_STATIC_ROUTES: { 137 255 /* Option binary format: … … 178 296 return ret; 179 297 } 298 case OPTION_6RD: 299 /* Option binary format (see RFC 5969): 300 * 0 1 2 3 301 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 302 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 303 * | OPTION_6RD | option-length | IPv4MaskLen | 6rdPrefixLen | 304 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 305 * | 6rdPrefix | 306 * ... (16 octets) ... 307 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 308 * ... 6rdBRIPv4Address(es) ... 309 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 310 * We convert it to a string 311 * "IPv4MaskLen 6rdPrefixLen 6rdPrefix 6rdBRIPv4Address..." 312 * 313 * Sanity check: ensure that our length is at least 22 bytes, that 314 * IPv4MaskLen <= 32, 315 * 6rdPrefixLen <= 128, 316 * 6rdPrefixLen + (32 - IPv4MaskLen) <= 128 317 * (2nd condition need no check - it follows from 1st and 3rd). 318 * Else, return envvar with empty value ("optname=") 319 */ 320 if (len >= (1 + 1 + 16 + 4) 321 && option[0] <= 32 322 && (option[1] + 32 - option[0]) <= 128 323 ) { 324 /* IPv4MaskLen */ 325 dest += sprintf(dest, "%u ", *option++); 326 /* 6rdPrefixLen */ 327 dest += sprintf(dest, "%u ", *option++); 328 /* 6rdPrefix */ 329 dest += sprint_nip6(dest, /* "", */ option); 330 option += 16; 331 len -= 1 + 1 + 16 + 4; 332 /* "+ 4" above corresponds to the length of IPv4 addr 333 * we consume in the loop below */ 334 while (1) { 335 /* 6rdBRIPv4Address(es) */ 336 dest += sprint_nip(dest, " ", option); 337 option += 4; 338 len -= 4; /* do we have yet another 4+ bytes? */ 339 if (len < 0) 340 break; /* no */ 341 } 342 } 343 344 return ret; 180 345 #if ENABLE_FEATURE_UDHCP_RFC3397 181 346 case OPTION_DNS_STRING: … … 217 382 #endif 218 383 } /* switch */ 384 385 /* If we are here, try to format any remaining data 386 * in the option as another, similarly-formatted option 387 */ 219 388 option += optlen; 220 389 len -= optlen; … … 222 391 // Should we bail out/warn if we see multi-ip option which is 223 392 // not allowed to be such (for example, DHCP_BROADCAST)? - 224 if (len < = 0/* || !(optflag->flags & OPTION_LIST) */)393 if (len < optlen /* || !(optflag->flags & OPTION_LIST) */) 225 394 break; 226 395 *dest++ = ' '; 227 396 *dest = '\0'; 228 } 397 } /* while */ 398 229 399 return ret; 230 400 } … … 239 409 uint8_t *temp; 240 410 uint8_t overload = 0; 411 412 #define BITMAP unsigned 413 #define BBITS (sizeof(BITMAP) * 8) 414 #define BMASK(i) (1 << (i & (sizeof(BITMAP) * 8 - 1))) 415 #define FOUND_OPTS(i) (found_opts[(unsigned)i / BBITS]) 416 BITMAP found_opts[256 / BBITS]; 417 418 memset(found_opts, 0, sizeof(found_opts)); 241 419 242 420 /* We need 6 elements for: … … 251 429 /* +1 element for each option, +2 for subnet option: */ 252 430 if (packet) { 253 for (i = 0; dhcp_optflags[i].code; i++) { 254 if (udhcp_get_option(packet, dhcp_optflags[i].code)) { 255 if (dhcp_optflags[i].code == DHCP_SUBNET) 256 envc++; /* for mton */ 431 /* note: do not search for "pad" (0) and "end" (255) options */ 432 //TODO: change logic to scan packet _once_ 433 for (i = 1; i < 255; i++) { 434 temp = udhcp_get_option(packet, i); 435 if (temp) { 436 if (i == DHCP_OPTION_OVERLOAD) 437 overload = *temp; 438 else if (i == DHCP_SUBNET) 439 envc++; /* for $mask */ 257 440 envc++; 441 /*if (i != DHCP_MESSAGE_TYPE)*/ 442 FOUND_OPTS(i) |= BMASK(i); 258 443 } 259 444 } 260 temp = udhcp_get_option(packet, DHCP_OPTION_OVERLOAD); 261 if (temp) 262 overload = *temp; 263 } 264 curr = envp = xzalloc(sizeof(char *) * envc); 445 } 446 curr = envp = xzalloc(sizeof(envp[0]) * envc); 265 447 266 448 *curr = xasprintf("interface=%s", client_config.interface); … … 270 452 return envp; 271 453 454 /* Export BOOTP fields. Fields we don't (yet?) export: 455 * uint8_t op; // always BOOTREPLY 456 * uint8_t htype; // hardware address type. 1 = 10mb ethernet 457 * uint8_t hlen; // hardware address length 458 * uint8_t hops; // used by relay agents only 459 * uint32_t xid; 460 * uint16_t secs; // elapsed since client began acquisition/renewal 461 * uint16_t flags; // only one flag so far: bcast. Never set by server 462 * uint32_t ciaddr; // client IP (usually == yiaddr. can it be different 463 * // if during renew server wants to give us differn IP?) 464 * uint32_t gateway_nip; // relay agent IP address 465 * uint8_t chaddr[16]; // link-layer client hardware address (MAC) 466 * TODO: export gateway_nip as $giaddr? 467 */ 468 /* Most important one: yiaddr as $ip */ 272 469 *curr = xmalloc(sizeof("ip=255.255.255.255")); 273 470 sprint_nip(*curr, "ip=", (uint8_t *) &packet->yiaddr); 274 471 putenv(*curr++); 275 276 opt_name = dhcp_option_strings;277 i = 0;278 while (*opt_name) {279 temp = udhcp_get_option(packet, dhcp_optflags[i].code);280 if (!temp)281 goto next;282 *curr = xmalloc_optname_optval(temp, &dhcp_optflags[i], opt_name);283 putenv(*curr++);284 if (dhcp_optflags[i].code == DHCP_SUBNET) {285 /* Subnet option: make things like "$ip/$mask" possible */286 uint32_t subnet;287 move_from_unaligned32(subnet, temp);288 *curr = xasprintf("mask=%d", mton(subnet));289 putenv(*curr++);290 }291 next:292 opt_name += strlen(opt_name) + 1;293 i++;294 }295 472 if (packet->siaddr_nip) { 473 /* IP address of next server to use in bootstrap */ 296 474 *curr = xmalloc(sizeof("siaddr=255.255.255.255")); 297 475 sprint_nip(*curr, "siaddr=", (uint8_t *) &packet->siaddr_nip); … … 308 486 putenv(*curr++); 309 487 } 488 489 /* Export known DHCP options */ 490 opt_name = dhcp_option_strings; 491 i = 0; 492 while (*opt_name) { 493 uint8_t code = dhcp_optflags[i].code; 494 BITMAP *found_ptr = &FOUND_OPTS(code); 495 BITMAP found_mask = BMASK(code); 496 if (!(*found_ptr & found_mask)) 497 goto next; 498 *found_ptr &= ~found_mask; /* leave only unknown options */ 499 temp = udhcp_get_option(packet, code); 500 *curr = xmalloc_optname_optval(temp, &dhcp_optflags[i], opt_name); 501 putenv(*curr++); 502 if (code == DHCP_SUBNET) { 503 /* Subnet option: make things like "$ip/$mask" possible */ 504 uint32_t subnet; 505 move_from_unaligned32(subnet, temp); 506 *curr = xasprintf("mask=%u", mton(subnet)); 507 putenv(*curr++); 508 } 509 next: 510 opt_name += strlen(opt_name) + 1; 511 i++; 512 } 513 /* Export unknown options */ 514 for (i = 0; i < 256;) { 515 BITMAP bitmap = FOUND_OPTS(i); 516 if (!bitmap) { 517 i += BBITS; 518 continue; 519 } 520 if (bitmap & BMASK(i)) { 521 unsigned len, ofs; 522 523 temp = udhcp_get_option(packet, i); 524 /* udhcp_get_option returns ptr to data portion, 525 * need to go back to get len 526 */ 527 len = temp[-OPT_DATA + OPT_LEN]; 528 *curr = xmalloc(sizeof("optNNN=") + 1 + len*2); 529 ofs = sprintf(*curr, "opt%u=", i); 530 *bin2hex(*curr + ofs, (void*) temp, len) = '\0'; 531 putenv(*curr++); 532 } 533 i++; 534 } 535 310 536 return envp; 311 537 } … … 316 542 char **envp, **curr; 317 543 char *argv[3]; 318 319 if (client_config.script == NULL)320 return;321 544 322 545 envp = fill_envp(packet); … … 347 570 static void init_packet(struct dhcp_packet *packet, char type) 348 571 { 572 uint16_t secs; 573 349 574 /* Fill in: op, htype, hlen, cookie fields; message type option: */ 350 575 udhcp_init_header(packet, type); 351 576 352 577 packet->xid = random_xid(); 578 579 client_config.last_secs = monotonic_sec(); 580 if (client_config.first_secs == 0) 581 client_config.first_secs = client_config.last_secs; 582 secs = client_config.last_secs - client_config.first_secs; 583 packet->secs = htons(secs); 353 584 354 585 memcpy(packet->chaddr, client_config.client_mac, 6); … … 359 590 static void add_client_options(struct dhcp_packet *packet) 360 591 { 361 uint8_t c;362 592 int i, end, len; 363 593 … … 369 599 end = udhcp_end_option(packet->options); 370 600 len = 0; 371 for (i = 0; (c = dhcp_optflags[i].code) != 0; i++) { 372 if (( (dhcp_optflags[i].flags & OPTION_REQ) 373 && !client_config.no_default_options 374 ) 375 || (client_config.opt_mask[c >> 3] & (1 << (c & 7))) 376 ) { 377 packet->options[end + OPT_DATA + len] = c; 601 for (i = 1; i < DHCP_END; i++) { 602 if (client_config.opt_mask[i >> 3] & (1 << (i & 7))) { 603 packet->options[end + OPT_DATA + len] = i; 378 604 len++; 379 605 } … … 392 618 udhcp_add_binary_option(packet, client_config.fqdn); 393 619 620 /* Request broadcast replies if we have no IP addr */ 621 if ((option_mask32 & OPT_B) && packet->ciaddr == 0) 622 packet->flags |= htons(BROADCAST_FLAG); 623 394 624 /* Add -x options if any */ 395 625 { … … 404 634 // strncpy((char*)packet->file, client_config.boot_file, sizeof(packet->file) - 1); 405 635 } 636 637 // This will be needed if we remove -V VENDOR_STR in favor of 638 // -x vendor:VENDOR_STR 639 //if (!udhcp_find_option(packet.options, DHCP_VENDOR)) 640 // /* not set, set the default vendor ID */ 641 // ...add (DHCP_VENDOR, "udhcp "BB_VER) opt... 406 642 } 407 643 … … 548 784 /* Broadcast a DHCP decline message */ 549 785 /* NOINLINE: limit stack usage in caller */ 550 static NOINLINE int send_decline( uint32_t xid,uint32_t server, uint32_t requested)786 static NOINLINE int send_decline(/*uint32_t xid,*/ uint32_t server, uint32_t requested) 551 787 { 552 788 struct dhcp_packet packet; … … 557 793 init_packet(&packet, DHCPDECLINE); 558 794 795 #if 0 559 796 /* RFC 2131 says DHCPDECLINE's xid is randomly selected by client, 560 797 * but in case the server is buggy and wants DHCPDECLINE's xid … … 563 800 */ 564 801 packet.xid = xid; 802 #endif 565 803 /* DHCPDECLINE uses "requested ip", not ciaddr, to store offered IP */ 566 804 udhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested); … … 599 837 struct ip_udp_dhcp_packet packet; 600 838 uint16_t check; 601 602 memset(&packet, 0, sizeof(packet)); 603 bytes = safe_read(fd, &packet, sizeof(packet)); 604 if (bytes < 0) { 605 log1("Packet read error, ignoring"); 606 /* NB: possible down interface, etc. Caller should pause. */ 607 return bytes; /* returns -1 */ 839 unsigned char cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))]; 840 struct iovec iov; 841 struct msghdr msg; 842 struct cmsghdr *cmsg; 843 844 /* used to use just safe_read(fd, &packet, sizeof(packet)) 845 * but we need to check for TP_STATUS_CSUMNOTREADY :( 846 */ 847 iov.iov_base = &packet; 848 iov.iov_len = sizeof(packet); 849 memset(&msg, 0, sizeof(msg)); 850 msg.msg_iov = &iov; 851 msg.msg_iovlen = 1; 852 msg.msg_control = cmsgbuf; 853 msg.msg_controllen = sizeof(cmsgbuf); 854 for (;;) { 855 bytes = recvmsg(fd, &msg, 0); 856 if (bytes < 0) { 857 if (errno == EINTR) 858 continue; 859 log1("Packet read error, ignoring"); 860 /* NB: possible down interface, etc. Caller should pause. */ 861 return bytes; /* returns -1 */ 862 } 863 break; 608 864 } 609 865 … … 623 879 624 880 /* make sure its the right packet for us, and that it passes sanity checks */ 625 if (packet.ip.protocol != IPPROTO_UDP || packet.ip.version != IPVERSION 881 if (packet.ip.protocol != IPPROTO_UDP 882 || packet.ip.version != IPVERSION 626 883 || packet.ip.ihl != (sizeof(packet.ip) >> 2) 627 884 || packet.udp.dest != htons(CLIENT_PORT) … … 636 893 check = packet.ip.check; 637 894 packet.ip.check = 0; 638 if (check != udhcp_checksum(&packet.ip, sizeof(packet.ip))) {895 if (check != inet_cksum((uint16_t *)&packet.ip, sizeof(packet.ip))) { 639 896 log1("Bad IP header checksum, ignoring"); 640 897 return -2; 898 } 899 900 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { 901 if (cmsg->cmsg_level == SOL_PACKET 902 && cmsg->cmsg_type == PACKET_AUXDATA 903 ) { 904 /* some VMs don't checksum UDP and TCP data 905 * they send to the same physical machine, 906 * here we detect this case: 907 */ 908 struct tpacket_auxdata *aux = (void *)CMSG_DATA(cmsg); 909 if (aux->tp_status & TP_STATUS_CSUMNOTREADY) 910 goto skip_udp_sum_check; 911 } 641 912 } 642 913 … … 647 918 check = packet.udp.check; 648 919 packet.udp.check = 0; 649 if (check && check != udhcp_checksum(&packet, bytes)) {920 if (check && check != inet_cksum((uint16_t *)&packet, bytes)) { 650 921 log1("Packet with bad UDP checksum received, ignoring"); 651 922 return -2; 652 923 } 653 654 memcpy(dhcp_pkt, &packet.data, bytes - (sizeof(packet.ip) + sizeof(packet.udp))); 655 656 if (dhcp_pkt->cookie != htonl(DHCP_MAGIC)) { 924 skip_udp_sum_check: 925 926 if (packet.data.cookie != htonl(DHCP_MAGIC)) { 657 927 bb_info_msg("Packet with bad magic, ignoring"); 658 928 return -2; 659 929 } 660 log1("Got valid DHCP packet"); 661 udhcp_dump_packet(dhcp_pkt); 662 return bytes - (sizeof(packet.ip) + sizeof(packet.udp)); 930 931 log1("Received a packet"); 932 udhcp_dump_packet(&packet.data); 933 934 bytes -= sizeof(packet.ip) + sizeof(packet.udp); 935 memcpy(dhcp_pkt, &packet.data, bytes); 936 return bytes; 663 937 } 664 938 … … 715 989 * TODO: make conditional? 716 990 */ 717 #define SERVER_AND_CLIENT_PORTS ((67 << 16) + 68)718 991 static const struct sock_filter filter_instr[] = { 719 /* check for udp*/992 /* load 9th byte (protocol) */ 720 993 BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9), 721 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 2, 0), /* L5, L1, is UDP? */ 722 /* ugly check for arp on ethernet-like and IPv4 */ 723 BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 2), /* L1: */ 724 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x08000604, 3, 4), /* L3, L4 */ 725 /* skip IP header */ 726 BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0), /* L5: */ 727 /* check udp source and destination ports */ 728 BPF_STMT(BPF_LD|BPF_W|BPF_IND, 0), 729 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, SERVER_AND_CLIENT_PORTS, 0, 1), /* L3, L4 */ 730 /* returns */ 731 BPF_STMT(BPF_RET|BPF_K, 0x0fffffff ), /* L3: pass */ 732 BPF_STMT(BPF_RET|BPF_K, 0), /* L4: reject */ 994 /* jump to L1 if it is IPPROTO_UDP, else to L4 */ 995 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 0, 6), 996 /* L1: load halfword from offset 6 (flags and frag offset) */ 997 BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 6), 998 /* jump to L4 if any bits in frag offset field are set, else to L2 */ 999 BPF_JUMP(BPF_JMP|BPF_JSET|BPF_K, 0x1fff, 4, 0), 1000 /* L2: skip IP header (load index reg with header len) */ 1001 BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0), 1002 /* load udp destination port from halfword[header_len + 2] */ 1003 BPF_STMT(BPF_LD|BPF_H|BPF_IND, 2), 1004 /* jump to L3 if udp dport is CLIENT_PORT, else to L4 */ 1005 BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 68, 0, 1), 1006 /* L3: accept packet */ 1007 BPF_STMT(BPF_RET|BPF_K, 0xffffffff), 1008 /* L4: discard packet */ 1009 BPF_STMT(BPF_RET|BPF_K, 0), 733 1010 }; 734 1011 static const struct sock_fprog filter_prog = { … … 741 1018 742 1019 fd = xsocket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); 743 log1("Got raw socket fd %d", fd); //log2? 744 745 if (SERVER_PORT == 67 && CLIENT_PORT == 68) { 746 /* Use only if standard ports are in use */ 747 /* Ignoring error (kernel may lack support for this) */ 748 if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog, 749 sizeof(filter_prog)) >= 0) 750 log1("Attached filter to raw socket fd %d", fd); // log? 751 } 1020 log1("Got raw socket fd"); //log2? 752 1021 753 1022 sock.sll_family = AF_PACKET; … … 755 1024 sock.sll_ifindex = ifindex; 756 1025 xbind(fd, (struct sockaddr *) &sock, sizeof(sock)); 1026 1027 if (CLIENT_PORT == 68) { 1028 /* Use only if standard port is in use */ 1029 /* Ignoring error (kernel may lack support for this) */ 1030 if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog, 1031 sizeof(filter_prog)) >= 0) 1032 log1("Attached filter to raw socket fd"); // log? 1033 } 1034 1035 if (setsockopt(fd, SOL_PACKET, PACKET_AUXDATA, 1036 &const_int_1, sizeof(int)) < 0 1037 ) { 1038 if (errno != ENOPROTOOPT) 1039 log1("Can't set PACKET_AUXDATA on raw socket"); 1040 } 1041 757 1042 log1("Created raw socket"); 758 1043 … … 780 1065 } 781 1066 1067 /* Called only on SIGUSR1 */ 782 1068 static void perform_renew(void) 783 1069 { … … 802 1088 } 803 1089 804 static void perform_release(uint32_t requested_ip, uint32_t server_addr)1090 static void perform_release(uint32_t server_addr, uint32_t requested_ip) 805 1091 { 806 1092 char buffer[sizeof("255.255.255.255")]; … … 850 1136 //usage:#endif 851 1137 //usage:#define udhcpc_trivial_usage 852 //usage: "[-fbnq"IF_UDHCP_VERBOSE("v")"oCR ] [-i IFACE] [-r IP] [-s PROG] [-p PIDFILE]\n"853 //usage: " [- H HOSTNAME] [-V VENDOR] [-x OPT:VAL]... [-O OPT]..." IF_FEATURE_UDHCP_PORT(" [-P N]")1138 //usage: "[-fbnq"IF_UDHCP_VERBOSE("v")"oCRB] [-i IFACE] [-r IP] [-s PROG] [-p PIDFILE]\n" 1139 //usage: " [-V VENDOR] [-x OPT:VAL]... [-O OPT]..." IF_FEATURE_UDHCP_PORT(" [-P N]") 854 1140 //usage:#define udhcpc_full_usage "\n" 855 1141 //usage: IF_LONG_OPTS( … … 857 1143 //usage: "\n -p,--pidfile FILE Create pidfile" 858 1144 //usage: "\n -s,--script PROG Run PROG at DHCP events (default "CONFIG_UDHCPC_DEFAULT_SCRIPT")" 1145 //usage: "\n -B,--broadcast Request broadcast replies" 859 1146 //usage: "\n -t,--retries N Send up to N discover packets" 860 1147 //usage: "\n -T,--timeout N Pause between packets (default 3 seconds)" … … 883 1170 //usage: "\n -x 0x3d:0100BEEFC0FFEE - option 61 (client id)" 884 1171 //usage: "\n -F,--fqdn NAME Ask server to update DNS mapping for NAME" 885 //usage: "\n -H,-h,--hostname NAME Send NAME as client hostname (default none)"886 1172 //usage: "\n -V,--vendorclass VENDOR Vendor identifier (default 'udhcp VERSION')" 887 1173 //usage: "\n -C,--clientid-none Don't send MAC as client identifier" … … 894 1180 //usage: "\n -p FILE Create pidfile" 895 1181 //usage: "\n -s PROG Run PROG at DHCP events (default "CONFIG_UDHCPC_DEFAULT_SCRIPT")" 1182 //usage: "\n -B Request broadcast replies" 896 1183 //usage: "\n -t N Send up to N discover packets" 897 1184 //usage: "\n -T N Pause between packets (default 3 seconds)" … … 920 1207 //usage: "\n -x 0x3d:0100BEEFC0FFEE - option 61 (client id)" 921 1208 //usage: "\n -F NAME Ask server to update DNS mapping for NAME" 922 //usage: "\n -H,-h NAME Send NAME as client hostname (default none)"923 1209 //usage: "\n -V VENDOR Vendor identifier (default 'udhcp VERSION')" 924 1210 //usage: "\n -C Don't send MAC as client identifier" … … 927 1213 //usage: ) 928 1214 //usage: ) 1215 //usage: "\nSignals:" 1216 //usage: "\n USR1 Renew lease" 1217 //usage: "\n USR2 Release lease" 1218 929 1219 930 1220 int udhcpc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; … … 942 1232 uint32_t server_addr = server_addr; /* for compiler */ 943 1233 uint32_t requested_ip = 0; 944 uint32_t xid = 0; 945 uint32_t lease_seconds = 0; /* can be given as 32-bit quantity */ 1234 uint32_t xid = xid; /* for compiler */ 946 1235 int packet_num; 947 1236 int timeout; /* must be signed */ … … 950 1239 int max_fd; 951 1240 int retval; 952 struct timeval tv;953 struct dhcp_packet packet;954 1241 fd_set rfds; 955 956 #if ENABLE_LONG_OPTS957 static const char udhcpc_longopts[] ALIGN1 =958 "clientid-none\0" No_argument "C"959 "vendorclass\0" Required_argument "V"960 "hostname\0" Required_argument "H"961 "fqdn\0" Required_argument "F"962 "interface\0" Required_argument "i"963 "now\0" No_argument "n"964 "pidfile\0" Required_argument "p"965 "quit\0" No_argument "q"966 "release\0" No_argument "R"967 "request\0" Required_argument "r"968 "script\0" Required_argument "s"969 "timeout\0" Required_argument "T"970 "version\0" No_argument "v"971 "retries\0" Required_argument "t"972 "tryagain\0" Required_argument "A"973 "syslog\0" No_argument "S"974 "request-option\0" Required_argument "O"975 "no-default-options\0" No_argument "o"976 "foreground\0" No_argument "f"977 "background\0" No_argument "b"978 IF_FEATURE_UDHCPC_ARPING("arping\0" No_argument "a")979 IF_FEATURE_UDHCP_PORT("client-port\0" Required_argument "P")980 ;981 #endif982 enum {983 OPT_C = 1 << 0,984 OPT_V = 1 << 1,985 OPT_H = 1 << 2,986 OPT_h = 1 << 3,987 OPT_F = 1 << 4,988 OPT_i = 1 << 5,989 OPT_n = 1 << 6,990 OPT_p = 1 << 7,991 OPT_q = 1 << 8,992 OPT_R = 1 << 9,993 OPT_r = 1 << 10,994 OPT_s = 1 << 11,995 OPT_T = 1 << 12,996 OPT_t = 1 << 13,997 OPT_S = 1 << 14,998 OPT_A = 1 << 15,999 OPT_O = 1 << 16,1000 OPT_o = 1 << 17,1001 OPT_x = 1 << 18,1002 OPT_f = 1 << 19,1003 /* The rest has variable bit positions, need to be clever */1004 OPTBIT_f = 19,1005 USE_FOR_MMU( OPTBIT_b,)1006 IF_FEATURE_UDHCPC_ARPING(OPTBIT_a,)1007 IF_FEATURE_UDHCP_PORT( OPTBIT_P,)1008 USE_FOR_MMU( OPT_b = 1 << OPTBIT_b,)1009 IF_FEATURE_UDHCPC_ARPING(OPT_a = 1 << OPTBIT_a,)1010 IF_FEATURE_UDHCP_PORT( OPT_P = 1 << OPTBIT_P,)1011 };1012 1242 1013 1243 /* Default options */ … … 1020 1250 /* Parse command line */ 1021 1251 /* O,x: list; -T,-t,-A take numeric param */ 1022 opt_complementary = "O::x::T+:t+:A+" 1023 #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1 1024 ":vv" 1025 #endif 1026 ; 1252 opt_complementary = "O::x::T+:t+:A+" IF_UDHCP_VERBOSE(":vv") ; 1027 1253 IF_LONG_OPTS(applet_long_options = udhcpc_longopts;) 1028 opt = getopt32(argv, "CV:H:h:F:i:np:qRr:s:T:t:SA:O:ox:f "1254 opt = getopt32(argv, "CV:H:h:F:i:np:qRr:s:T:t:SA:O:ox:fB" 1029 1255 USE_FOR_MMU("b") 1030 1256 IF_FEATURE_UDHCPC_ARPING("a") … … 1038 1264 , &list_x 1039 1265 IF_FEATURE_UDHCP_PORT(, &str_P) 1040 #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1 1041 , &dhcp_verbose1042 #endif 1043 );1044 if (opt & (OPT_h|OPT_H))1266 IF_UDHCP_VERBOSE(, &dhcp_verbose) 1267 ); 1268 if (opt & (OPT_h|OPT_H)) { 1269 //msg added 2011-11 1270 bb_error_msg("option -h NAME is deprecated, use -x hostname:NAME"); 1045 1271 client_config.hostname = alloc_dhcp_option(DHCP_HOST_NAME, str_h, 0); 1272 } 1046 1273 if (opt & OPT_F) { 1047 1274 /* FQDN option format: [0x51][len][flags][0][0]<fqdn> */ … … 1067 1294 } 1068 1295 #endif 1069 if (opt & OPT_o)1070 client_config.no_default_options = 1;1071 1296 while (list_O) { 1072 1297 char *optstr = llist_pop(&list_O); 1073 unsigned n = udhcp_option_idx(optstr); 1074 n = dhcp_optflags[n].code; 1298 unsigned n = bb_strtou(optstr, NULL, 0); 1299 if (errno || n > 254) { 1300 n = udhcp_option_idx(optstr); 1301 n = dhcp_optflags[n].code; 1302 } 1075 1303 client_config.opt_mask[n >> 3] |= 1 << (n & 7); 1304 } 1305 if (!(opt & OPT_o)) { 1306 unsigned i, n; 1307 for (i = 0; (n = dhcp_optflags[i].code) != 0; i++) { 1308 if (dhcp_optflags[i].flags & OPTION_REQ) { 1309 client_config.opt_mask[n >> 3] |= 1 << (n & 7); 1310 } 1311 } 1076 1312 } 1077 1313 while (list_x) { … … 1101 1337 memcpy(clientid_mac_ptr, client_config.client_mac, 6); 1102 1338 } 1103 if (str_V[0] != '\0') 1339 if (str_V[0] != '\0') { 1340 // can drop -V, str_V, client_config.vendorclass, 1341 // but need to add "vendor" to the list of recognized 1342 // string opts for this to work; 1343 // and need to tweak add_client_options() too... 1344 // ...so the question is, should we? 1345 //bb_error_msg("option -V VENDOR is deprecated, use -x vendor:VENDOR"); 1104 1346 client_config.vendorclass = alloc_dhcp_option(DHCP_VENDOR, str_V, 0); 1347 } 1348 1105 1349 #if !BB_MMU 1106 1350 /* on NOMMU reexec (i.e., background) early */ … … 1140 1384 */ 1141 1385 for (;;) { 1386 struct timeval tv; 1387 struct dhcp_packet packet; 1142 1388 /* silence "uninitialized!" warning */ 1143 1389 unsigned timestamp_before_wait = timestamp_before_wait; … … 1159 1405 /* If we already timed out, fall through with retval = 0, else... */ 1160 1406 if ((int)tv.tv_sec > 0) { 1407 log1("Waiting on select %u seconds", (int)tv.tv_sec); 1161 1408 timestamp_before_wait = (unsigned)monotonic_sec(); 1162 log1("Waiting on select...");1163 1409 retval = select(max_fd + 1, &rfds, NULL, NULL, &tv); 1164 1410 if (retval < 0) { … … 1187 1433 client_config.client_mac) 1188 1434 ) { 1189 return 1; /* iface is gone? */1435 goto ret0; /* iface is gone? */ 1190 1436 } 1191 1437 if (clientid_mac_ptr) … … 1197 1443 switch (state) { 1198 1444 case INIT_SELECTING: 1199 if ( packet_num < discover_retries) {1445 if (!discover_retries || packet_num < discover_retries) { 1200 1446 if (packet_num == 0) 1201 1447 xid = random_xid(); … … 1226 1472 continue; 1227 1473 case REQUESTING: 1228 if ( packet_num < discover_retries) {1474 if (!discover_retries || packet_num < discover_retries) { 1229 1475 /* send broadcast select packet */ 1230 1476 send_select(xid, server_addr, requested_ip); … … 1243 1489 /* 1/2 lease passed, enter renewing state */ 1244 1490 state = RENEWING; 1491 client_config.first_secs = 0; /* make secs field count from 0 */ 1245 1492 change_listen_mode(LISTEN_KERNEL); 1246 1493 log1("Entering renew state"); … … 1282 1529 udhcp_run_script(NULL, "deconfig"); 1283 1530 state = INIT_SELECTING; 1531 client_config.first_secs = 0; /* make secs field count from 0 */ 1284 1532 /*timeout = 0; - already is */ 1285 1533 packet_num = 0; … … 1298 1546 switch (udhcp_sp_read(&rfds)) { 1299 1547 case SIGUSR1: 1548 client_config.first_secs = 0; /* make secs field count from 0 */ 1549 already_waited_sec = 0; 1300 1550 perform_renew(); 1301 if (state == RENEW_REQUESTED) 1551 if (state == RENEW_REQUESTED) { 1552 /* We might be either on the same network 1553 * (in which case renew might work), 1554 * or we might be on a completely different one 1555 * (in which case renew won't ever succeed). 1556 * For the second case, must make sure timeout 1557 * is not too big, or else we can send 1558 * futile renew requests for hours. 1559 * (Ab)use -A TIMEOUT value (usually 20 sec) 1560 * as a cap on the timeout. 1561 */ 1562 if (timeout > tryagain_timeout) 1563 timeout = tryagain_timeout; 1302 1564 goto case_RENEW_REQUESTED; 1565 } 1303 1566 /* Start things over */ 1304 1567 packet_num = 0; … … 1307 1570 continue; 1308 1571 case SIGUSR2: 1309 perform_release( requested_ip, server_addr);1572 perform_release(server_addr, requested_ip); 1310 1573 timeout = INT_MAX; 1311 1574 continue; 1312 1575 case SIGTERM: 1313 1576 bb_info_msg("Received SIGTERM"); 1314 if (opt & OPT_R) /* release on quit */1315 perform_release(requested_ip, server_addr);1316 1577 goto ret0; 1317 1578 } … … 1366 1627 switch (state) { 1367 1628 case INIT_SELECTING: 1368 /* Must be a DHCPOFFER to one of our xid's*/1629 /* Must be a DHCPOFFER */ 1369 1630 if (*message == DHCPOFFER) { 1370 /* TODO: why we don't just fetch server's IP from IP header? */ 1631 /* What exactly is server's IP? There are several values. 1632 * Example DHCP offer captured with tchdump: 1633 * 1634 * 10.34.25.254:67 > 10.34.25.202:68 // IP header's src 1635 * BOOTP fields: 1636 * Your-IP 10.34.25.202 1637 * Server-IP 10.34.32.125 // "next server" IP 1638 * Gateway-IP 10.34.25.254 // relay's address (if DHCP relays are in use) 1639 * DHCP options: 1640 * DHCP-Message Option 53, length 1: Offer 1641 * Server-ID Option 54, length 4: 10.34.255.7 // "server ID" 1642 * Default-Gateway Option 3, length 4: 10.34.25.254 // router 1643 * 1644 * We think that real server IP (one to use in renew/release) 1645 * is one in Server-ID option. But I am not 100% sure. 1646 * IP header's src and Gateway-IP (same in this example) 1647 * might work too. 1648 * "Next server" and router are definitely wrong ones to use, though... 1649 */ 1371 1650 temp = udhcp_get_option(&packet, DHCP_SERVER_ID); 1372 1651 if (!temp) { … … 1392 1671 case REBINDING: 1393 1672 if (*message == DHCPACK) { 1673 uint32_t lease_seconds; 1674 struct in_addr temp_addr; 1675 1394 1676 temp = udhcp_get_option(&packet, DHCP_LEASE_TIME); 1395 1677 if (!temp) { … … 1400 1682 move_from_unaligned32(lease_seconds, temp); 1401 1683 lease_seconds = ntohl(lease_seconds); 1402 lease_seconds &= 0x0fffffff; /* paranoia: must not be prone to overflows */ 1403 if (lease_seconds < 10) /* and not too small */ 1404 lease_seconds = 10; 1684 /* paranoia: must not be too small and not prone to overflows */ 1685 if (lease_seconds < 0x10) 1686 lease_seconds = 0x10; 1687 if (lease_seconds >= 0x10000000) 1688 lease_seconds = 0x0fffffff; 1405 1689 } 1406 1690 #if ENABLE_FEATURE_UDHCPC_ARPING … … 1423 1707 bb_info_msg("Offered address is in use " 1424 1708 "(got ARP reply), declining"); 1425 send_decline( xid,server_addr, packet.yiaddr);1709 send_decline(/*xid,*/ server_addr, packet.yiaddr); 1426 1710 1427 1711 if (state != REQUESTING) … … 1429 1713 change_listen_mode(LISTEN_RAW); 1430 1714 state = INIT_SELECTING; 1715 client_config.first_secs = 0; /* make secs field count from 0 */ 1431 1716 requested_ip = 0; 1432 1717 timeout = tryagain_timeout; … … 1439 1724 /* enter bound state */ 1440 1725 timeout = lease_seconds / 2; 1441 { 1442 struct in_addr temp_addr; 1443 temp_addr.s_addr = packet.yiaddr; 1444 bb_info_msg("Lease of %s obtained, lease time %u", 1445 inet_ntoa(temp_addr), (unsigned)lease_seconds); 1446 } 1726 temp_addr.s_addr = packet.yiaddr; 1727 bb_info_msg("Lease of %s obtained, lease time %u", 1728 inet_ntoa(temp_addr), (unsigned)lease_seconds); 1447 1729 requested_ip = packet.yiaddr; 1448 1730 udhcp_run_script(&packet, state == REQUESTING ? "bound" : "renew"); … … 1451 1733 change_listen_mode(LISTEN_NONE); 1452 1734 if (opt & OPT_q) { /* quit after lease */ 1453 if (opt & OPT_R) /* release on quit */1454 perform_release(requested_ip, server_addr);1455 1735 goto ret0; 1456 1736 } … … 1464 1744 } 1465 1745 #endif 1746 /* make future renew packets use different xid */ 1747 /* xid = random_xid(); ...but why bother? */ 1466 1748 already_waited_sec = 0; 1467 1749 continue; /* back to main loop */ … … 1476 1758 sleep(3); /* avoid excessive network traffic */ 1477 1759 state = INIT_SELECTING; 1760 client_config.first_secs = 0; /* make secs field count from 0 */ 1478 1761 requested_ip = 0; 1479 1762 timeout = 0; … … 1489 1772 1490 1773 ret0: 1774 if (opt & OPT_R) /* release on quit */ 1775 perform_release(server_addr, requested_ip); 1491 1776 retval = 0; 1492 1777 ret:
Note:
See TracChangeset
for help on using the changeset viewer.