carp: Fix routes reset issue
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Wed, 29 May 2013 12:41:40 +0000 (20:41 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Thu, 30 May 2013 07:13:01 +0000 (15:13 +0800)
Before this commit, routes related to carp or carp backing device were
reset, i.e. only prefix route was left, when carp state changed, e.g.
carp is down.

This commit fixes this routes reset issue by replacing the carp or carp
device routes' rt_ifa and rt_ifp with proper ifaddr and ifnet, instead
of discarding the old routes and installing the new prefix route.

Reported-by: robgar
sys/net/route.c
sys/net/route.h
sys/netinet/in.c
sys/netinet/in_rmx.c
sys/netinet/in_var.h
sys/netinet/ip_carp.c

index cda4758..5fda2ab 100644 (file)
@@ -1781,3 +1781,99 @@ rtmask_add_msghandler(netmsg_t msg)
 
 /* This must be before ip6_init2(), which is now SI_ORDER_MIDDLE */
 SYSINIT(route, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, route_init, 0);
+
+struct rtchange_arg {
+       struct ifaddr   *old_ifa;
+       struct ifaddr   *new_ifa;
+       struct rtentry  *rt;
+       int             changed;
+};
+
+static void
+rtchange_ifa(struct rtentry *rt, struct rtchange_arg *ap)
+{
+       if (rt->rt_ifa->ifa_rtrequest != NULL)
+               rt->rt_ifa->ifa_rtrequest(RTM_DELETE, rt, NULL);
+       IFAFREE(rt->rt_ifa);
+
+       IFAREF(ap->new_ifa);
+       rt->rt_ifa = ap->new_ifa;
+       rt->rt_ifp = ap->new_ifa->ifa_ifp;
+       if (rt->rt_ifa->ifa_rtrequest != NULL)
+               rt->rt_ifa->ifa_rtrequest(RTM_ADD, rt, NULL);
+
+       ap->changed = 1;
+}
+
+static int
+rtchange_callback(struct radix_node *rn, void *xap)
+{
+       struct rtchange_arg *ap = xap;
+       struct rtentry *rt = (struct rtentry *)rn;
+
+       if (rt->rt_ifa == ap->old_ifa) {
+               if (rt->rt_flags & (RTF_CLONING | RTF_PRCLONING)) {
+                       /*
+                        * We could saw the branch off when we are
+                        * still sitting on it, if the ifa_rtrequest
+                        * DEL/ADD are called directly from here.
+                        */
+                       ap->rt = rt;
+                       return EJUSTRETURN;
+               }
+               rtchange_ifa(rt, ap);
+       }
+       return 0;
+}
+
+int
+rtchange(struct ifaddr *old_ifa, struct ifaddr *new_ifa)
+{
+       struct rtchange_arg arg;
+       int origcpu, cpu;
+
+       memset(&arg, 0, sizeof(arg));
+       arg.old_ifa = old_ifa;
+       arg.new_ifa = new_ifa;
+
+       /*
+        * XXX individual requests are not independantly chained,
+        * which means that the per-cpu route tables will not be
+        * consistent in the middle of the operation.  If routes
+        * related to the interface are manipulated while we are
+        * doing this the inconsistancy could trigger a panic.
+        */
+       origcpu = mycpuid;
+       for (cpu = 0; cpu < ncpus; cpu++) {
+               struct radix_node_head *rnh;
+
+               lwkt_migratecpu(cpu);
+
+               rnh = rt_tables[cpu][AF_INET];
+               for (;;) {
+                       int error;
+
+                       KKASSERT(arg.rt == NULL);
+                       error = rnh->rnh_walktree(rnh,
+                           rtchange_callback, &arg);
+                       if (arg.rt != NULL) {
+                               struct rtentry *rt;
+
+                               rt = arg.rt;
+                               arg.rt = NULL;
+                               rtchange_ifa(rt, &arg);
+                       } else {
+                               break;
+                       }
+               }
+       }
+       lwkt_migratecpu(origcpu);
+
+       if (arg.changed) {
+               old_ifa->ifa_flags &= ~IFA_ROUTE;
+               new_ifa->ifa_flags |= IFA_ROUTE;
+               return 0;
+       } else {
+               return ENOENT;
+       }
+}
index 6a163f8..a3b4ff8 100644 (file)
@@ -388,6 +388,7 @@ typedef int (*rtsearch_callback_func_t)(int, struct rt_addrinfo *,
 
 void    rtfree (struct rtentry *);
 int     rtinit (struct ifaddr *, int, int);
+int     rtchange (struct ifaddr *, struct ifaddr *);
 int     rtioctl (u_long, caddr_t, struct ucred *);
 void    rtredirect (struct sockaddr *, struct sockaddr *,
            struct sockaddr *, int, struct sockaddr *);
index fa3f730..b0319b0 100644 (file)
@@ -1198,6 +1198,16 @@ in_scrubprefix(struct in_ifaddr *target)
        struct in_addr prefix, mask;
        int error;
 
+#ifdef CARP
+       /*
+        * Don't scrub prefix routes for CARP interfaces.
+        * Prefix routes deletion is handled by CARP
+        * interfaces themselves.
+        */
+       if (target->ia_ifp->if_type == IFT_CARP)
+               return;
+#endif
+
        if ((target->ia_flags & IFA_ROUTE) == 0)
                return;
 
index c3edc01..6ec5e6d 100644 (file)
@@ -43,6 +43,8 @@
  *     indefinitely.  See in_rtqtimo() below for the exact mechanism.
  */
 
+#include "opt_carp.h"
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
@@ -56,6 +58,9 @@
 #include <net/if.h>
 #include <net/route.h>
 #include <net/if_var.h>
+#ifdef CARP
+#include <net/if_types.h>
+#endif
 #include <netinet/in.h>
 #include <netinet/in_var.h>
 #include <netinet/ip_var.h>
@@ -426,7 +431,7 @@ in_ifadownkill(struct radix_node *rn, void *xap)
 }
 
 int
-in_ifadown(struct ifaddr *ifa, int delete)
+in_ifadown_force(struct ifaddr *ifa, int delete)
 {
        struct in_ifadown_arg arg;
        struct radix_node_head *rnh;
@@ -457,3 +462,12 @@ in_ifadown(struct ifaddr *ifa, int delete)
        return 0;
 }
 
+int
+in_ifadown(struct ifaddr *ifa, int delete)
+{
+#ifdef CARP
+       if (ifa->ifa_ifp->if_type == IFT_CARP)
+               return 0;
+#endif
+       return in_ifadown_force(ifa, delete);
+}
index 03e4b46..4e5a2e2 100644 (file)
@@ -250,6 +250,7 @@ void        in_rtqdrain (void);
 void   ip_input (struct mbuf *);
 void   ip_forward (struct mbuf *, boolean_t, struct sockaddr_in *);
 int    in_ifadown (struct ifaddr *ifa, int);
+int    in_ifadown_force (struct ifaddr *ifa, int);
 void   in_ifscrub (struct ifnet *, struct in_ifaddr *);
 void   in_iaunlink (struct in_ifaddr *);
 void   in_iahash_insert (struct in_ifaddr *);
index c67fa44..a02c96c 100644 (file)
@@ -3132,7 +3132,6 @@ static int
 carp_addroute_vhaddr(struct carp_softc *sc, struct carp_vhaddr *vha)
 {
        struct in_ifaddr *ia, *iaback;
-       int error;
 
        if (sc->sc_state != MASTER)
                return 0;
@@ -3143,14 +3142,7 @@ carp_addroute_vhaddr(struct carp_softc *sc, struct carp_vhaddr *vha)
        iaback = vha->vha_iaback;
        KKASSERT(iaback != NULL);
 
-       rtinit(&iaback->ia_ifa, RTM_DELETE, rtinitflags(iaback));
-       in_ifadown(&iaback->ia_ifa, 1);
-       iaback->ia_flags &= ~IFA_ROUTE;
-
-       error = rtinit(&ia->ia_ifa, RTM_ADD, rtinitflags(ia) | RTF_UP);
-       if (!error)
-               ia->ia_flags |= IFA_ROUTE;
-       return error;
+       return rtchange(&iaback->ia_ifa, &ia->ia_ifa);
 }
 
 static void
@@ -3165,18 +3157,14 @@ carp_delroute_vhaddr(struct carp_softc *sc, struct carp_vhaddr *vha,
        iaback = vha->vha_iaback;
        KKASSERT(iaback != NULL);
 
-       rtinit(&ia->ia_ifa, RTM_DELETE, rtinitflags(ia));
-       in_ifadown(&ia->ia_ifa, 1);
-       ia->ia_flags &= ~IFA_ROUTE;
-
        if (!del_iaback && (iaback->ia_ifp->if_flags & IFF_UP)) {
-               int error;
-
-               error = rtinit(&iaback->ia_ifa, RTM_ADD,
-                   rtinitflags(iaback) | RTF_UP);
-               if (!error)
-                       iaback->ia_flags |= IFA_ROUTE;
+               rtchange(&ia->ia_ifa, &iaback->ia_ifa);
+               return;
        }
+
+       rtinit(&ia->ia_ifa, RTM_DELETE, rtinitflags(ia));
+       in_ifadown_force(&ia->ia_ifa, 1);
+       ia->ia_flags &= ~IFA_ROUTE;
 }
 
 static int