From e44ddcac635f009c8e1b8c473bfecdc31b6e65bc Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Sat, 18 Feb 2012 17:08:50 +0000 Subject: [PATCH] Fix hang at startup when DHCPv6 enabled on a complex network configuration - we have to read all the MAC addresses from netlink, not bail when we find a suitable one. Fix thinko in dhcp_update_configs - thanks to Hartmut for spotting that. Get a sensible address for the default DNS server even when using a relay. --- src/dhcp-common.c | 4 ++-- src/dhcp6.c | 54 +++++++++++++++++++++++++++++------------------ src/dnsmasq.h | 5 +++-- src/netlink.c | 19 +++++++++-------- src/rfc3315.c | 28 ++++++++++++++---------- 5 files changed, 66 insertions(+), 44 deletions(-) diff --git a/src/dhcp-common.c b/src/dhcp-common.c index 6de8e61..8b8bb67 100644 --- a/src/dhcp-common.c +++ b/src/dhcp-common.c @@ -332,9 +332,9 @@ void dhcp_update_configs(struct dhcp_config *configs) } #ifdef HAVE_DHCP6 - if (prot == AF_INET6 && !config_find_by_address6(configs, &crec->addr.addr.addr.addr6, 129, 0)) + if (prot == AF_INET6 && !config_find_by_address6(configs, &crec->addr.addr.addr.addr6, 128, 0)) { - memcpy(config->hwaddr, &crec->addr.addr.addr.addr6, IN6ADDRSZ); + memcpy(&config->addr6, &crec->addr.addr.addr.addr6, IN6ADDRSZ); config->flags |= CONFIG_ADDR6 | CONFIG_ADDR_HOSTS; continue; } diff --git a/src/dhcp6.c b/src/dhcp6.c index ba61ae2..45a9bfd 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -20,6 +20,7 @@ struct iface_param { struct dhcp_context *current; + struct in6_addr fallback; int ind; }; @@ -200,10 +201,14 @@ void dhcp6_packet(time_t now) /* unlinked contexts are marked by context->current == context */ for (context = daemon->dhcp6; context; context = context->next) - context->current = context; - + { + context->current = context; + memset(&context->local6, 0, IN6ADDRSZ); + } + parm.current = NULL; parm.ind = if_index; + memset(&parm.fallback, 0, IN6ADDRSZ); if (!iface_enumerate(AF_INET6, &parm, complete_context6)) return; @@ -211,7 +216,7 @@ void dhcp6_packet(time_t now) lease_prune(NULL, now); /* lose any expired leases */ msg.msg_iov = &daemon->dhcp_packet; - sz = dhcp6_reply(parm.current, if_index, ifr.ifr_name, sz, IN6_IS_ADDR_MULTICAST(&from.in6.sin6_addr), now); + sz = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback, sz, IN6_IS_ADDR_MULTICAST(&from.in6.sin6_addr), now); lease_update_file(now); lease_update_dns(); @@ -229,23 +234,32 @@ static int complete_context6(struct in6_addr *local, int prefix, (void)scope; /* warning */ (void)dad; - - for (context = daemon->dhcp6; context; context = context->next) + + if (if_index == param->ind && + !IN6_IS_ADDR_LOOPBACK(local) && + !IN6_IS_ADDR_LINKLOCAL(local) && + !IN6_IS_ADDR_MULTICAST(local)) { - if (prefix == context->prefix && - !IN6_IS_ADDR_LOOPBACK(local) && - !IN6_IS_ADDR_LINKLOCAL(local) && - !IN6_IS_ADDR_MULTICAST(local) && - is_same_net6(local, &context->start6, prefix) && - is_same_net6(local, &context->end6, prefix)) - { - /* link it onto the current chain if we've not seen it before */ - if (if_index == param->ind && context->current == context) - { - context->current = param->current; - param->current = context; - context->local6 = *local; - } + /* Determine a globally address on the arrival interface, even + if we have no matching dhcp-context, because we're only + allocating on remote subnets via relays. This + is used as a default for the DNS server option. */ + memcpy(¶m->fallback, &local, IN6ADDRSZ); + + for (context = daemon->dhcp6; context; context = context->next) + { + if (prefix == context->prefix && + is_same_net6(local, &context->start6, prefix) && + is_same_net6(local, &context->end6, prefix)) + { + /* link it onto the current chain if we've not seen it before */ + if (context->current == context) + { + context->current = param->current; + param->current = context; + context->local6 = *local; + } + } } } return 1; @@ -305,7 +319,7 @@ int address6_allocate(struct dhcp_context *context, unsigned char *clid, int cl do { /* eliminate addresses in use by the server. */ for (d = context; d; d = d->current) - if (addr == addr6part(&d->router6)) + if (addr == addr6part(&d->local6)) break; if (!d && diff --git a/src/dnsmasq.h b/src/dnsmasq.h index b175092..575d9a7 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -603,7 +603,7 @@ struct dhcp_context { struct in_addr start, end; /* range of available addresses */ #ifdef HAVE_DHCP6 struct in6_addr start6, end6; /* range of available addresses */ - struct in6_addr local6, router6; + struct in6_addr local6; int prefix; #endif int flags; @@ -1036,7 +1036,8 @@ void make_duid(time_t now); /* rfc3315.c */ #ifdef HAVE_DHCP6 -size_t dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name, size_t sz, int is_multicast, time_t now); +size_t dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name, + struct in6_addr *fallback, size_t sz, int is_multicast, time_t now); #endif /* dhcp-common.c */ diff --git a/src/netlink.c b/src/netlink.c index f4e9402..a7d8fb2 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -136,6 +136,7 @@ int iface_enumerate(int family, void *parm, int (*callback)()) struct nlmsghdr *h; ssize_t len; static unsigned int seq = 0; + int callback_ok = 1; struct { struct nlmsghdr nlh; @@ -186,7 +187,7 @@ int iface_enumerate(int family, void *parm, int (*callback)()) else if (h->nlmsg_type == NLMSG_ERROR) nl_err(h); else if (h->nlmsg_type == NLMSG_DONE) - return 1; + return callback_ok; else if (h->nlmsg_type == RTM_NEWADDR && family != AF_UNSPEC && family != AF_LOCAL) { struct ifaddrmsg *ifa = NLMSG_DATA(h); @@ -213,9 +214,9 @@ int iface_enumerate(int family, void *parm, int (*callback)()) rta = RTA_NEXT(rta, len1); } - if (addr.s_addr) + if (addr.s_addr && callback_ok) if (!((*callback)(addr, ifa->ifa_index, netmask, broadcast, parm))) - return 0; + callback_ok = 0; } #ifdef HAVE_IPV6 else if (ifa->ifa_family == AF_INET6) @@ -229,10 +230,10 @@ int iface_enumerate(int family, void *parm, int (*callback)()) rta = RTA_NEXT(rta, len1); } - if (addrp) + if (addrp && callback_ok) if (!((*callback)(addrp, (int)(ifa->ifa_prefixlen), (int)(ifa->ifa_scope), (int)(ifa->ifa_index), (int)(ifa->ifa_flags & IFA_F_TENTATIVE), parm))) - return 0; + callback_ok = 0; } #endif } @@ -258,9 +259,9 @@ int iface_enumerate(int family, void *parm, int (*callback)()) rta = RTA_NEXT(rta, len1); } - if (inaddr && mac) + if (inaddr && mac && callback_ok) if (!((*callback)(neigh->ndm_family, inaddr, mac, maclen, parm))) - return 0; + callback_ok = 0; } #ifdef HAVE_DHCP6 else if (h->nlmsg_type == RTM_NEWLINK && family == AF_LOCAL) @@ -282,9 +283,9 @@ int iface_enumerate(int family, void *parm, int (*callback)()) rta = RTA_NEXT(rta, len1); } - if (mac && !((link->ifi_flags & (IFF_LOOPBACK | IFF_POINTOPOINT))) && + if (mac && callback_ok && !((link->ifi_flags & (IFF_LOOPBACK | IFF_POINTOPOINT))) && !((*callback)((unsigned int)link->ifi_type, mac, maclen, parm))) - return 0; + callback_ok = 0; } #endif } diff --git a/src/rfc3315.c b/src/rfc3315.c index c8f62d0..4a39e2b 100644 --- a/src/rfc3315.c +++ b/src/rfc3315.c @@ -31,9 +31,9 @@ static void put_opt6_long(unsigned int val); static void put_opt6_string(char *s); static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid **relay_tagsp, struct dhcp_context *context, - int interface, char *iface_name, void *inbuff, size_t sz, int is_unicast, time_t now); + int interface, char *iface_name, struct in6_addr *fallback, void *inbuff, size_t sz, int is_unicast, time_t now); static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dhcp_netid *tags, struct dhcp_context *context, - int interface, char *iface_name, void *inbuff, size_t sz, int is_unicast, time_t now); + int interface, char *iface_name, struct in6_addr *fallback, void *inbuff, size_t sz, int is_unicast, time_t now); static void log6_packet(char *type, unsigned char *clid, int clid_len, struct in6_addr *addr, int xid, char *iface, char *string); static void *opt6_find (void *opts, void *end, unsigned int search, unsigned int minsize); @@ -45,7 +45,8 @@ static unsigned int opt6_uint(unsigned char *opt, int offset, int size); #define opt6_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[4+(i)])) -size_t dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name, size_t sz, int is_unicast, time_t now) +size_t dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name, + struct in6_addr *fallback, size_t sz, int is_unicast, time_t now) { struct dhcp_netid *relay_tags = NULL; struct dhcp_vendor *vendor; @@ -56,7 +57,7 @@ size_t dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name outpacket_counter = 0; - if (dhcp6_maybe_relay(NULL, &relay_tags, context, interface, iface_name, daemon->dhcp_packet.iov_base, sz, is_unicast, now)) + if (dhcp6_maybe_relay(NULL, &relay_tags, context, interface, iface_name, fallback, daemon->dhcp_packet.iov_base, sz, is_unicast, now)) return outpacket_counter; return 0; @@ -64,7 +65,7 @@ size_t dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name /* This cost me blood to write, it will probably cost you blood to understand - srk. */ static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid **relay_tagsp, struct dhcp_context *context, - int interface, char *iface_name, void *inbuff, size_t sz, int is_unicast, time_t now) + int interface, char *iface_name, struct in6_addr *fallback, void *inbuff, size_t sz, int is_unicast, time_t now) { void *end = inbuff + sz; void *opts = inbuff + 34; @@ -114,7 +115,7 @@ static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid ** return 0; } - return dhcp6_no_relay(msg_type, link_address, *relay_tagsp, context, interface, iface_name, inbuff, sz, is_unicast, now); + return dhcp6_no_relay(msg_type, link_address, *relay_tagsp, context, interface, iface_name, fallback, inbuff, sz, is_unicast, now); } /* must have at least msg_type+hopcount+link_address+peer_address+minimal size option @@ -159,7 +160,7 @@ static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid ** memcpy(&link_address, inbuff + 2, IN6ADDRSZ); /* Not, zero is_unicast since that is now known to refer to the relayed packet, not the original sent by the client */ - if (!dhcp6_maybe_relay(&link_address, relay_tagsp, context, interface, iface_name, opt6_ptr(opt, 0), opt6_len(opt), 0, now)) + if (!dhcp6_maybe_relay(&link_address, relay_tagsp, context, interface, iface_name, fallback, opt6_ptr(opt, 0), opt6_len(opt), 0, now)) return 0; } else @@ -171,7 +172,7 @@ static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid ** } static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dhcp_netid *tags, struct dhcp_context *context, - int interface, char *iface_name, void *inbuff, size_t sz, int is_unicast, time_t now) + int interface, char *iface_name, struct in6_addr *fallback, void *inbuff, size_t sz, int is_unicast, time_t now) { void *packet_options = inbuff + 4; void *end = inbuff + sz; @@ -685,7 +686,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh /* link temporarily */ for (n = context_tags; n && n->next; n = n->next); - if (l = n) + if ((l = n)) l->next = tags; for (n = run_tag_if(context_tags); n; n = n->next) @@ -1191,10 +1192,15 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh } - if (!done_dns) + if (!done_dns && + (!IN6_IS_ADDR_UNSPECIFIED(&context->local6) || + !IN6_IS_ADDR_UNSPECIFIED(fallback))) { o = new_opt6(OPTION6_DNS_SERVER); - put_opt6(&context->local6, IN6ADDRSZ); + if (IN6_IS_ADDR_UNSPECIFIED(&context->local6)) + put_opt6(fallback, IN6ADDRSZ); + else + put_opt6(&context->local6, IN6ADDRSZ); end_opt6(o); }