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.
This commit is contained in:
Simon Kelley
2012-02-18 17:08:50 +00:00
parent 00e9ad5217
commit e44ddcac63
5 changed files with 66 additions and 44 deletions

View File

@@ -332,9 +332,9 @@ void dhcp_update_configs(struct dhcp_config *configs)
} }
#ifdef HAVE_DHCP6 #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; config->flags |= CONFIG_ADDR6 | CONFIG_ADDR_HOSTS;
continue; continue;
} }

View File

@@ -20,6 +20,7 @@
struct iface_param { struct iface_param {
struct dhcp_context *current; struct dhcp_context *current;
struct in6_addr fallback;
int ind; int ind;
}; };
@@ -200,10 +201,14 @@ void dhcp6_packet(time_t now)
/* unlinked contexts are marked by context->current == context */ /* unlinked contexts are marked by context->current == context */
for (context = daemon->dhcp6; context; context = context->next) for (context = daemon->dhcp6; context; context = context->next)
context->current = context; {
context->current = context;
memset(&context->local6, 0, IN6ADDRSZ);
}
parm.current = NULL; parm.current = NULL;
parm.ind = if_index; parm.ind = if_index;
memset(&parm.fallback, 0, IN6ADDRSZ);
if (!iface_enumerate(AF_INET6, &parm, complete_context6)) if (!iface_enumerate(AF_INET6, &parm, complete_context6))
return; return;
@@ -211,7 +216,7 @@ void dhcp6_packet(time_t now)
lease_prune(NULL, now); /* lose any expired leases */ lease_prune(NULL, now); /* lose any expired leases */
msg.msg_iov = &daemon->dhcp_packet; 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_file(now);
lease_update_dns(); lease_update_dns();
@@ -229,23 +234,32 @@ static int complete_context6(struct in6_addr *local, int prefix,
(void)scope; /* warning */ (void)scope; /* warning */
(void)dad; (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 && /* Determine a globally address on the arrival interface, even
!IN6_IS_ADDR_LOOPBACK(local) && if we have no matching dhcp-context, because we're only
!IN6_IS_ADDR_LINKLOCAL(local) && allocating on remote subnets via relays. This
!IN6_IS_ADDR_MULTICAST(local) && is used as a default for the DNS server option. */
is_same_net6(local, &context->start6, prefix) && memcpy(&param->fallback, &local, IN6ADDRSZ);
is_same_net6(local, &context->end6, prefix))
{ for (context = daemon->dhcp6; context; context = context->next)
/* link it onto the current chain if we've not seen it before */ {
if (if_index == param->ind && context->current == context) if (prefix == context->prefix &&
{ is_same_net6(local, &context->start6, prefix) &&
context->current = param->current; is_same_net6(local, &context->end6, prefix))
param->current = context; {
context->local6 = *local; /* 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; return 1;
@@ -305,7 +319,7 @@ int address6_allocate(struct dhcp_context *context, unsigned char *clid, int cl
do { do {
/* eliminate addresses in use by the server. */ /* eliminate addresses in use by the server. */
for (d = context; d; d = d->current) for (d = context; d; d = d->current)
if (addr == addr6part(&d->router6)) if (addr == addr6part(&d->local6))
break; break;
if (!d && if (!d &&

View File

@@ -603,7 +603,7 @@ struct dhcp_context {
struct in_addr start, end; /* range of available addresses */ struct in_addr start, end; /* range of available addresses */
#ifdef HAVE_DHCP6 #ifdef HAVE_DHCP6
struct in6_addr start6, end6; /* range of available addresses */ struct in6_addr start6, end6; /* range of available addresses */
struct in6_addr local6, router6; struct in6_addr local6;
int prefix; int prefix;
#endif #endif
int flags; int flags;
@@ -1036,7 +1036,8 @@ void make_duid(time_t now);
/* rfc3315.c */ /* rfc3315.c */
#ifdef HAVE_DHCP6 #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 #endif
/* dhcp-common.c */ /* dhcp-common.c */

View File

@@ -136,6 +136,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
struct nlmsghdr *h; struct nlmsghdr *h;
ssize_t len; ssize_t len;
static unsigned int seq = 0; static unsigned int seq = 0;
int callback_ok = 1;
struct { struct {
struct nlmsghdr nlh; struct nlmsghdr nlh;
@@ -186,7 +187,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
else if (h->nlmsg_type == NLMSG_ERROR) else if (h->nlmsg_type == NLMSG_ERROR)
nl_err(h); nl_err(h);
else if (h->nlmsg_type == NLMSG_DONE) 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) else if (h->nlmsg_type == RTM_NEWADDR && family != AF_UNSPEC && family != AF_LOCAL)
{ {
struct ifaddrmsg *ifa = NLMSG_DATA(h); struct ifaddrmsg *ifa = NLMSG_DATA(h);
@@ -213,9 +214,9 @@ int iface_enumerate(int family, void *parm, int (*callback)())
rta = RTA_NEXT(rta, len1); rta = RTA_NEXT(rta, len1);
} }
if (addr.s_addr) if (addr.s_addr && callback_ok)
if (!((*callback)(addr, ifa->ifa_index, netmask, broadcast, parm))) if (!((*callback)(addr, ifa->ifa_index, netmask, broadcast, parm)))
return 0; callback_ok = 0;
} }
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
else if (ifa->ifa_family == AF_INET6) 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); rta = RTA_NEXT(rta, len1);
} }
if (addrp) if (addrp && callback_ok)
if (!((*callback)(addrp, (int)(ifa->ifa_prefixlen), (int)(ifa->ifa_scope), if (!((*callback)(addrp, (int)(ifa->ifa_prefixlen), (int)(ifa->ifa_scope),
(int)(ifa->ifa_index), (int)(ifa->ifa_flags & IFA_F_TENTATIVE), parm))) (int)(ifa->ifa_index), (int)(ifa->ifa_flags & IFA_F_TENTATIVE), parm)))
return 0; callback_ok = 0;
} }
#endif #endif
} }
@@ -258,9 +259,9 @@ int iface_enumerate(int family, void *parm, int (*callback)())
rta = RTA_NEXT(rta, len1); rta = RTA_NEXT(rta, len1);
} }
if (inaddr && mac) if (inaddr && mac && callback_ok)
if (!((*callback)(neigh->ndm_family, inaddr, mac, maclen, parm))) if (!((*callback)(neigh->ndm_family, inaddr, mac, maclen, parm)))
return 0; callback_ok = 0;
} }
#ifdef HAVE_DHCP6 #ifdef HAVE_DHCP6
else if (h->nlmsg_type == RTM_NEWLINK && family == AF_LOCAL) 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); 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))) !((*callback)((unsigned int)link->ifi_type, mac, maclen, parm)))
return 0; callback_ok = 0;
} }
#endif #endif
} }

