aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Lewis <martin.lewis.x84@gmail.com>2020-06-23 15:25:09 -0500
committerDenys Vlasenko <vda.linux@googlemail.com>2020-06-29 15:26:09 +0200
commit1f86ecb72974d0b284d73e7fea5acdab104af5f8 (patch)
treee4cc4430914512f88b2c118d555639748bfece01
parentacdc8eed89399a9fa5e7478ee40b928c65e3ab4e (diff)
downloadbusybox-w32-1f86ecb72974d0b284d73e7fea5acdab104af5f8.tar.gz
busybox-w32-1f86ecb72974d0b284d73e7fea5acdab104af5f8.tar.bz2
busybox-w32-1f86ecb72974d0b284d73e7fea5acdab104af5f8.zip
udhcpc: fix a TODO in fill_envp using option scanner
fill_envp now iterates over the packet only once instead of a few hundred times using the new option scanner. function old new delta udhcp_scan_options - 189 +189 putenvp - 46 +46 init_scan_state - 22 +22 udhcp_get_option 227 104 -123 udhcp_run_script 835 601 -234 ------------------------------------------------------------------------------ (add/remove: 3/0 grow/shrink: 0/2 up/down: 257/-357) Total: -100 bytes Signed-off-by: Martin Lewis <martin.lewis.x84@gmail.com> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--networking/udhcp/dhcpc.c198
1 files changed, 84 insertions, 114 deletions
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c
index 102178a4f..50dfead63 100644
--- a/networking/udhcp/dhcpc.c
+++ b/networking/udhcp/dhcpc.c
@@ -386,59 +386,78 @@ static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_
386 return ret; 386 return ret;
387} 387}
388 388
389/* put all the parameters into the environment */ 389static void putenvp(llist_t **envp, char *new_opt)
390static char **fill_envp(struct dhcp_packet *packet) 390{
391 putenv(new_opt);
392 log2(" %s", new_opt);
393 llist_add_to(envp, new_opt);
394}
395
396static const char* get_optname(uint8_t code, const struct dhcp_optflag **dh)
391{ 397{
392 int envc; 398 /* Find the option:
393 int i; 399 * dhcp_optflags is sorted so we stop searching when dh->code >= code, which is faster
394 char **envp, **curr; 400 * than iterating over the entire array.
395 const char *opt_name; 401 * Options which don't have a match in dhcp_option_strings[], e.g DHCP_REQUESTED_IP,
396 uint8_t *temp; 402 * are located after the sorted array, so these entries will never be reached
397 uint8_t overload = 0; 403 * and they'll count as unknown options.
398
399#define BITMAP unsigned
400#define BBITS (sizeof(BITMAP) * 8)
401#define BMASK(i) (1 << (i & (sizeof(BITMAP) * 8 - 1)))
402#define FOUND_OPTS(i) (found_opts[(unsigned)i / BBITS])
403 BITMAP found_opts[256 / BBITS];
404
405 memset(found_opts, 0, sizeof(found_opts));
406
407 /* We need 7 elements for:
408 * "interface=IFACE"
409 * "ip=N.N.N.N" from packet->yiaddr
410 * "giaddr=IP" from packet->gateway_nip (unless 0)
411 * "siaddr=IP" from packet->siaddr_nip (unless 0)
412 * "boot_file=FILE" from packet->file (unless overloaded)
413 * "sname=SERVER_HOSTNAME" from packet->sname (unless overloaded)
414 * terminating NULL
415 */ 404 */
416 envc = 7; 405 for (*dh = dhcp_optflags; (*dh)->code && (*dh)->code < code; (*dh)++)
417 /* +1 element for each option, +2 for subnet option: */ 406 continue;
418 if (packet) { 407
419 /* note: do not search for "pad" (0) and "end" (255) options */ 408 if ((*dh)->code == code)
420//TODO: change logic to scan packet _once_ 409 return nth_string(dhcp_option_strings, (*dh - dhcp_optflags));
421 for (i = 1; i < 255; i++) {
422 temp = udhcp_get_option(packet, i);
423 if (temp) {
424 if (i == DHCP_OPTION_OVERLOAD)
425 overload |= *temp;
426 else if (i == DHCP_SUBNET)
427 envc++; /* for $mask */
428 envc++;
429 /*if (i != DHCP_MESSAGE_TYPE)*/
430 FOUND_OPTS(i) |= BMASK(i);
431 }
432 }
433 }
434 curr = envp = xzalloc(sizeof(envp[0]) * envc);
435 410
436 *curr = xasprintf("interface=%s", client_data.interface); 411 return NULL;
437 putenv(*curr++); 412}
413
414/* put all the parameters into the environment */
415static llist_t *fill_envp(struct dhcp_packet *packet)
416{
417 uint8_t *optptr;
418 struct dhcp_scan_state scan_state;
419 char *new_opt;
420 llist_t *envp = NULL;
421
422 putenvp(&envp, xasprintf("interface=%s", client_data.interface));
438 423
439 if (!packet) 424 if (!packet)
440 return envp; 425 return envp;
441 426
427 init_scan_state(packet, &scan_state);
428
429 /* Iterate over the packet options.
430 * Handle each option based on whether it's an unknown / known option.
431 * There may be (although unlikely) duplicate options. For now, only the last
432 * appearing option will be stored in the environment, and all duplicates
433 * are freed properly.
434 * Long options may be implemented in the future (see RFC 3396) if needed.
435 */
436 while ((optptr = udhcp_scan_options(packet, &scan_state)) != NULL) {
437 const struct dhcp_optflag *dh;
438 const char *opt_name;
439 uint8_t code = optptr[OPT_CODE];
440 uint8_t len = optptr[OPT_LEN];
441 uint8_t *data = optptr + OPT_DATA;
442
443 opt_name = get_optname(code, &dh);
444 if (opt_name) {
445 new_opt = xmalloc_optname_optval(data, dh, opt_name);
446 if (code == DHCP_SUBNET && len == 4) {
447 uint32_t subnet;
448 putenvp(&envp, new_opt);
449 move_from_unaligned32(subnet, data);
450 new_opt = xasprintf("mask=%u", mton(subnet));
451 }
452 } else {
453 unsigned ofs;
454 new_opt = xmalloc(sizeof("optNNN=") + 1 + len*2);
455 ofs = sprintf(new_opt, "opt%u=", code);
456 bin2hex(new_opt + ofs, (char *)data, len)[0] = '\0';
457 }
458 putenvp(&envp, new_opt);
459 }
460
442 /* Export BOOTP fields. Fields we don't (yet?) export: 461 /* Export BOOTP fields. Fields we don't (yet?) export:
443 * uint8_t op; // always BOOTREPLY 462 * uint8_t op; // always BOOTREPLY
444 * uint8_t htype; // hardware address type. 1 = 10mb ethernet 463 * uint8_t htype; // hardware address type. 1 = 10mb ethernet
@@ -452,77 +471,31 @@ static char **fill_envp(struct dhcp_packet *packet)
452 * uint8_t chaddr[16]; // link-layer client hardware address (MAC) 471 * uint8_t chaddr[16]; // link-layer client hardware address (MAC)
453 */ 472 */
454 /* Most important one: yiaddr as $ip */ 473 /* Most important one: yiaddr as $ip */
455 *curr = xmalloc(sizeof("ip=255.255.255.255")); 474 new_opt = xmalloc(sizeof("ip=255.255.255.255"));
456 sprint_nip(*curr, "ip=", (uint8_t *) &packet->yiaddr); 475 sprint_nip(new_opt, "ip=", (uint8_t *) &packet->yiaddr);
457 putenv(*curr++); 476 putenvp(&envp, new_opt);
477
458 if (packet->siaddr_nip) { 478 if (packet->siaddr_nip) {
459 /* IP address of next server to use in bootstrap */ 479 /* IP address of next server to use in bootstrap */
460 *curr = xmalloc(sizeof("siaddr=255.255.255.255")); 480 new_opt = xmalloc(sizeof("siaddr=255.255.255.255"));
461 sprint_nip(*curr, "siaddr=", (uint8_t *) &packet->siaddr_nip); 481 sprint_nip(new_opt, "siaddr=", (uint8_t *) &packet->siaddr_nip);
462 putenv(*curr++); 482 putenvp(&envp, new_opt);
463 } 483 }
464 if (packet->gateway_nip) { 484 if (packet->gateway_nip) {
465 /* IP address of DHCP relay agent */ 485 /* IP address of DHCP relay agent */
466 *curr = xmalloc(sizeof("giaddr=255.255.255.255")); 486 new_opt = xmalloc(sizeof("giaddr=255.255.255.255"));
467 sprint_nip(*curr, "giaddr=", (uint8_t *) &packet->gateway_nip); 487 sprint_nip(new_opt, "giaddr=", (uint8_t *) &packet->gateway_nip);
468 putenv(*curr++); 488 putenvp(&envp, new_opt);
469 } 489 }
470 if (!(overload & FILE_FIELD) && packet->file[0]) { 490 if (!(scan_state.overload & FILE_FIELD) && packet->file[0]) {
471 /* watch out for invalid packets */ 491 /* watch out for invalid packets */
472 *curr = xasprintf("boot_file=%."DHCP_PKT_FILE_LEN_STR"s", packet->file); 492 new_opt = xasprintf("boot_file=%."DHCP_PKT_FILE_LEN_STR"s", packet->file);
473 putenv(*curr++); 493 putenvp(&envp, new_opt);
474 } 494 }
475 if (!(overload & SNAME_FIELD) && packet->sname[0]) { 495 if (!(scan_state.overload & SNAME_FIELD) && packet->sname[0]) {
476 /* watch out for invalid packets */ 496 /* watch out for invalid packets */
477 *curr = xasprintf("sname=%."DHCP_PKT_SNAME_LEN_STR"s", packet->sname); 497 new_opt = xasprintf("sname=%."DHCP_PKT_SNAME_LEN_STR"s", packet->sname);
478 putenv(*curr++); 498 putenvp(&envp, new_opt);
479 }
480
481 /* Export known DHCP options */
482 opt_name = dhcp_option_strings;
483 i = 0;
484 while (*opt_name) {
485 uint8_t code = dhcp_optflags[i].code;
486 BITMAP *found_ptr = &FOUND_OPTS(code);
487 BITMAP found_mask = BMASK(code);
488 if (!(*found_ptr & found_mask))
489 goto next;
490 *found_ptr &= ~found_mask; /* leave only unknown options */
491 temp = udhcp_get_option(packet, code);
492 *curr = xmalloc_optname_optval(temp, &dhcp_optflags[i], opt_name);
493 putenv(*curr++);
494 if (code == DHCP_SUBNET && temp[-OPT_DATA + OPT_LEN] == 4) {
495 /* Subnet option: make things like "$ip/$mask" possible */
496 uint32_t subnet;
497 move_from_unaligned32(subnet, temp);
498 *curr = xasprintf("mask=%u", mton(subnet));
499 putenv(*curr++);
500 }
501 next:
502 opt_name += strlen(opt_name) + 1;
503 i++;
504 }
505 /* Export unknown options */
506 for (i = 0; i < 256;) {
507 BITMAP bitmap = FOUND_OPTS(i);
508 if (!bitmap) {
509 i += BBITS;
510 continue;
511 }
512 if (bitmap & BMASK(i)) {
513 unsigned len, ofs;
514
515 temp = udhcp_get_option(packet, i);
516 /* udhcp_get_option returns ptr to data portion,
517 * need to go back to get len
518 */
519 len = temp[-OPT_DATA + OPT_LEN];
520 *curr = xmalloc(sizeof("optNNN=") + 1 + len*2);
521 ofs = sprintf(*curr, "opt%u=", i);
522 *bin2hex(*curr + ofs, (void*) temp, len) = '\0';
523 putenv(*curr++);
524 }
525 i++;
526 } 499 }
527 500
528 return envp; 501 return envp;
@@ -531,7 +504,7 @@ static char **fill_envp(struct dhcp_packet *packet)
531/* Call a script with a par file and env vars */ 504/* Call a script with a par file and env vars */
532static void udhcp_run_script(struct dhcp_packet *packet, const char *name) 505static void udhcp_run_script(struct dhcp_packet *packet, const char *name)
533{ 506{
534 char **envp, **curr; 507 llist_t *envp;
535 char *argv[3]; 508 char *argv[3];
536 509
537 envp = fill_envp(packet); 510 envp = fill_envp(packet);
@@ -543,11 +516,8 @@ static void udhcp_run_script(struct dhcp_packet *packet, const char *name)
543 argv[2] = NULL; 516 argv[2] = NULL;
544 spawn_and_wait(argv); 517 spawn_and_wait(argv);
545 518
546 for (curr = envp; *curr; curr++) { 519 /* Free all allocated environment variables */
547 log2(" %s", *curr); 520 llist_free(envp, (void (*)(void *))bb_unsetenv_and_free);
548 bb_unsetenv_and_free(*curr);
549 }
550 free(envp);
551} 521}
552 522
553 523