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:
parent
e263ec16a9
commit
17a036563d
@ -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 */
|
||||
|
@ -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))
|
||||
if (!(flags & RTF_DONE) && rt) {
|
||||
if (!sa_equal(src, rt->rt_gateway)) {
|
||||
error = EINVAL;
|
||||
else if (ifa_ifwithaddr_check(gateway))
|
||||
error = EHOSTUNREACH;
|
||||
if (error)
|
||||
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;
|
||||
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,6 +662,7 @@ 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);
|
||||
if (gwrt)
|
||||
RTFREE_LOCKED(gwrt);
|
||||
}
|
||||
} else
|
||||
|
@ -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 */
|
||||
{
|
||||
|
@ -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) */
|
||||
else {
|
||||
if (is_newentry) /* (6-7) */
|
||||
ln->ln_router = 0;
|
||||
ln->la_flags |= LLE_REDIRECT;
|
||||
}
|
||||
break;
|
||||
case ND_ROUTER_SOLICIT:
|
||||
/*
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user