diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2017-06-28 19:18:17 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2017-06-28 19:18:17 +0200 |
commit | ba4fbca8a81d765f81aefc74db7f73ec9ded3550 (patch) | |
tree | 3790acaa5ef3c682831aa15428beb313eea5dcb7 /networking/udhcp/d6_dhcpc.c | |
parent | ae2b9f286c985394410aec19b12c1ebecfbe20f6 (diff) | |
download | busybox-w32-ba4fbca8a81d765f81aefc74db7f73ec9ded3550.tar.gz busybox-w32-ba4fbca8a81d765f81aefc74db7f73ec9ded3550.tar.bz2 busybox-w32-ba4fbca8a81d765f81aefc74db7f73ec9ded3550.zip |
udhcpc6: make -O OPT work
Patch is based on work by tiggerswelt.net.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'networking/udhcp/d6_dhcpc.c')
-rw-r--r-- | networking/udhcp/d6_dhcpc.c | 150 |
1 files changed, 109 insertions, 41 deletions
diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c index ef9b9a5f2..f6d3fb98b 100644 --- a/networking/udhcp/d6_dhcpc.c +++ b/networking/udhcp/d6_dhcpc.c | |||
@@ -2,26 +2,49 @@ | |||
2 | /* | 2 | /* |
3 | * DHCPv6 client. | 3 | * DHCPv6 client. |
4 | * | 4 | * |
5 | * 2011-11. | 5 | * WARNING: THIS CODE IS INCOMPLETE. |
6 | * WARNING: THIS CODE IS INCOMPLETE. IT IS NOWHERE NEAR | ||
7 | * TO BE READY FOR PRODUCTION USE. | ||
8 | * | 6 | * |
9 | * Copyright (C) 2011 Denys Vlasenko. | 7 | * Copyright (C) 2011-2017 Denys Vlasenko. |
10 | * | 8 | * |
11 | * Licensed under GPLv2, see file LICENSE in this source tree. | 9 | * Licensed under GPLv2, see file LICENSE in this source tree. |
12 | */ | 10 | */ |
13 | 11 | ||
14 | //config:config UDHCPC6 | 12 | //config:config UDHCPC6 |
15 | //config: bool "udhcpc6 (DHCPv6 client, NOT READY)" | 13 | //config: bool "udhcpc6 (DHCPv6 client, EXPERIMENTAL)" |
16 | //config: default n # not yet ready | 14 | //config: default n # not yet ready |
17 | //config: depends on FEATURE_IPV6 | 15 | //config: depends on FEATURE_IPV6 |
18 | //config: help | 16 | //config: help |
19 | //config: udhcpc6 is a DHCPv6 client | 17 | //config: udhcpc6 is a DHCPv6 client |
18 | //config: | ||
19 | //config:config FEATURE_UDHCPC6_RFC3646 | ||
20 | //config: bool "Support RFC 3646 (DNS server and search list)" | ||
21 | //config: default y | ||
22 | //config: depends on UDHCPC6 | ||
23 | //config: help | ||
24 | //config: List of DNS servers and domain search list can be requested with | ||
25 | //config: "-O dns" and "-O search". If server gives these values, | ||
26 | //config: they will be set in environment variables "dns" and "search". | ||
27 | //config: | ||
28 | //config:config FEATURE_UDHCPC6_RFC4704 | ||
29 | //config: bool "Support RFC 4704 (Client FQDN)" | ||
30 | //config: default y | ||
31 | //config: depends on UDHCPC6 | ||
32 | //config: help | ||
33 | //config: You can request FQDN to be given by server using "-O fqdn". | ||
34 | //config: | ||
35 | //config:config FEATURE_UDHCPC6_RFC4833 | ||
36 | //config: bool "Support RFC 4833 (Timezones)" | ||
37 | //config: default y | ||
38 | //config: depends on UDHCPC6 | ||
39 | //config: help | ||
40 | //config: You can request POSIX timezone with "-O tz" and timezone name | ||
41 | //config: with "-O timezone". | ||
20 | 42 | ||
21 | //applet:IF_UDHCPC6(APPLET(udhcpc6, BB_DIR_USR_BIN, BB_SUID_DROP)) | 43 | //applet:IF_UDHCPC6(APPLET(udhcpc6, BB_DIR_USR_BIN, BB_SUID_DROP)) |
22 | 44 | ||
23 | //kbuild:lib-$(CONFIG_UDHCPC6) += d6_dhcpc.o d6_packet.o d6_socket.o common.o socket.o signalpipe.o | 45 | //kbuild:lib-$(CONFIG_UDHCPC6) += d6_dhcpc.o d6_packet.o d6_socket.o common.o socket.o signalpipe.o |
24 | 46 | //kbuild:lib-$(CONFIG_FEATURE_UDHCPC6_RFC3646) += domain_codec.o | |
47 | //kbuild:lib-$(CONFIG_FEATURE_UDHCPC6_RFC4704) += domain_codec.o | ||
25 | 48 | ||
26 | #include <syslog.h> | 49 | #include <syslog.h> |
27 | /* Override ENABLE_FEATURE_PIDFILE - ifupdown needs our pidfile to always exist */ | 50 | /* Override ENABLE_FEATURE_PIDFILE - ifupdown needs our pidfile to always exist */ |
@@ -37,6 +60,34 @@ | |||
37 | 60 | ||
38 | /* "struct client_config_t client_config" is in bb_common_bufsiz1 */ | 61 | /* "struct client_config_t client_config" is in bb_common_bufsiz1 */ |
39 | 62 | ||
63 | static const struct dhcp_optflag d6_optflags[] = { | ||
64 | #if ENABLE_FEATURE_UDHCPC6_RFC3646 | ||
65 | { OPTION_6RD | OPTION_LIST | OPTION_REQ, D6_OPT_DNS_SERVERS }, | ||
66 | { OPTION_DNS_STRING | OPTION_LIST | OPTION_REQ, D6_OPT_DOMAIN_LIST }, | ||
67 | #endif | ||
68 | #if ENABLE_FEATURE_UDHCPC6_RFC4704 | ||
69 | { OPTION_DNS_STRING, D6_OPT_CLIENT_FQDN }, | ||
70 | #endif | ||
71 | #if ENABLE_FEATURE_UDHCPC6_RFC4833 | ||
72 | { OPTION_STRING, D6_OPT_TZ_POSIX }, | ||
73 | { OPTION_STRING, D6_OPT_TZ_NAME }, | ||
74 | #endif | ||
75 | { 0, 0 } | ||
76 | }; | ||
77 | /* Must match d6_optflags[] order */ | ||
78 | static const char d6_option_strings[] ALIGN1 = | ||
79 | #if ENABLE_FEATURE_UDHCPC6_RFC3646 | ||
80 | "dns" "\0" /* D6_OPT_DNS_SERVERS */ | ||
81 | "search" "\0" /* D6_OPT_DOMAIN_LIST */ | ||
82 | #endif | ||
83 | #if ENABLE_FEATURE_UDHCPC6_RFC4704 | ||
84 | "fqdn" "\0" /* D6_OPT_CLIENT_FQDN */ | ||
85 | #endif | ||
86 | #if ENABLE_FEATURE_UDHCPC6_RFC4833 | ||
87 | "tz" "\0" /* D6_OPT_TZ_POSIX */ | ||
88 | "timezone" "\0" /* D6_OPT_TZ_NAME */ | ||
89 | #endif | ||
90 | "\0"; | ||
40 | 91 | ||
41 | #if ENABLE_LONG_OPTS | 92 | #if ENABLE_LONG_OPTS |
42 | static const char udhcpc6_longopts[] ALIGN1 = | 93 | static const char udhcpc6_longopts[] ALIGN1 = |
@@ -88,14 +139,7 @@ enum { | |||
88 | IF_FEATURE_UDHCP_PORT( OPT_P = 1 << OPTBIT_P,) | 139 | IF_FEATURE_UDHCP_PORT( OPT_P = 1 << OPTBIT_P,) |
89 | }; | 140 | }; |
90 | 141 | ||
91 | static const char opt_req[] = { | 142 | #if ENABLE_FEATURE_UDHCPC6_RFC4704 |
92 | (D6_OPT_ORO >> 8), (D6_OPT_ORO & 0xff), | ||
93 | 0, 6, | ||
94 | (D6_OPT_DNS_SERVERS >> 8), (D6_OPT_DNS_SERVERS & 0xff), | ||
95 | (D6_OPT_DOMAIN_LIST >> 8), (D6_OPT_DOMAIN_LIST & 0xff), | ||
96 | (D6_OPT_CLIENT_FQDN >> 8), (D6_OPT_CLIENT_FQDN & 0xff), | ||
97 | }; | ||
98 | |||
99 | static const char opt_fqdn_req[] = { | 143 | static const char opt_fqdn_req[] = { |
100 | (D6_OPT_CLIENT_FQDN >> 8), (D6_OPT_CLIENT_FQDN & 0xff), | 144 | (D6_OPT_CLIENT_FQDN >> 8), (D6_OPT_CLIENT_FQDN & 0xff), |
101 | 0, 2, /* optlen */ | 145 | 0, 2, /* optlen */ |
@@ -105,6 +149,7 @@ static const char opt_fqdn_req[] = { | |||
105 | /* N=0: server SHOULD perform updates (PTR RR only in our case, since S=0) */ | 149 | /* N=0: server SHOULD perform updates (PTR RR only in our case, since S=0) */ |
106 | 0 /* empty DNS-encoded name */ | 150 | 0 /* empty DNS-encoded name */ |
107 | }; | 151 | }; |
152 | #endif | ||
108 | 153 | ||
109 | /*** Utility functions ***/ | 154 | /*** Utility functions ***/ |
110 | 155 | ||
@@ -152,10 +197,12 @@ static char** new_env(void) | |||
152 | /* put all the parameters into the environment */ | 197 | /* put all the parameters into the environment */ |
153 | static void option_to_env(uint8_t *option, uint8_t *option_end) | 198 | static void option_to_env(uint8_t *option, uint8_t *option_end) |
154 | { | 199 | { |
155 | char *dlist; | 200 | #if ENABLE_FEATURE_UDHCPC6_RFC3646 |
201 | int addrs, option_offset; | ||
202 | #endif | ||
156 | /* "length minus 4" */ | 203 | /* "length minus 4" */ |
157 | int len_m4 = option_end - option - 4; | 204 | int len_m4 = option_end - option - 4; |
158 | int addrs, option_offset; | 205 | |
159 | while (len_m4 >= 0) { | 206 | while (len_m4 >= 0) { |
160 | uint32_t v32; | 207 | uint32_t v32; |
161 | char ipv6str[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")]; | 208 | char ipv6str[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")]; |
@@ -237,7 +284,10 @@ static void option_to_env(uint8_t *option, uint8_t *option_end) | |||
237 | sprint_nip6(ipv6str, option + 4 + 4 + 1); | 284 | sprint_nip6(ipv6str, option + 4 + 4 + 1); |
238 | *new_env() = xasprintf("ipv6prefix=%s/%u", ipv6str, (unsigned)(option[4 + 4])); | 285 | *new_env() = xasprintf("ipv6prefix=%s/%u", ipv6str, (unsigned)(option[4 + 4])); |
239 | break; | 286 | break; |
240 | case D6_OPT_DNS_SERVERS: | 287 | #if ENABLE_FEATURE_UDHCPC6_RFC3646 |
288 | case D6_OPT_DNS_SERVERS: { | ||
289 | char *dlist; | ||
290 | |||
241 | /* Make sure payload-size is a multiple of 16 */ | 291 | /* Make sure payload-size is a multiple of 16 */ |
242 | if ((option[3] & 0x0f) != 0) | 292 | if ((option[3] & 0x0f) != 0) |
243 | break; | 293 | break; |
@@ -259,13 +309,21 @@ static void option_to_env(uint8_t *option, uint8_t *option_end) | |||
259 | } | 309 | } |
260 | 310 | ||
261 | break; | 311 | break; |
262 | case D6_OPT_DOMAIN_LIST: | 312 | } |
313 | case D6_OPT_DOMAIN_LIST: { | ||
314 | char *dlist; | ||
315 | |||
263 | dlist = dname_dec(option + 4, (option[2] << 8) | option[3], "search="); | 316 | dlist = dname_dec(option + 4, (option[2] << 8) | option[3], "search="); |
264 | if (!dlist) | 317 | if (!dlist) |
265 | break; | 318 | break; |
266 | *new_env() = dlist; | 319 | *new_env() = dlist; |
267 | break; | 320 | break; |
268 | case D6_OPT_CLIENT_FQDN: | 321 | } |
322 | #endif | ||
323 | #if ENABLE_FEATURE_UDHCPC6_RFC4704 | ||
324 | case D6_OPT_CLIENT_FQDN: { | ||
325 | char *dlist; | ||
326 | |||
269 | if (option[3] == 0) | 327 | if (option[3] == 0) |
270 | break; | 328 | break; |
271 | /* Work around broken ISC DHCPD6. | 329 | /* Work around broken ISC DHCPD6. |
@@ -284,6 +342,9 @@ static void option_to_env(uint8_t *option, uint8_t *option_end) | |||
284 | break; | 342 | break; |
285 | *new_env() = dlist; | 343 | *new_env() = dlist; |
286 | break; | 344 | break; |
345 | } | ||
346 | #endif | ||
347 | #if ENABLE_FEATURE_UDHCPC6_RFC4833 | ||
287 | /* RFC 4833 Timezones */ | 348 | /* RFC 4833 Timezones */ |
288 | case D6_OPT_TZ_POSIX: | 349 | case D6_OPT_TZ_POSIX: |
289 | *new_env() = xasprintf("tz=%.*s", (int)option[3], (char*)option + 4); | 350 | *new_env() = xasprintf("tz=%.*s", (int)option[3], (char*)option + 4); |
@@ -291,6 +352,7 @@ static void option_to_env(uint8_t *option, uint8_t *option_end) | |||
291 | case D6_OPT_TZ_NAME: | 352 | case D6_OPT_TZ_NAME: |
292 | *new_env() = xasprintf("tz_name=%.*s", (int)option[3], (char*)option + 4); | 353 | *new_env() = xasprintf("tz_name=%.*s", (int)option[3], (char*)option + 4); |
293 | break; | 354 | break; |
355 | #endif | ||
294 | } | 356 | } |
295 | len_m4 -= 4 + option[3]; | 357 | len_m4 -= 4 + option[3]; |
296 | option += 4 + option[3]; | 358 | option += 4 + option[3]; |
@@ -363,17 +425,33 @@ static uint8_t *init_d6_packet(struct d6_packet *packet, char type, uint32_t xid | |||
363 | 425 | ||
364 | static uint8_t *add_d6_client_options(uint8_t *ptr) | 426 | static uint8_t *add_d6_client_options(uint8_t *ptr) |
365 | { | 427 | { |
366 | return ptr; | 428 | uint8_t *start = ptr; |
367 | //uint8_t c; | 429 | unsigned option; |
368 | //int i, end, len; | 430 | |
431 | ptr += 4; | ||
432 | for (option = 1; option < 256; option++) { | ||
433 | if (client_config.opt_mask[option >> 3] & (1 << (option & 7))) { | ||
434 | ptr[0] = (option >> 8); | ||
435 | ptr[1] = option; | ||
436 | ptr += 2; | ||
437 | } | ||
438 | } | ||
369 | 439 | ||
370 | /* Add a "param req" option with the list of options we'd like to have | 440 | if ((ptr - start - 4) != 0) { |
371 | * from stubborn DHCP servers. Pull the data from the struct in common.c. | 441 | start[0] = (D6_OPT_ORO >> 8); |
372 | * No bounds checking because it goes towards the head of the packet. */ | 442 | start[1] = D6_OPT_ORO; |
373 | //... | 443 | start[2] = ((ptr - start - 4) >> 8); |
444 | start[3] = (ptr - start - 4); | ||
445 | } else | ||
446 | ptr = start; | ||
374 | 447 | ||
448 | #if ENABLE_FEATURE_UDHCPC6_RFC4704 | ||
449 | ptr = mempcpy(ptr, &opt_fqdn_req, sizeof(opt_fqdn_req)); | ||
450 | #endif | ||
375 | /* Add -x options if any */ | 451 | /* Add -x options if any */ |
376 | //... | 452 | //... |
453 | |||
454 | return ptr; | ||
377 | } | 455 | } |
378 | 456 | ||
379 | static int d6_mcast_from_client_config_ifindex(struct d6_packet *packet, uint8_t *end) | 457 | static int d6_mcast_from_client_config_ifindex(struct d6_packet *packet, uint8_t *end) |
@@ -497,10 +575,6 @@ static NOINLINE int send_d6_discover(uint32_t xid, struct in6_addr *requested_ip | |||
497 | } | 575 | } |
498 | opt_ptr = mempcpy(opt_ptr, client6_data.ia_na, len); | 576 | opt_ptr = mempcpy(opt_ptr, client6_data.ia_na, len); |
499 | 577 | ||
500 | /* Request additional options */ | ||
501 | opt_ptr = mempcpy(opt_ptr, &opt_req, sizeof(opt_req)); | ||
502 | opt_ptr = mempcpy(opt_ptr, &opt_fqdn_req, sizeof(opt_fqdn_req)); | ||
503 | |||
504 | /* Add options: | 578 | /* Add options: |
505 | * "param req" option according to -O, options specified with -x | 579 | * "param req" option according to -O, options specified with -x |
506 | */ | 580 | */ |
@@ -554,10 +628,6 @@ static NOINLINE int send_d6_select(uint32_t xid) | |||
554 | /* IA NA (contains requested IP) */ | 628 | /* IA NA (contains requested IP) */ |
555 | opt_ptr = mempcpy(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2); | 629 | opt_ptr = mempcpy(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2); |
556 | 630 | ||
557 | /* Request additional options */ | ||
558 | opt_ptr = mempcpy(opt_ptr, &opt_req, sizeof(opt_req)); | ||
559 | opt_ptr = mempcpy(opt_ptr, &opt_fqdn_req, sizeof(opt_fqdn_req)); | ||
560 | |||
561 | /* Add options: | 631 | /* Add options: |
562 | * "param req" option according to -O, options specified with -x | 632 | * "param req" option according to -O, options specified with -x |
563 | */ | 633 | */ |
@@ -1063,20 +1133,18 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) | |||
1063 | char *optstr = llist_pop(&list_O); | 1133 | char *optstr = llist_pop(&list_O); |
1064 | unsigned n = bb_strtou(optstr, NULL, 0); | 1134 | unsigned n = bb_strtou(optstr, NULL, 0); |
1065 | if (errno || n > 254) { | 1135 | if (errno || n > 254) { |
1066 | n = udhcp_option_idx(optstr); | 1136 | n = udhcp_option_idx(optstr, d6_option_strings); |
1067 | n = dhcp_optflags[n].code; | 1137 | n = d6_optflags[n].code; |
1068 | } | 1138 | } |
1069 | client_config.opt_mask[n >> 3] |= 1 << (n & 7); | 1139 | client_config.opt_mask[n >> 3] |= 1 << (n & 7); |
1070 | } | 1140 | } |
1071 | if (!(opt & OPT_o)) { | 1141 | if (!(opt & OPT_o)) { |
1072 | /* | ||
1073 | unsigned i, n; | 1142 | unsigned i, n; |
1074 | for (i = 0; (n = dhcp_optflags[i].code) != 0; i++) { | 1143 | for (i = 0; (n = d6_optflags[i].code) != 0; i++) { |
1075 | if (dhcp_optflags[i].flags & OPTION_REQ) { | 1144 | if (d6_optflags[i].flags & OPTION_REQ) { |
1076 | client_config.opt_mask[n >> 3] |= 1 << (n & 7); | 1145 | client_config.opt_mask[n >> 3] |= 1 << (n & 7); |
1077 | } | 1146 | } |
1078 | } | 1147 | } |
1079 | */ | ||
1080 | } | 1148 | } |
1081 | while (list_x) { | 1149 | while (list_x) { |
1082 | char *optstr = llist_pop(&list_x); | 1150 | char *optstr = llist_pop(&list_x); |
@@ -1085,7 +1153,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) | |||
1085 | *colon = ' '; | 1153 | *colon = ' '; |
1086 | /* now it looks similar to udhcpd's config file line: | 1154 | /* now it looks similar to udhcpd's config file line: |
1087 | * "optname optval", using the common routine: */ | 1155 | * "optname optval", using the common routine: */ |
1088 | udhcp_str2optset(optstr, &client_config.options); | 1156 | udhcp_str2optset(optstr, &client_config.options, d6_optflags, d6_option_strings); |
1089 | if (colon) | 1157 | if (colon) |
1090 | *colon = ':'; /* restore it for NOMMU reexec */ | 1158 | *colon = ':'; /* restore it for NOMMU reexec */ |
1091 | } | 1159 | } |