View File

@@ -31,9 +31,9 @@ static void put_opt6_long(unsigned int val);
static void put_opt6_string(char *s); 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, 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, 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 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); 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)])) #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_netid *relay_tags = NULL;
struct dhcp_vendor *vendor; struct dhcp_vendor *vendor;
@@ -56,7 +57,7 @@ size_t dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name
outpacket_counter = 0; 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 outpacket_counter;
return 0; 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. */ /* 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, 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 *end = inbuff + sz;
void *opts = inbuff + 34; void *opts = inbuff + 34;
@@ -114,7 +115,7 @@ static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid **
return 0; 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 /* 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); memcpy(&link_address, inbuff + 2, IN6ADDRSZ);
/* Not, zero is_unicast since that is now known to refer to the /* Not, zero is_unicast since that is now known to refer to the
relayed packet, not the original sent by the client */ 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; return 0;
} }
else 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, 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 *packet_options = inbuff + 4;
void *end = inbuff + sz; 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 */ /* link temporarily */
for (n = context_tags; n && n->next; n = n->next); for (n = context_tags; n && n->next; n = n->next);
if (l = n) if ((l = n))
l->next = tags; l->next = tags;
for (n = run_tag_if(context_tags); n; n = n->next) 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); 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); end_opt6(o);
} }