Fix the handling of IPv6 On-Link Redirects.

On receipt of a redirect message, install an interface route for the
redirected destination.  On removal of the corresponding Neighbor Cache
entry, remove the interface route.

This requires changes in rtredirect_fib() to cope with an AF_LINK
address for the gateway and with the absence of RTF_GATEWAY.

This fixes the "Redirected On-Link" test cases in the Tahi IPv6 Ready Logo
Phase 2 test suite.

Unrelated to the above, fix a recursion on the radix node head lock
triggered by the Tahi Redirected to Alternate Router test cases.

When I first wrote this patch in October 2012, all Section 2
(Neighbor Discovery) test cases passed on 10-CURRENT, 9-STABLE,
and 8-STABLE.  cem@ recently rebased the 10.x patch onto head and reported
that it passes Tahi.  (Thanks!)

These other test cases also passed in 2012:

* the RTF_MODIFIED case, with IPv4 and IPv6 (using a
  RTF_HOST|RTF_GATEWAY route for the destination)

* the redirected-to-self case, with IPv4 and IPv6

* a valid IPv4 redirect

All testing in 2012 was done with WITNESS and INVARIANTS.

Tested by:    EMC / Isilon Storage Division via Conrad Meyer (cem) in 2015,
              Mark Kelley <mark_kelley@dell.com> in 2012,
              TC Telkamp <terence_telkamp@dell.com> in 2012
PR:           152791
Reviewed by:  melifaro (current rev), bz (earlier rev)
Approved by:  kib (mentor)
MFC after:    1 month
Relnotes:     yes
Sponsored by: Dell Inc.
Differential Revision: https://reviews.freebsd.org/D3602
This commit is contained in:
Eric van Gyzen 2015-09-14 19:17:25 +00:00
parent e263ec16a9
commit 17a036563d
5 changed files with 85 additions and 21 deletions

View File

@ -183,6 +183,7 @@ MALLOC_DECLARE(M_LLTABLE);
#define LLE_STATIC 0x0002 /* entry is static */
#define LLE_IFADDR 0x0004 /* entry is interface addr */
#define LLE_VALID 0x0008 /* ll_addr is valid */
#define LLE_REDIRECT 0x0010 /* installed by redirect; has host rtentry */
#define LLE_PUB 0x0020 /* publish entry ??? */
#define LLE_LINKED 0x0040 /* linked to lookup structure */
/* LLE request flags */

View File

@ -584,13 +584,20 @@ rtredirect_fib(struct sockaddr *dst,
* we have a routing loop, perhaps as a result of an interface
* going down recently.
*/
if (!(flags & RTF_DONE) && rt &&
(!sa_equal(src, rt->rt_gateway) || rt->rt_ifa != ifa))
error = EINVAL;
else if (ifa_ifwithaddr_check(gateway))
if (!(flags & RTF_DONE) && rt) {
if (!sa_equal(src, rt->rt_gateway)) {
error = EINVAL;
goto done;
}
if (rt->rt_ifa != ifa && ifa->ifa_addr->sa_family != AF_LINK) {
error = EINVAL;
goto done;
}
}
if ((flags & RTF_GATEWAY) && ifa_ifwithaddr_check(gateway)) {
error = EHOSTUNREACH;
if (error)
goto done;
}
/*
* Create a new entry if we just got back a wildcard entry
* or the lookup failed. This is necessary for hosts
@ -613,7 +620,7 @@ rtredirect_fib(struct sockaddr *dst,
rt0 = rt;
rt = NULL;
flags |= RTF_GATEWAY | RTF_DYNAMIC;
flags |= RTF_DYNAMIC;
bzero((caddr_t)&info, sizeof(info));
info.rti_info[RTAX_DST] = dst;
info.rti_info[RTAX_GATEWAY] = gateway;
@ -640,6 +647,8 @@ rtredirect_fib(struct sockaddr *dst,
* Smash the current notion of the gateway to
* this destination. Should check about netmask!!!
*/
if ((flags & RTF_GATEWAY) == 0)
rt->rt_flags &= ~RTF_GATEWAY;
rt->rt_flags |= RTF_MODIFIED;
flags |= RTF_MODIFIED;
stat = &V_rtstat.rts_newgateway;
@ -653,7 +662,8 @@ rtredirect_fib(struct sockaddr *dst,
gwrt = rtalloc1(gateway, 1, RTF_RNH_LOCKED);
RADIX_NODE_HEAD_UNLOCK(rnh);
EVENTHANDLER_INVOKE(route_redirect_event, rt, gwrt, dst);
RTFREE_LOCKED(gwrt);
if (gwrt)
RTFREE_LOCKED(gwrt);
}
} else
error = EHOSTUNREACH;

View File

