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
da6ab8d992
commit
75d72d4482
@ -183,6 +183,7 @@ MALLOC_DECLARE(M_LLTABLE);
|
|||||||
#define LLE_STATIC 0x0002 /* entry is static */
|
#define LLE_STATIC 0x0002 /* entry is static */
|
||||||
#define LLE_IFADDR 0x0004 /* entry is interface addr */
|
#define LLE_IFADDR 0x0004 /* entry is interface addr */
|
||||||
#define LLE_VALID 0x0008 /* ll_addr is valid */
|
#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_PUB 0x0020 /* publish entry ??? */
|
||||||
#define LLE_LINKED 0x0040 /* linked to lookup structure */
|
#define LLE_LINKED 0x0040 /* linked to lookup structure */
|
||||||
/* LLE request flags */
|
/* LLE request flags */
|
||||||
|
@ -584,13 +584,20 @@ rtredirect_fib(struct sockaddr *dst,
|
|||||||
* we have a routing loop, perhaps as a result of an interface
|
* we have a routing loop, perhaps as a result of an interface
|
||||||
* going down recently.
|
* going down recently.
|
||||||
*/
|
*/
|
||||||
if (!(flags & RTF_DONE) && rt &&
|
if (!(flags & RTF_DONE) && rt) {
|
||||||
(!sa_equal(src, rt->rt_gateway) || rt->rt_ifa != ifa))
|
if (!sa_equal(src, rt->rt_gateway)) {
|
||||||
error = EINVAL;
|
error = EINVAL;
|
||||||
else if (ifa_ifwithaddr_check(gateway))
|
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;
|
error = EHOSTUNREACH;
|
||||||
if (error)
|
|
||||||
goto done;
|
goto done;
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* Create a new entry if we just got back a wildcard entry
|
* Create a new entry if we just got back a wildcard entry
|
||||||
* or the lookup failed. This is necessary for hosts
|
* or the lookup failed. This is necessary for hosts
|
||||||
@ -613,7 +620,7 @@ rtredirect_fib(struct sockaddr *dst,
|
|||||||
rt0 = rt;
|
rt0 = rt;
|
||||||
rt = NULL;
|
rt = NULL;
|
||||||
|
|
||||||
flags |= RTF_GATEWAY | RTF_DYNAMIC;
|
flags |= RTF_DYNAMIC;
|
||||||
bzero((caddr_t)&info, sizeof(info));
|
bzero((caddr_t)&info, sizeof(info));
|
||||||
info.rti_info[RTAX_DST] = dst;
|
info.rti_info[RTAX_DST] = dst;
|
||||||
info.rti_info[RTAX_GATEWAY] = gateway;
|
info.rti_info[RTAX_GATEWAY] = gateway;
|
||||||
@ -640,6 +647,8 @@ rtredirect_fib(struct sockaddr *dst,
|
|||||||
* Smash the current notion of the gateway to
|
* Smash the current notion of the gateway to
|
||||||
* this destination. Should check about netmask!!!
|
* this destination. Should check about netmask!!!
|
||||||
*/
|
*/
|
||||||
|
if ((flags & RTF_GATEWAY) == 0)
|
||||||
|
rt->rt_flags &= ~RTF_GATEWAY;
|
||||||
rt->rt_flags |= RTF_MODIFIED;
|
rt->rt_flags |= RTF_MODIFIED;
|
||||||
flags |= RTF_MODIFIED;
|
flags |= RTF_MODIFIED;
|
||||||
stat = &V_rtstat.rts_newgateway;
|
stat = &V_rtstat.rts_newgateway;
|
||||||
@ -653,7 +662,8 @@ rtredirect_fib(struct sockaddr *dst,
|
|||||||
gwrt = rtalloc1(gateway, 1, RTF_RNH_LOCKED);
|
gwrt = rtalloc1(gateway, 1, RTF_RNH_LOCKED);
|
||||||
RADIX_NODE_HEAD_UNLOCK(rnh);
|
RADIX_NODE_HEAD_UNLOCK(rnh);
|
||||||
EVENTHANDLER_INVOKE(route_redirect_event, rt, gwrt, dst);
|
EVENTHANDLER_INVOKE(route_redirect_event, rt, gwrt, dst);
|
||||||
RTFREE_LOCKED(gwrt);
|
if (gwrt)
|
||||||
|
RTFREE_LOCKED(gwrt);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
error = EHOSTUNREACH;
|
error = EHOSTUNREACH;
|
||||||
|
@ -2434,27 +2434,39 @@ icmp6_redirect_input(struct mbuf *m, int off)
|
|||||||
nd6_cache_lladdr(ifp, &redtgt6, lladdr, lladdrlen, ND_REDIRECT,
|
nd6_cache_lladdr(ifp, &redtgt6, lladdr, lladdrlen, ND_REDIRECT,
|
||||||
is_onlink ? ND_REDIRECT_ONLINK : ND_REDIRECT_ROUTER);
|
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 sdst;
|
||||||
struct sockaddr_in6 sgw;
|
struct sockaddr_in6 sgw;
|
||||||
struct sockaddr_in6 ssrc;
|
struct sockaddr_in6 ssrc;
|
||||||
|
struct sockaddr *gw;
|
||||||
|
int rt_flags;
|
||||||
u_int fibnum;
|
u_int fibnum;
|
||||||
|
|
||||||
bzero(&sdst, sizeof(sdst));
|
bzero(&sdst, sizeof(sdst));
|
||||||
bzero(&sgw, sizeof(sgw));
|
|
||||||
bzero(&ssrc, sizeof(ssrc));
|
bzero(&ssrc, sizeof(ssrc));
|
||||||
sdst.sin6_family = sgw.sin6_family = ssrc.sin6_family = AF_INET6;
|
sdst.sin6_family = ssrc.sin6_family = AF_INET6;
|
||||||
sdst.sin6_len = sgw.sin6_len = ssrc.sin6_len =
|
sdst.sin6_len = ssrc.sin6_len = sizeof(struct sockaddr_in6);
|
||||||
sizeof(struct sockaddr_in6);
|
|
||||||
bcopy(&redtgt6, &sgw.sin6_addr, sizeof(struct in6_addr));
|
|
||||||
bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr));
|
bcopy(&reddst6, &sdst.sin6_addr, sizeof(struct in6_addr));
|
||||||
bcopy(&src6, &ssrc.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++)
|
for (fibnum = 0; fibnum < rt_numfibs; fibnum++)
|
||||||
in6_rtredirect((struct sockaddr *)&sdst,
|
in6_rtredirect((struct sockaddr *)&sdst, gw,
|
||||||
(struct sockaddr *)&sgw, (struct sockaddr *)NULL,
|
(struct sockaddr *)NULL, rt_flags,
|
||||||
RTF_GATEWAY | RTF_HOST, (struct sockaddr *)&ssrc,
|
(struct sockaddr *)&ssrc, fibnum);
|
||||||
fibnum);
|
|
||||||
}
|
}
|
||||||
/* finally update cached route in each socket via pfctlinput */
|
/* 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 void nd6_slowtimo(void *);
|
||||||
static int regen_tmpaddr(struct in6_ifaddr *);
|
static int regen_tmpaddr(struct in6_ifaddr *);
|
||||||
static struct llentry *nd6_free(struct llentry *, int);
|
static struct llentry *nd6_free(struct llentry *, int);
|
||||||
|
static void nd6_free_redirect(const struct llentry *);
|
||||||
static void nd6_llinfo_timer(void *);
|
static void nd6_llinfo_timer(void *);
|
||||||
static void clear_llinfo_pqueue(struct llentry *);
|
static void clear_llinfo_pqueue(struct llentry *);
|
||||||
static void nd6_rtrequest(int, struct rtentry *, struct rt_addrinfo *);
|
static void nd6_rtrequest(int, struct rtentry *, struct rt_addrinfo *);
|
||||||
@ -1223,6 +1224,13 @@ nd6_free(struct llentry *ln, int gc)
|
|||||||
defrouter_select();
|
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)
|
if (ln->ln_router || dr)
|
||||||
LLE_WLOCK(ln);
|
LLE_WLOCK(ln);
|
||||||
}
|
}
|
||||||
@ -1255,6 +1263,36 @@ nd6_free(struct llentry *ln, int gc)
|
|||||||
return (next);
|
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.
|
* 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)
|
if (code == ND_REDIRECT_ROUTER)
|
||||||
ln->ln_router = 1;
|
ln->ln_router = 1;
|
||||||
else if (is_newentry) /* (6-7) */
|
else {
|
||||||
ln->ln_router = 0;
|
if (is_newentry) /* (6-7) */
|
||||||
|
ln->ln_router = 0;
|
||||||
|
ln->la_flags |= LLE_REDIRECT;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ND_ROUTER_SOLICIT:
|
case ND_ROUTER_SOLICIT:
|
||||||
/*
|
/*
|
||||||
|
@ -2105,7 +2105,7 @@ rt6_deleteroute(struct rtentry *rt, void *arg)
|
|||||||
return (0);
|
return (0);
|
||||||
|
|
||||||
return (in6_rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway,
|
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
|
#undef SIN6
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user