Because not all OS's send RTM_NEWADDR for a refreshed RA we need

to manage a list of all IPv6 addresses on an interface so that we
can know if we need to wait for DAD to complete or not.
This commit is contained in:
Roy Marples 2013-06-09 07:31:08 +00:00
parent af4140cdb4
commit d5690e937b
6 changed files with 88 additions and 58 deletions

18
dhcp6.c
View File

@ -1860,7 +1860,7 @@ dhcp6_handledata(__unused void *arg)
const struct dhcp6_option *o;
const struct dhcp_opt *opt;
const struct if_options *ifo;
const struct ipv6_addr *ap;
struct ipv6_addr *ap;
uint8_t has_new;
int error;
@ -2129,11 +2129,14 @@ recv:
len = 1;
/* If all addresses have completed DAD run the script */
TAILQ_FOREACH(ap, &state->addrs, next) {
if (ap->flags & IPV6_AF_ONLINK &&
(ap->flags & IPV6_AF_DADCOMPLETED) == 0)
{
len = 0;
break;
if (ap->flags & IPV6_AF_ONLINK) {
if (!(ap->flags & IPV6_AF_DADCOMPLETED) &&
ipv6_findaddr(ap->iface, &ap->addr))
ap->flags |= IPV6_AF_DADCOMPLETED;
if ((ap->flags & IPV6_AF_DADCOMPLETED) == 0) {
len = 0;
break;
}
}
}
if (len) {
@ -2373,6 +2376,9 @@ dhcp6_handleifa(int cmd, const char *ifname,
struct interface *ifp;
struct dhcp6_state *state;
if (ifaces == NULL)
return;
TAILQ_FOREACH(ifp, ifaces, next) {
state = D6_STATE(ifp);
if (state == NULL || strcmp(ifp->name, ifname))

View File

@ -332,8 +332,8 @@ link_addr(struct nlmsghdr *nlm)
errno = EBADMSG;
return -1;
}
if (nlm->nlmsg_pid == (uint32_t)getpid())
return 1;
// if (nlm->nlmsg_pid == (uint32_t)getpid())
// return 1;
ifa = NLMSG_DATA(nlm);
if (if_indextoname(ifa->ifa_index, ifn) == NULL)
return -1;

76
ipv6.c
View File

@ -141,8 +141,7 @@ int
ipv6_makeaddr(struct in6_addr *addr, const struct interface *ifp,
const struct in6_addr *prefix, int prefix_len)
{
const struct ipv6_state *state;
const struct ll_addr *ap;
const struct ipv6_addr_l *ap;
#if 0
static u_int8_t allzero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
static u_int8_t allone[8] =
@ -157,14 +156,11 @@ ipv6_makeaddr(struct in6_addr *addr, const struct interface *ifp,
memcpy(addr, prefix, sizeof(*prefix));
/* Try and make the address from the first local-link address */
state = IPV6_CSTATE(ifp);
if (state) {
ap = TAILQ_FIRST(&state->ll_addrs);
if (ap) {
addr->s6_addr32[2] = ap->addr.s6_addr32[2];
addr->s6_addr32[3] = ap->addr.s6_addr32[3];
return 0;
}
ap = ipv6_linklocal(ifp);
if (ap) {
addr->s6_addr32[2] = ap->addr.s6_addr32[2];
addr->s6_addr32[3] = ap->addr.s6_addr32[3];
return 0;
}
/* Because we delay a few functions until we get a local-link address
@ -412,6 +408,9 @@ ipv6_addaddr(struct ipv6_addr *ap)
syslog(ap->flags & IPV6_AF_NEW ? LOG_INFO : LOG_DEBUG,
"%s: adding address %s", ap->iface->name, ap->saddr);
if (!(ap->flags & IPV6_AF_DADCOMPLETED) &&
ipv6_findaddr(ap->iface, &ap->addr))
ap->flags |= IPV6_AF_DADCOMPLETED;
if (add_address6(ap) == -1) {
syslog(LOG_ERR, "add_address6 %m");
return -1;
@ -455,7 +454,7 @@ ipv6_getstate(struct interface *ifp)
syslog(LOG_ERR, "%s: %m", __func__);
return NULL;
}
TAILQ_INIT(&state->ll_addrs);
TAILQ_INIT(&state->addrs);
TAILQ_INIT(&state->ll_callbacks);
}
return state;
@ -467,7 +466,7 @@ ipv6_handleifa(int cmd, struct if_head *ifs, const char *ifname,
{
struct interface *ifp;
struct ipv6_state *state;
struct ll_addr *ap;
struct ipv6_addr_l *ap;
struct ll_callback *cb;
#if 0
@ -503,21 +502,20 @@ ipv6_handleifa(int cmd, struct if_head *ifs, const char *ifname,
return;
}
if (!IN6_IS_ADDR_LINKLOCAL(addr)) {
ipv6rs_handleifa(cmd, ifname, addr, flags);
dhcp6_handleifa(cmd, ifname, addr, flags);
return;
}
state = ipv6_getstate(ifp);
if (state == NULL)
return;
/* We don't care about duplicated LL addresses, so remove them */
if (!IN6_IS_ADDR_LINKLOCAL(addr)) {
ipv6rs_handleifa(cmd, ifname, addr, flags);
dhcp6_handleifa(cmd, ifname, addr, flags);
}
/* We don't care about duplicated addresses, so remove them */
if (flags & IN6_IFF_DUPLICATED)
cmd = RTM_DELADDR;
TAILQ_FOREACH(ap, &state->ll_addrs, next) {
TAILQ_FOREACH(ap, &state->addrs, next) {
if (IN6_ARE_ADDR_EQUAL(&ap->addr, addr))
break;
}
@ -525,7 +523,7 @@ ipv6_handleifa(int cmd, struct if_head *ifs, const char *ifname,
switch (cmd) {
case RTM_DELADDR:
if (ap) {
TAILQ_REMOVE(&state->ll_addrs, ap, next);
TAILQ_REMOVE(&state->addrs, ap, next);
free(ap);
}
break;
@ -534,7 +532,7 @@ ipv6_handleifa(int cmd, struct if_head *ifs, const char *ifname,
ap = calloc(1, sizeof(*ap));
memcpy(ap->addr.s6_addr, addr->s6_addr,
sizeof(ap->addr.s6_addr));
TAILQ_INSERT_TAIL(&state->ll_addrs,
TAILQ_INSERT_TAIL(&state->addrs,
ap, next);
/* Now run any callbacks.
@ -551,14 +549,35 @@ ipv6_handleifa(int cmd, struct if_head *ifs, const char *ifname,
}
}
const struct ll_addr *
const struct ipv6_addr_l *
ipv6_linklocal(const struct interface *ifp)
{
const struct ipv6_state *state;
const struct ipv6_addr_l *ap;
state = IPV6_CSTATE(ifp);
if (state)
return TAILQ_FIRST(&state->ll_addrs);
if (state) {
TAILQ_FOREACH(ap, &state->addrs, next) {
if (IN6_IS_ADDR_LINKLOCAL(&ap->addr))
return ap;
}
}
return NULL;
}
const struct ipv6_addr_l *
ipv6_findaddr(const struct interface *ifp, const struct in6_addr *addr)
{
const struct ipv6_state *state;
const struct ipv6_addr_l *ap;
state = IPV6_CSTATE(ifp);
if (state) {
TAILQ_FOREACH(ap, &state->addrs, next) {
if (IN6_ARE_ADDR_EQUAL(&ap->addr, addr))
return ap;
}
}
return NULL;
}
@ -600,17 +619,18 @@ ipv6_free_ll_callbacks(struct interface *ifp)
}
}
}
void
ipv6_free(struct interface *ifp)
{
struct ipv6_state *state;
struct ll_addr *ap;
struct ipv6_addr_l *ap;
ipv6_free_ll_callbacks(ifp);
state = IPV6_STATE(ifp);
if (state) {
while ((ap = TAILQ_FIRST(&state->ll_addrs))) {
TAILQ_REMOVE(&state->ll_addrs, ap, next);
while ((ap = TAILQ_FIRST(&state->addrs))) {
TAILQ_REMOVE(&state->addrs, ap, next);
free(ap);
}
free(state);

12
ipv6.h
View File

@ -113,12 +113,12 @@ struct rt6 {
};
TAILQ_HEAD(rt6head, rt6);
struct ll_addr {
TAILQ_ENTRY(ll_addr) next;
struct ipv6_addr_l {
TAILQ_ENTRY(ipv6_addr_l) next;
struct in6_addr addr;
};
TAILQ_HEAD(ll_addr_head, ll_addr);
TAILQ_HEAD(ipv6_addr_l_head, ipv6_addr_l);
struct ll_callback {
TAILQ_ENTRY(ll_callback) next;
@ -128,7 +128,7 @@ struct ll_callback {
TAILQ_HEAD(ll_callback_head, ll_callback);
struct ipv6_state {
struct ll_addr_head ll_addrs;
struct ipv6_addr_l_head addrs;
struct ll_callback_head ll_callbacks;
};
@ -151,7 +151,9 @@ void ipv6_handleifa(int, struct if_head *,
const char *, const struct in6_addr *, int);
int ipv6_handleifa_addrs(int, struct ipv6_addrhead *,
const struct in6_addr *, int);
const struct ll_addr *ipv6_linklocal(const struct interface *);
const struct ipv6_addr_l *ipv6_linklocal(const struct interface *);
const struct ipv6_addr_l *ipv6_findaddr(const struct interface *,
const struct in6_addr *);
int ipv6_addlinklocalcallback(struct interface *, void (*)(void *), void *);
void ipv6_free_ll_callbacks(struct interface *);
void ipv6_free(struct interface *);

View File

@ -406,20 +406,25 @@ add_router(struct ra *router)
}
static void
ipv6rs_scriptrun(const struct ra *rap)
ipv6rs_scriptrun(struct ra *rap)
{
int hasdns;
const struct ipv6_addr *ap;
struct ipv6_addr *ap;
const struct ra_opt *rao;
/* If all addresses have completed DAD run the script */
TAILQ_FOREACH(ap, &rap->addrs, next) {
if ((ap->flags & IPV6_AF_DADCOMPLETED) == 0) {
syslog(LOG_DEBUG,
"%s: waiting for Router Advertisement"
" DAD to complete",
rap->iface->name);
return;
if (ap->flags & IPV6_AF_ONLINK) {
if (!(ap->flags & IPV6_AF_DADCOMPLETED) &&
ipv6_findaddr(ap->iface, &ap->addr))
ap->flags |= IPV6_AF_DADCOMPLETED;
if ((ap->flags & IPV6_AF_DADCOMPLETED) == 0) {
syslog(LOG_DEBUG,
"%s: waiting for Router Advertisement"
" DAD to complete",
rap->iface->name);
return;
}
}
}

15
net.c
View File

@ -441,21 +441,18 @@ discover_interfaces(int argc, char * const *argv)
}
#ifdef INET6
/* Capture local link addresses */
for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {
if (ifa->ifa_addr != NULL &&
ifa->ifa_addr->sa_family == AF_INET6)
{
sin6 = (const struct sockaddr_in6 *)
(void *)ifa->ifa_addr;
if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
ifa_flags = in6_addr_flags(ifa->ifa_name,
&sin6->sin6_addr);
if (ifa_flags != -1)
ipv6_handleifa(RTM_NEWADDR, ifs,
ifa->ifa_name,
&sin6->sin6_addr, ifa_flags);
}
ifa_flags = in6_addr_flags(ifa->ifa_name,
&sin6->sin6_addr);
if (ifa_flags != -1)
ipv6_handleifa(RTM_NEWADDR, ifs,
ifa->ifa_name,
&sin6->sin6_addr, ifa_flags);
}
}
#endif