⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 nd6_nbr.c

📁 嵌入式操作系统ECOS的网络开发包
💻 C
📖 第 1 页 / 共 3 页
字号:
		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 + -