Always create a local route for every configured IPv4 address on the
authormpi <mpi@openbsd.org>
Sat, 12 Jul 2014 14:26:00 +0000 (14:26 +0000)
committermpi <mpi@openbsd.org>
Sat, 12 Jul 2014 14:26:00 +0000 (14:26 +0000)
machine and restore the original behavior of RTM_ADD and RTM_DELETE
by always generating one message per locally configured address.

This time, make sure the local route is removed during an address change,
since at least pppoe(4) do some funky magics with wildcard addresses that
might corrupt the routing tree, as found by naddy@

Also do not add a local route if the specified address is 0.0.0.0, to
prevent a tree corruption, as found by guenther@.

Putting this in now so that it gets tested, claudio@ agrees.  Please
contact me if you find any route-related regression caused by this
change.

sys/net/route.c
sys/netinet/if_ether.c
sys/netinet/in.c

index 7601cb5..65e7162 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: route.c,v 1.171 2014/06/16 19:47:21 mpi Exp $ */
+/*     $OpenBSD: route.c,v 1.172 2014/07/12 14:26:00 mpi Exp $ */
 /*     $NetBSD: route.c,v 1.14 1996/02/13 22:00:46 christos Exp $      */
 
 /*
@@ -1129,7 +1129,8 @@ rt_ifa_add(struct ifaddr *ifa, int flags, struct sockaddr *dst)
                        if (ifa->ifa_rtrequest)
                                ifa->ifa_rtrequest(RTM_ADD, rt);
                }
-               rt_newaddrmsg(RTM_ADD, ifa, error, nrt);
+               if (flags & RTF_LOCAL)
+                       rt_newaddrmsg(RTM_ADD, ifa, error, nrt);
        }
        return (error);
 }
@@ -1183,7 +1184,8 @@ rt_ifa_del(struct ifaddr *ifa, int flags, struct sockaddr *dst)
 
        error = rtrequest1(RTM_DELETE, &info, prio, &nrt, rtableid);
        if (error == 0 && (rt = nrt) != NULL) {
-               rt_newaddrmsg(RTM_DELETE, ifa, error, nrt);
+               if (flags & RTF_LOCAL)
+                       rt_newaddrmsg(RTM_DELETE, ifa, error, nrt);
                if (rt->rt_refcnt <= 0) {
                        rt->rt_refcnt++;
                        rtfree(rt);
@@ -1203,6 +1205,26 @@ rt_ifa_addloop(struct ifaddr *ifa)
 {
        struct rtentry *rt;
 
+       /*
+        * If the configured address correspond to the magical "any"
+        * address do not add a local route entry because that might
+        * corrupt the routing tree which uses this value for the
+        * default routes.
+        */
+       switch (ifa->ifa_addr->sa_family) {
+       case AF_INET:
+               if (satosin(ifa->ifa_addr)->sin_addr.s_addr == INADDR_ANY)
+                       return;
+               break;
+       case AF_INET6:
+               if (IN6_ARE_ADDR_EQUAL(&satosin6(ifa->ifa_addr)->sin6_addr,
+                   &in6addr_any))
+                       return;
+               break;
+       default:
+               break;
+       }
+
        /* If there is no loopback entry, allocate one. */
        rt = rtalloc1(ifa->ifa_addr, 0, ifa->ifa_ifp->if_rdomain);
        if (rt == NULL || (rt->rt_flags & RTF_HOST) == 0 ||
@@ -1221,6 +1243,24 @@ rt_ifa_delloop(struct ifaddr *ifa)
 {
        struct rtentry *rt;
 
+       /*
+        * We do not add local routes for such address, so do not bother
+        * removing them.
+        */
+       switch (ifa->ifa_addr->sa_family) {
+       case AF_INET:
+               if (satosin(ifa->ifa_addr)->sin_addr.s_addr == INADDR_ANY)
+                       return;
+               break;
+       case AF_INET6:
+               if (IN6_ARE_ADDR_EQUAL(&satosin6(ifa->ifa_addr)->sin6_addr,
+                   &in6addr_any))
+                       return;
+               break;
+       default:
+               break;
+       }
+
        /*
         * Before deleting, check if a corresponding loopbacked host
         * route surely exists.  With this check, we can avoid to
index 0edf1a9..b3c7b81 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: if_ether.c,v 1.129 2014/06/16 19:47:21 mpi Exp $      */
+/*     $OpenBSD: if_ether.c,v 1.130 2014/07/12 14:26:00 mpi Exp $      */
 /*     $NetBSD: if_ether.c,v 1.31 1996/05/11 12:59:58 mycroft Exp $    */
 
 /*
@@ -174,7 +174,8 @@ arp_rtrequest(int req, struct rtentry *rt)
                if ((rt->rt_flags & RTF_HOST) == 0 && rt_mask(rt) &&
                    satosin(rt_mask(rt))->sin_addr.s_addr != 0xffffffff)
                        rt->rt_flags |= RTF_CLONING;
-               if (rt->rt_flags & RTF_CLONING) {
+               if (rt->rt_flags & RTF_CLONING ||
+                   ((rt->rt_flags & RTF_LLINFO) && !la)) {
                        /*
                         * Case 1: This route should come from a route to iface.
                         */
@@ -189,7 +190,8 @@ arp_rtrequest(int req, struct rtentry *rt)
                         * from it do not need their expiration time set.
                         */
                        rt->rt_expire = time_second;
-                       break;
+                       if ((rt->rt_flags & RTF_CLONING) != 0)
+                               break;
                }
                /* Announce a new entry if requested. */
                if (rt->rt_flags & RTF_ANNOUNCE)
index 425b408..70e1a8d 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: in.c,v 1.99 2014/06/26 13:08:25 mpi Exp $     */
+/*     $OpenBSD: in.c,v 1.100 2014/07/12 14:26:00 mpi Exp $    */
 /*     $NetBSD: in.c,v 1.26 1996/02/13 23:41:39 christos Exp $ */
 
 /*
@@ -619,8 +619,10 @@ in_ifinit(struct ifnet *ifp, struct in_ifaddr *ia, struct sockaddr_in *sin,
         * Always remove the address from the tree to make sure its
         * position gets updated in case the key changes.
         */
-       if (!newaddr)
+       if (!newaddr) {
+               rt_ifa_delloop(&ia->ia_ifa);
                ifa_del(ifp, &ia->ia_ifa);
+       }
        oldaddr = ia->ia_addr;
        ia->ia_addr = *sin;
 
@@ -702,6 +704,7 @@ out:
         * carp(4).
         */
        ifa_add(ifp, &ia->ia_ifa);
+       rt_ifa_addloop(&ia->ia_ifa);
 
        if (error && newaddr)
                in_purgeaddr(&ia->ia_ifa);
@@ -719,7 +722,9 @@ in_purgeaddr(struct ifaddr *ifa)
 
        in_ifscrub(ifp, ia);
 
+       rt_ifa_delloop(&ia->ia_ifa);
        ifa_del(ifp, &ia->ia_ifa);
+
        TAILQ_REMOVE(&in_ifaddr, ia, ia_list);
        if (ia->ia_allhosts != NULL) {
                in_delmulti(ia->ia_allhosts);