@ -2434,27 +2434,39 @@ icmp6_redirect_input(struct mbuf *m, int off)
nd6_cache_lladdr(ifp, &redtgt6, lladdr, lladdrlen, ND_REDIRECT,
is_onlink ? ND_REDIRECT_ONLINK : ND_REDIRECT_ROUTER);
if (!is_onlink) { /* better router case. perform rtredirect. */
/* perform rtredirect */
/*
* Install a gateway route in the better-router case or an interface
* route in the on-link-destination case.
*/
{
struct sockaddr_in6 sdst;
struct sockaddr_in6 sgw;
struct sockaddr_in6 ssrc;
struct sockaddr *gw;
int rt_flags;
u_int fibnum;
bzero(&sdst, sizeof(sdst));
bzero(&sgw, sizeof(sgw));
bzero(&ssrc, sizeof(ssrc));
sdst.sin6_family = sgw.sin6_family = ssrc.sin6_family = AF_INET6;
sdst.sin6_len = sgw.sin6_len = ssrc.sin6_len =
sizeof(struct sockaddr_in6);
bcopy(&redtgt6, &sgw.sin6_addr, sizeof(struct in6_addr));
sdst.sin6_family = ssrc.sin6_family = AF_INET6;
sdst.sin6_len = ssrc.sin6_len = sizeof(struct sockaddr_in6);
bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr));
bcopy(&src6, &ssrc.sin6_addr, sizeof(struct in6_addr));
rt_flags = RTF_HOST;
if (is_router) {
bzero(&sgw, sizeof(sgw));
sgw.sin6_family = AF_INET6;
sgw.sin6_len = sizeof(struct sockaddr_in6);
bcopy(&redtgt6, &sgw.sin6_addr,
sizeof(struct in6_addr));
gw = (struct sockaddr *)&sgw;
rt_flags |= RTF_GATEWAY;
} else
gw = ifp->if_addr->ifa_addr;
for (fibnum = 0; fibnum < rt_numfibs; fibnum++)
in6_rtredirect((struct sockaddr *)&sdst,
(struct sockaddr *)&sgw, (struct sockaddr *)NULL,
RTF_GATEWAY | RTF_HOST, (struct sockaddr *)&ssrc,
fibnum);
in6_rtredirect((struct sockaddr *)&sdst, gw,
(struct sockaddr *)NULL, rt_flags,
(struct sockaddr *)&ssrc, fibnum);
}
/* finally update cached route in each socket via pfctlinput */
{

View File

@ -132,6 +132,7 @@ static void nd6_setmtu0(struct ifnet *, struct nd_ifinfo *);
static void nd6_slowtimo(void *);
static int regen_tmpaddr(struct in6_ifaddr *);
static struct llentry *nd6_free(struct llentry *, int);
static void nd6_free_redirect(const struct llentry *);
static void nd6_llinfo_timer(void *);
static void clear_llinfo_pqueue(struct llentry *);
static void nd6_rtrequest(int, struct rtentry *, struct rt_addrinfo *);
@ -1223,6 +1224,13 @@ nd6_free(struct llentry *ln, int gc)
defrouter_select();
}
/*
* If this entry was added by an on-link redirect, remove the
* corresponding host route.
*/
if (ln->la_flags & LLE_REDIRECT)
nd6_free_redirect(ln);
if (ln->ln_router || dr)
LLE_WLOCK(ln);
}
@ -1255,6 +1263,36 @@ nd6_free(struct llentry *ln, int gc)
return (next);
}
/*
* Remove the rtentry for the given llentry,
* both of which were installed by a redirect.
*/
static void
nd6_free_redirect(const struct llentry *ln)
{
int fibnum;
struct rtentry *rt;
struct radix_node_head *rnh;
struct sockaddr_in6 sin6;
lltable_fill_sa_entry(ln, (struct sockaddr *)&sin6);
for (fibnum = 0; fibnum < rt_numfibs; fibnum++) {
rnh = rt_tables_get_rnh(fibnum, AF_INET6);
if (rnh == NULL)
continue;
RADIX_NODE_HEAD_LOCK(rnh);
rt = in6_rtalloc1((struct sockaddr *)&sin6, 0,
RTF_RNH_LOCKED, fibnum);
if (rt) {
if (rt->rt_flags == (RTF_UP | RTF_HOST | RTF_DYNAMIC))
rt_expunge(rnh, rt);
RTFREE_LOCKED(rt);
}
RADIX_NODE_HEAD_UNLOCK(rnh);
}
}
/*
* Upper-layer reachability hint for Neighbor Unreachability Detection.
*
@ -1746,8 +1784,11 @@ nd6_cache_lladdr(struct ifnet *ifp, struct in6_addr *from, char *lladdr,
*/
if (code == ND_REDIRECT_ROUTER)
ln->ln_router = 1;
else if (is_newentry) /* (6-7) */
ln->ln_router = 0;
else {
if (is_newentry) /* (6-7) */
ln->ln_router = 0;
ln->la_flags |= LLE_REDIRECT;
}
break;
case ND_ROUTER_SOLICIT:
/*

View File

@ -2105,7 +2105,7 @@ rt6_deleteroute(struct rtentry *rt, void *arg)
return (0);
return (in6_rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway,
rt_mask(rt), rt->rt_flags, NULL, rt->rt_fibnum));
rt_mask(rt), rt->rt_flags | RTF_RNH_LOCKED, NULL, rt->rt_fibnum));
#undef SIN6
}