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