diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2017-03-27 22:22:09 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2017-03-27 22:41:59 +0200 |
commit | 64d58aa8061c7c848cf0fd37de3ccbb8582d0fc5 (patch) | |
tree | 8ff5d55d0e52a594957036268915c50a7c32a0cb | |
parent | e09f5e3045fc90547be9ec49c63b633d103cfc45 (diff) | |
download | busybox-w32-64d58aa8061c7c848cf0fd37de3ccbb8582d0fc5.tar.gz busybox-w32-64d58aa8061c7c848cf0fd37de3ccbb8582d0fc5.tar.bz2 busybox-w32-64d58aa8061c7c848cf0fd37de3ccbb8582d0fc5.zip |
udhcp6: fix problems found running against dnsmasq
Patch is based on work by tiggerswelt.net. They say:
"
But when we tried to use dnsmasq on server-side, udhcpc6 was unable to
forward the acquired address to its setup-script although the
IPv6-Address had been assigned by the server as we could see via
tcpdump. We traced this issue down to a problem on how udhcpc6 parses
DHCPv6-Options: When moving to next option, a pointer-address is
increased and a length buffer is decreased by the length of the option.
The problem is that it is done in this order:
option += 4 + option[3];
len_m4 -= 4 + option[3];
But this has to be switched as the length is decreased by the length of
the *next* option, not the current one. This affected both - internal
checks if a required option is present and the function to expose
options to the environment of the setup-script.
There was also a bug parsing D6_OPT_STATUS_CODE Options, that made
dnsmasq not work as udhcpc6 thought it is receiving a non-positive
status-code (because it did not parse the status-code as required in RFC
3315).
In addition we introduced basic support for RFC 3646 (OPTION_DNS_SERVERS
and OPTION_DOMAIN_LIST) and RFC 4704 (OPTION_CLIENT_FQDN).
"
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | networking/udhcp/d6_common.h | 5 | ||||
-rw-r--r-- | networking/udhcp/d6_dhcpc.c | 74 |
2 files changed, 76 insertions, 3 deletions
diff --git a/networking/udhcp/d6_common.h b/networking/udhcp/d6_common.h index f7cfa4ab8..335a0927a 100644 --- a/networking/udhcp/d6_common.h +++ b/networking/udhcp/d6_common.h | |||
@@ -81,9 +81,14 @@ struct d6_option { | |||
81 | #define D6_OPT_RECONF_MSG 19 | 81 | #define D6_OPT_RECONF_MSG 19 |
82 | #define D6_OPT_RECONF_ACCEPT 20 | 82 | #define D6_OPT_RECONF_ACCEPT 20 |
83 | 83 | ||
84 | #define D6_OPT_DNS_SERVERS 23 | ||
85 | #define D6_OPT_DOMAIN_LIST 24 | ||
86 | |||
84 | #define D6_OPT_IA_PD 25 | 87 | #define D6_OPT_IA_PD 25 |
85 | #define D6_OPT_IAPREFIX 26 | 88 | #define D6_OPT_IAPREFIX 26 |
86 | 89 | ||
90 | #define D6_OPT_CLIENT_FQDN 39 | ||
91 | |||
87 | /*** Other shared functions ***/ | 92 | /*** Other shared functions ***/ |
88 | 93 | ||
89 | struct client6_data_t { | 94 | struct client6_data_t { |
diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c index 95f8939b4..a0cdded11 100644 --- a/networking/udhcp/d6_dhcpc.c +++ b/networking/udhcp/d6_dhcpc.c | |||
@@ -86,6 +86,19 @@ enum { | |||
86 | IF_FEATURE_UDHCP_PORT( OPT_P = 1 << OPTBIT_P,) | 86 | IF_FEATURE_UDHCP_PORT( OPT_P = 1 << OPTBIT_P,) |
87 | }; | 87 | }; |
88 | 88 | ||
89 | static const char opt_req[] = { | ||
90 | (D6_OPT_ORO >> 8), (D6_OPT_ORO & 0xff), | ||
91 | 0, 6, | ||
92 | (D6_OPT_DNS_SERVERS >> 8), (D6_OPT_DNS_SERVERS & 0xff), | ||
93 | (D6_OPT_DOMAIN_LIST >> 8), (D6_OPT_DOMAIN_LIST & 0xff), | ||
94 | (D6_OPT_CLIENT_FQDN >> 8), (D6_OPT_CLIENT_FQDN & 0xff) | ||
95 | }; | ||
96 | |||
97 | static const char opt_fqdn_req[] = { | ||
98 | (D6_OPT_CLIENT_FQDN >> 8), (D6_OPT_CLIENT_FQDN & 0xff), | ||
99 | 0, 2, | ||
100 | 0, 0 | ||
101 | }; | ||
89 | 102 | ||
90 | /*** Utility functions ***/ | 103 | /*** Utility functions ***/ |
91 | 104 | ||
@@ -107,8 +120,8 @@ static void *d6_find_option(uint8_t *option, uint8_t *option_end, unsigned code) | |||
107 | /* Does its code match? */ | 120 | /* Does its code match? */ |
108 | if (option[1] == code) | 121 | if (option[1] == code) |
109 | return option; /* yes! */ | 122 | return option; /* yes! */ |
110 | option += option[3] + 4; | ||
111 | len_m4 -= option[3] + 4; | 123 | len_m4 -= option[3] + 4; |
124 | option += option[3] + 4; | ||
112 | } | 125 | } |
113 | return NULL; | 126 | return NULL; |
114 | } | 127 | } |
@@ -140,7 +153,9 @@ static char** new_env(void) | |||
140 | static void option_to_env(uint8_t *option, uint8_t *option_end) | 153 | static void option_to_env(uint8_t *option, uint8_t *option_end) |
141 | { | 154 | { |
142 | /* "length minus 4" */ | 155 | /* "length minus 4" */ |
156 | char *dlist, *ptr; | ||
143 | int len_m4 = option_end - option - 4; | 157 | int len_m4 = option_end - option - 4; |
158 | int olen, ooff; | ||
144 | while (len_m4 >= 0) { | 159 | while (len_m4 >= 0) { |
145 | uint32_t v32; | 160 | uint32_t v32; |
146 | char ipv6str[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")]; | 161 | char ipv6str[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")]; |
@@ -217,9 +232,54 @@ static void option_to_env(uint8_t *option, uint8_t *option_end) | |||
217 | 232 | ||
218 | sprint_nip6(ipv6str, option + 4 + 4 + 1); | 233 | sprint_nip6(ipv6str, option + 4 + 4 + 1); |
219 | *new_env() = xasprintf("ipv6prefix=%s/%u", ipv6str, (unsigned)(option[4 + 4])); | 234 | *new_env() = xasprintf("ipv6prefix=%s/%u", ipv6str, (unsigned)(option[4 + 4])); |
235 | break; | ||
236 | case D6_OPT_DNS_SERVERS: | ||
237 | olen = ((option[2] << 8) | option[3]) / 16; | ||
238 | dlist = ptr = malloc (4 + olen * 40 - 1); | ||
239 | |||
240 | memcpy (ptr, "dns=", 4); | ||
241 | ptr += 4; | ||
242 | ooff = 0; | ||
243 | |||
244 | while (olen--) { | ||
245 | sprint_nip6(ptr, option + 4 + ooff); | ||
246 | ptr += 39; | ||
247 | ooff += 16; | ||
248 | if (olen) | ||
249 | *ptr++ = ' '; | ||
250 | } | ||
251 | |||
252 | *new_env() = dlist; | ||
253 | |||
254 | break; | ||
255 | case D6_OPT_DOMAIN_LIST: | ||
256 | dlist = dname_dec(option + 4, (option[2] << 8) | option[3], "search="); | ||
257 | if (!dlist) | ||
258 | break; | ||
259 | *new_env() = dlist; | ||
260 | break; | ||
261 | case D6_OPT_CLIENT_FQDN: | ||
262 | // Work around broken ISC DHCPD6 | ||
263 | if (option[4] & 0xf8) { | ||
264 | olen = ((option[2] << 8) | option[3]); | ||
265 | dlist = xmalloc(olen); | ||
266 | //fixme: | ||
267 | //- explain | ||
268 | //- add len error check | ||
269 | //- merge two allocs into one | ||
270 | memcpy(dlist, option + 4, olen); | ||
271 | *new_env() = xasprintf("fqdn=%s", dlist); | ||
272 | free(dlist); | ||
273 | break; | ||
274 | } | ||
275 | dlist = dname_dec(option + 5, ((option[2] << 8) | option[3]) - 1, "fqdn="); | ||
276 | if (!dlist) | ||
277 | break; | ||
278 | *new_env() = dlist; | ||
279 | break; | ||
220 | } | 280 | } |
221 | option += 4 + option[3]; | ||
222 | len_m4 -= 4 + option[3]; | 281 | len_m4 -= 4 + option[3]; |
282 | option += 4 + option[3]; | ||
223 | } | 283 | } |
224 | } | 284 | } |
225 | 285 | ||
@@ -423,6 +483,10 @@ static NOINLINE int send_d6_discover(uint32_t xid, struct in6_addr *requested_ip | |||
423 | } | 483 | } |
424 | opt_ptr = d6_store_blob(opt_ptr, client6_data.ia_na, len); | 484 | opt_ptr = d6_store_blob(opt_ptr, client6_data.ia_na, len); |
425 | 485 | ||
486 | /* Request additional options */ | ||
487 | opt_ptr = d6_store_blob(opt_ptr, &opt_req, sizeof(opt_req)); | ||
488 | opt_ptr = d6_store_blob(opt_ptr, &opt_fqdn_req, sizeof(opt_fqdn_req)); | ||
489 | |||
426 | /* Add options: | 490 | /* Add options: |
427 | * "param req" option according to -O, options specified with -x | 491 | * "param req" option according to -O, options specified with -x |
428 | */ | 492 | */ |
@@ -476,6 +540,10 @@ static NOINLINE int send_d6_select(uint32_t xid) | |||
476 | /* IA NA (contains requested IP) */ | 540 | /* IA NA (contains requested IP) */ |
477 | opt_ptr = d6_store_blob(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2); | 541 | opt_ptr = d6_store_blob(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2); |
478 | 542 | ||
543 | /* Request additional options */ | ||
544 | opt_ptr = d6_store_blob(opt_ptr, &opt_req, sizeof(opt_req)); | ||
545 | opt_ptr = d6_store_blob(opt_ptr, &opt_fqdn_req, sizeof(opt_fqdn_req)); | ||
546 | |||
479 | /* Add options: | 547 | /* Add options: |
480 | * "param req" option according to -O, options specified with -x | 548 | * "param req" option according to -O, options specified with -x |
481 | */ | 549 | */ |
@@ -1306,7 +1374,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv) | |||
1306 | struct d6_option *option, *iaaddr; | 1374 | struct d6_option *option, *iaaddr; |
1307 | type_is_ok: | 1375 | type_is_ok: |
1308 | option = d6_find_option(packet.d6_options, packet_end, D6_OPT_STATUS_CODE); | 1376 | option = d6_find_option(packet.d6_options, packet_end, D6_OPT_STATUS_CODE); |
1309 | if (option && option->data[4] != 0) { | 1377 | if (option && (option->data[0] | option->data[1]) != 0) { |
1310 | /* return to init state */ | 1378 | /* return to init state */ |
1311 | bb_error_msg("received DHCP NAK (%u)", option->data[4]); | 1379 | bb_error_msg("received DHCP NAK (%u)", option->data[4]); |
1312 | d6_run_script(&packet, "nak"); | 1380 | d6_run_script(&packet, "nak"); |