diff options
| author | Roy Marples <roy@marples.name> | 2012-10-31 17:10:22 +0000 |
|---|---|---|
| committer | Roy Marples <roy@marples.name> | 2012-10-31 17:10:22 +0000 |
| commit | 35308011406a69fd748ee920760373ffd5194ed7 (patch) | |
| tree | 8b2eb08921157dff3afba3e04c7ca21180fd9c31 /ipv6rs.c | |
| parent | 376e8b8055eda6a7458182997e7984f8b37ae309 (diff) | |
| download | dhcpcd-35308011406a69fd748ee920760373ffd5194ed7.tar.xz | |
When we receive a RA with a lifetime of 0, we should just remove
the default router and let any options expire as set in accordance
with RFC4861 section 4.2
Diffstat (limited to 'ipv6rs.c')
| -rw-r--r-- | ipv6rs.c | 115 |
1 files changed, 84 insertions, 31 deletions
@@ -405,7 +405,7 @@ ipv6rs_handledata(_unused void *arg) struct ipv6_addr *ap; char *opt, *tmp; struct timeval expire; - uint8_t has_dns, new_rap, new_data; + uint8_t has_prefix, has_dns, new_rap, new_data; len = recvmsg(sock, &rcvhdr, 0); if (len == -1) { @@ -473,21 +473,10 @@ ipv6rs_handledata(_unused void *arg) break; } - nd_ra = (struct nd_router_advert *)icp; - /* If the lifetime of the router is zero, just expire it - * if we already have it and move on. */ - if (nd_ra->nd_ra_router_lifetime == 0) { - if (rap) { - rap->lifetime = 0; - ipv6rs_expire(ifp); - } - return; - } - /* We don't want to spam the log with the fact we got an RA every * 30 seconds or so, so only spam the log if it's different. */ if (options & DHCPCD_DEBUG || rap == NULL || - (rap->expired || rap->data_len != len || + (rap->data_len != len || memcmp(rap->data, (unsigned char *)icp, rap->data_len) != 0)) { if (rap) { @@ -521,6 +510,7 @@ ipv6rs_handledata(_unused void *arg) } get_monotonic(&rap->received); + nd_ra = (struct nd_router_advert *)icp; rap->flags = nd_ra->nd_ra_flags_reserved; rap->lifetime = ntohs(nd_ra->nd_ra_router_lifetime); if (nd_ra->nd_ra_reachable) { @@ -530,13 +520,13 @@ ipv6rs_handledata(_unused void *arg) } if (nd_ra->nd_ra_retransmit) rap->retrans = ntohl(nd_ra->nd_ra_retransmit); - rap->expired = 0; + rap->expired = (rap->lifetime == 0); len -= sizeof(struct nd_router_advert); p = ((uint8_t *)icp) + sizeof(struct nd_router_advert); olen = 0; lifetime = ~0U; - has_dns = 0; + has_prefix = has_dns = 0; for (olen = 0; len > 0; p += olen, len -= olen) { if ((size_t)len < sizeof(struct nd_opt_hdr)) { syslog(LOG_ERR, "%s: Short option", ifp->name); @@ -619,6 +609,8 @@ ipv6rs_handledata(_unused void *arg) } else opt = xstrdup(ap->saddr); lifetime = ap->prefix_vltime; + if (lifetime > 0) + has_prefix = 1; break; case ND_OPT_MTU: @@ -724,6 +716,8 @@ ipv6rs_handledata(_unused void *arg) add_router(rap); if (options & DHCPCD_IPV6RA_OWN && !(options & DHCPCD_TEST)) { TAILQ_FOREACH(ap, &rap->addrs, next) { + if (ap->prefix_vltime == 0) + continue; syslog(ap->new ? LOG_INFO : LOG_DEBUG, "%s: adding address %s", ifp->name, ap->saddr); @@ -749,12 +743,11 @@ ipv6rs_handledata(_unused void *arg) if (!(ifp->state->options->options & DHCPCD_IPV6RA_REQRDNSS)) has_dns = 1; - if (has_dns) + if (has_prefix && has_dns) delete_q_timeout(0, handle_exit_timeout, NULL); delete_timeout(NULL, ifp); delete_timeout(NULL, rap); /* reachable timer */ - ipv6rs_expire(ifp); - if (has_dns) + if (has_prefix && has_dns) daemonise(); else if (options & DHCPCD_DAEMONISE && !(options & DHCPCD_DAEMONISED) && new_data) @@ -786,6 +779,10 @@ handle_flag: syslog(LOG_DEBUG, "%s: No DHCPv6 instruction in RA", ifp->name); } + + /* Expire should be called last as the rap object could be destroyed */ + if (!(options & DHCPCD_TEST)) + ipv6rs_expire(ifp); } int @@ -857,11 +854,28 @@ ipv6rs_env(char **env, const char *prefix, const struct interface *ifp) return l; } +static const struct ipv6_addr * +ipv6rs_findsameaddr(const struct ipv6_addr *ap) +{ + const struct ra *rap; + const struct ipv6_addr *a; + + TAILQ_FOREACH(rap, &ipv6_routers, next) { + TAILQ_FOREACH(a, &rap->addrs, next) { + if (ap != a && + memcmp(&a->addr, &ap->addr, sizeof(ap->addr) == 0)) + return a; + } + } + return NULL; +} + void ipv6rs_expire(void *arg) { struct interface *ifp; struct ra *rap, *ran; + struct ipv6_addr *ap, *apn; struct ra_opt *rao, *raon; struct timeval now, lt, expire, next; int expired; @@ -878,22 +892,46 @@ ipv6rs_expire(void *arg) lt.tv_usec = 0; timeradd(&rap->received, <, &expire); if (timercmp(&now, &expire, >)) { - syslog(LOG_INFO, "%s: %s: expired Router Advertisement", - ifp->name, rap->sfrom); - rap->expired = expired = 1; - continue; + if (!rap->expired) { + syslog(LOG_INFO, + "%s: %s: expired default Router", + ifp->name, rap->sfrom); + rap->expired = expired = 1; + } + } else { + timersub(&expire, &now, <); + if (!timerisset(&next) || timercmp(&next, <, >)) + next = lt; + } + + if (options & DHCPCD_IPV6RA_OWN) { + TAILQ_FOREACH_SAFE(ap, &rap->addrs, next, apn) { + lt.tv_sec = ap->prefix_vltime; + lt.tv_usec = 0; + timeradd(&rap->received, <, &expire); + if (timercmp(&now, &expire, >) && + ipv6rs_findsameaddr(ap) == NULL) + { + syslog(LOG_INFO, + "%s: %s: expired address", + ifp->name, ap->saddr); + TAILQ_REMOVE(&rap->addrs, ap, next); + free(ap); + /* No need to delete it as the kernel + * should have done this. */ + } + } } - timersub(&expire, &now, <); - if (!timerisset(&next) || timercmp(&next, <, >)) - next = lt; TAILQ_FOREACH_SAFE(rao, &rap->options, next, raon) { if (!timerisset(&rao->expire)) continue; if (timercmp(&now, &rao->expire, >)) { - syslog(LOG_INFO, - "%s: %s: expired option %d", - ifp->name, rap->sfrom, rao->type); + /* Expired prefixes are logged above */ + if (rao->type != ND_OPT_PREFIX_INFORMATION) + syslog(LOG_INFO, + "%s: %s: expired option %d", + ifp->name, rap->sfrom, rao->type); TAILQ_REMOVE(&rap->options, rao, next); expired = 1; free(rao->option); @@ -941,14 +979,29 @@ ipv6rs_start(struct interface *ifp) void ipv6rs_drop(struct interface *ifp) { - struct ra *rap; + struct ra *rap, *ran; int expired = 0; + TAILQ_HEAD(, ipv6_addr) addrs; - TAILQ_FOREACH(rap, &ipv6_routers, next) - if (rap->iface == ifp) + /* We need to drop routes before addresses + * We do this by moving addresses to a local list, then building + * the routes and finally adding the addresses back to a RA before + * dropping it. Which RA the addresses end up on does not matter. */ + TAILQ_INIT(&addrs); + TAILQ_FOREACH(rap, &ipv6_routers, next) { + if (rap->iface == ifp) { rap->expired = expired = 1; + TAILQ_CONCAT(&addrs, &rap->addrs, next); + } + } if (expired) { ipv6_build_routes(); + TAILQ_FOREACH_SAFE(rap, &ipv6_routers, next, ran) { + if (rap->iface == ifp) { + TAILQ_CONCAT(&rap->addrs, &addrs, next); + ipv6rs_drop_ra(rap); + } + } run_script_reason(ifp, "ROUTERADVERT"); } } |
