I'll give it a try when I get home :)
On Mon, Mar 31, 2014 at 6:30 PM, Martin Pieuchot <mpieuc...@nolizard.org> wrote: > 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_ */ >> > -- This message is strictly personal and the opinions expressed do not represent those of my employers, either past or present.