Changeset View
Standalone View
sys/netinet6/nd6_rtr.c
Show First 20 Lines • Show All 494 Lines • ▼ Show 20 Lines | defrouter_addreq(struct nd_defrouter *new) | ||||
def.sin6_len = mask.sin6_len = gate.sin6_len = | def.sin6_len = mask.sin6_len = gate.sin6_len = | ||||
sizeof(struct sockaddr_in6); | sizeof(struct sockaddr_in6); | ||||
def.sin6_family = gate.sin6_family = AF_INET6; | def.sin6_family = gate.sin6_family = AF_INET6; | ||||
gate.sin6_addr = new->rtaddr; | gate.sin6_addr = new->rtaddr; | ||||
error = in6_rtrequest(RTM_ADD, (struct sockaddr *)&def, | error = in6_rtrequest(RTM_ADD, (struct sockaddr *)&def, | ||||
(struct sockaddr *)&gate, (struct sockaddr *)&mask, | (struct sockaddr *)&gate, (struct sockaddr *)&mask, | ||||
RTF_GATEWAY, &newrt, RT_DEFAULT_FIB); | RTF_GATEWAY, &newrt, new->ifp->if_fib); | ||||
if (newrt) { | if (newrt) { | ||||
nd6_rtmsg(RTM_ADD, newrt); /* tell user process */ | nd6_rtmsg(RTM_ADD, newrt); /* tell user process */ | ||||
RTFREE(newrt); | RTFREE(newrt); | ||||
} | } | ||||
if (error == 0) | if (error == 0) | ||||
new->installed = 1; | new->installed = 1; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | defrouter_delreq(struct nd_defrouter *dr) | ||||
def.sin6_len = mask.sin6_len = gate.sin6_len = | def.sin6_len = mask.sin6_len = gate.sin6_len = | ||||
sizeof(struct sockaddr_in6); | sizeof(struct sockaddr_in6); | ||||
def.sin6_family = gate.sin6_family = AF_INET6; | def.sin6_family = gate.sin6_family = AF_INET6; | ||||
gate.sin6_addr = dr->rtaddr; | gate.sin6_addr = dr->rtaddr; | ||||
in6_rtrequest(RTM_DELETE, (struct sockaddr *)&def, | in6_rtrequest(RTM_DELETE, (struct sockaddr *)&def, | ||||
(struct sockaddr *)&gate, | (struct sockaddr *)&gate, | ||||
(struct sockaddr *)&mask, RTF_GATEWAY, &oldrt, RT_DEFAULT_FIB); | (struct sockaddr *)&mask, RTF_GATEWAY, &oldrt, dr->ifp->if_fib); | ||||
jhujhiti_adjectivism.org: I'm now wondering if this is incorrect. If the interface's FIB changes after the router has… | |||||
Done Inline ActionsI think that's a limitation of the design of our network stack, and I don't think that fixing it should be a part of this change. I think that fixing would require either:
asomers: I think that's a limitation of the design of our network stack, and I don't think that fixing… | |||||
if (oldrt) { | if (oldrt) { | ||||
nd6_rtmsg(RTM_DELETE, oldrt); | nd6_rtmsg(RTM_DELETE, oldrt); | ||||
RTFREE(oldrt); | RTFREE(oldrt); | ||||
} | } | ||||
dr->installed = 0; | dr->installed = 0; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 114 Lines • ▼ Show 20 Lines | defrouter_del(struct nd_defrouter *dr) | ||||
pfxlist_onlink_check(); | pfxlist_onlink_check(); | ||||
/* | /* | ||||
* If the router is the primary one, choose a new one. | * If the router is the primary one, choose a new one. | ||||
* Note that defrouter_select() will remove the current gateway | * Note that defrouter_select() will remove the current gateway | ||||
* from the routing table. | * from the routing table. | ||||
*/ | */ | ||||
if (deldr) | if (deldr) | ||||
defrouter_select(); | defrouter_select(deldr->ifp->if_fib); | ||||
/* | /* | ||||
* Release the list reference. | * Release the list reference. | ||||
*/ | */ | ||||
defrouter_rele(dr); | defrouter_rele(dr); | ||||
} | } | ||||
/* | /* | ||||
Show All 13 Lines | |||||
* we do not need to classify the cases by ifdef. | * we do not need to classify the cases by ifdef. | ||||
* | * | ||||
* At this moment, we do not try to install more than one default router, | * At this moment, we do not try to install more than one default router, | ||||
* even when the multipath routing is available, because we're not sure about | * even when the multipath routing is available, because we're not sure about | ||||
* the benefits for stub hosts comparing to the risk of making the code | * the benefits for stub hosts comparing to the risk of making the code | ||||
* complicated and the possibility of introducing bugs. | * complicated and the possibility of introducing bugs. | ||||
*/ | */ | ||||
void | void | ||||
defrouter_select(void) | defrouter_select(int fibnum) | ||||
asomersUnsubmitted Done Inline ActionsPlease document the new parameter. Also, I'm not sure the code is correct when fibnum == RT_ADDR_ALLFIBS. In that case, no interface will ever have if_fib == fibnum. You should probably fall back to legacy behavior in that case. asomers: Please document the new parameter. Also, I'm not sure the code is correct when `fibnum ==… | |||||
jhujhiti_adjectivism.orgAuthorUnsubmitted Done Inline ActionsI would expect a value RT_ADDR_ALLFIBS to either (1) be an invalid argument (assertion failure?) or (2) call ourselves recursively to loop over all FIBs rather than falling back to old behavior, which doesn't make a lot of sense -- old behavior wasn't FIB-aware and could trample over multiple selected routers. jhujhiti_adjectivism.org: I would expect a value RT_ADDR_ALLFIBS to either (1) be an invalid argument (assertion failure? | |||||
asomersUnsubmitted Done Inline ActionsLooping sounds like a good idea. asomers: Looping sounds like a good idea. | |||||
{ | { | ||||
struct nd_defrouter *dr, *selected_dr, *installed_dr; | struct nd_defrouter *dr, *selected_dr, *installed_dr; | ||||
struct llentry *ln = NULL; | struct llentry *ln = NULL; | ||||
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_nd_defrouter)) { | if (TAILQ_EMPTY(&V_nd_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_nd_defrouter, dr_entry) { | TAILQ_FOREACH(dr, &V_nd_defrouter, dr_entry) { | ||||
IF_AFDATA_RLOCK(dr->ifp); | IF_AFDATA_RLOCK(dr->ifp); | ||||
if (selected_dr == NULL && | if (selected_dr == NULL && dr->ifp->if_fib == fibnum && | ||||
(ln = nd6_lookup(&dr->rtaddr, 0, dr->ifp)) && | (ln = nd6_lookup(&dr->rtaddr, 0, dr->ifp)) && | ||||
ND6_IS_LLINFO_PROBREACH(ln)) { | ND6_IS_LLINFO_PROBREACH(ln)) { | ||||
selected_dr = dr; | selected_dr = dr; | ||||
defrouter_ref(selected_dr); | defrouter_ref(selected_dr); | ||||
} | } | ||||
IF_AFDATA_RUNLOCK(dr->ifp); | IF_AFDATA_RUNLOCK(dr->ifp); | ||||
if (ln != NULL) { | if (ln != NULL) { | ||||
LLE_RUNLOCK(ln); | LLE_RUNLOCK(ln); | ||||
ln = NULL; | ln = NULL; | ||||
} | } | ||||
if (dr->installed) { | if (dr->installed && dr->ifp->if_fib == fibnum) { | ||||
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. warn for diagnosis. */ | /* this should not happen. warn for diagnosis. */ | ||||
log(LOG_ERR, | log(LOG_ERR, | ||||
"defrouter_select: more than one router is installed\n"); | "defrouter_select: more than 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) | ||||
selected_dr = TAILQ_FIRST(&V_nd_defrouter); | dr = TAILQ_FIRST(&V_nd_defrouter); | ||||
else | else | ||||
selected_dr = TAILQ_NEXT(installed_dr, dr_entry); | dr = TAILQ_NEXT(installed_dr, dr_entry); | ||||
/* Ensure we select a router for this FIB. */ | |||||
TAILQ_FOREACH_FROM(dr, &V_nd_defrouter, dr_entry) { | |||||
if (dr->ifp->if_fib == fibnum) { | |||||
selected_dr = dr; | |||||
defrouter_ref(selected_dr); | defrouter_ref(selected_dr); | ||||
break; | |||||
} | |||||
} | |||||
} else if (installed_dr != NULL) { | } else if (installed_dr != NULL) { | ||||
IF_AFDATA_RLOCK(installed_dr->ifp); | IF_AFDATA_RLOCK(installed_dr->ifp); | ||||
if ((ln = nd6_lookup(&installed_dr->rtaddr, 0, installed_dr->ifp)) && | if ((ln = nd6_lookup(&installed_dr->rtaddr, 0, installed_dr->ifp)) && | ||||
asomersUnsubmitted Done Inline ActionsWhile you're here, you may as well fix the style on this line. It's > 80 chars. asomers: While you're here, you may as well fix the style on this line. It's > 80 chars. | |||||
ND6_IS_LLINFO_PROBREACH(ln) && | 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; | ||||
} | } | ||||
IF_AFDATA_RUNLOCK(installed_dr->ifp); | IF_AFDATA_RUNLOCK(installed_dr->ifp); | ||||
if (ln != NULL) | if (ln != NULL) | ||||
LLE_RUNLOCK(ln); | LLE_RUNLOCK(ln); | ||||
} | } | ||||
ND6_RUNLOCK(); | ND6_RUNLOCK(); | ||||
/* | /* | ||||
* If the selected router is different than the installed one, | * If we selected a router for this FIB and it's different | ||||
* remove the installed router and install the selected one. | * than the installed one, remove the installed router and | ||||
* Note that the selected router is never NULL here. | * 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); | ||||
defrouter_rele(installed_dr); | defrouter_rele(installed_dr); | ||||
} | } | ||||
if (selected_dr != NULL) | |||||
defrouter_addreq(selected_dr); | defrouter_addreq(selected_dr); | ||||
} | } | ||||
if (selected_dr != NULL) | |||||
defrouter_rele(selected_dr); | defrouter_rele(selected_dr); | ||||
} | } | ||||
/* | /* | ||||
* for default router selection | * for default router selection | ||||
* regards router-preference field as a 2-bit signed integer | * regards router-preference field as a 2-bit signed integer | ||||
*/ | */ | ||||
static int | static int | ||||
rtpref(struct nd_defrouter *dr) | rtpref(struct nd_defrouter *dr) | ||||
▲ Show 20 Lines • Show All 106 Lines • ▼ Show 20 Lines | restart: | ||||
} | } | ||||
if (dr != NULL) | if (dr != NULL) | ||||
TAILQ_INSERT_BEFORE(dr, n, dr_entry); | TAILQ_INSERT_BEFORE(dr, n, dr_entry); | ||||
else | else | ||||
TAILQ_INSERT_TAIL(&V_nd_defrouter, n, dr_entry); | TAILQ_INSERT_TAIL(&V_nd_defrouter, n, dr_entry); | ||||
V_nd6_list_genid++; | V_nd6_list_genid++; | ||||
ND6_WUNLOCK(); | ND6_WUNLOCK(); | ||||
defrouter_select(); | defrouter_select(new->ifp->if_fib); | ||||
return (n); | return (n); | ||||
} | } | ||||
static struct nd_pfxrouter * | static struct nd_pfxrouter * | ||||
pfxrtr_lookup(struct nd_prefix *pr, struct nd_defrouter *dr) | pfxrtr_lookup(struct nd_prefix *pr, struct nd_defrouter *dr) | ||||
{ | { | ||||
struct nd_pfxrouter *search; | struct nd_pfxrouter *search; | ||||
▲ Show 20 Lines • Show All 774 Lines • ▼ Show 20 Lines | |||||
static int | static int | ||||
nd6_prefix_onlink_rtrequest(struct nd_prefix *pr, struct ifaddr *ifa) | nd6_prefix_onlink_rtrequest(struct nd_prefix *pr, struct ifaddr *ifa) | ||||
{ | { | ||||
static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK}; | static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK}; | ||||
struct rib_head *rnh; | struct rib_head *rnh; | ||||
struct rtentry *rt; | struct rtentry *rt; | ||||
struct sockaddr_in6 mask6; | struct sockaddr_in6 mask6; | ||||
u_long rtflags; | u_long rtflags; | ||||
int error, a_failure, fibnum; | int error, a_failure, fibnum, maxfib; | ||||
/* | /* | ||||
* in6_ifinit() sets nd6_rtrequest to ifa_rtrequest for all ifaddrs. | * in6_ifinit() sets nd6_rtrequest to ifa_rtrequest for all ifaddrs. | ||||
* ifa->ifa_rtrequest = nd6_rtrequest; | * ifa->ifa_rtrequest = nd6_rtrequest; | ||||
*/ | */ | ||||
bzero(&mask6, sizeof(mask6)); | bzero(&mask6, sizeof(mask6)); | ||||
mask6.sin6_len = sizeof(mask6); | mask6.sin6_len = sizeof(mask6); | ||||
mask6.sin6_addr = pr->ndpr_mask; | mask6.sin6_addr = pr->ndpr_mask; | ||||
rtflags = (ifa->ifa_flags & ~IFA_RTSELF) | RTF_UP; | rtflags = (ifa->ifa_flags & ~IFA_RTSELF) | RTF_UP; | ||||
if(rt_add_addr_allfibs) { | |||||
asomersUnsubmitted Done Inline ActionsShould be V_rt_add_addr_allfibs asomers: Should be V_rt_add_addr_allfibs | |||||
fibnum = 0; | |||||
maxfib = rt_numfibs; | |||||
} else { | |||||
fibnum = ifa->ifa_ifp->if_fib; | |||||
maxfib = fibnum + 1; | |||||
} | |||||
a_failure = 0; | a_failure = 0; | ||||
for (fibnum = 0; fibnum < rt_numfibs; fibnum++) { | for (; fibnum < maxfib; fibnum++) { | ||||
rt = NULL; | rt = NULL; | ||||
error = in6_rtrequest(RTM_ADD, | error = in6_rtrequest(RTM_ADD, | ||||
(struct sockaddr *)&pr->ndpr_prefix, ifa->ifa_addr, | (struct sockaddr *)&pr->ndpr_prefix, ifa->ifa_addr, | ||||
(struct sockaddr *)&mask6, rtflags, &rt, fibnum); | (struct sockaddr *)&mask6, rtflags, &rt, fibnum); | ||||
if (error == 0) { | if (error == 0) { | ||||
KASSERT(rt != NULL, ("%s: in6_rtrequest return no " | KASSERT(rt != NULL, ("%s: in6_rtrequest return no " | ||||
"error(%d) but rt is NULL, pr=%p, ifa=%p", __func__, | "error(%d) but rt is NULL, pr=%p, ifa=%p", __func__, | ||||
▲ Show 20 Lines • Show All 71 Lines • ▼ Show 20 Lines | nd6_prefix_onlink(struct nd_prefix *pr) | ||||
ND6_RLOCK(); | ND6_RLOCK(); | ||||
LIST_FOREACH(opr, &V_nd_prefix, ndpr_entry) { | LIST_FOREACH(opr, &V_nd_prefix, ndpr_entry) { | ||||
if (opr == pr) | if (opr == pr) | ||||
continue; | continue; | ||||
if ((opr->ndpr_stateflags & NDPRF_ONLINK) == 0) | if ((opr->ndpr_stateflags & NDPRF_ONLINK) == 0) | ||||
continue; | continue; | ||||
if (!rt_add_addr_allfibs && | |||||
asomersUnsubmitted Done Inline ActionsShould be V_rt_add_addr_allfibs asomers: Should be V_rt_add_addr_allfibs | |||||
opr->ndpr_ifp->if_fib != pr->ndpr_ifp->if_fib) | |||||
continue; | |||||
if (opr->ndpr_plen == pr->ndpr_plen && | if (opr->ndpr_plen == pr->ndpr_plen && | ||||
in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr, | in6_are_prefix_equal(&pr->ndpr_prefix.sin6_addr, | ||||
&opr->ndpr_prefix.sin6_addr, pr->ndpr_plen)) { | &opr->ndpr_prefix.sin6_addr, pr->ndpr_plen)) { | ||||
ND6_RUNLOCK(); | ND6_RUNLOCK(); | ||||
return (0); | return (0); | ||||
} | } | ||||
} | } | ||||
ND6_RUNLOCK(); | ND6_RUNLOCK(); | ||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
int error = 0; | int error = 0; | ||||
struct ifnet *ifp = pr->ndpr_ifp; | struct ifnet *ifp = pr->ndpr_ifp; | ||||
struct nd_prefix *opr; | struct nd_prefix *opr; | ||||
struct sockaddr_in6 sa6, mask6; | struct sockaddr_in6 sa6, mask6; | ||||
struct rtentry *rt; | struct rtentry *rt; | ||||
char ip6buf[INET6_ADDRSTRLEN]; | char ip6buf[INET6_ADDRSTRLEN]; | ||||
uint64_t genid; | uint64_t genid; | ||||
int fibnum, a_failure; | int fibnum, maxfib, a_failure; | ||||
ND6_ONLINK_LOCK_ASSERT(); | ND6_ONLINK_LOCK_ASSERT(); | ||||
ND6_UNLOCK_ASSERT(); | ND6_UNLOCK_ASSERT(); | ||||
if ((pr->ndpr_stateflags & NDPRF_ONLINK) == 0) | if ((pr->ndpr_stateflags & NDPRF_ONLINK) == 0) | ||||
return (EEXIST); | return (EEXIST); | ||||
bzero(&sa6, sizeof(sa6)); | bzero(&sa6, sizeof(sa6)); | ||||
sa6.sin6_family = AF_INET6; | sa6.sin6_family = AF_INET6; | ||||
sa6.sin6_len = sizeof(sa6); | sa6.sin6_len = sizeof(sa6); | ||||
bcopy(&pr->ndpr_prefix.sin6_addr, &sa6.sin6_addr, | bcopy(&pr->ndpr_prefix.sin6_addr, &sa6.sin6_addr, | ||||
sizeof(struct in6_addr)); | sizeof(struct in6_addr)); | ||||
bzero(&mask6, sizeof(mask6)); | bzero(&mask6, sizeof(mask6)); | ||||
mask6.sin6_family = AF_INET6; | mask6.sin6_family = AF_INET6; | ||||
mask6.sin6_len = sizeof(sa6); | mask6.sin6_len = sizeof(sa6); | ||||
bcopy(&pr->ndpr_mask, &mask6.sin6_addr, sizeof(struct in6_addr)); | bcopy(&pr->ndpr_mask, &mask6.sin6_addr, sizeof(struct in6_addr)); | ||||
if (rt_add_addr_allfibs) { | |||||
asomersUnsubmitted Done Inline ActionsShould be V_rt_add_addr_allfibs asomers: Should be V_rt_add_addr_allfibs | |||||
fibnum = 0; | |||||
maxfib = rt_numfibs; | |||||
} else { | |||||
fibnum = ifp->if_fib; | |||||
maxfib = fibnum + 1; | |||||
} | |||||
a_failure = 0; | a_failure = 0; | ||||
for (fibnum = 0; fibnum < rt_numfibs; fibnum++) { | for (; fibnum < maxfib; fibnum++) { | ||||
rt = NULL; | rt = NULL; | ||||
error = in6_rtrequest(RTM_DELETE, (struct sockaddr *)&sa6, NULL, | error = in6_rtrequest(RTM_DELETE, (struct sockaddr *)&sa6, NULL, | ||||
(struct sockaddr *)&mask6, 0, &rt, fibnum); | (struct sockaddr *)&mask6, 0, &rt, fibnum); | ||||
if (error == 0) { | if (error == 0) { | ||||
/* report the route deletion to the routing socket. */ | /* report the route deletion to the routing socket. */ | ||||
if (rt != NULL) | if (rt != NULL) | ||||
nd6_rtmsg(RTM_DELETE, rt); | nd6_rtmsg(RTM_DELETE, rt); | ||||
} else { | } else { | ||||
▲ Show 20 Lines • Show All 420 Lines • Show Last 20 Lines |
I'm now wondering if this is incorrect. If the interface's FIB changes after the router has been added to the list, we would fail to remove it here.
Should the default router (and maybe prefix) structs include a new field to store the FIB in which the router(/prefix) was originally learned?