If you do, please test the diff below and make sure it does not change
anything in your routing table!

This diff is a first step to merge all the various code paths that 
manipulate auto-magically created routes.  While the code in sys/netinet
makes use of rtinit() to create routes with the RTP_CONNECTED priority,
in sys/netinet6 land there is no equivalent.

The diff below only deals with routes to loopback for IPv6 addresses.
These routes are used to indicate that an address is local and that lo0
should be used instead of the interface to output packets.  When you
look at your routing table these routes have "lo0" as Iface but their
prefix correspond to the real interface, for example:


Destination                        Gateway                        Flags   Refs  
    Use   Mtu  Prio Iface
...
fe80::200:5eff:fe00:102%carp2      00:00:5e:00:01:02              UHL        0  
      0     -     4 lo0 



So the diff below makes use of the actual rtinit() code to create such
routes, but introduce a new interface: rt_ifa_addloop() & rt_ifa_delloop()

The next step will be to replace rtinit() by the underlying functions
introduced in this diff: rt_ifa_add() and rt_ifa_del(), and document
them.  Once that's done we should be able to replace any custom code
creating or deleting a route with RTP_CONNECTED by one of these
functions.

Here is the diff, ok?

Index: net/route.c
===================================================================
RCS file: /home/ncvs/src/sys/net/route.c,v
retrieving revision 1.157
diff -u -p -r1.157 route.c
--- net/route.c 27 Mar 2014 10:39:23 -0000      1.157
+++ net/route.c 27 Mar 2014 13:48:39 -0000
@@ -150,6 +150,9 @@ int rtflushclone1(struct radix_node *, v
 void   rtflushclone(struct radix_node_head *, struct rtentry *);
 int    rt_if_remove_rtdelete(struct radix_node *, void *, u_int);
 
+int    rt_ifa_add(struct ifaddr *, int, struct sockaddr *);
+int    rt_ifa_del(struct ifaddr *, int, struct sockaddr *);
+
 #define        LABELID_MAX     50000
 
 struct rt_label {
@@ -1083,67 +1086,46 @@ rt_maskedcopy(struct sockaddr *src, stru
 int
 rtinit(struct ifaddr *ifa, int cmd, int flags)
 {
-       struct rtentry          *rt;
-       struct sockaddr         *dst, *deldst;
-       struct mbuf             *m = NULL;
-       struct rtentry          *nrt = NULL;
-       int                      error;
-       struct rt_addrinfo       info;
+       struct sockaddr         *dst;
+       int error;
+
+       KASSERT(cmd == RTM_ADD || cmd == RTM_DELETE);
+
+       dst = flags & RTF_HOST ? ifa->ifa_dstaddr : ifa->ifa_addr;
+
+       if (cmd == RTM_ADD)
+               error = rt_ifa_add(ifa, flags, dst);
+       else
+               error = rt_ifa_del(ifa, flags, dst);
+
+       return (error);
+}
+
+int
+rt_ifa_add(struct ifaddr *ifa, int flags, struct sockaddr *dst)
+{
+       struct rtentry          *rt, *nrt = NULL;
        struct sockaddr_rtlabel  sa_rl;
+       struct rt_addrinfo       info;
        u_short                  rtableid = ifa->ifa_ifp->if_rdomain;
+       int                      error;
 
-       dst = flags & RTF_HOST ? ifa->ifa_dstaddr : ifa->ifa_addr;
-       if (cmd == RTM_DELETE) {
-               if ((flags & RTF_HOST) == 0 && ifa->ifa_netmask) {
-                       m = m_get(M_DONTWAIT, MT_SONAME);
-                       if (m == NULL)
-                               return (ENOBUFS);
-                       deldst = mtod(m, struct sockaddr *);
-                       rt_maskedcopy(dst, deldst, ifa->ifa_netmask);
-                       dst = deldst;
-               }
-               if ((rt = rtalloc1(dst, 0, rtableid)) != NULL) {
-                       rt->rt_refcnt--;
-                       /* try to find the right route */
-                       while (rt && rt->rt_ifa != ifa)
-                               rt = (struct rtentry *)
-                                   ((struct radix_node *)rt)->rn_dupedkey;
-                       if (!rt) {
-                               if (m != NULL)
-                                       (void) m_free(m);
-                               return (flags & RTF_HOST ? EHOSTUNREACH
-                                                       : ENETUNREACH);
-                       }
-               }
-       }
-       bzero(&info, sizeof(info));
+       memset(&info, 0, sizeof(info));
        info.rti_ifa = ifa;
        info.rti_flags = flags;
        info.rti_info[RTAX_DST] = dst;
-       if (cmd == RTM_ADD)
-               info.rti_info[RTAX_GATEWAY] = ifa->ifa_addr;
+       info.rti_info[RTAX_GATEWAY] = ifa->ifa_addr;
        info.rti_info[RTAX_LABEL] =
            rtlabel_id2sa(ifa->ifa_ifp->if_rtlabelid, &sa_rl);
 
        if ((flags & RTF_HOST) == 0)
                info.rti_info[RTAX_NETMASK] = ifa->ifa_netmask;
 
-       error = rtrequest1(cmd, &info, RTP_CONNECTED, &nrt, rtableid);
-       if (cmd == RTM_DELETE) {
-               if (error == 0 && (rt = nrt) != NULL) {
-                       rt_newaddrmsg(cmd, ifa, error, nrt);
-                       if (rt->rt_refcnt <= 0) {
-                               rt->rt_refcnt++;
-                               rtfree(rt);
-                       }
-               }
-               if (m != NULL)
-                       (void) m_free(m);
-       }
-       if (cmd == RTM_ADD && error == 0 && (rt = nrt) != NULL) {
+       error = rtrequest1(RTM_ADD, &info, RTP_CONNECTED, &nrt, rtableid);
+       if (error == 0 && (rt = nrt) != NULL) {
                rt->rt_refcnt--;
                if (rt->rt_ifa != ifa) {
-                       printf("rtinit: wrong ifa (%p) was (%p)\n",
+                       printf("%s: wrong ifa (%p) was (%p)\n", __func__,
                            ifa, rt->rt_ifa);
                        if (rt->rt_ifa->ifa_rtrequest)
                                rt->rt_ifa->ifa_rtrequest(RTM_DELETE, rt);
@@ -1154,9 +1136,107 @@ rtinit(struct ifaddr *ifa, int cmd, int 
                        if (ifa->ifa_rtrequest)
                                ifa->ifa_rtrequest(RTM_ADD, rt);
                }
-               rt_newaddrmsg(cmd, ifa, error, nrt);
+               rt_newaddrmsg(RTM_ADD, ifa, error, nrt);
+       }
+       return (error);
+}
+
+int
+rt_ifa_del(struct ifaddr *ifa, int flags, struct sockaddr *dst)
+{
+       struct rtentry          *rt, *nrt = NULL;
+       struct mbuf             *m = NULL;
+       struct sockaddr         *deldst;
+       struct rt_addrinfo       info;
+       struct sockaddr_rtlabel  sa_rl;
+       u_short                  rtableid = ifa->ifa_ifp->if_rdomain;
+       int                      error;
+
+       if ((flags & RTF_HOST) == 0 && ifa->ifa_netmask) {
+               m = m_get(M_DONTWAIT, MT_SONAME);
+               if (m == NULL)
+                       return (ENOBUFS);
+               deldst = mtod(m, struct sockaddr *);
+               rt_maskedcopy(dst, deldst, ifa->ifa_netmask);
+               dst = deldst;
+       }
+       if ((rt = rtalloc1(dst, 0, rtableid)) != NULL) {
+               rt->rt_refcnt--;
+               /* try to find the right route */
+               while (rt && rt->rt_ifa != ifa)
+                       rt = (struct rtentry *)
+                           ((struct radix_node *)rt)->rn_dupedkey;
+               if (!rt) {
+                       if (m != NULL)
+                               (void) m_free(m);
+                       return (flags & RTF_HOST ? EHOSTUNREACH
+                                               : ENETUNREACH);
+               }
+       }
+
+       memset(&info, 0, sizeof(info));
+       info.rti_ifa = ifa;
+       info.rti_flags = flags;
+       info.rti_info[RTAX_DST] = dst;
+       info.rti_info[RTAX_LABEL] =
+           rtlabel_id2sa(ifa->ifa_ifp->if_rtlabelid, &sa_rl);
+
+       if ((flags & RTF_HOST) == 0)
+               info.rti_info[RTAX_NETMASK] = ifa->ifa_netmask;
+
+       error = rtrequest1(RTM_DELETE, &info, RTP_CONNECTED, &nrt, rtableid);
+       if (error == 0 && (rt = nrt) != NULL) {
+               rt_newaddrmsg(RTM_DELETE, ifa, error, nrt);
+               if (rt->rt_refcnt <= 0) {
+                       rt->rt_refcnt++;
+                       rtfree(rt);
+               }
        }
+       if (m != NULL)
+               m_free(m);
+
        return (error);
+}
+
+/*
+ * Add ifa's address as a loopback rtentry.
+ */
+void
+rt_ifa_addloop(struct ifaddr *ifa)
+{
+       struct rtentry *rt;
+
+       /* 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 ||
+           (rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0)
+               rt_ifa_add(ifa, RTF_UP| RTF_HOST | RTF_LLINFO, ifa->ifa_addr);
+       if (rt)
+               rt->rt_refcnt--;
+}
+
+/*
+ * Remove loopback rtentry of ifa's addresss if it exists.
+ */
+void
+rt_ifa_delloop(struct ifaddr *ifa)
+{
+       struct rtentry *rt;
+
+       /*
+        * Before deleting, check if a corresponding loopbacked host
+        * route surely exists.  With this check, we can avoid to
+        * delete an interface direct route whose destination is same
+        * as the address being removed.  This can happen when removing
+        * a subnet-router anycast address on an interface attached
+        * to a shared medium.
+        */
+       rt = rtalloc1(ifa->ifa_addr, 0, ifa->ifa_ifp->if_rdomain);
+       if (rt != NULL && (rt->rt_flags & RTF_HOST) != 0 &&
+           (rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0)
+               rt_ifa_del(ifa,  RTF_HOST | RTF_LLINFO,  ifa->ifa_addr);
+       if (rt)
+               rt->rt_refcnt--;
 }
 
 /*
Index: net/route.h
===================================================================
RCS file: /home/ncvs/src/sys/net/route.h,v
retrieving revision 1.89
diff -u -p -r1.89 route.h
--- net/route.h 21 Mar 2014 10:44:42 -0000      1.89
+++ net/route.h 27 Mar 2014 13:48:39 -0000
@@ -402,6 +402,8 @@ struct rtentry *
 void    rtfree(struct rtentry *);
 int     rt_getifa(struct rt_addrinfo *, u_int);
 int     rtinit(struct ifaddr *, int, int);
+void    rt_ifa_addloop(struct ifaddr *);
+void    rt_ifa_delloop(struct ifaddr *);
 int     rtioctl(u_long, caddr_t, struct proc *);
 void    rtredirect(struct sockaddr *, struct sockaddr *,
                         struct sockaddr *, int, struct sockaddr *,
Index: netinet6/in6.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet6/in6.c,v
retrieving revision 1.133
diff -u -p -r1.133 in6.c
--- netinet6/in6.c      27 Mar 2014 10:39:23 -0000      1.133
+++ netinet6/in6.c      27 Mar 2014 13:48:39 -0000
@@ -122,156 +122,11 @@ const struct in6_addr in6mask128 = IN6MA
 int in6_lifaddr_ioctl(struct socket *, u_long, caddr_t, struct ifnet *);
 int in6_ifinit(struct ifnet *, struct in6_ifaddr *, int);
 void in6_unlink_ifa(struct in6_ifaddr *, struct ifnet *);
-void in6_ifloop_request(int, struct ifaddr *);
 
 const struct sockaddr_in6 sa6_any = {
        sizeof(sa6_any), AF_INET6, 0, 0, IN6ADDR_ANY_INIT, 0
 };
 
-/*
- * Subroutine for in6_ifaddloop() and in6_ifremloop().
- * This routine does actual work.
- */
-void
-in6_ifloop_request(int cmd, struct ifaddr *ifa)
-{
-       struct rt_addrinfo info;
-       struct rtentry *nrt = NULL;
-       int e;
-
-       /*
-        * We specify the address itself as the gateway, and set the
-        * RTF_LLINFO flag, so that the corresponding host route would have
-        * the flag, and thus applications that assume traditional behavior
-        * would be happy.  Note that we assume the caller of the function
-        * (probably implicitly) set nd6_rtrequest() to ifa->ifa_rtrequest,
-        * which changes the outgoing interface to the loopback interface.
-        * XXX only table 0 for now
-        */
-       bzero(&info, sizeof(info));
-       info.rti_flags = RTF_UP | RTF_HOST | RTF_LLINFO;
-       info.rti_info[RTAX_DST] = ifa->ifa_addr;
-       if (cmd != RTM_DELETE)
-               info.rti_info[RTAX_GATEWAY] = ifa->ifa_addr;
-       e = rtrequest1(cmd, &info, RTP_CONNECTED, &nrt,
-           ifa->ifa_ifp->if_rdomain);
-       if (e != 0) {
-               char addr[INET6_ADDRSTRLEN];
-               log(LOG_ERR, "in6_ifloop_request: "
-                   "%s operation failed for %s (errno=%d)\n",
-                   cmd == RTM_ADD ? "ADD" : "DELETE",
-                   inet_ntop(AF_INET6,
-                       &ifatoia6(ifa)->ia_addr.sin6_addr, addr, sizeof(addr)),
-                   e);
-       }
-
-       /*
-        * Make sure rt_ifa be equal to IFA, the second argument of the
-        * function.
-        * We need this because when we refer to rt_ifa->ia6_flags in
-        * ip6_input, we assume that the rt_ifa points to the address instead
-        * of the loopback address.
-        */
-       if (cmd == RTM_ADD && nrt && ifa != nrt->rt_ifa) {
-               ifafree(nrt->rt_ifa);
-               ifa->ifa_refcnt++;
-               nrt->rt_ifa = ifa;
-       }
-
-       /*
-        * Report the addition/removal of the address to the routing socket.
-        * XXX: since we called rtinit for a p2p interface with a destination,
-        *      we end up reporting twice in such a case.  Should we rather
-        *      omit the second report?
-        */
-       if (nrt) {
-               rt_newaddrmsg(cmd, ifa, e, nrt);
-               if (cmd == RTM_DELETE) {
-                       if (nrt->rt_refcnt <= 0) {
-                               /* XXX: we should free the entry ourselves. */
-                               nrt->rt_refcnt++;
-                               rtfree(nrt);
-                       }
-               } else {
-                       /* the cmd must be RTM_ADD here */
-                       nrt->rt_refcnt--;
-               }
-       }
-}
-
-/*
- * Add ownaddr as loopback rtentry.  We previously add the route only if
- * necessary (ex. on a p2p link).  However, since we now manage addresses
- * separately from prefixes, we should always add the route.  We can't
- * rely on the cloning mechanism from the corresponding interface route
- * any more.
- */
-void
-in6_ifaddloop(struct ifaddr *ifa)
-{
-       struct rtentry *rt;
-
-       /* 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 ||
-           (rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0)
-               in6_ifloop_request(RTM_ADD, ifa);
-       if (rt)
-               rt->rt_refcnt--;
-}
-
-/*
- * Remove loopback rtentry of ownaddr generated by in6_ifaddloop(),
- * if it exists.
- */
-void
-in6_ifremloop(struct ifaddr *ifa)
-{
-       struct in6_ifaddr *ia6;
-       struct rtentry *rt;
-       int ia_count = 0;
-
-       /*
-        * Some of BSD variants do not remove cloned routes
-        * from an interface direct route, when removing the direct route
-        * (see comments in net/net_osdep.h).  Even for variants that do remove
-        * cloned routes, they could fail to remove the cloned routes when
-        * we handle multple addresses that share a common prefix.
-        * So, we should remove the route corresponding to the deleted address.
-        */
-
-       /*
-        * Delete the entry only if exact one ifa exists.  More than one ifa
-        * can exist if we assign a same single address to multiple
-        * (probably p2p) interfaces.
-        * XXX: we should avoid such a configuration in IPv6...
-        */
-       TAILQ_FOREACH(ia6, &in6_ifaddr, ia_list) {
-               if (IN6_ARE_ADDR_EQUAL(IFA_IN6(ifa), &ia6->ia_addr.sin6_addr)) {
-                       ia_count++;
-                       if (ia_count > 1)
-                               break;
-               }
-       }
-
-       if (ia_count == 1) {
-               /*
-                * Before deleting, check if a corresponding loopbacked host
-                * route surely exists.  With this check, we can avoid to
-                * delete an interface direct route whose destination is same
-                * as the address being removed.  This can happen when removing
-                * a subnet-router anycast address on an interface attached
-                * to a shared medium.
-                */
-               rt = rtalloc1(ifa->ifa_addr, 0, ifa->ifa_ifp->if_rdomain);
-               if (rt != NULL && (rt->rt_flags & RTF_HOST) != 0 &&
-                   (rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0) {
-                       rt->rt_refcnt--;
-                       in6_ifloop_request(RTM_DELETE, ifa);
-               }
-       }
-}
-
 int
 in6_mask2len(struct in6_addr *mask, u_char *lim0)
 {
@@ -1151,8 +1006,9 @@ void
 in6_purgeaddr(struct ifaddr *ifa)
 {
        struct ifnet *ifp = ifa->ifa_ifp;
-       struct in6_ifaddr *ia6 = ifatoia6(ifa);
+       struct in6_ifaddr *tmp, *ia6 = ifatoia6(ifa);
        struct in6_multi_mship *imm;
+       int ia6_count = 0;
 
        /* stop DAD processing */
        nd6_dad_stop(ifa);
@@ -1178,8 +1034,32 @@ in6_purgeaddr(struct ifaddr *ifa)
                        ia6->ia_flags &= ~IFA_ROUTE;
        }
 
-       /* Remove ownaddr's loopback rtentry, if it exists. */
-       in6_ifremloop(&(ia6->ia_ifa));
+       /* Remove ownaddr's loopback rtentry, if it exists.
+        *
+        * Some of BSD variants do not remove cloned routes from an
+        * interface direct route, when removing the direct route (see
+        * comments in net/net_osdep.h).  Even for variants that do
+        * remove cloned routes, they could fail to remove the cloned
+        * routes when we handle multiple addresses that share a common
+        * prefix.  So, we should remove the route corresponding to the
+        * deleted address.
+        *
+        * Delete the entry only if exact one ifa exists.  More than one
+        * ifa can exist if we assign a same single address to multiple
+        * (probably p2p) interfaces.
+        * XXX: we should avoid such a configuration in IPv6...
+        */
+       TAILQ_FOREACH(tmp, &in6_ifaddr, ia_list) {
+               if (IN6_ARE_ADDR_EQUAL(&tmp->ia_addr.sin6_addr,
+                   &ia6->ia_addr.sin6_addr)) {
+                       ia6_count++;
+                       if (ia6_count > 1)
+                               break;
+               }
+       }
+
+       if (ia6_count == 1)
+               rt_ifa_delloop(&(ia6->ia_ifa));
 
        /*
         * leave from multicast groups we have joined for the interface
@@ -1517,7 +1397,7 @@ in6_ifinit(struct ifnet *ifp, struct in6
        if (newhost) {
                /* set the rtrequest function to create llinfo */
                ia6->ia_ifa.ifa_rtrequest = nd6_rtrequest;
-               in6_ifaddloop(&(ia6->ia_ifa));
+               rt_ifa_addloop(&(ia6->ia_ifa));
        }
 
        return (error);
Index: netinet6/in6_var.h
===================================================================
RCS file: /home/ncvs/src/sys/netinet6/in6_var.h,v
retrieving revision 1.48
diff -u -p -r1.48 in6_var.h
--- netinet6/in6_var.h  27 Mar 2014 10:39:23 -0000      1.48
+++ netinet6/in6_var.h  27 Mar 2014 13:48:39 -0000
@@ -524,8 +524,6 @@ int in6_matchlen(struct in6_addr *, stru
 int    in6_are_prefix_equal(struct in6_addr *, struct in6_addr *, int);
 void   in6_prefixlen2mask(struct in6_addr *, int);
 void   in6_purgeprefix(struct ifnet *);
-void   in6_ifaddloop(struct ifaddr *);
-void   in6_ifremloop(struct ifaddr *);
 #endif /* _KERNEL */
 
 #endif /* _NETINET6_IN6_VAR_H_ */

Reply via email to