From: Sepherosa Ziehau Date: Wed, 29 May 2013 12:41:40 +0000 (+0800) Subject: carp: Fix routes reset issue X-Git-Tag: v3.7.0~1054 X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/commitdiff_plain/2c1793932940230c08a7de65090d8a49ede91459 carp: Fix routes reset issue 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 --- diff --git a/sys/net/route.c b/sys/net/route.c index cda47581b1..5fda2ab3b7 100644 --- a/sys/net/route.c +++ b/sys/net/route.c @@ -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; + } +} diff --git a/sys/net/route.h b/sys/net/route.h index 6a163f89a6..a3b4ff87c1 100644 --- a/sys/net/route.h +++ b/sys/net/route.h @@ -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 *); diff --git a/sys/netinet/in.c b/sys/netinet/in.c index fa3f7309f3..b0319b09b6 100644 --- a/sys/netinet/in.c +++ b/sys/netinet/in.c @@ -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; diff --git a/sys/netinet/in_rmx.c b/sys/netinet/in_rmx.c index c3edc01a57..6ec5e6db5b 100644 --- a/sys/netinet/in_rmx.c +++ b/sys/netinet/in_rmx.c @@ -43,6 +43,8 @@ * indefinitely. See in_rtqtimo() below for the exact mechanism. */ +#include "opt_carp.h" + #include #include #include @@ -56,6 +58,9 @@ #include #include #include +#ifdef CARP +#include +#endif #include #include #include @@ -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); +} diff --git a/sys/netinet/in_var.h b/sys/netinet/in_var.h index 03e4b46fbe..4e5a2e2f89 100644 --- a/sys/netinet/in_var.h +++ b/sys/netinet/in_var.h @@ -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 *); diff --git a/sys/netinet/ip_carp.c b/sys/netinet/ip_carp.c index c67fa44a89..a02c96cbf6 100644 --- a/sys/netinet/ip_carp.c +++ b/sys/netinet/ip_carp.c @@ -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