]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Move SIOCGIFAFLAG_IN6 into if-bsd as in6_addr_flags
authorRoy Marples <roy@marples.name>
Thu, 30 May 2013 05:38:21 +0000 (05:38 +0000)
committerRoy Marples <roy@marples.name>
Thu, 30 May 2013 05:38:21 +0000 (05:38 +0000)
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.

dhcp6.c
dhcpcd.c
if-bsd.c
if-linux.c
ipv6.c
ipv6.h
ipv6ns.c
ipv6rs.c
net.c
net.h

diff --git a/dhcp6.c b/dhcp6.c
index 951ea7f92638cd80bd2dc2135ab476b1da01a785..1d8be510dbfe7986906d76c5cfc66c049ba97580 100644 (file)
--- 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;
index 8d042d4fe22b0379f83ffa9c13e263c12ba42bc9..3996d9a15756933df3356faa9a147887be8f3b5a 100644 (file)
--- 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);
index bc3d82fbc1a47471f04fbb10c2129f83cf968747..7f313423e7c0bef4e7932d5f0f4cf58d5138b9a7 100644 (file)
--- 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
                                }
index c56225706f53719e49e0cecd0947cde3b27c4d50..76cfbcbb4f7fc13feecb4688c2af24574fa7e215 100644 (file)
@@ -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 781783f15fdc2b70372ae80cda1a57bcfed1f079..6998c5ae847517a6574c20c04641ebd80a613d99 100644 (file)
--- 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 6ef4f6f985c1105e3ad7e6b424895278c8666f36..c99227e42facee1adc13e449036c8557cf024049 100644 (file)
--- 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)
index fd33c9ddfe77d170320c46b8544d51cf398ad08b..c588bc0e0889c7d68504a3d8154592fda9c5489a 100644 (file)
--- 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);
index a12925b7b5fca46680add572feee922eb0c68dc1..bc151c797b1b046b9ab2f56fbfed6e2d47aee389 100644 (file)
--- 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 b2503c960260af8b1cc0f804be37b3e950adea33..9aa5efb6e2a1d594023a5fe8002ee214b9463276 100644 (file)
--- 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 50c8f3c468250df05095900f2ef3855baef42d78..85f6289e6873787b9b7c1387a8fc15c34f88309c 100644 (file)
--- 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);