diff options
author | Martin Lewis <martin.lewis.x84@gmail.com> | 2020-06-23 15:25:09 -0500 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2020-06-29 15:26:09 +0200 |
commit | 1f86ecb72974d0b284d73e7fea5acdab104af5f8 (patch) | |
tree | e4cc4430914512f88b2c118d555639748bfece01 | |
parent | acdc8eed89399a9fa5e7478ee40b928c65e3ab4e (diff) | |
download | busybox-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.c | 198 |
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 */ | 389 | static void putenvp(llist_t **envp, char *new_opt) |
390 | static 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 | |||
396 | static 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 */ | ||
415 | static 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 */ |
532 | static void udhcp_run_script(struct dhcp_packet *packet, const char *name) | 505 | static 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 | ||