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_ */
>