On 27/03/14(Thu) 15:14, Martin Pieuchot wrote:
> 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?

Anybody?

> 
> 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