[git commit] udhcp6: fix problems found running against dnsmasq
Denys Vlasenko
vda.linux at googlemail.com
Mon Mar 27 20:41:59 UTC 2017
commit: https://git.busybox.net/busybox/commit/?id=64d58aa8061c7c848cf0fd37de3ccbb8582d0fc5
branch: https://git.busybox.net/busybox/commit/?id=refs/heads/master
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 at googlemail.com>
---
networking/udhcp/d6_common.h | 5 +++
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 f7cfa4a..335a092 100644
--- a/networking/udhcp/d6_common.h
+++ b/networking/udhcp/d6_common.h
@@ -81,9 +81,14 @@ struct d6_option {
#define D6_OPT_RECONF_MSG 19
#define D6_OPT_RECONF_ACCEPT 20
+#define D6_OPT_DNS_SERVERS 23
+#define D6_OPT_DOMAIN_LIST 24
+
#define D6_OPT_IA_PD 25
#define D6_OPT_IAPREFIX 26
+#define D6_OPT_CLIENT_FQDN 39
+
/*** Other shared functions ***/
struct client6_data_t {
diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c
index 95f8939..a0cdded 100644
--- a/networking/udhcp/d6_dhcpc.c
+++ b/networking/udhcp/d6_dhcpc.c
@@ -86,6 +86,19 @@ enum {
IF_FEATURE_UDHCP_PORT( OPT_P = 1 << OPTBIT_P,)
};
+static const char opt_req[] = {
+ (D6_OPT_ORO >> 8), (D6_OPT_ORO & 0xff),
+ 0, 6,
+ (D6_OPT_DNS_SERVERS >> 8), (D6_OPT_DNS_SERVERS & 0xff),
+ (D6_OPT_DOMAIN_LIST >> 8), (D6_OPT_DOMAIN_LIST & 0xff),
+ (D6_OPT_CLIENT_FQDN >> 8), (D6_OPT_CLIENT_FQDN & 0xff)
+};
+
+static const char opt_fqdn_req[] = {
+ (D6_OPT_CLIENT_FQDN >> 8), (D6_OPT_CLIENT_FQDN & 0xff),
+ 0, 2,
+ 0, 0
+};
/*** Utility functions ***/
@@ -107,8 +120,8 @@ static void *d6_find_option(uint8_t *option, uint8_t *option_end, unsigned code)
/* Does its code match? */
if (option[1] == code)
return option; /* yes! */
- option += option[3] + 4;
len_m4 -= option[3] + 4;
+ option += option[3] + 4;
}
return NULL;
}
@@ -140,7 +153,9 @@ static char** new_env(void)
static void option_to_env(uint8_t *option, uint8_t *option_end)
{
/* "length minus 4" */
+ char *dlist, *ptr;
int len_m4 = option_end - option - 4;
+ int olen, ooff;
while (len_m4 >= 0) {
uint32_t v32;
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)
sprint_nip6(ipv6str, option + 4 + 4 + 1);
*new_env() = xasprintf("ipv6prefix=%s/%u", ipv6str, (unsigned)(option[4 + 4]));
+ break;
+ case D6_OPT_DNS_SERVERS:
+ olen = ((option[2] << 8) | option[3]) / 16;
+ dlist = ptr = malloc (4 + olen * 40 - 1);
+
+ memcpy (ptr, "dns=", 4);
+ ptr += 4;
+ ooff = 0;
+
+ while (olen--) {
+ sprint_nip6(ptr, option + 4 + ooff);
+ ptr += 39;
+ ooff += 16;
+ if (olen)
+ *ptr++ = ' ';
+ }
+
+ *new_env() = dlist;
+
+ break;
+ case D6_OPT_DOMAIN_LIST:
+ dlist = dname_dec(option + 4, (option[2] << 8) | option[3], "search=");
+ if (!dlist)
+ break;
+ *new_env() = dlist;
+ break;
+ case D6_OPT_CLIENT_FQDN:
+ // Work around broken ISC DHCPD6
+ if (option[4] & 0xf8) {
+ olen = ((option[2] << 8) | option[3]);
+ dlist = xmalloc(olen);
+//fixme:
+//- explain
+//- add len error check
+//- merge two allocs into one
+ memcpy(dlist, option + 4, olen);
+ *new_env() = xasprintf("fqdn=%s", dlist);
+ free(dlist);
+ break;
+ }
+ dlist = dname_dec(option + 5, ((option[2] << 8) | option[3]) - 1, "fqdn=");
+ if (!dlist)
+ break;
+ *new_env() = dlist;
+ break;
}
- option += 4 + option[3];
len_m4 -= 4 + option[3];
+ option += 4 + option[3];
}
}
@@ -423,6 +483,10 @@ static NOINLINE int send_d6_discover(uint32_t xid, struct in6_addr *requested_ip
}
opt_ptr = d6_store_blob(opt_ptr, client6_data.ia_na, len);
+ /* Request additional options */
+ opt_ptr = d6_store_blob(opt_ptr, &opt_req, sizeof(opt_req));
+ opt_ptr = d6_store_blob(opt_ptr, &opt_fqdn_req, sizeof(opt_fqdn_req));
+
/* Add options:
* "param req" option according to -O, options specified with -x
*/
@@ -476,6 +540,10 @@ static NOINLINE int send_d6_select(uint32_t xid)
/* IA NA (contains requested IP) */
opt_ptr = d6_store_blob(opt_ptr, client6_data.ia_na, client6_data.ia_na->len + 2+2);
+ /* Request additional options */
+ opt_ptr = d6_store_blob(opt_ptr, &opt_req, sizeof(opt_req));
+ opt_ptr = d6_store_blob(opt_ptr, &opt_fqdn_req, sizeof(opt_fqdn_req));
+
/* Add options:
* "param req" option according to -O, options specified with -x
*/
@@ -1306,7 +1374,7 @@ int udhcpc6_main(int argc UNUSED_PARAM, char **argv)
struct d6_option *option, *iaaddr;
type_is_ok:
option = d6_find_option(packet.d6_options, packet_end, D6_OPT_STATUS_CODE);
- if (option && option->data[4] != 0) {
+ if (option && (option->data[0] | option->data[1]) != 0) {
/* return to init state */
bb_error_msg("received DHCP NAK (%u)", option->data[4]);
d6_run_script(&packet, "nak");
More information about the busybox-cvs
mailing list