aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2017-03-27 22:22:09 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2017-03-27 22:41:59 +0200
commit64d58aa8061c7c848cf0fd37de3ccbb8582d0fc5 (patch)
tree8ff5d55d0e52a594957036268915c50a7c32a0cb
parente09f5e3045fc90547be9ec49c63b633d103cfc45 (diff)
downloadbusybox-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.h5
-rw-r--r--networking/udhcp/d6_dhcpc.c74
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
89struct client6_data_t { 94struct 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
89static 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
97static 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)
140static void option_to_env(uint8_t *option, uint8_t *option_end) 153static 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");