📄 nd6_nbr.c
字号:
nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
nd_opt->nd_opt_len = optlen >> 3;
bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen);
} else
flags &= ~ND_NA_FLAG_OVERRIDE;
ip6->ip6_plen = htons((u_short)icmp6len);
nd_na->nd_na_flags_reserved = flags;
nd_na->nd_na_cksum = 0;
nd_na->nd_na_cksum =
in6_cksum(m, IPPROTO_ICMPV6, sizeof(struct ip6_hdr), icmp6len);
#ifdef IPSEC
/* Don't lookup socket */
(void)ipsec_setsocket(m, NULL);
#endif
ip6_output(m, NULL, NULL, 0, &im6o, NULL);
icmp6_ifstat_inc(ifp, ifs6_out_msg);
icmp6_ifstat_inc(ifp, ifs6_out_neighboradvert);
icmp6stat.icp6s_outhist[ND_NEIGHBOR_ADVERT]++;
}
caddr_t
nd6_ifptomac(ifp)
struct ifnet *ifp;
{
switch (ifp->if_type) {
case IFT_ARCNET:
case IFT_ETHER:
case IFT_FDDI:
case IFT_IEEE1394:
#ifdef IFT_PROPVIRTUAL
case IFT_PROPVIRTUAL:
#endif
#ifdef IFT_L2VLAN
case IFT_L2VLAN:
#endif
#ifdef IFT_IEEE80211
case IFT_IEEE80211:
#endif
#ifdef __NetBSD__
return LLADDR(ifp->if_sadl);
#else
return ((caddr_t)(ifp + 1));
#endif
break;
default:
return NULL;
}
}
TAILQ_HEAD(dadq_head, dadq);
struct dadq {
TAILQ_ENTRY(dadq) dad_list;
struct ifaddr *dad_ifa;
int dad_count; /* max NS to send */
int dad_ns_tcount; /* # of trials to send NS */
int dad_ns_ocount; /* NS sent so far */
int dad_ns_icount;
int dad_na_icount;
#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3)
struct callout dad_timer_ch;
#elif defined(__OpenBSD__)
struct timeout dad_timer_ch;
#endif
};
static struct dadq_head dadq;
static int dad_init = 0;
static struct dadq *
nd6_dad_find(ifa)
struct ifaddr *ifa;
{
struct dadq *dp;
for (dp = dadq.tqh_first; dp; dp = dp->dad_list.tqe_next) {
if (dp->dad_ifa == ifa)
return dp;
}
return NULL;
}
static void
nd6_dad_starttimer(dp, _ticks)
struct dadq *dp;
int _ticks;
{
#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3)
callout_reset(&dp->dad_timer_ch, _ticks,
(void (*) __P((void *)))nd6_dad_timer, (void *)dp->dad_ifa);
#elif defined(__OpenBSD__)
timeout_set(&dp->dad_timer_ch, (void (*) __P((void *)))nd6_dad_timer,
(void *)dp->dad_ifa);
timeout_add(&dp->dad_timer_ch, _ticks);
#else
timeout((void (*) __P((void *)))nd6_dad_timer, (void *)dp->dad_ifa,
_ticks);
#endif
}
static void
nd6_dad_stoptimer(dp)
struct dadq *dp;
{
#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3)
callout_stop(&dp->dad_timer_ch);
#elif defined(__OpenBSD__)
timeout_del(&dp->dad_timer_ch);
#else
untimeout((void (*) __P((void *)))nd6_dad_timer, (void *)dp->dad_ifa);
#endif
}
/*
* Start Duplicated Address Detection (DAD) for specified interface address.
*/
void
nd6_dad_start(ifa, tick)
struct ifaddr *ifa;
int *tick; /* minimum delay ticks for IFF_UP event */
{
struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
struct dadq *dp;
int ntick;
if (!dad_init) {
TAILQ_INIT(&dadq);
dad_init++;
}
/*
* If we don't need DAD, don't do it.
* There are several cases:
* - DAD is disabled (ip6_dad_count == 0)
* - the interface address is anycast
*/
if (!(ia->ia6_flags & IN6_IFF_TENTATIVE)) {
log(LOG_DEBUG,
"nd6_dad_start: called with non-tentative address "
"%s(%s)\n",
ip6_sprintf(&ia->ia_addr.sin6_addr),
ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???");
return;
}
if (ia->ia6_flags & IN6_IFF_ANYCAST) {
ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
return;
}
if (!ip6_dad_count) {
ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
return;
}
if (!ifa->ifa_ifp)
panic("nd6_dad_start: ifa->ifa_ifp == NULL");
if (!(ifa->ifa_ifp->if_flags & IFF_UP))
return;
if (nd6_dad_find(ifa) != NULL) {
/* DAD already in progress */
return;
}
dp = malloc(sizeof(*dp), M_IP6NDP, M_NOWAIT);
if (dp == NULL) {
log(LOG_ERR, "nd6_dad_start: memory allocation failed for "
"%s(%s)\n",
ip6_sprintf(&ia->ia_addr.sin6_addr),
ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???");
return;
}
bzero(dp, sizeof(*dp));
#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3)
callout_init(&dp->dad_timer_ch);
#elif defined(__OpenBSD__)
bzero(&dp->dad_timer_ch, sizeof(dp->dad_timer_ch));
#endif
TAILQ_INSERT_TAIL(&dadq, (struct dadq *)dp, dad_list);
nd6log((LOG_DEBUG, "%s: starting DAD for %s\n", if_name(ifa->ifa_ifp),
ip6_sprintf(&ia->ia_addr.sin6_addr)));
/*
* Send NS packet for DAD, ip6_dad_count times.
* Note that we must delay the first transmission, if this is the
* first packet to be sent from the interface after interface
* (re)initialization.
*/
dp->dad_ifa = ifa;
IFAREF(ifa); /* just for safety */
dp->dad_count = ip6_dad_count;
dp->dad_ns_icount = dp->dad_na_icount = 0;
dp->dad_ns_ocount = dp->dad_ns_tcount = 0;
if (tick == NULL) {
nd6_dad_ns_output(dp, ifa);
ntick = nd_ifinfo[ifa->ifa_ifp->if_index].retrans * hz / 1000;
} else {
#ifdef __OpenBSD__
#define random arc4random
#endif
if (*tick == 0)
ntick = random() % (MAX_RTR_SOLICITATION_DELAY * hz);
else
ntick = *tick + random() % (hz / 2);
#ifdef __OpenBSD__
#undef random
#endif
*tick = ntick;
}
if (ntick <= 0) ntick = 1; // Defensive
nd6_dad_starttimer(dp, ntick);
}
/*
* terminate DAD unconditionally. used for address removals.
*/
void
nd6_dad_stop(ifa)
struct ifaddr *ifa;
{
struct dadq *dp;
if (!dad_init)
return;
dp = nd6_dad_find(ifa);
if (!dp) {
/* DAD wasn't started yet */
return;
}
nd6_dad_stoptimer(dp);
TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list);
free(dp, M_IP6NDP);
dp = NULL;
IFAFREE(ifa);
}
static void
nd6_dad_timer(ifa)
struct ifaddr *ifa;
{
int s;
struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
struct dadq *dp;
#ifdef __NetBSD__
s = splsoftnet(); /* XXX */
#else
s = splnet(); /* XXX */
#endif
/* Sanity check */
if (ia == NULL) {
log(LOG_ERR, "nd6_dad_timer: called with null parameter\n");
goto done;
}
dp = nd6_dad_find(ifa);
if (dp == NULL) {
log(LOG_ERR, "nd6_dad_timer: DAD structure not found\n");
goto done;
}
if (ia->ia6_flags & IN6_IFF_DUPLICATED) {
log(LOG_ERR, "nd6_dad_timer: called with duplicated address "
"%s(%s)\n",
ip6_sprintf(&ia->ia_addr.sin6_addr),
ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???");
goto done;
}
if ((ia->ia6_flags & IN6_IFF_TENTATIVE) == 0) {
log(LOG_ERR, "nd6_dad_timer: called with non-tentative address "
"%s(%s)\n",
ip6_sprintf(&ia->ia_addr.sin6_addr),
ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???");
goto done;
}
/* timeouted with IFF_{RUNNING,UP} check */
if (dp->dad_ns_tcount > dad_maxtry) {
nd6log((LOG_INFO, "%s: could not run DAD, driver problem?\n",
if_name(ifa->ifa_ifp)));
TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list);
free(dp, M_IP6NDP);
dp = NULL;
IFAFREE(ifa);
goto done;
}
/* Need more checks? */
if (dp->dad_ns_ocount < dp->dad_count) {
/*
* We have more NS to go. Send NS packet for DAD.
*/
nd6_dad_ns_output(dp, ifa);
nd6_dad_starttimer(dp,
nd_ifinfo[ifa->ifa_ifp->if_index].retrans * hz / 1000);
} else {
/*
* We have transmitted sufficient number of DAD packets.
* See what we've got.
*/
int duplicate;
duplicate = 0;
if (dp->dad_na_icount) {
/*
* the check is in nd6_dad_na_input(),
* but just in case
*/
duplicate++;
}
if (dp->dad_ns_icount) {
#if 0 /* heuristics */
/*
* if
* - we have sent many(?) DAD NS, and
* - the number of NS we sent equals to the
* number of NS we've got, and
* - we've got no NA
* we may have a faulty network card/driver which
* loops back multicasts to myself.
*/
if (3 < dp->dad_count
&& dp->dad_ns_icount == dp->dad_count
&& dp->dad_na_icount == 0) {
log(LOG_INFO, "DAD questionable for %s(%s): "
"network card loops back multicast?\n",
ip6_sprintf(&ia->ia_addr.sin6_addr),
if_name(ifa->ifa_ifp));
/* XXX consider it a duplicate or not? */
/* duplicate++; */
} else {
/* We've seen NS, means DAD has failed. */
duplicate++;
}
#else
/* We've seen NS, means DAD has failed. */
duplicate++;
#endif
}
if (duplicate) {
/* (*dp) will be freed in nd6_dad_duplicated() */
dp = NULL;
nd6_dad_duplicated(ifa);
} else {
/*
* We are done with DAD. No NA came, no NS came.
* duplicated address found.
*/
ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
nd6log((LOG_DEBUG,
"%s: DAD complete for %s - no duplicates found\n",
if_name(ifa->ifa_ifp),
ip6_sprintf(&ia->ia_addr.sin6_addr)));
TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list);
free(dp, M_IP6NDP);
dp = NULL;
#ifdef MIP6
if (mip6_dad_success(ifa) == ENOENT)
#endif
IFAFREE(ifa);
}
}
done:
splx(s);
}
void
nd6_dad_duplicated(ifa)
struct ifaddr *ifa;
{
struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
struct dadq *dp;
dp = nd6_dad_find(ifa);
if (dp == NULL) {
log(LOG_ERR, "nd6_dad_duplicated: DAD structure not found\n");
return;
}
log(LOG_ERR, "%s: DAD detected duplicate IPv6 address %s: "
"NS in/out=%d/%d, NA in=%d\n",
if_name(ifa->ifa_ifp), ip6_sprintf(&ia->ia_addr.sin6_addr),
dp->dad_ns_icount, dp->dad_ns_ocount, dp->dad_na_icount);
ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
ia->ia6_flags |= IN6_IFF_DUPLICATED;
/* We are done with DAD, with duplicated address found. (failure) */
nd6_dad_stoptimer(dp);
log(LOG_ERR, "%s: DAD complete for %s - duplicate found\n",
if_name(ifa->ifa_ifp), ip6_sprintf(&ia->ia_addr.sin6_addr));
log(LOG_ERR, "%s: manual intervention required\n",
if_name(ifa->ifa_ifp));
TAILQ_REMOVE(&dadq, (struct dadq *)dp, dad_list);
free(dp, M_IP6NDP);
dp = NULL;
#ifdef MIP6
if (mip6_dad_duplicated(ifa) == ENOENT)
#endif
IFAFREE(ifa);
}
static void
nd6_dad_ns_output(dp, ifa)
struct dadq *dp;
struct ifaddr *ifa;
{
struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
struct ifnet *ifp = ifa->ifa_ifp;
dp->dad_ns_tcount++;
if ((ifp->if_flags & IFF_UP) == 0) {
#if 0
printf("%s: interface down?\n", if_name(ifp));
#endif
return;
}
if ((ifp->if_flags & IFF_RUNNING) == 0) {
#if 0
printf("%s: interface not running?\n", if_name(ifp));
#endif
return;
}
dp->dad_ns_ocount++;
nd6_ns_output(ifp, NULL, &ia->ia_addr.sin6_addr, NULL, 1);
}
static void
nd6_dad_ns_input(ifa)
struct ifaddr *ifa;
{
struct in6_ifaddr *ia;
struct ifnet *ifp;
const struct in6_addr *taddr6;
struct dadq *dp;
int duplicate;
if (!ifa)
panic("ifa == NULL in nd6_dad_ns_input");
ia = (struct in6_ifaddr *)ifa;
ifp = ifa->ifa_ifp;
taddr6 = &ia->ia_addr.sin6_addr;
duplicate = 0;
dp = nd6_dad_find(ifa);
/* Quickhack - completely ignore DAD NS packets */
if (dad_ignore_ns) {
nd6log((LOG_INFO,
"nd6_dad_ns_input: ignoring DAD NS packet for "
"address %s(%s)\n", ip6_sprintf(taddr6),
if_name(ifa->ifa_ifp)));
return;
}
/*
* if I'm yet to start DAD, someone else started using this address
* first. I have a duplicate and you win.
*/
if (!dp || dp->dad_ns_ocount == 0)
duplicate++;
/* XXX more checks for loopback situation - see nd6_dad_timer too */
if (duplicate) {
dp = NULL; /* will be freed in nd6_dad_duplicated() */
nd6_dad_duplicated(ifa);
} else {
/*
* not sure if I got a duplicate.
* increment ns count and see what happens.
*/
if (dp)
dp->dad_ns_icount++;
}
}
static void
nd6_dad_na_input(ifa)
struct ifaddr *ifa;
{
struct dadq *dp;
if (!ifa)
panic("ifa == NULL in nd6_dad_na_input");
dp = nd6_dad_find(ifa);
if (dp)
dp->dad_na_icount++;
/* remove the address. */
nd6_dad_duplicated(ifa);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -