Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet6/nd6_nbr.c
Show First 20 Lines • Show All 81 Lines • ▼ Show 20 Lines | |||||
#define SDL(s) ((struct sockaddr_dl *)s) | #define SDL(s) ((struct sockaddr_dl *)s) | ||||
struct dadq; | struct dadq; | ||||
static struct dadq *nd6_dad_find(struct ifaddr *, struct nd_opt_nonce *); | static struct dadq *nd6_dad_find(struct ifaddr *, struct nd_opt_nonce *); | ||||
static void nd6_dad_add(struct dadq *dp); | static void nd6_dad_add(struct dadq *dp); | ||||
static void nd6_dad_del(struct dadq *dp); | static void nd6_dad_del(struct dadq *dp); | ||||
static void nd6_dad_rele(struct dadq *); | static void nd6_dad_rele(struct dadq *); | ||||
static void nd6_dad_starttimer(struct dadq *, int, int); | static void nd6_dad_starttimer(struct dadq *, int); | ||||
static void nd6_dad_stoptimer(struct dadq *); | static void nd6_dad_stoptimer(struct dadq *); | ||||
static void nd6_dad_timer(struct dadq *); | static void nd6_dad_timer(struct dadq *); | ||||
static void nd6_dad_duplicated(struct ifaddr *, struct dadq *); | static void nd6_dad_duplicated(struct ifaddr *, struct dadq *); | ||||
static void nd6_dad_ns_output(struct dadq *); | static void nd6_dad_ns_output(struct dadq *); | ||||
static void nd6_dad_ns_input(struct ifaddr *, struct nd_opt_nonce *); | static void nd6_dad_ns_input(struct ifaddr *, struct nd_opt_nonce *); | ||||
static void nd6_dad_na_input(struct ifaddr *); | static void nd6_dad_na_input(struct ifaddr *); | ||||
static void nd6_na_output_fib(struct ifnet *, const struct in6_addr *, | static void nd6_na_output_fib(struct ifnet *, const struct in6_addr *, | ||||
const struct in6_addr *, u_long, int, struct sockaddr *, u_int); | const struct in6_addr *, u_long, int, struct sockaddr *, u_int); | ||||
▲ Show 20 Lines • Show All 1,021 Lines • ▼ Show 20 Lines | #define ND_OPT_NONCE_LEN32 \ | ||||
bool dad_ondadq; /* on dadq? Protected by DADQ_WLOCK. */ | bool dad_ondadq; /* on dadq? Protected by DADQ_WLOCK. */ | ||||
}; | }; | ||||
VNET_DEFINE_STATIC(TAILQ_HEAD(, dadq), dadq); | VNET_DEFINE_STATIC(TAILQ_HEAD(, dadq), dadq); | ||||
VNET_DEFINE_STATIC(struct rwlock, dad_rwlock); | VNET_DEFINE_STATIC(struct rwlock, dad_rwlock); | ||||
#define V_dadq VNET(dadq) | #define V_dadq VNET(dadq) | ||||
#define V_dad_rwlock VNET(dad_rwlock) | #define V_dad_rwlock VNET(dad_rwlock) | ||||
#define DADQ_RLOCK() rw_rlock(&V_dad_rwlock) | #define DADQ_RLOCK() rw_rlock(&V_dad_rwlock) | ||||
#define DADQ_RUNLOCK() rw_runlock(&V_dad_rwlock) | #define DADQ_RUNLOCK() rw_runlock(&V_dad_rwlock) | ||||
#define DADQ_WLOCK() rw_wlock(&V_dad_rwlock) | #define DADQ_WLOCK() rw_wlock(&V_dad_rwlock) | ||||
#define DADQ_WUNLOCK() rw_wunlock(&V_dad_rwlock) | #define DADQ_WUNLOCK() rw_wunlock(&V_dad_rwlock) | ||||
bz: Thes 4 lines are just whitespace changes, right? | |||||
Done Inline ActionsYes. markj: Yes. | |||||
#define DADQ_RLOCK_ASSERT() rw_assert(&V_dad_rwlock, RA_RLOCKED) | |||||
#define DADQ_WLOCK_ASSERT() rw_assert(&V_dad_rwlock, RA_WLOCKED) | |||||
#define DADQ_LOCK_ASSERT() rw_assert(&V_dad_rwlock, RA_LOCKED) | |||||
static void | static void | ||||
nd6_dad_add(struct dadq *dp) | nd6_dad_add(struct dadq *dp) | ||||
{ | { | ||||
DADQ_WLOCK(); | DADQ_WLOCK_ASSERT(); | ||||
TAILQ_INSERT_TAIL(&V_dadq, dp, dad_list); | TAILQ_INSERT_TAIL(&V_dadq, dp, dad_list); | ||||
dp->dad_ondadq = true; | dp->dad_ondadq = true; | ||||
DADQ_WUNLOCK(); | |||||
} | } | ||||
static void | static void | ||||
nd6_dad_del(struct dadq *dp) | nd6_dad_del(struct dadq *dp) | ||||
{ | { | ||||
DADQ_WLOCK(); | DADQ_WLOCK(); | ||||
if (dp->dad_ondadq) { | if (dp->dad_ondadq) { | ||||
Show All 34 Lines | TAILQ_FOREACH(dp, &V_dadq, dad_list) { | ||||
break; | break; | ||||
} | } | ||||
DADQ_RUNLOCK(); | DADQ_RUNLOCK(); | ||||
return (dp); | return (dp); | ||||
} | } | ||||
static void | static void | ||||
nd6_dad_starttimer(struct dadq *dp, int ticks, int send_ns) | nd6_dad_starttimer(struct dadq *dp, int ticks) | ||||
{ | { | ||||
if (send_ns != 0) | DADQ_WLOCK_ASSERT(); | ||||
nd6_dad_ns_output(dp); | |||||
callout_reset(&dp->dad_timer_ch, ticks, | callout_reset(&dp->dad_timer_ch, ticks, | ||||
(void (*)(void *))nd6_dad_timer, (void *)dp); | (void (*)(void *))nd6_dad_timer, dp); | ||||
} | } | ||||
static void | static void | ||||
nd6_dad_stoptimer(struct dadq *dp) | nd6_dad_stoptimer(struct dadq *dp) | ||||
{ | { | ||||
callout_drain(&dp->dad_timer_ch); | callout_drain(&dp->dad_timer_ch); | ||||
} | } | ||||
static void | static void | ||||
nd6_dad_rele(struct dadq *dp) | nd6_dad_rele(struct dadq *dp) | ||||
{ | { | ||||
if (refcount_release(&dp->dad_refcnt)) { | if (refcount_release(&dp->dad_refcnt)) { | ||||
KASSERT(!dp->dad_ondadq, ("dp %p still on DAD queue", dp)); | |||||
ifa_free(dp->dad_ifa); | ifa_free(dp->dad_ifa); | ||||
free(dp, M_IP6NDP); | free(dp, M_IP6NDP); | ||||
} | } | ||||
} | } | ||||
void | void | ||||
nd6_dad_init(void) | nd6_dad_init(void) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | nd6_dad_start(struct ifaddr *ifa, int delay) | ||||
dp = malloc(sizeof(*dp), M_IP6NDP, M_NOWAIT | M_ZERO); | dp = malloc(sizeof(*dp), M_IP6NDP, M_NOWAIT | M_ZERO); | ||||
if (dp == NULL) { | if (dp == NULL) { | ||||
log(LOG_ERR, "nd6_dad_start: memory allocation failed for " | log(LOG_ERR, "nd6_dad_start: memory allocation failed for " | ||||
"%s(%s)\n", | "%s(%s)\n", | ||||
ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr), | ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr), | ||||
ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???"); | ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???"); | ||||
return; | return; | ||||
} | } | ||||
callout_init(&dp->dad_timer_ch, 0); | callout_init_rw(&dp->dad_timer_ch, &V_dad_rwlock, | ||||
CALLOUT_RETURNUNLOCKED); | |||||
#ifdef VIMAGE | #ifdef VIMAGE | ||||
dp->dad_vnet = curvnet; | dp->dad_vnet = curvnet; | ||||
#endif | #endif | ||||
nd6log((LOG_DEBUG, "%s: starting DAD for %s\n", if_name(ifa->ifa_ifp), | nd6log((LOG_DEBUG, "%s: starting DAD for %s\n", if_name(ifa->ifa_ifp), | ||||
ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr))); | ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr))); | ||||
/* | /* | ||||
* Send NS packet for DAD, ip6_dad_count times. | * Send NS packet for DAD, ip6_dad_count times. | ||||
* Note that we must delay the first transmission, if this is the | * Note that we must delay the first transmission, if this is the | ||||
* first packet to be sent from the interface after interface | * first packet to be sent from the interface after interface | ||||
* (re)initialization. | * (re)initialization. | ||||
*/ | */ | ||||
dp->dad_ifa = ifa; | dp->dad_ifa = ifa; | ||||
ifa_ref(dp->dad_ifa); | ifa_ref(dp->dad_ifa); | ||||
dp->dad_count = V_ip6_dad_count; | dp->dad_count = V_ip6_dad_count; | ||||
dp->dad_ns_icount = dp->dad_na_icount = 0; | dp->dad_ns_icount = dp->dad_na_icount = 0; | ||||
dp->dad_ns_ocount = dp->dad_ns_tcount = 0; | dp->dad_ns_ocount = dp->dad_ns_tcount = 0; | ||||
dp->dad_ns_lcount = dp->dad_loopbackprobe = 0; | dp->dad_ns_lcount = dp->dad_loopbackprobe = 0; | ||||
/* Add this to the dadq and add a reference for the dadq. */ | /* Add this to the dadq and add a reference for the dadq. */ | ||||
refcount_init(&dp->dad_refcnt, 1); | refcount_init(&dp->dad_refcnt, 1); | ||||
DADQ_WLOCK(); | |||||
nd6_dad_add(dp); | nd6_dad_add(dp); | ||||
nd6_dad_starttimer(dp, delay, 0); | nd6_dad_starttimer(dp, delay); | ||||
DADQ_WUNLOCK(); | |||||
} | } | ||||
/* | /* | ||||
* terminate DAD unconditionally. used for address removals. | * terminate DAD unconditionally. used for address removals. | ||||
*/ | */ | ||||
void | void | ||||
nd6_dad_stop(struct ifaddr *ifa) | nd6_dad_stop(struct ifaddr *ifa) | ||||
{ | { | ||||
Show All 16 Lines | |||||
nd6_dad_timer(struct dadq *dp) | nd6_dad_timer(struct dadq *dp) | ||||
{ | { | ||||
CURVNET_SET(dp->dad_vnet); | CURVNET_SET(dp->dad_vnet); | ||||
struct ifaddr *ifa = dp->dad_ifa; | struct ifaddr *ifa = dp->dad_ifa; | ||||
struct ifnet *ifp = dp->dad_ifa->ifa_ifp; | struct ifnet *ifp = dp->dad_ifa->ifa_ifp; | ||||
struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; | struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; | ||||
char ip6buf[INET6_ADDRSTRLEN]; | char ip6buf[INET6_ADDRSTRLEN]; | ||||
DADQ_WLOCK_ASSERT(); | |||||
KASSERT(ia != NULL, ("DAD entry %p with no address", dp)); | KASSERT(ia != NULL, ("DAD entry %p with no address", dp)); | ||||
if (ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) { | if (ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) { | ||||
/* Do not need DAD for ifdisabled interface. */ | /* Do not need DAD for ifdisabled interface. */ | ||||
log(LOG_ERR, "nd6_dad_timer: cancel DAD on %s because of " | log(LOG_ERR, "nd6_dad_timer: cancel DAD on %s because of " | ||||
"ND6_IFF_IFDISABLED.\n", ifp->if_xname); | "ND6_IFF_IFDISABLED.\n", ifp->if_xname); | ||||
goto err; | goto err; | ||||
} | } | ||||
Show All 23 Lines | nd6_dad_timer(struct dadq *dp) | ||||
} | } | ||||
/* Need more checks? */ | /* Need more checks? */ | ||||
if (dp->dad_ns_ocount < dp->dad_count) { | if (dp->dad_ns_ocount < dp->dad_count) { | ||||
/* | /* | ||||
* We have more NS to go. Send NS packet for DAD. | * We have more NS to go. Send NS packet for DAD. | ||||
*/ | */ | ||||
nd6_dad_starttimer(dp, | nd6_dad_starttimer(dp, | ||||
(long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000, 1); | (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000); | ||||
nd6_dad_ns_output(dp); | |||||
/* DAD queue has been unlocked. */ | |||||
goto done; | goto done; | ||||
} else { | } else { | ||||
/* | /* | ||||
* We have transmitted sufficient number of DAD packets. | * We have transmitted sufficient number of DAD packets. | ||||
* See what we've got. | * See what we've got. | ||||
*/ | */ | ||||
if (dp->dad_ns_icount > 0 || dp->dad_na_icount > 0) | if (dp->dad_ns_icount > 0 || dp->dad_na_icount > 0) | ||||
/* We've seen NS or NA, means DAD has failed. */ | /* We've seen NS or NA, means DAD has failed. */ | ||||
Show All 14 Lines | else if (V_dad_enhanced != 0 && | ||||
dp->dad_loopbackprobe = dp->dad_ns_lcount; | dp->dad_loopbackprobe = dp->dad_ns_lcount; | ||||
/* | /* | ||||
* Send an NS immediately and increase dad_count by | * Send an NS immediately and increase dad_count by | ||||
* V_nd6_mmaxtries - 1. | * V_nd6_mmaxtries - 1. | ||||
*/ | */ | ||||
dp->dad_count = | dp->dad_count = | ||||
dp->dad_ns_ocount + V_nd6_mmaxtries - 1; | dp->dad_ns_ocount + V_nd6_mmaxtries - 1; | ||||
nd6_dad_starttimer(dp, | nd6_dad_starttimer(dp, | ||||
(long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000, | (long)ND_IFINFO(ifa->ifa_ifp)->retrans * hz / 1000); | ||||
1); | nd6_dad_ns_output(dp); | ||||
/* DAD queue has been unlocked. */ | |||||
goto done; | goto done; | ||||
} else { | } else { | ||||
/* | /* | ||||
* We are done with DAD. No NA came, no NS came. | * We are done with DAD. No NA came, no NS came. | ||||
* No duplicate address found. Check IFDISABLED flag | * No duplicate address found. Check IFDISABLED flag | ||||
* again in case that it is changed between the | * again in case that it is changed between the | ||||
* beginning of this function and here. | * beginning of this function and here. | ||||
*/ | */ | ||||
if ((ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) == 0) | if ((ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) == 0) | ||||
ia->ia6_flags &= ~IN6_IFF_TENTATIVE; | ia->ia6_flags &= ~IN6_IFF_TENTATIVE; | ||||
nd6log((LOG_DEBUG, | nd6log((LOG_DEBUG, | ||||
"%s: DAD complete for %s - no duplicates found\n", | "%s: DAD complete for %s - no duplicates found\n", | ||||
if_name(ifa->ifa_ifp), | if_name(ifa->ifa_ifp), | ||||
ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr))); | ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr))); | ||||
if (dp->dad_ns_lcount > 0) | if (dp->dad_ns_lcount > 0) | ||||
log(LOG_ERR, "%s: DAD completed while " | log(LOG_ERR, "%s: DAD completed while " | ||||
"a looped back NS message is detected " | "a looped back NS message is detected " | ||||
"during DAD for %s.\n", | "during DAD for %s.\n", | ||||
if_name(ifa->ifa_ifp), | if_name(ifa->ifa_ifp), | ||||
ip6_sprintf(ip6buf, IFA_IN6(ifa))); | ip6_sprintf(ip6buf, IFA_IN6(ifa))); | ||||
} | } | ||||
} | } | ||||
err: | err: | ||||
nd6_dad_del(dp); | TAILQ_REMOVE(&V_dadq, dp, dad_list); | ||||
nd6_dad_rele(dp); | |||||
DADQ_WUNLOCK(); | |||||
Not Done Inline ActionsIf that is the case, can we assert it? bz: If that is the case, can we assert it? | |||||
Done Inline ActionsI'll just assert it at the beginning of the function. markj: I'll just assert it at the beginning of the function. | |||||
done: | done: | ||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
} | } | ||||
static void | static void | ||||
nd6_dad_duplicated(struct ifaddr *ifa, struct dadq *dp) | nd6_dad_duplicated(struct ifaddr *ifa, struct dadq *dp) | ||||
{ | { | ||||
struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; | struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | |||||
static void | static void | ||||
nd6_dad_ns_output(struct dadq *dp) | nd6_dad_ns_output(struct dadq *dp) | ||||
{ | { | ||||
struct in6_ifaddr *ia = (struct in6_ifaddr *)dp->dad_ifa; | struct in6_ifaddr *ia = (struct in6_ifaddr *)dp->dad_ifa; | ||||
struct ifnet *ifp = dp->dad_ifa->ifa_ifp; | struct ifnet *ifp = dp->dad_ifa->ifa_ifp; | ||||
int i; | int i; | ||||
DADQ_WLOCK_ASSERT(); | |||||
dp->dad_ns_tcount++; | dp->dad_ns_tcount++; | ||||
if ((ifp->if_flags & IFF_UP) == 0) { | if ((ifp->if_flags & IFF_UP) == 0) { | ||||
DADQ_WUNLOCK(); | |||||
return; | return; | ||||
} | } | ||||
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { | if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { | ||||
DADQ_WUNLOCK(); | |||||
return; | return; | ||||
} | } | ||||
dp->dad_ns_ocount++; | dp->dad_ns_ocount++; | ||||
if (V_dad_enhanced != 0) { | if (V_dad_enhanced != 0) { | ||||
for (i = 0; i < ND_OPT_NONCE_LEN32; i++) | for (i = 0; i < ND_OPT_NONCE_LEN32; i++) | ||||
dp->dad_nonce[i] = arc4random(); | dp->dad_nonce[i] = arc4random(); | ||||
/* | /* | ||||
* XXXHRS: Note that in the case that | * XXXHRS: Note that in the case that | ||||
* DupAddrDetectTransmits > 1, multiple NS messages with | * DupAddrDetectTransmits > 1, multiple NS messages with | ||||
* different nonces can be looped back in an unexpected | * different nonces can be looped back in an unexpected | ||||
* order. The current implementation recognizes only | * order. The current implementation recognizes only | ||||
* the latest nonce on the sender side. Practically it | * the latest nonce on the sender side. Practically it | ||||
* should work well in almost all cases. | * should work well in almost all cases. | ||||
*/ | */ | ||||
} | } | ||||
DADQ_WUNLOCK(); | |||||
nd6_ns_output(ifp, NULL, NULL, &ia->ia_addr.sin6_addr, | nd6_ns_output(ifp, NULL, NULL, &ia->ia_addr.sin6_addr, | ||||
(uint8_t *)&dp->dad_nonce[0]); | (uint8_t *)&dp->dad_nonce[0]); | ||||
} | } | ||||
static void | static void | ||||
nd6_dad_ns_input(struct ifaddr *ifa, struct nd_opt_nonce *ndopt_nonce) | nd6_dad_ns_input(struct ifaddr *ifa, struct nd_opt_nonce *ndopt_nonce) | ||||
{ | { | ||||
struct dadq *dp; | struct dadq *dp; | ||||
Show All 29 Lines |
Thes 4 lines are just whitespace changes, right?