summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/netinet6/nd6_rtr.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/netinet6/nd6_rtr.c')
-rw-r--r--freebsd/sys/netinet6/nd6_rtr.c136
1 files changed, 106 insertions, 30 deletions
diff --git a/freebsd/sys/netinet6/nd6_rtr.c b/freebsd/sys/netinet6/nd6_rtr.c
index 59868383..0ba1e416 100644
--- a/freebsd/sys/netinet6/nd6_rtr.c
+++ b/freebsd/sys/netinet6/nd6_rtr.c
@@ -108,6 +108,10 @@ VNET_DEFINE(u_int32_t, ip6_temp_valid_lifetime) = DEF_TEMP_VALID_LIFETIME;
VNET_DEFINE(int, ip6_temp_regen_advance) = TEMPADDR_REGEN_ADVANCE;
+#ifdef EXPERIMENTAL
+VNET_DEFINE(int, nd6_ignore_ipv6_only_ra) = 1;
+#endif
+
/* RTPREF_MEDIUM has to be 0! */
#define RTPREF_HIGH 1
#define RTPREF_MEDIUM 0
@@ -210,7 +214,7 @@ nd6_rs_input(struct mbuf *m, int off, int icmp6len)
/*
* An initial update routine for draft-ietf-6man-ipv6only-flag.
* We need to iterate over all default routers for the given
- * interface to see whether they are all advertising the "6"
+ * interface to see whether they are all advertising the "S"
* (IPv6-Only) flag. If they do set, otherwise unset, the
* interface flag we later use to filter on.
*/
@@ -218,7 +222,15 @@ static void
defrtr_ipv6_only_ifp(struct ifnet *ifp)
{
struct nd_defrouter *dr;
- bool ipv6_only;
+ bool ipv6_only, ipv6_only_old;
+#ifdef INET
+ struct epoch_tracker et;
+ struct ifaddr *ifa;
+ bool has_ipv4_addr;
+#endif
+
+ if (V_nd6_ignore_ipv6_only_ra != 0)
+ return;
ipv6_only = true;
ND6_RLOCK();
@@ -229,13 +241,78 @@ defrtr_ipv6_only_ifp(struct ifnet *ifp)
ND6_RUNLOCK();
IF_AFDATA_WLOCK(ifp);
+ ipv6_only_old = ND_IFINFO(ifp)->flags & ND6_IFF_IPV6_ONLY;
+ IF_AFDATA_WUNLOCK(ifp);
+
+ /* If nothing changed, we have an early exit. */
+ if (ipv6_only == ipv6_only_old)
+ return;
+
+#ifdef INET
+ /*
+ * Should we want to set the IPV6-ONLY flag, check if the
+ * interface has a non-0/0 and non-link-local IPv4 address
+ * configured on it. If it has we will assume working
+ * IPv4 operations and will clear the interface flag.
+ */
+ has_ipv4_addr = false;
+ if (ipv6_only) {
+ NET_EPOCH_ENTER(et);
+ CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
+ if (ifa->ifa_addr->sa_family != AF_INET)
+ continue;
+ if (in_canforward(
+ satosin(ifa->ifa_addr)->sin_addr)) {
+ has_ipv4_addr = true;
+ break;
+ }
+ }
+ NET_EPOCH_EXIT(et);
+ }
+ if (ipv6_only && has_ipv4_addr) {
+ log(LOG_NOTICE, "%s rcvd RA w/ IPv6-Only flag set but has IPv4 "
+ "configured, ignoring IPv6-Only flag.\n", ifp->if_xname);
+ ipv6_only = false;
+ }
+#endif
+
+ IF_AFDATA_WLOCK(ifp);
if (ipv6_only)
ND_IFINFO(ifp)->flags |= ND6_IFF_IPV6_ONLY;
else
ND_IFINFO(ifp)->flags &= ~ND6_IFF_IPV6_ONLY;
IF_AFDATA_WUNLOCK(ifp);
+
+#ifdef notyet
+ /* Send notification of flag change. */
+#endif
+}
+
+static void
+defrtr_ipv6_only_ipf_down(struct ifnet *ifp)
+{
+
+ IF_AFDATA_WLOCK(ifp);
+ ND_IFINFO(ifp)->flags &= ~ND6_IFF_IPV6_ONLY;
+ IF_AFDATA_WUNLOCK(ifp);
}
+#endif /* EXPERIMENTAL */
+
+void
+nd6_ifnet_link_event(void *arg __unused, struct ifnet *ifp, int linkstate)
+{
+
+ /*
+ * XXX-BZ we might want to trigger re-evaluation of our default router
+ * availability. E.g., on link down the default router might be
+ * unreachable but a different interface might still have connectivity.
+ */
+
+#ifdef EXPERIMENTAL
+ if (linkstate == LINK_STATE_DOWN)
+ defrtr_ipv6_only_ipf_down(ifp);
#endif
+}
/*
* Receive Router Advertisement Message.
@@ -513,11 +590,13 @@ nd6_rtmsg(int cmd, struct rtentry *rt)
info.rti_info[RTAX_NETMASK] = rt_mask(rt);
ifp = rt->rt_ifp;
if (ifp != NULL) {
- IF_ADDR_RLOCK(ifp);
+ struct epoch_tracker et;
+
+ NET_EPOCH_ENTER(et);
ifa = CK_STAILQ_FIRST(&ifp->if_addrhead);
info.rti_info[RTAX_IFP] = ifa->ifa_addr;
ifa_ref(ifa);
- IF_ADDR_RUNLOCK(ifp);
+ NET_EPOCH_EXIT(et);
info.rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr;
} else
ifa = NULL;
@@ -791,6 +870,7 @@ defrouter_del(struct nd_defrouter *dr)
void
defrouter_select_fib(int fibnum)
{
+ struct epoch_tracker et;
struct nd_defrouter *dr, *selected_dr, *installed_dr;
struct llentry *ln = NULL;
@@ -817,14 +897,14 @@ defrouter_select_fib(int fibnum)
*/
selected_dr = installed_dr = NULL;
TAILQ_FOREACH(dr, &V_nd_defrouter, dr_entry) {
- IF_AFDATA_RLOCK(dr->ifp);
+ NET_EPOCH_ENTER(et);
if (selected_dr == NULL && dr->ifp->if_fib == fibnum &&
(ln = nd6_lookup(&dr->rtaddr, 0, dr->ifp)) &&
ND6_IS_LLINFO_PROBREACH(ln)) {
selected_dr = dr;
defrouter_ref(selected_dr);
}
- IF_AFDATA_RUNLOCK(dr->ifp);
+ NET_EPOCH_EXIT(et);
if (ln != NULL) {
LLE_RUNLOCK(ln);
ln = NULL;
@@ -868,7 +948,7 @@ defrouter_select_fib(int fibnum)
}
}
} else if (installed_dr != NULL) {
- IF_AFDATA_RLOCK(installed_dr->ifp);
+ NET_EPOCH_ENTER(et);
if ((ln = nd6_lookup(&installed_dr->rtaddr, 0,
installed_dr->ifp)) &&
ND6_IS_LLINFO_PROBREACH(ln) &&
@@ -877,7 +957,7 @@ defrouter_select_fib(int fibnum)
defrouter_rele(selected_dr);
selected_dr = installed_dr;
}
- IF_AFDATA_RUNLOCK(installed_dr->ifp);
+ NET_EPOCH_EXIT(et);
if (ln != NULL)
LLE_RUNLOCK(ln);
}
@@ -1273,6 +1353,7 @@ prelist_update(struct nd_prefixctl *new, struct nd_defrouter *dr,
int auth;
struct in6_addrlifetime lt6_tmp;
char ip6buf[INET6_ADDRSTRLEN];
+ struct epoch_tracker et;
auth = 0;
if (m) {
@@ -1386,7 +1467,7 @@ prelist_update(struct nd_prefixctl *new, struct nd_defrouter *dr,
* consider autoconfigured addresses while RFC2462 simply said
* "address".
*/
- IF_ADDR_RLOCK(ifp);
+ NET_EPOCH_ENTER(et);
CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
struct in6_ifaddr *ifa6;
u_int32_t remaininglifetime;
@@ -1509,7 +1590,7 @@ prelist_update(struct nd_prefixctl *new, struct nd_defrouter *dr,
ifa6->ia6_lifetime = lt6_tmp;
ifa6->ia6_updatetime = time_uptime;
}
- IF_ADDR_RUNLOCK(ifp);
+ NET_EPOCH_EXIT(et);
if (ia6_match == NULL && new->ndpr_vltime) {
int ifidlen;
@@ -1598,6 +1679,7 @@ end:
static struct nd_pfxrouter *
find_pfxlist_reachable_router(struct nd_prefix *pr)
{
+ struct epoch_tracker et;
struct nd_pfxrouter *pfxrtr;
struct llentry *ln;
int canreach;
@@ -1605,9 +1687,9 @@ find_pfxlist_reachable_router(struct nd_prefix *pr)
ND6_LOCK_ASSERT();
LIST_FOREACH(pfxrtr, &pr->ndpr_advrtrs, pfr_entry) {
- IF_AFDATA_RLOCK(pfxrtr->router->ifp);
+ NET_EPOCH_ENTER(et);
ln = nd6_lookup(&pfxrtr->router->rtaddr, 0, pfxrtr->router->ifp);
- IF_AFDATA_RUNLOCK(pfxrtr->router->ifp);
+ NET_EPOCH_EXIT(et);
if (ln == NULL)
continue;
canreach = ND6_IS_LLINFO_PROBREACH(ln);
@@ -1814,8 +1896,7 @@ restart:
static int
nd6_prefix_onlink_rtrequest(struct nd_prefix *pr, struct ifaddr *ifa)
{
- static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK};
- struct rib_head *rnh;
+ struct sockaddr_dl sdl;
struct rtentry *rt;
struct sockaddr_in6 mask6;
u_long rtflags;
@@ -1830,6 +1911,12 @@ nd6_prefix_onlink_rtrequest(struct nd_prefix *pr, struct ifaddr *ifa)
mask6.sin6_addr = pr->ndpr_mask;
rtflags = (ifa->ifa_flags & ~IFA_RTSELF) | RTF_UP;
+ bzero(&sdl, sizeof(struct sockaddr_dl));
+ sdl.sdl_len = sizeof(struct sockaddr_dl);
+ sdl.sdl_family = AF_LINK;
+ sdl.sdl_type = ifa->ifa_ifp->if_type;
+ sdl.sdl_index = ifa->ifa_ifp->if_index;
+
if(V_rt_add_addr_allfibs) {
fibnum = 0;
maxfib = rt_numfibs;
@@ -1842,26 +1929,13 @@ nd6_prefix_onlink_rtrequest(struct nd_prefix *pr, struct ifaddr *ifa)
rt = NULL;
error = in6_rtrequest(RTM_ADD,
- (struct sockaddr *)&pr->ndpr_prefix, ifa->ifa_addr,
+ (struct sockaddr *)&pr->ndpr_prefix, (struct sockaddr *)&sdl,
(struct sockaddr *)&mask6, rtflags, &rt, fibnum);
if (error == 0) {
KASSERT(rt != NULL, ("%s: in6_rtrequest return no "
"error(%d) but rt is NULL, pr=%p, ifa=%p", __func__,
error, pr, ifa));
-
- rnh = rt_tables_get_rnh(rt->rt_fibnum, AF_INET6);
- /* XXX what if rhn == NULL? */
- RIB_WLOCK(rnh);
RT_LOCK(rt);
- if (rt_setgate(rt, rt_key(rt),
- (struct sockaddr *)&null_sdl) == 0) {
- struct sockaddr_dl *dl;
-
- dl = (struct sockaddr_dl *)rt->rt_gateway;
- dl->sdl_type = rt->rt_ifp->if_type;
- dl->sdl_index = rt->rt_ifp->if_index;
- }
- RIB_WUNLOCK(rnh);
nd6_rtmsg(RTM_ADD, rt);
RT_UNLOCK(rt);
pr->ndpr_stateflags |= NDPRF_ONLINK;
@@ -1946,15 +2020,17 @@ nd6_prefix_onlink(struct nd_prefix *pr)
ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp,
IN6_IFF_NOTREADY | IN6_IFF_ANYCAST);
if (ifa == NULL) {
+ struct epoch_tracker et;
+
/* XXX: freebsd does not have ifa_ifwithaf */
- IF_ADDR_RLOCK(ifp);
+ NET_EPOCH_ENTER(et);
CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
if (ifa->ifa_addr->sa_family == AF_INET6) {
ifa_ref(ifa);
break;
}
}
- IF_ADDR_RUNLOCK(ifp);
+ NET_EPOCH_EXIT(et);
/* should we care about ia6_flags? */
}
if (ifa == NULL) {