Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet6/nd6_rtr.c
Show First 20 Lines • Show All 909 Lines • ▼ Show 20 Lines | default: | ||||
* Or, can we even panic? | * Or, can we even panic? | ||||
*/ | */ | ||||
log(LOG_ERR, "rtpref: impossible RA flag %x\n", dr->raflags); | log(LOG_ERR, "rtpref: impossible RA flag %x\n", dr->raflags); | ||||
return (RTPREF_INVALID); | return (RTPREF_INVALID); | ||||
} | } | ||||
/* NOTREACHED */ | /* NOTREACHED */ | ||||
} | } | ||||
static bool | |||||
is_dr_reachable(const struct nd_defrouter *dr) { | |||||
struct llentry *ln = NULL; | |||||
ln = nd6_lookup(&dr->rtaddr, LLE_SF(AF_INET6, 0), dr->ifp); | |||||
if (ln == NULL) | |||||
return (false); | |||||
bool reachable = ND6_IS_LLINFO_PROBREACH(ln); | |||||
LLE_RUNLOCK(ln); | |||||
return reachable; | |||||
} | |||||
/* | /* | ||||
* Default Router Selection according to Section 6.3.6 of RFC 2461 and | * Default Router Selection according to Section 6.3.6 of RFC 2461 and | ||||
* draft-ietf-ipngwg-router-selection: | * draft-ietf-ipngwg-router-selection: | ||||
* 1) Routers that are reachable or probably reachable should be preferred. | * 1) Routers that are reachable or probably reachable should be preferred. | ||||
* If we have more than one (probably) reachable router, prefer ones | * If we have more than one (probably) reachable router, prefer ones | ||||
* with the highest router preference. | * with the highest router preference. | ||||
* 2) When no routers on the list are known to be reachable or | * 2) When no routers on the list are known to be reachable or | ||||
* probably reachable, routers SHOULD be selected in a round-robin | * probably reachable, routers SHOULD be selected in a round-robin | ||||
Show All 14 Lines | |||||
* at a time based on the receiving interface's FIB. If @fibnum is RT_ALL_FIBS, | * at a time based on the receiving interface's FIB. If @fibnum is RT_ALL_FIBS, | ||||
* we do the whole thing multiple times. | * we do the whole thing multiple times. | ||||
*/ | */ | ||||
void | void | ||||
defrouter_select_fib(int fibnum) | defrouter_select_fib(int fibnum) | ||||
{ | { | ||||
struct epoch_tracker et; | struct epoch_tracker et; | ||||
struct nd_defrouter *dr, *selected_dr, *installed_dr; | struct nd_defrouter *dr, *selected_dr, *installed_dr; | ||||
struct llentry *ln = NULL; | |||||
if (fibnum == RT_ALL_FIBS) { | if (fibnum == RT_ALL_FIBS) { | ||||
for (fibnum = 0; fibnum < rt_numfibs; fibnum++) { | for (fibnum = 0; fibnum < rt_numfibs; fibnum++) { | ||||
defrouter_select_fib(fibnum); | defrouter_select_fib(fibnum); | ||||
} | } | ||||
return; | |||||
} | } | ||||
ND6_RLOCK(); | ND6_RLOCK(); | ||||
/* | /* | ||||
* Let's handle easy case (3) first: | * Let's handle easy case (3) first: | ||||
* If default router list is empty, there's nothing to be done. | * If default router list is empty, there's nothing to be done. | ||||
*/ | */ | ||||
if (TAILQ_EMPTY(&V_nd6_defrouter)) { | if (TAILQ_EMPTY(&V_nd6_defrouter)) { | ||||
ND6_RUNLOCK(); | ND6_RUNLOCK(); | ||||
return; | return; | ||||
} | } | ||||
/* | /* | ||||
* Search for a (probably) reachable router from the list. | * Search for a (probably) reachable router from the list. | ||||
* We just pick up the first reachable one (if any), assuming that | * We just pick up the first reachable one (if any), assuming that | ||||
* the ordering rule of the list described in defrtrlist_update(). | * the ordering rule of the list described in defrtrlist_update(). | ||||
*/ | */ | ||||
selected_dr = installed_dr = NULL; | selected_dr = installed_dr = NULL; | ||||
TAILQ_FOREACH(dr, &V_nd6_defrouter, dr_entry) { | |||||
NET_EPOCH_ENTER(et); | NET_EPOCH_ENTER(et); | ||||
if (selected_dr == NULL && dr->ifp->if_fib == fibnum && | TAILQ_FOREACH(dr, &V_nd6_defrouter, dr_entry) { | ||||
(ln = nd6_lookup(&dr->rtaddr, LLE_SF(AF_INET6, 0), dr->ifp)) && | if (dr->ifp->if_fib != fibnum) | ||||
ND6_IS_LLINFO_PROBREACH(ln)) { | continue; | ||||
if (selected_dr == NULL && is_dr_reachable(dr)) { | |||||
selected_dr = dr; | selected_dr = dr; | ||||
defrouter_ref(selected_dr); | defrouter_ref(selected_dr); | ||||
} | } | ||||
NET_EPOCH_EXIT(et); | |||||
if (ln != NULL) { | |||||
LLE_RUNLOCK(ln); | |||||
ln = NULL; | |||||
} | |||||
if (dr->installed && dr->ifp->if_fib == fibnum) { | if (dr->installed) { | ||||
if (installed_dr == NULL) { | if (installed_dr == NULL) { | ||||
installed_dr = dr; | installed_dr = dr; | ||||
defrouter_ref(installed_dr); | defrouter_ref(installed_dr); | ||||
} else { | } else { | ||||
/* | /* | ||||
* this should not happen. | * this should not happen. | ||||
* warn for diagnosis. | * warn for diagnosis. | ||||
*/ | */ | ||||
log(LOG_ERR, "defrouter_select_fib: more than " | log(LOG_ERR, "defrouter_select_fib: more than " | ||||
"one router is installed\n"); | "one router is installed\n"); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* If none of the default routers was found to be reachable, | * If none of the default routers was found to be reachable, | ||||
* round-robin the list regardless of preference. | * round-robin the list regardless of preference. | ||||
* Otherwise, if we have an installed router, check if the selected | * Otherwise, if we have an installed router, check if the selected | ||||
* (reachable) router should really be preferred to the installed one. | * (reachable) router should really be preferred to the installed one. | ||||
* We only prefer the new router when the old one is not reachable | * We only prefer the new router when the old one is not reachable | ||||
* or when the new one has a really higher preference value. | * or when the new one has a really higher preference value. | ||||
*/ | */ | ||||
if (selected_dr == NULL) { | if (selected_dr == NULL) { | ||||
if (installed_dr == NULL || | if (installed_dr == NULL || | ||||
TAILQ_NEXT(installed_dr, dr_entry) == NULL) | TAILQ_NEXT(installed_dr, dr_entry) == NULL) | ||||
dr = TAILQ_FIRST(&V_nd6_defrouter); | dr = TAILQ_FIRST(&V_nd6_defrouter); | ||||
else | else | ||||
dr = TAILQ_NEXT(installed_dr, dr_entry); | dr = TAILQ_NEXT(installed_dr, dr_entry); | ||||
/* Ensure we select a router for this FIB. */ | /* Ensure we select a router for this FIB. */ | ||||
TAILQ_FOREACH_FROM(dr, &V_nd6_defrouter, dr_entry) { | TAILQ_FOREACH_FROM(dr, &V_nd6_defrouter, dr_entry) { | ||||
if (dr->ifp->if_fib == fibnum) { | if (dr->ifp->if_fib == fibnum) { | ||||
selected_dr = dr; | selected_dr = dr; | ||||
defrouter_ref(selected_dr); | defrouter_ref(selected_dr); | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
} else if (installed_dr != NULL) { | } else if (installed_dr != NULL) { | ||||
NET_EPOCH_ENTER(et); | if (is_dr_reachable(installed_dr) && | ||||
if ((ln = nd6_lookup(&installed_dr->rtaddr, 0, | |||||
installed_dr->ifp)) && | |||||
ND6_IS_LLINFO_PROBREACH(ln) && | |||||
installed_dr->ifp->if_fib == fibnum && | |||||
rtpref(selected_dr) <= rtpref(installed_dr)) { | rtpref(selected_dr) <= rtpref(installed_dr)) { | ||||
defrouter_rele(selected_dr); | defrouter_rele(selected_dr); | ||||
selected_dr = installed_dr; | selected_dr = installed_dr; | ||||
} | } | ||||
NET_EPOCH_EXIT(et); | |||||
if (ln != NULL) | |||||
LLE_RUNLOCK(ln); | |||||
} | } | ||||
ND6_RUNLOCK(); | ND6_RUNLOCK(); | ||||
NET_EPOCH_ENTER(et); | |||||
/* | /* | ||||
* If we selected a router for this FIB and it's different | * If we selected a router for this FIB and it's different | ||||
* than the installed one, remove the installed router and | * than the installed one, remove the installed router and | ||||
* install the selected one in its place. | * install the selected one in its place. | ||||
*/ | */ | ||||
if (installed_dr != selected_dr) { | if (installed_dr != selected_dr) { | ||||
if (installed_dr != NULL) { | if (installed_dr != NULL) { | ||||
defrouter_delreq(installed_dr); | defrouter_delreq(installed_dr); | ||||
▲ Show 20 Lines • Show All 754 Lines • ▼ Show 20 Lines | |||||
* detect if a given prefix has a (probably) reachable advertising router. | * detect if a given prefix has a (probably) reachable advertising router. | ||||
* XXX: lengthy function name... | * XXX: lengthy function name... | ||||
*/ | */ | ||||
static struct nd_pfxrouter * | static struct nd_pfxrouter * | ||||
find_pfxlist_reachable_router(struct nd_prefix *pr) | find_pfxlist_reachable_router(struct nd_prefix *pr) | ||||
{ | { | ||||
struct epoch_tracker et; | struct epoch_tracker et; | ||||
struct nd_pfxrouter *pfxrtr; | struct nd_pfxrouter *pfxrtr; | ||||
struct llentry *ln; | |||||
int canreach; | |||||
ND6_LOCK_ASSERT(); | ND6_LOCK_ASSERT(); | ||||
NET_EPOCH_ENTER(et); | NET_EPOCH_ENTER(et); | ||||
LIST_FOREACH(pfxrtr, &pr->ndpr_advrtrs, pfr_entry) { | LIST_FOREACH(pfxrtr, &pr->ndpr_advrtrs, pfr_entry) { | ||||
ln = nd6_lookup(&pfxrtr->router->rtaddr, LLE_SF(AF_INET6, 0), | if (is_dr_reachable(pfxrtr->router)) | ||||
pfxrtr->router->ifp); | |||||
if (ln == NULL) | |||||
continue; | |||||
canreach = ND6_IS_LLINFO_PROBREACH(ln); | |||||
LLE_RUNLOCK(ln); | |||||
if (canreach) | |||||
break; | break; | ||||
} | } | ||||
NET_EPOCH_EXIT(et); | NET_EPOCH_EXIT(et); | ||||
return (pfxrtr); | return (pfxrtr); | ||||
} | } | ||||
/* | /* | ||||
* Check if each prefix in the prefix list has at least one available router | * Check if each prefix in the prefix list has at least one available router | ||||
▲ Show 20 Lines • Show All 752 Lines • Show Last 20 Lines |