Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet6/nd6_nbr.c
Show All 34 Lines | |||||
#include "opt_inet.h" | #include "opt_inet.h" | ||||
#include "opt_inet6.h" | #include "opt_inet6.h" | ||||
#include "opt_ipsec.h" | #include "opt_ipsec.h" | ||||
#include "opt_mpath.h" | #include "opt_mpath.h" | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/libkern.h> | |||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/rwlock.h> | #include <sys/rwlock.h> | ||||
#include <sys/mbuf.h> | #include <sys/mbuf.h> | ||||
#include <sys/socket.h> | #include <sys/socket.h> | ||||
#include <sys/sockio.h> | #include <sys/sockio.h> | ||||
#include <sys/time.h> | #include <sys/time.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/errno.h> | #include <sys/errno.h> | ||||
#include <sys/sysctl.h> | |||||
#include <sys/syslog.h> | #include <sys/syslog.h> | ||||
#include <sys/queue.h> | #include <sys/queue.h> | ||||
#include <sys/callout.h> | #include <sys/callout.h> | ||||
#include <sys/refcount.h> | #include <sys/refcount.h> | ||||
#include <net/if.h> | #include <net/if.h> | ||||
#include <net/if_types.h> | #include <net/if_types.h> | ||||
#include <net/if_dl.h> | #include <net/if_dl.h> | ||||
#include <net/if_var.h> | #include <net/if_var.h> | ||||
#include <net/route.h> | #include <net/route.h> | ||||
#ifdef RADIX_MPATH | #ifdef RADIX_MPATH | ||||
#include <net/radix_mpath.h> | #include <net/radix_mpath.h> | ||||
#endif | #endif | ||||
#include <net/vnet.h> | |||||
#include <netinet/in.h> | #include <netinet/in.h> | ||||
#include <netinet/in_var.h> | #include <netinet/in_var.h> | ||||
#include <net/if_llatbl.h> | #include <net/if_llatbl.h> | ||||
#define L3_ADDR_SIN6(le) ((struct sockaddr_in6 *) L3_ADDR(le)) | #define L3_ADDR_SIN6(le) ((struct sockaddr_in6 *) L3_ADDR(le)) | ||||
#include <netinet6/in6_var.h> | #include <netinet6/in6_var.h> | ||||
#include <netinet6/in6_ifattach.h> | #include <netinet6/in6_ifattach.h> | ||||
#include <netinet/ip6.h> | #include <netinet/ip6.h> | ||||
#include <netinet6/ip6_var.h> | #include <netinet6/ip6_var.h> | ||||
#include <netinet6/scope6_var.h> | #include <netinet6/scope6_var.h> | ||||
#include <netinet6/nd6.h> | #include <netinet6/nd6.h> | ||||
#include <netinet/icmp6.h> | #include <netinet/icmp6.h> | ||||
#include <netinet/ip_carp.h> | #include <netinet/ip_carp.h> | ||||
#include <netinet6/send.h> | #include <netinet6/send.h> | ||||
#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 *); | 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); | 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 *, struct ifaddr *); | static void nd6_dad_ns_output(struct dadq *, struct ifaddr *); | ||||
static void nd6_dad_ns_input(struct ifaddr *); | 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); | ||||
static VNET_DEFINE(int, dad_ignore_ns) = 0; /* ignore NS in DAD | static VNET_DEFINE(int, dad_enhanced) = 1; | ||||
- specwise incorrect */ | #define V_dad_enhanced VNET(dad_enhanced) | ||||
SYSCTL_DECL(_net_inet6_ip6); | |||||
SYSCTL_INT(_net_inet6_ip6, OID_AUTO, dad_enhanced, CTLFLAG_VNET | CTLFLAG_RW, | |||||
&VNET_NAME(dad_enhanced), 0, | |||||
"Enable Enhanced DAD, which adds a random nonce to NS messages for DAD."); | |||||
static VNET_DEFINE(int, dad_maxtry) = 15; /* max # of *tries* to | static VNET_DEFINE(int, dad_maxtry) = 15; /* max # of *tries* to | ||||
transmit DAD packet */ | transmit DAD packet */ | ||||
#define V_dad_ignore_ns VNET(dad_ignore_ns) | |||||
#define V_dad_maxtry VNET(dad_maxtry) | #define V_dad_maxtry VNET(dad_maxtry) | ||||
/* | /* | ||||
* Input a Neighbor Solicitation Message. | * Input a Neighbor Solicitation Message. | ||||
* | * | ||||
* Based on RFC 2461 | * Based on RFC 2461 | ||||
* Based on RFC 2462 (duplicate address detection) | * Based on RFC 2462 (duplicate address detection) | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 207 Lines • ▼ Show 20 Lines | if (tentative) { | ||||
/* | /* | ||||
* If source address is unspecified address, it is for | * If source address is unspecified address, it is for | ||||
* duplicate address detection. | * duplicate address detection. | ||||
* | * | ||||
* If not, the packet is for addess resolution; | * If not, the packet is for addess resolution; | ||||
* silently ignore it. | * silently ignore it. | ||||
*/ | */ | ||||
if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) | if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) | ||||
nd6_dad_ns_input(ifa); | nd6_dad_ns_input(ifa, ndopts.nd_opts_nonce); | ||||
goto freeit; | goto freeit; | ||||
} | } | ||||
/* | /* | ||||
* If the source address is unspecified address, entries must not | * If the source address is unspecified address, entries must not | ||||
* be created or updated. | * be created or updated. | ||||
* It looks that sender is performing DAD. Output NA toward | * It looks that sender is performing DAD. Output NA toward | ||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | |||||
* Output a Neighbor Solicitation Message. Caller specifies: | * Output a Neighbor Solicitation Message. Caller specifies: | ||||
* - ICMP6 header source IP6 address | * - ICMP6 header source IP6 address | ||||
* - ND6 header target IP6 address | * - ND6 header target IP6 address | ||||
* - ND6 header source datalink address | * - ND6 header source datalink address | ||||
* | * | ||||
* Based on RFC 2461 | * Based on RFC 2461 | ||||
* Based on RFC 2462 (duplicate address detection) | * Based on RFC 2462 (duplicate address detection) | ||||
* | * | ||||
* ln - for source address determination | * ln - for source address determination | ||||
* dad - duplicate address detection | * nonce - If non-NULL, NS is used for duplicate address detection and | ||||
* the value (length is ND_OPT_NONCE_LEN) is used as a random nonce. | |||||
*/ | */ | ||||
void | void | ||||
nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6, | nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6, | ||||
const struct in6_addr *taddr6, struct llentry *ln, int dad) | const struct in6_addr *taddr6, struct llentry *ln, uint8_t *nonce) | ||||
{ | { | ||||
struct mbuf *m; | struct mbuf *m; | ||||
struct m_tag *mtag; | struct m_tag *mtag; | ||||
struct ip6_hdr *ip6; | struct ip6_hdr *ip6; | ||||
struct nd_neighbor_solicit *nd_ns; | struct nd_neighbor_solicit *nd_ns; | ||||
struct ip6_moptions im6o; | struct ip6_moptions im6o; | ||||
int icmp6len; | int icmp6len; | ||||
int maxlen; | int maxlen; | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | else { | ||||
ip6->ip6_dst.s6_addr16[1] = 0; | ip6->ip6_dst.s6_addr16[1] = 0; | ||||
ip6->ip6_dst.s6_addr32[1] = 0; | ip6->ip6_dst.s6_addr32[1] = 0; | ||||
ip6->ip6_dst.s6_addr32[2] = IPV6_ADDR_INT32_ONE; | ip6->ip6_dst.s6_addr32[2] = IPV6_ADDR_INT32_ONE; | ||||
ip6->ip6_dst.s6_addr32[3] = taddr6->s6_addr32[3]; | ip6->ip6_dst.s6_addr32[3] = taddr6->s6_addr32[3]; | ||||
ip6->ip6_dst.s6_addr8[12] = 0xff; | ip6->ip6_dst.s6_addr8[12] = 0xff; | ||||
if (in6_setscope(&ip6->ip6_dst, ifp, NULL) != 0) | if (in6_setscope(&ip6->ip6_dst, ifp, NULL) != 0) | ||||
goto bad; | goto bad; | ||||
} | } | ||||
if (!dad) { | if (nonce == NULL) { | ||||
struct ifaddr *ifa; | struct ifaddr *ifa; | ||||
/* | /* | ||||
* RFC2461 7.2.2: | * RFC2461 7.2.2: | ||||
* "If the source address of the packet prompting the | * "If the source address of the packet prompting the | ||||
* solicitation is the same as one of the addresses assigned | * solicitation is the same as one of the addresses assigned | ||||
* to the outgoing interface, that address SHOULD be placed | * to the outgoing interface, that address SHOULD be placed | ||||
* in the IP Source Address of the outgoing solicitation. | * in the IP Source Address of the outgoing solicitation. | ||||
▲ Show 20 Lines • Show All 80 Lines • ▼ Show 20 Lines | #endif | ||||
* --- --- | * --- --- | ||||
* DAD packet MUST NOT do not add the option | * DAD packet MUST NOT do not add the option | ||||
* there's no link layer address: | * there's no link layer address: | ||||
* impossible do not add the option | * impossible do not add the option | ||||
* there's link layer address: | * there's link layer address: | ||||
* Multicast NS MUST add one add the option | * Multicast NS MUST add one add the option | ||||
* Unicast NS SHOULD add one add the option | * Unicast NS SHOULD add one add the option | ||||
*/ | */ | ||||
if (!dad && (mac = nd6_ifptomac(ifp))) { | if (nonce == NULL && (mac = nd6_ifptomac(ifp))) { | ||||
int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen; | int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen; | ||||
struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_ns + 1); | struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_ns + 1); | ||||
/* 8 byte alignments... */ | /* 8 byte alignments... */ | ||||
optlen = (optlen + 7) & ~7; | optlen = (optlen + 7) & ~7; | ||||
m->m_pkthdr.len += optlen; | m->m_pkthdr.len += optlen; | ||||
m->m_len += optlen; | m->m_len += optlen; | ||||
icmp6len += optlen; | icmp6len += optlen; | ||||
bzero((caddr_t)nd_opt, optlen); | bzero((caddr_t)nd_opt, optlen); | ||||
nd_opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; | nd_opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; | ||||
nd_opt->nd_opt_len = optlen >> 3; | nd_opt->nd_opt_len = optlen >> 3; | ||||
bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen); | bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen); | ||||
} | } | ||||
/* | |||||
* Add a Nonce option (RFC 3971) to detect looped back NS messages. | |||||
* This behavior is documented as Enhanced Duplicate Address | |||||
* Detection in draft-ietf-6man-enhanced-dad-13. | |||||
* net.inet6.ip6.dad_enhanced=0 disables this. | |||||
*/ | |||||
if (V_dad_enhanced != 0 && nonce != NULL) { | |||||
int optlen = sizeof(struct nd_opt_hdr) + ND_OPT_NONCE_LEN; | |||||
struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_ns + 1); | |||||
/* 8-byte alignment is required. */ | |||||
optlen = (optlen + 7) & ~7; | |||||
m->m_pkthdr.len += optlen; | |||||
m->m_len += optlen; | |||||
icmp6len += optlen; | |||||
bzero((caddr_t)nd_opt, optlen); | |||||
nd_opt->nd_opt_type = ND_OPT_NONCE; | |||||
nd_opt->nd_opt_len = optlen >> 3; | |||||
bcopy(nonce, (caddr_t)(nd_opt + 1), ND_OPT_NONCE_LEN); | |||||
} | |||||
ip6->ip6_plen = htons((u_short)icmp6len); | ip6->ip6_plen = htons((u_short)icmp6len); | ||||
nd_ns->nd_ns_cksum = 0; | nd_ns->nd_ns_cksum = 0; | ||||
nd_ns->nd_ns_cksum = | nd_ns->nd_ns_cksum = | ||||
in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), icmp6len); | in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), icmp6len); | ||||
if (send_sendso_input_hook != NULL) { | if (send_sendso_input_hook != NULL) { | ||||
mtag = m_tag_get(PACKET_TAG_ND_OUTGOING, | mtag = m_tag_get(PACKET_TAG_ND_OUTGOING, | ||||
sizeof(unsigned short), M_NOWAIT); | sizeof(unsigned short), M_NOWAIT); | ||||
if (mtag == NULL) | if (mtag == NULL) | ||||
goto bad; | goto bad; | ||||
*(unsigned short *)(mtag + 1) = nd_ns->nd_ns_type; | *(unsigned short *)(mtag + 1) = nd_ns->nd_ns_type; | ||||
m_tag_prepend(m, mtag); | m_tag_prepend(m, mtag); | ||||
} | } | ||||
ip6_output(m, NULL, &ro, dad ? IPV6_UNSPECSRC : 0, &im6o, NULL, NULL); | ip6_output(m, NULL, &ro, (nonce != NULL) ? IPV6_UNSPECSRC : 0, | ||||
&im6o, NULL, NULL); | |||||
icmp6_ifstat_inc(ifp, ifs6_out_msg); | icmp6_ifstat_inc(ifp, ifs6_out_msg); | ||||
icmp6_ifstat_inc(ifp, ifs6_out_neighborsolicit); | icmp6_ifstat_inc(ifp, ifs6_out_neighborsolicit); | ||||
ICMP6STAT_INC(icp6s_outhist[ND_NEIGHBOR_SOLICIT]); | ICMP6STAT_INC(icp6s_outhist[ND_NEIGHBOR_SOLICIT]); | ||||
/* We don't cache this route. */ | /* We don't cache this route. */ | ||||
RO_RTFREE(&ro); | RO_RTFREE(&ro); | ||||
return; | return; | ||||
▲ Show 20 Lines • Show All 543 Lines • ▼ Show 20 Lines | |||||
struct dadq { | struct dadq { | ||||
TAILQ_ENTRY(dadq) dad_list; | TAILQ_ENTRY(dadq) dad_list; | ||||
struct ifaddr *dad_ifa; | struct ifaddr *dad_ifa; | ||||
int dad_count; /* max NS to send */ | int dad_count; /* max NS to send */ | ||||
int dad_ns_tcount; /* # of trials to send NS */ | int dad_ns_tcount; /* # of trials to send NS */ | ||||
int dad_ns_ocount; /* NS sent so far */ | int dad_ns_ocount; /* NS sent so far */ | ||||
int dad_ns_icount; | int dad_ns_icount; | ||||
int dad_na_icount; | int dad_na_icount; | ||||
int dad_ns_lcount; /* looped back NS */ | |||||
struct callout dad_timer_ch; | struct callout dad_timer_ch; | ||||
struct vnet *dad_vnet; | struct vnet *dad_vnet; | ||||
u_int dad_refcnt; | u_int dad_refcnt; | ||||
#define ND_OPT_NONCE_LEN32 \ | |||||
((ND_OPT_NONCE_LEN + sizeof(uint32_t) - 1)/sizeof(uint32_t)) | |||||
uint32_t dad_nonce[ND_OPT_NONCE_LEN32]; | |||||
}; | }; | ||||
static VNET_DEFINE(TAILQ_HEAD(, dadq), dadq); | static VNET_DEFINE(TAILQ_HEAD(, dadq), dadq); | ||||
static VNET_DEFINE(struct rwlock, dad_rwlock); | static VNET_DEFINE(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) | ||||
Show All 16 Lines | nd6_dad_del(struct dadq *dp) | ||||
DADQ_WLOCK(); | DADQ_WLOCK(); | ||||
TAILQ_REMOVE(&V_dadq, dp, dad_list); | TAILQ_REMOVE(&V_dadq, dp, dad_list); | ||||
DADQ_WUNLOCK(); | DADQ_WUNLOCK(); | ||||
nd6_dad_rele(dp); | nd6_dad_rele(dp); | ||||
} | } | ||||
static struct dadq * | static struct dadq * | ||||
nd6_dad_find(struct ifaddr *ifa) | nd6_dad_find(struct ifaddr *ifa, struct nd_opt_nonce *n) | ||||
{ | { | ||||
struct dadq *dp; | struct dadq *dp; | ||||
char ip6buf[INET6_ADDRSTRLEN]; | |||||
DADQ_RLOCK(); | DADQ_RLOCK(); | ||||
TAILQ_FOREACH(dp, &V_dadq, dad_list) | TAILQ_FOREACH(dp, &V_dadq, dad_list) { | ||||
if (dp->dad_ifa == ifa) { | if (dp->dad_ifa != ifa) | ||||
continue; | |||||
/* | |||||
* Skip if the nonce matches the received one. | |||||
* +2 in the length is required because of type and | |||||
* length fields are included in a header. | |||||
*/ | |||||
if (n != NULL && | |||||
n->nd_opt_nonce_len == (ND_OPT_NONCE_LEN + 2) / 8 && | |||||
memcmp(&n->nd_opt_nonce[0], &dp->dad_nonce[0], | |||||
ND_OPT_NONCE_LEN) == 0) { | |||||
log(LOG_ERR, "%s: a looped back NS message is " | |||||
ae: Maybe it will be better hide this log message under nd6_debug? | |||||
hrsAuthorUnsubmitted Not Done Inline ActionsThis is because Sec. 4.2 in draft-ietf-6man-enhanced-dad-13 specifies that receivers SHOULD log a system management message when an NS message which includes a nonce is received regardless if it matches or not. This log message may be annoying, though. hrs: This is because Sec. 4.2 in draft-ietf-6man-enhanced-dad-13 specifies that receivers SHOULD log… | |||||
"detected during DAD for %s.\n", | |||||
if_name(ifa->ifa_ifp), | |||||
ip6_sprintf(ip6buf, IFA_IN6(ifa))); | |||||
dp->dad_ns_lcount++; | |||||
continue; | |||||
} | |||||
refcount_acquire(&dp->dad_refcnt); | refcount_acquire(&dp->dad_refcnt); | ||||
break; | break; | ||||
} | } | ||||
DADQ_RUNLOCK(); | DADQ_RUNLOCK(); | ||||
return (dp); | return (dp); | ||||
} | } | ||||
static void | static void | ||||
nd6_dad_starttimer(struct dadq *dp, int ticks) | nd6_dad_starttimer(struct dadq *dp, int ticks) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | nd6_dad_start(struct ifaddr *ifa, int delay) | ||||
} | } | ||||
if (ifa->ifa_ifp == NULL) | if (ifa->ifa_ifp == NULL) | ||||
panic("nd6_dad_start: ifa->ifa_ifp == NULL"); | panic("nd6_dad_start: ifa->ifa_ifp == NULL"); | ||||
if (!(ifa->ifa_ifp->if_flags & IFF_UP)) { | if (!(ifa->ifa_ifp->if_flags & IFF_UP)) { | ||||
return; | return; | ||||
} | } | ||||
if (ND_IFINFO(ifa->ifa_ifp)->flags & ND6_IFF_IFDISABLED) | if (ND_IFINFO(ifa->ifa_ifp)->flags & ND6_IFF_IFDISABLED) | ||||
return; | return; | ||||
if ((dp = nd6_dad_find(ifa)) != NULL) { | if ((dp = nd6_dad_find(ifa, NULL)) != NULL) { | ||||
/* DAD already in progress */ | /* DAD already in progress */ | ||||
nd6_dad_rele(dp); | nd6_dad_rele(dp); | ||||
return; | return; | ||||
} | } | ||||
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 " | ||||
Show All 15 Lines | #endif | ||||
* 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 = 0; | |||||
refcount_init(&dp->dad_refcnt, 1); | refcount_init(&dp->dad_refcnt, 1); | ||||
nd6_dad_add(dp); | nd6_dad_add(dp); | ||||
if (delay == 0) { | if (delay == 0) { | ||||
nd6_dad_ns_output(dp, ifa); | nd6_dad_ns_output(dp, ifa); | ||||
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); | ||||
} else { | } else { | ||||
nd6_dad_starttimer(dp, delay); | nd6_dad_starttimer(dp, delay); | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* 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) | ||||
{ | { | ||||
struct dadq *dp; | struct dadq *dp; | ||||
dp = nd6_dad_find(ifa); | dp = nd6_dad_find(ifa, NULL); | ||||
if (!dp) { | if (!dp) { | ||||
/* DAD wasn't started yet */ | /* DAD wasn't started yet */ | ||||
return; | return; | ||||
} | } | ||||
nd6_dad_stoptimer(dp); | nd6_dad_stoptimer(dp); | ||||
/* | /* | ||||
* The DAD queue entry may have been removed by nd6_dad_timer() while | * The DAD queue entry may have been removed by nd6_dad_timer() while | ||||
* we were waiting for it to stop, so re-do the lookup. | * we were waiting for it to stop, so re-do the lookup. | ||||
*/ | */ | ||||
nd6_dad_rele(dp); | nd6_dad_rele(dp); | ||||
if (nd6_dad_find(ifa) == NULL) | if (nd6_dad_find(ifa, NULL) == NULL) | ||||
return; | return; | ||||
nd6_dad_del(dp); | nd6_dad_del(dp); | ||||
nd6_dad_rele(dp); | nd6_dad_rele(dp); | ||||
} | } | ||||
static void | static void | ||||
nd6_dad_timer(struct dadq *dp) | nd6_dad_timer(struct dadq *dp) | ||||
▲ Show 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | |||||
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; | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
char ip6buf[INET6_ADDRSTRLEN]; | char ip6buf[INET6_ADDRSTRLEN]; | ||||
log(LOG_ERR, "%s: DAD detected duplicate IPv6 address %s: " | log(LOG_ERR, "%s: DAD detected duplicate IPv6 address %s: " | ||||
"NS in/out=%d/%d, NA in=%d\n", | "NS in/out/loopback=%d/%d/%d, NA in=%d\n", | ||||
if_name(ifa->ifa_ifp), ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr), | if_name(ifa->ifa_ifp), ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr), | ||||
dp->dad_ns_icount, dp->dad_ns_ocount, dp->dad_na_icount); | dp->dad_ns_icount, dp->dad_ns_ocount, dp->dad_ns_lcount, | ||||
dp->dad_na_icount); | |||||
ia->ia6_flags &= ~IN6_IFF_TENTATIVE; | ia->ia6_flags &= ~IN6_IFF_TENTATIVE; | ||||
ia->ia6_flags |= IN6_IFF_DUPLICATED; | ia->ia6_flags |= IN6_IFF_DUPLICATED; | ||||
ifp = ifa->ifa_ifp; | ifp = ifa->ifa_ifp; | ||||
log(LOG_ERR, "%s: DAD complete for %s - duplicate found\n", | log(LOG_ERR, "%s: DAD complete for %s - duplicate found\n", | ||||
if_name(ifp), ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr)); | if_name(ifp), ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr)); | ||||
log(LOG_ERR, "%s: manual intervention required\n", | log(LOG_ERR, "%s: manual intervention required\n", | ||||
Show All 35 Lines | #endif | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
nd6_dad_ns_output(struct dadq *dp, struct ifaddr *ifa) | nd6_dad_ns_output(struct dadq *dp, struct ifaddr *ifa) | ||||
{ | { | ||||
struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; | struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa; | ||||
struct ifnet *ifp = ifa->ifa_ifp; | struct ifnet *ifp = ifa->ifa_ifp; | ||||
uint8_t *nonce; | |||||
int i; | |||||
dp->dad_ns_tcount++; | dp->dad_ns_tcount++; | ||||
if ((ifp->if_flags & IFF_UP) == 0) { | if ((ifp->if_flags & IFF_UP) == 0) { | ||||
return; | return; | ||||
} | } | ||||
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { | if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { | ||||
return; | return; | ||||
} | } | ||||
dp->dad_ns_ocount++; | dp->dad_ns_ocount++; | ||||
nd6_ns_output(ifp, NULL, &ia->ia_addr.sin6_addr, NULL, 1); | if (V_dad_enhanced != 0) { | ||||
for (i = 0; i < ND_OPT_NONCE_LEN32; i++) | |||||
dp->dad_nonce[i] = arc4random(); | |||||
nonce = (uint8_t *)&dp->dad_nonce[0]; | |||||
/* | |||||
* XXXHRS: Note that in the case that | |||||
* DupAddrDetectTransmits > 1, multiple NS messages with | |||||
* different nonces can be looped back in an unexpected | |||||
* order. The current implementation recognizes only | |||||
* the latest nonce on the sender side. Practically it | |||||
* should work well in almost all cases. | |||||
*/ | |||||
aeUnsubmitted Not Done Inline ActionsDAD does retransmits with 1s delay, it seems impossible to receive looped back message with such delay :) ae: DAD does retransmits with 1s delay, it seems impossible to receive looped back message with… | |||||
} else | |||||
nonce = NULL; | |||||
nd6_ns_output(ifp, NULL, &ia->ia_addr.sin6_addr, NULL, nonce); | |||||
} | } | ||||
static void | static void | ||||
nd6_dad_ns_input(struct ifaddr *ifa) | nd6_dad_ns_input(struct ifaddr *ifa, struct nd_opt_nonce *ndopt_nonce) | ||||
{ | { | ||||
struct in6_ifaddr *ia; | struct in6_ifaddr *ia; | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
const struct in6_addr *taddr6; | const struct in6_addr *taddr6; | ||||
struct dadq *dp; | struct dadq *dp; | ||||
if (ifa == NULL) | if (ifa == NULL) | ||||
panic("ifa == NULL in nd6_dad_ns_input"); | panic("ifa == NULL in nd6_dad_ns_input"); | ||||
ia = (struct in6_ifaddr *)ifa; | ia = (struct in6_ifaddr *)ifa; | ||||
ifp = ifa->ifa_ifp; | ifp = ifa->ifa_ifp; | ||||
taddr6 = &ia->ia_addr.sin6_addr; | taddr6 = &ia->ia_addr.sin6_addr; | ||||
dp = nd6_dad_find(ifa); | /* Ignore Nonce option when Enhanced DAD is disabled. */ | ||||
if (V_dad_enhanced == 0) | |||||
ndopt_nonce = NULL; | |||||
dp = nd6_dad_find(ifa, ndopt_nonce); | |||||
if (dp == NULL) | if (dp == NULL) | ||||
return; | return; | ||||
/* Quickhack - completely ignore DAD NS packets */ | |||||
if (V_dad_ignore_ns) { | |||||
char ip6buf[INET6_ADDRSTRLEN]; | |||||
nd6log((LOG_INFO, | |||||
"nd6_dad_ns_input: ignoring DAD NS packet for " | |||||
"address %s(%s)\n", ip6_sprintf(ip6buf, taddr6), | |||||
if_name(ifa->ifa_ifp))); | |||||
return; | |||||
} | |||||
/* XXX more checks for loopback situation - see nd6_dad_timer too */ | |||||
dp->dad_ns_icount++; | dp->dad_ns_icount++; | ||||
nd6_dad_rele(dp); | nd6_dad_rele(dp); | ||||
} | } | ||||
static void | static void | ||||
nd6_dad_na_input(struct ifaddr *ifa) | nd6_dad_na_input(struct ifaddr *ifa) | ||||
{ | { | ||||
struct dadq *dp; | struct dadq *dp; | ||||
if (ifa == NULL) | if (ifa == NULL) | ||||
panic("ifa == NULL in nd6_dad_na_input"); | panic("ifa == NULL in nd6_dad_na_input"); | ||||
dp = nd6_dad_find(ifa); | dp = nd6_dad_find(ifa, NULL); | ||||
if (dp != NULL) { | if (dp != NULL) { | ||||
dp->dad_na_icount++; | dp->dad_na_icount++; | ||||
nd6_dad_rele(dp); | nd6_dad_rele(dp); | ||||
} | } | ||||
} | } |
Maybe it will be better hide this log message under nd6_debug?