diff options
Diffstat (limited to 'freebsd/sys/netinet6/nd6_rtr.c')
-rw-r--r-- | freebsd/sys/netinet6/nd6_rtr.c | 136 |
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) { |