From: Roy Marples Date: Thu, 30 May 2013 05:38:21 +0000 (+0000) Subject: Move SIOCGIFAFLAG_IN6 into if-bsd as in6_addr_flags X-Git-Tag: v5.99.7~31 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c5d2e393a55539a2c1efe26af61ee944d7a7ed12;p=thirdparty%2Fdhcpcd.git Move SIOCGIFAFLAG_IN6 into if-bsd as in6_addr_flags Add pfx_flush to flush ND6 entries when changing routes. Both these functions only work for BSD, just stubs on Linux. This tidies the code a little and fixes changing IPv6 routes on BSD, although the kernel may complain network is down for a few moments. --- diff --git a/dhcp6.c b/dhcp6.c index 951ea7f9..1d8be510 100644 --- a/dhcp6.c +++ b/dhcp6.c @@ -714,7 +714,7 @@ dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *)) memcpy(CMSG_DATA(cm), &pi, sizeof(pi)); if (sendmsg(sock, &sndhdr, 0) == -1) { - syslog(LOG_ERR, "%s: sendmsg: %m", ifp->name); + syslog(LOG_ERR, "%s: %s: sendmsg: %m", ifp->name, __func__); ifp->options->options &= ~DHCPCD_IPV6; dhcp6_drop(ifp, "EXPIRE6"); return -1; diff --git a/dhcpcd.c b/dhcpcd.c index 8d042d4f..3996d9a1 100644 --- a/dhcpcd.c +++ b/dhcpcd.c @@ -383,16 +383,21 @@ handle_carrier(int action, int flags, const char *ifname) if (carrier == -1) syslog(LOG_ERR, "%s: carrier_status: %m", ifname); - else if (carrier == 0 || ~ifp->flags & IFF_UP) { + else if (carrier == 0 || + (ifp->flags & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING)) + { if (ifp->carrier != LINK_DOWN) { ifp->carrier = LINK_DOWN; syslog(LOG_INFO, "%s: carrier lost", ifp->name); dhcp_close(ifp); dhcp6_drop(ifp, "EXPIRE6"); ipv6rs_drop(ifp); + ipv6_free(ifp); dhcp_drop(ifp, "NOCARRIER"); } - } else if (carrier == 1 && !(~ifp->flags & IFF_UP)) { + } else if (carrier == 1 && + (ifp->flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING)) + { if (ifp->carrier != LINK_UP) { ifp->carrier = LINK_UP; syslog(LOG_INFO, "%s: carrier acquired", ifp->name); diff --git a/if-bsd.c b/if-bsd.c index bc3d82fb..7f313423 100644 --- a/if-bsd.c +++ b/if-bsd.c @@ -450,6 +450,20 @@ if_route6(const struct rt6 *rt, int action) retval = -1; return retval; } + +int +pfx_flush(void) +{ + int s; + char dummy[IFNAMSIZ + 1]; + + s = socket(AF_INET6, SOCK_DGRAM, 0); + if (s != -1) { + strcpy(dummy, "lo0"); + s = ioctl(s, SIOCSPFXFLUSH_IN6, (caddr_t)&dummy); + } + return s; +} #endif static void @@ -471,6 +485,26 @@ get_addrs(int type, char *cp, struct sockaddr **sa) } } +int +in6_addr_flags(const char *ifname, const struct in6_addr *addr) +{ + int s, flags; + struct in6_ifreq ifr6; + + s = socket(AF_INET6, SOCK_DGRAM, 0); + flags = -1; + if (s != -1) { + memset(&ifr6, 0, sizeof(ifr6)); + strncpy(ifr6.ifr_name, ifname, sizeof(ifr6.ifr_name)); + ifr6.ifr_addr.sin6_family = AF_INET6; + ifr6.ifr_addr.sin6_addr = *addr; + if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) != -1) + flags = ifr6.ifr_flags; + close(s); + } + return flags; +} + int manage_link(int fd) { @@ -491,6 +525,7 @@ manage_link(int fd) #if defined(INET6) && !defined(LISTEN_DAD) struct in6_addr ia6; struct sockaddr_in6 *sin6; + int ifa_flags; #endif for (;;) { @@ -620,8 +655,16 @@ manage_link(int fd) memcpy(ia6.s6_addr, sin6->sin6_addr.s6_addr, sizeof(ia6.s6_addr)); + if (rtm->rtm_type == RTM_NEWADDR) { + ifa_flags = in6_addr_flags( + ifname, + &ia6); + if (ifa_flags == -1) + break; + } else + ifa_flags = 0; ipv6_handleifa(rtm->rtm_type, NULL, - ifname, &ia6, ifam->ifam_flags); + ifname, &ia6, ifa_flags); break; #endif } diff --git a/if-linux.c b/if-linux.c index c5622570..76cfbcbb 100644 --- a/if-linux.c +++ b/if-linux.c @@ -767,4 +767,22 @@ if_route6(const struct rt6 *rt, int action) errno = ENOTSUP; return -1; } + +int +in6_addr_flags(const char *ifname, const struct in6_addr *addr) +{ + + /* How do I get IPv6 address flags on Linux? */ + return 0; +} + +int +pfx_flush(void) +{ + + /* Flush prefixes, ip -6 neigh flush equivalent */ + /* There seems to be no need for this on Linux right now though, + * probably due to route metrics */ + return 0; +} #endif diff --git a/ipv6.c b/ipv6.c index 781783f1..6998c5ae 100644 --- a/ipv6.c +++ b/ipv6.c @@ -66,6 +66,7 @@ #endif static struct rt6head *routes; +static uint8_t do_pfx_flush; #ifdef DEBUG_MEMORY static void @@ -305,8 +306,14 @@ ipv6_handleifa(int cmd, struct if_head *ifs, const char *ifname, #endif /* Safety - ignore tentative announcements */ - if (cmd == RTM_NEWADDR && flags & IN6_IFF_TENTATIVE) - return; + if (cmd == RTM_NEWADDR) { + if (flags & (IN6_IFF_TENTATIVE || IN6_IFF_DUPLICATED)) + cmd = RTM_DELADDR; +#ifdef IN6_IFF_DETACHED + if (flags & IN6_IFF_DETACHED) + cmd = RTM_DELADDR; +#endif + } if (ifs == NULL) ifs = ifaces; @@ -372,7 +379,7 @@ ipv6_interfacehaslinklocal(const struct interface *ifp) const struct ipv6_state *state; state = IPV6_CSTATE(ifp); - return state && TAILQ_FIRST(&state->ll_addrs) ? 1 : 0; + return (state && TAILQ_FIRST(&state->ll_addrs)) ? 1 : 0; } int ipv6_addlinklocalcallback(struct interface *ifp, @@ -417,6 +424,7 @@ ipv6_free(struct interface *ifp) free(cb); } free(state); + ifp->if_data[IF_DATA_IPV6] = NULL; } } @@ -517,8 +525,9 @@ nc_route(int add, struct rt6 *ort, struct rt6 *nrt) desc_route(add ? "adding" : "changing", nrt); /* We delete and add the route so that we can change metric and * prefer the interface. */ - del_route6(ort); - if (!add_route6(nrt)) + if (del_route6(ort) == 0 && add) + do_pfx_flush = 1; + if (add_route6(nrt) == 0) return 0; syslog(LOG_ERR, "%s: add_route6: %m", nrt->iface->name); return -1; @@ -668,7 +677,7 @@ ipv6_buildroutes(void) struct ipv6_addr *addr; const struct interface *ifp; const struct dhcp6_state *d6_state; - int have_default; + uint8_t have_default; if (!(options & (DHCPCD_IPV6RA_OWN | DHCPCD_IPV6RA_OWN_DEFAULT))) return; @@ -718,7 +727,7 @@ ipv6_buildroutes(void) return; } TAILQ_INIT(nrs); - have_default = 0; + have_default = do_pfx_flush = 0; TAILQ_FOREACH_SAFE(rt, &dnr, next, rtn) { /* Is this route already in our table? */ if (find_route6(nrs, rt) != NULL) @@ -733,6 +742,7 @@ ipv6_buildroutes(void) { if (c_route(or, rt) != 0) continue; + do_pfx_flush = 1; } TAILQ_REMOVE(routes, or, next); free(or); @@ -772,6 +782,22 @@ ipv6_buildroutes(void) free(rt); } + if (do_pfx_flush) { + /* We need to flush all entries in our prefix list + * if we changed a route so that we leave via the + * correct interface */ + if (pfx_flush() == -1) + syslog(LOG_ERR, "pfx_flush: %m"); + + /* XXX FIXME + * On multi-homed systems on the same network + * (ie wired and wireless) swapping the active routes + * between interfaces (ie, bringing down the wired and + * back up again while wireless is active) causes the + * ping6 command to emit bogus Network is down errors. + * This could be a kernel bug though */ + } + free(routes); routes = nrs; } diff --git a/ipv6.h b/ipv6.h index 6ef4f6f9..c99227e4 100644 --- a/ipv6.h +++ b/ipv6.h @@ -148,7 +148,6 @@ int ipv6_addlinklocalcallback(struct interface *, void (*)(void *), void *); void ipv6_free(struct interface *); int ipv6_removesubnet(const struct interface *, struct ipv6_addr *); void ipv6_buildroutes(void); -void ipv6_drop(struct interface *); #else #define ipv6_init() -1 #define ipv6_free(a) diff --git a/ipv6ns.c b/ipv6ns.c index fd33c9dd..c588bc0e 100644 --- a/ipv6ns.c +++ b/ipv6ns.c @@ -339,7 +339,7 @@ ipv6ns_probeaddr(void *arg) #endif if (sendmsg(unspec_sock, &sndhdr, 0) == -1) syslog(LOG_ERR, "%s: %s: sendmsg: %m", - __func__, ap->iface->name); + ap->iface->name, __func__); if (ap->dadcallback) { ms_to_tv(&tv, RETRANS_TIMER); @@ -462,8 +462,7 @@ ipv6ns_proberouter(void *arg) #endif if (sendmsg(sock, &sndhdr, 0) == -1) syslog(LOG_ERR, "%s: %s: sendmsg: %m", - __func__, rap->iface->name); - + rap->iface->name, __func__); ms_to_tv(&tv, rap->retrans == 0 ? RETRANS_TIMER : rap->retrans); ms_to_tv(&rtv, MIN_RANDOM_FACTOR); diff --git a/ipv6rs.c b/ipv6rs.c index a12925b7..bc151c79 100644 --- a/ipv6rs.c +++ b/ipv6rs.c @@ -266,7 +266,7 @@ ipv6rs_sendprobe(void *arg) syslog(LOG_DEBUG, "%s: sending Router Solicitation", ifp->name); if (sendmsg(sock, &sndhdr, 0) == -1) { - syslog(LOG_ERR, "%s: sendmsg: %m", ifp->name); + syslog(LOG_ERR, "%s: %s: sendmsg: %m", ifp->name, __func__); ipv6rs_drop(ifp); ifp->options->options &= ~(DHCPCD_IPV6 | DHCPCD_IPV6RS); return; @@ -1301,11 +1301,11 @@ ipv6rs_drop(struct interface *ifp) } } if (expired) { - ipv6_buildroutes(); while ((rap = TAILQ_FIRST(&rtrs))) { TAILQ_REMOVE(&rtrs, rap, next); ipv6rs_drop_ra(rap); } + ipv6_buildroutes(); script_runreason(ifp, "ROUTERADVERT"); } } diff --git a/net.c b/net.c index b2503c96..9aa5efb6 100644 --- a/net.c +++ b/net.c @@ -225,10 +225,6 @@ discover_interfaces(int argc, char * const *argv) #ifdef INET6 const struct sockaddr_in6 *sin6; int ifa_flags; -#ifdef SIOCGIFAFLAG_IN6 - struct in6_ifreq ifr6; - int s6; -#endif #endif #ifdef AF_LINK const struct sockaddr_dl *sdl; @@ -443,12 +439,6 @@ discover_interfaces(int argc, char * const *argv) #ifdef INET6 /* Capture local link addresses */ -#ifdef SIOCGIFAFLAG_IN6 - s6 = socket(AF_INET6, SOCK_DGRAM, 0); - if (s6 == -1) - syslog(LOG_ERR, "%s: socket: %m", __func__); -#endif - for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_addr != NULL && ifa->ifa_addr->sa_family == AF_INET6) @@ -456,34 +446,15 @@ discover_interfaces(int argc, char * const *argv) sin6 = (const struct sockaddr_in6 *) (void *)ifa->ifa_addr; if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { -#ifdef SIOCGIFAFLAG_IN6 - memset(&ifr6, 0, sizeof(ifr6)); - strncpy(ifr6.ifr_name, ifa->ifa_name, - sizeof(ifr6.ifr_name)); - ifr6.ifr_addr = *sin6; - if (ioctl(s6, SIOCGIFAFLAG_IN6, &ifr6) == -1) { - syslog(LOG_ERR, - "%s: SIOCGIFAFLAG_IN6: %m", - ifa->ifa_name); - continue; - } - ifa_flags = ifr6.ifr_ifru.ifru_flags6; -#else - /* We have no way of getting the address flags - * such as tentative on Linux at this point. - * We have to hope that getifaddrs only returns - * useable addresses. */ - ifa_flags = 0; -#endif - 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); } } } -#ifdef SIOCGIFAFLAG_IN6 - if (s6 != -1) - close(s6); -#endif #endif freeifaddrs(ifaddrs); diff --git a/net.h b/net.h index 50c8f3c4..85f6289e 100644 --- a/net.h +++ b/net.h @@ -106,12 +106,14 @@ int if_init(struct interface *); int if_address6(const struct interface *, const struct ipv6_addr *, int); #define add_address6(ifp, a) if_address6(ifp, a, 1) #define del_address6(ifp, a) if_address6(ifp, a, -1) +int in6_addr_flags(const char *, const struct in6_addr *); int if_route6(const struct rt6 *rt, int); #define add_route6(rt) if_route6(rt, 1) #define change_route6(rt) if_route6(rt, 0) #define del_route6(rt) if_route6(rt, -1) #define del_src_route6(rt) if_route6(rt, -2); +int pfx_flush(void); int open_link_socket(void); int manage_link(int);