This diff removes a tricky "rt->rt_refcnt--". It changes the behavior
of nd6_lookup() to always return a route entry with a valid reference
count. That means that rtfree(9) should be called after nd6_lookup().
There's on hairy case in nd6_output(). This is because of the dance
done in rt_checkgate(). This will be simplified with my rtalloc(9)
rewrite.
I'd like to have careful reviews. I'm running with this for a couple
of months but I doubt I'm exercising all the code paths.
Index: netinet6/icmp6.c
===================================================================
RCS file: /cvs/src/sys/netinet6/icmp6.c,v
retrieving revision 1.171
diff -u -p -r1.171 icmp6.c
--- netinet6/icmp6.c 11 Sep 2015 22:00:36 -0000 1.171
+++ netinet6/icmp6.c 12 Sep 2015 09:08:32 -0000
@@ -1677,24 +1677,21 @@ icmp6_redirect_output(struct mbuf *m0, s
{
/* target lladdr option */
- struct rtentry *rt_nexthop = NULL;
int len;
struct sockaddr_dl *sdl;
struct nd_opt_hdr *nd_opt;
char *lladdr;
- rt_nexthop = nd6_lookup(nexthop, 0, ifp, ifp->if_rdomain);
- if (!rt_nexthop)
- goto nolladdropt;
len = sizeof(*nd_opt) + ifp->if_addrlen;
len = (len + 7) & ~7; /* round by 8 */
/* safety check */
if (len + (p - (u_char *)ip6) > maxlen)
goto nolladdropt;
- if (!(rt_nexthop->rt_flags & RTF_GATEWAY) &&
- (rt_nexthop->rt_flags & RTF_LLINFO) &&
- (rt_nexthop->rt_gateway->sa_family == AF_LINK) &&
- (sdl = (struct sockaddr_dl *)rt_nexthop->rt_gateway) &&
+ rt = nd6_lookup(nexthop, 0, ifp, ifp->if_rdomain);
+ if ((rt != NULL) &&
+ (rt->rt_flags & (RTF_GATEWAY|RTF_LLINFO)) == RTF_LLINFO &&
+ (rt->rt_gateway->sa_family == AF_LINK) &&
+ (sdl = (struct sockaddr_dl *)rt->rt_gateway) &&
sdl->sdl_alen) {
nd_opt = (struct nd_opt_hdr *)p;
nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
@@ -1703,6 +1700,7 @@ icmp6_redirect_output(struct mbuf *m0, s
bcopy(LLADDR(sdl), lladdr, ifp->if_addrlen);
p += len;
}
+ rtfree(rt);
}
nolladdropt:;
Index: netinet6/in6_src.c
===================================================================
RCS file: /cvs/src/sys/netinet6/in6_src.c,v
retrieving revision 1.61
diff -u -p -r1.61 in6_src.c
--- netinet6/in6_src.c 11 Sep 2015 20:16:03 -0000 1.61
+++ netinet6/in6_src.c 12 Sep 2015 09:08:32 -0000
@@ -232,11 +232,12 @@ in6_selectsrc(struct in6_addr **in6src,
sin6_next = satosin6(opts->ip6po_nexthop);
rt = nd6_lookup(&sin6_next->sin6_addr, 1, NULL,
rtableid);
- if (rt) {
+ if (rt != NULL) {
ia6 = in6_ifawithscope(rt->rt_ifp, dst,
rtableid);
if (ia6 == NULL)
ia6 = ifatoia6(rt->rt_ifa);
+ rtfree(rt);
}
if (ia6 == NULL)
return (EADDRNOTAVAIL);
Index: netinet6/nd6.c
===================================================================
RCS file: /cvs/src/sys/netinet6/nd6.c,v
retrieving revision 1.150
diff -u -p -r1.150 nd6.c
--- netinet6/nd6.c 10 Sep 2015 17:52:05 -0000 1.150
+++ netinet6/nd6.c 12 Sep 2015 09:10:14 -0000
@@ -631,7 +631,7 @@ nd6_lookup(struct in6_addr *addr6, int c
flags = (create) ? (RT_REPORT|RT_RESOLVE) : 0;
rt = rtalloc(sin6tosa(&sin6), flags, rtableid);
- if (rt && (rt->rt_flags & RTF_LLINFO) == 0) {
+ if (rt != NULL && (rt->rt_flags & RTF_LLINFO) == 0) {
/*
* This is the case for the default route.
* If we want to create a neighbor cache for the address, we
@@ -643,7 +643,7 @@ nd6_lookup(struct in6_addr *addr6, int c
rt = NULL;
}
}
- if (!rt) {
+ if (rt == NULL) {
if (create && ifp) {
struct rt_addrinfo info;
int error;
@@ -683,7 +683,6 @@ nd6_lookup(struct in6_addr *addr6, int c
} else
return (NULL);
}
- rt->rt_refcnt--;
/*
* Validation for the entry.
* Note that the check for rt_llinfo is necessary because a cloned
@@ -706,6 +705,7 @@ nd6_lookup(struct in6_addr *addr6, int c
inet_ntop(AF_INET6, addr6, addr, sizeof(addr)),
ifp ? ifp->if_xname : "unspec"));
}
+ rtfree(rt);
return (NULL);
}
return (rt);
@@ -769,9 +769,11 @@ nd6_is_addr_neighbor(struct sockaddr_in6
* Even if the address matches none of our addresses, it might be
* in the neighbor cache.
*/
- if ((rt = nd6_lookup(&addr->sin6_addr, 0, ifp,
- ifp->if_rdomain)) != NULL)
+ rt = nd6_lookup(&addr->sin6_addr, 0, ifp, ifp->if_rdomain);
+ if (rt != NULL) {
+ rtfree(rt);
return (1);
+ }
return (0);
}
@@ -1281,9 +1283,11 @@ nd6_ioctl(u_long cmd, caddr_t data, stru
}
s = splsoftnet();
- if ((rt = nd6_lookup(&nb_addr, 0, ifp, ifp->if_rdomain)) ==
NULL ||
+ rt = nd6_lookup(&nb_addr, 0, ifp, ifp->if_rdomain);
+ if (rt == NULL ||
(ln = (struct llinfo_nd6 *)rt->rt_llinfo) == NULL) {
error = EINVAL;
+ rtfree(rt);
splx(s);
break;
}
@@ -1291,6 +1295,7 @@ nd6_ioctl(u_long cmd, caddr_t data, stru
nbi->asked = ln->ln_asked;
nbi->isrouter = ln->ln_router;
nbi->expire = ln->ln_expire;
+ rtfree(rt);
splx(s);
break;
@@ -1339,7 +1344,7 @@ nd6_cache_lladdr(struct ifnet *ifp, stru
*/
rt = nd6_lookup(from, 0, ifp, ifp->if_rdomain);
- if (!rt) {
+ if (rt == NULL) {
#if 0
/* nothing must be done if there's no lladdr */
if (!lladdr || !lladdrlen)
@@ -1351,6 +1356,7 @@ nd6_cache_lladdr(struct ifnet *ifp, stru
} else {
/* do nothing if static ndp is set */
if (rt->rt_flags & RTF_STATIC)
+ rtfree(rt);
return;
is_newentry = 0;
}
@@ -1360,6 +1366,7 @@ nd6_cache_lladdr(struct ifnet *ifp, stru
if ((rt->rt_flags & (RTF_GATEWAY | RTF_LLINFO)) != RTF_LLINFO) {
fail:
(void)nd6_free(rt, 0);
+ rtfree(rt);
return;
}
ln = (struct llinfo_nd6 *)rt->rt_llinfo;
@@ -1535,6 +1542,8 @@ fail:
*/
if (do_update && ln->ln_router && (ifp->if_xflags & IFXF_AUTOCONF6))
defrouter_select();
+
+ rtfree(rt);
}
void
@@ -1563,7 +1572,6 @@ nd6_slowtimo(void *ignored_arg)
splx(s);
}
-#define senderr(e) { error = (e); goto bad;}
int
nd6_output(struct ifnet *ifp, struct mbuf *m0, struct sockaddr_in6 *dst,
struct rtentry *rt0)
@@ -1571,7 +1579,7 @@ nd6_output(struct ifnet *ifp, struct mbu
struct mbuf *m = m0;
struct rtentry *rt = rt0;
struct llinfo_nd6 *ln = NULL;
- int error = 0;
+ int created = 0, error = 0;
if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr))
goto sendpkt;
@@ -1620,10 +1628,14 @@ nd6_output(struct ifnet *ifp, struct mbu
* the condition below is not very efficient. But we believe
* it is tolerable, because this should be a rare case.
*/
- if (nd6_is_addr_neighbor(dst, ifp) &&
- (rt = nd6_lookup(&dst->sin6_addr, 1, ifp,
- ifp->if_rdomain)) != NULL)
- ln = (struct llinfo_nd6 *)rt->rt_llinfo;
+ if (nd6_is_addr_neighbor(dst, ifp)) {
+ rt = nd6_lookup(&dst->sin6_addr, 1, ifp,
+ ifp->if_rdomain);
+ if (rt != NULL) {
+ created = 1;
+ ln = (struct llinfo_nd6 *)rt->rt_llinfo;
+ }
+ }
}
if (!ln || !rt) {
if ((ifp->if_flags & IFF_POINTOPOINT) == 0 &&
@@ -1635,7 +1647,10 @@ nd6_output(struct ifnet *ifp, struct mbu
inet_ntop(AF_INET6, &dst->sin6_addr,
addr, sizeof(addr)),
ln, rt);
- senderr(EIO); /* XXX: good error? */
+ m_freem(m);
+ if (created)
+ rtfree(rt);
+ return (EIO); /* XXX: good error? */
}
goto sendpkt; /* send anyway */
@@ -1699,13 +1714,11 @@ nd6_output(struct ifnet *ifp, struct mbu
return (0);
sendpkt:
- return ((*ifp->if_output)(ifp, m, sin6tosa(dst), rt));
-
- bad:
- m_freem(m);
+ error = ((*ifp->if_output)(ifp, m, sin6tosa(dst), rt));
+ if (created)
+ rtfree(rt);
return (error);
}
-#undef senderr
int
nd6_need_cache(struct ifnet *ifp)
Index: netinet6/nd6_nbr.c
===================================================================
RCS file: /cvs/src/sys/netinet6/nd6_nbr.c,v
retrieving revision 1.95
diff -u -p -r1.95 nd6_nbr.c
--- netinet6/nd6_nbr.c 11 Sep 2015 20:13:22 -0000 1.95
+++ netinet6/nd6_nbr.c 12 Sep 2015 09:08:37 -0000
@@ -888,6 +888,7 @@ nd6_na_input(struct mbuf *m, int off, in
}
freeit:
+ rtfree(rt);
m_freem(m);
if_put(ifp);
return;
Index: netinet6/nd6_rtr.c
===================================================================
RCS file: /cvs/src/sys/netinet6/nd6_rtr.c,v
retrieving revision 1.123
diff -u -p -r1.123 nd6_rtr.c
--- netinet6/nd6_rtr.c 11 Sep 2015 08:17:06 -0000 1.123
+++ netinet6/nd6_rtr.c 12 Sep 2015 09:10:15 -0000
@@ -804,12 +804,15 @@ defrouter_select(void)
TAILQ_FOREACH(dr, &nd_defrouter, dr_entry) {
if (!(dr->ifp->if_xflags & IFXF_AUTOCONF6))
continue;
- if (!selected_dr &&
- (rt = nd6_lookup(&dr->rtaddr, 0, dr->ifp,
- dr->ifp->if_rdomain)) &&
- (ln = (struct llinfo_nd6 *)rt->rt_llinfo) &&
- ND6_IS_LLINFO_PROBREACH(ln)) {
- selected_dr = dr;
+ if (!selected_dr) {
+ rt = nd6_lookup(&dr->rtaddr, 0, dr->ifp,
+ dr->ifp->if_rdomain);
+ if ((rt != NULL) &&
+ (ln = (struct llinfo_nd6 *)rt->rt_llinfo) &&
+ ND6_IS_LLINFO_PROBREACH(ln)) {
+ selected_dr = dr;
+ }
+ rtfree(rt);
}
if (dr->installed && !installed_dr)
@@ -833,13 +836,15 @@ defrouter_select(void)
selected_dr = TAILQ_FIRST(&nd_defrouter);
else
selected_dr = TAILQ_NEXT(installed_dr, dr_entry);
- } else if (installed_dr &&
- (rt = nd6_lookup(&installed_dr->rtaddr, 0, installed_dr->ifp,
- installed_dr->ifp->if_rdomain)) &&
- (ln = (struct llinfo_nd6 *)rt->rt_llinfo) &&
- ND6_IS_LLINFO_PROBREACH(ln) &&
- rtpref(selected_dr) <= rtpref(installed_dr)) {
- selected_dr = installed_dr;
+ } else if (installed_dr) {
+ rt = nd6_lookup(&installed_dr->rtaddr, 0, installed_dr->ifp,
+ installed_dr->ifp->if_rdomain);
+ if ((rt != NULL) && (ln = (struct llinfo_nd6 *)rt->rt_llinfo) &&
+ ND6_IS_LLINFO_PROBREACH(ln) &&
+ rtpref(selected_dr) <= rtpref(installed_dr)) {
+ selected_dr = installed_dr;
+ }
+ rtfree(rt);
}
/*
@@ -1536,7 +1541,7 @@ struct nd_pfxrouter *
find_pfxlist_reachable_router(struct nd_prefix *pr)
{
struct nd_pfxrouter *pfxrtr;
- struct rtentry *rt;
+ struct rtentry *rt = NULL;
struct llinfo_nd6 *ln;
LIST_FOREACH(pfxrtr, &pr->ndpr_advrtrs, pfr_entry) {
@@ -1545,7 +1550,9 @@ find_pfxlist_reachable_router(struct nd_
(ln = (struct llinfo_nd6 *)rt->rt_llinfo) &&
ND6_IS_LLINFO_PROBREACH(ln))
break; /* found */
+ rtfree(rt);
}
+ rtfree(rt);
return (pfxrtr);
}