nd6_nbr.c

来自「eCos操作系统源码」· C语言 代码 · 共 1,526 行 · 第 1/3 页

C
1,526
字号
		else {			ia = in6_ifawithifp(ifp, &ip6->ip6_dst);			if (ia == NULL) {				m_freem(m);				return;			}			ip6->ip6_src = ia->ia_addr.sin6_addr;		}	} else {		/*		 * Source address for DAD packet must always be IPv6		 * unspecified address. (0::0)		 */		bzero(&ip6->ip6_src, sizeof(ip6->ip6_src));	}	nd_ns = (struct nd_neighbor_solicit *)(ip6 + 1);	nd_ns->nd_ns_type = ND_NEIGHBOR_SOLICIT;	nd_ns->nd_ns_code = 0;	nd_ns->nd_ns_reserved = 0;	nd_ns->nd_ns_target = *taddr6;	if (IN6_IS_SCOPE_LINKLOCAL(&nd_ns->nd_ns_target))		nd_ns->nd_ns_target.s6_addr16[1] = 0;	/*	 * Add source link-layer address option.	 *	 *				spec		implementation	 *				---		---	 * DAD packet			MUST NOT	do not add the option	 * there's no link layer address:	 *				impossible	do not add the option	 * there's link layer address:	 *	Multicast NS		MUST add one	add the option	 *	Unicast NS		SHOULD add one	add the option	 */	if (!dad && (mac = nd6_ifptomac(ifp))) {		int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen;		struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_ns + 1);		/* 8 byte alignments... */		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_SOURCE_LINKADDR;		nd_opt->nd_opt_len = optlen >> 3;		bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen);	}	ip6->ip6_plen = htons((u_short)icmp6len);	nd_ns->nd_ns_cksum = 0;	nd_ns->nd_ns_cksum		= in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), icmp6len);#ifdef IPSEC	/* Don't lookup socket */	(void)ipsec_setsocket(m, NULL);#endif	ip6_output(m, NULL, NULL, dad ? IPV6_UNSPECSRC : 0, &im6o, NULL);	icmp6_ifstat_inc(ifp, ifs6_out_msg);	icmp6_ifstat_inc(ifp, ifs6_out_neighborsolicit);	icmp6stat.icp6s_outhist[ND_NEIGHBOR_SOLICIT]++;	return;  bad:	m_freem(m);	return;}/* * Neighbor advertisement input handling. * * Based on RFC 2461 * Based on RFC 2462 (duplicated address detection) * * the following items are not implemented yet: * - proxy advertisement delay rule (RFC2461 7.2.8, last paragraph, SHOULD) * - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD) */voidnd6_na_input(m, off, icmp6len)	struct mbuf *m;	int off, icmp6len;{	struct ifnet *ifp = m->m_pkthdr.rcvif;	struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);	struct nd_neighbor_advert *nd_na;#if 0	struct in6_addr saddr6 = ip6->ip6_src;#endif	struct in6_addr daddr6 = ip6->ip6_dst;	struct in6_addr taddr6;	int flags;	int is_router;	int is_solicited;	int is_override;	char *lladdr = NULL;	int lladdrlen = 0;	struct ifaddr *ifa;	struct llinfo_nd6 *ln;	struct rtentry *rt;	struct sockaddr_dl *sdl;	union nd_opts ndopts;#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)	long time_second = time.tv_sec;#endif	if (ip6->ip6_hlim != 255) {		nd6log((LOG_ERR,		    "nd6_na_input: invalid hlim (%d) from %s to %s on %s\n",		    ip6->ip6_hlim, ip6_sprintf(&ip6->ip6_src),		    ip6_sprintf(&ip6->ip6_dst), if_name(ifp)));		goto bad;	}#ifndef PULLDOWN_TEST	IP6_EXTHDR_CHECK(m, off, icmp6len,);	nd_na = (struct nd_neighbor_advert *)((caddr_t)ip6 + off);#else	IP6_EXTHDR_GET(nd_na, struct nd_neighbor_advert *, m, off, icmp6len);	if (nd_na == NULL) {		icmp6stat.icp6s_tooshort++;		return;	}#endif	taddr6 = nd_na->nd_na_target;	flags = nd_na->nd_na_flags_reserved;	is_router = ((flags & ND_NA_FLAG_ROUTER) != 0);	is_solicited = ((flags & ND_NA_FLAG_SOLICITED) != 0);	is_override = ((flags & ND_NA_FLAG_OVERRIDE) != 0);	if (IN6_IS_SCOPE_LINKLOCAL(&taddr6))		taddr6.s6_addr16[1] = htons(ifp->if_index);	if (IN6_IS_ADDR_MULTICAST(&taddr6)) {		nd6log((LOG_ERR,		    "nd6_na_input: invalid target address %s\n",		    ip6_sprintf(&taddr6)));		goto bad;	}	if (IN6_IS_ADDR_MULTICAST(&daddr6))		if (is_solicited) {			nd6log((LOG_ERR,			    "nd6_na_input: a solicited adv is multicasted\n"));			goto bad;		}	icmp6len -= sizeof(*nd_na);	nd6_option_init(nd_na + 1, icmp6len, &ndopts);	if (nd6_options(&ndopts) < 0) {		nd6log((LOG_INFO,		    "nd6_na_input: invalid ND option, ignored\n"));		/* nd6_options have incremented stats */		goto freeit;	}	if (ndopts.nd_opts_tgt_lladdr) {		lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1);		lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3;	}	ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6);#ifdef MIP6	if (!ifa) {		ifa = mip6_dad_find(&taddr6, ifp);	}#endif	/*	 * Target address matches one of my interface address.	 *	 * If my address is tentative, this means that there's somebody	 * already using the same address as mine.  This indicates DAD failure.	 * This is defined in RFC 2462.	 *	 * Otherwise, process as defined in RFC 2461.	 */	if (ifa	 && (((struct in6_ifaddr *)ifa)->ia6_flags & IN6_IFF_TENTATIVE)) {		nd6_dad_na_input(ifa);		goto freeit;	}	/* Just for safety, maybe unnecessary. */	if (ifa) {		log(LOG_ERR,		    "nd6_na_input: duplicate IP6 address %s\n",		    ip6_sprintf(&taddr6));		goto freeit;	}	if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) {		nd6log((LOG_INFO,		    "nd6_na_input: lladdrlen mismatch for %s "		    "(if %d, NA packet %d)\n",			ip6_sprintf(&taddr6), ifp->if_addrlen, lladdrlen - 2));		goto bad;	}	/*	 * If no neighbor cache entry is found, NA SHOULD silently be discarded.	 */	rt = nd6_lookup(&taddr6, 0, ifp);	if ((rt == NULL) ||	   ((ln = (struct llinfo_nd6 *)rt->rt_llinfo) == NULL) ||	   ((sdl = SDL(rt->rt_gateway)) == NULL))		goto freeit;	if (ln->ln_state == ND6_LLINFO_INCOMPLETE) {		/*		 * If the link-layer has address, and no lladdr option came,		 * discard the packet.		 */		if (ifp->if_addrlen && !lladdr)			goto freeit;		/*		 * Record link-layer address, and update the state.		 */		sdl->sdl_alen = ifp->if_addrlen;		bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen);		if (is_solicited) {			ln->ln_state = ND6_LLINFO_REACHABLE;			ln->ln_byhint = 0;			if (ln->ln_expire)				ln->ln_expire = time_second +				    nd_ifinfo[rt->rt_ifp->if_index].reachable;		} else {			ln->ln_state = ND6_LLINFO_STALE;			ln->ln_expire = time_second + nd6_gctimer;		}		if ((ln->ln_router = is_router) != 0) {			/*			 * This means a router's state has changed from			 * non-reachable to probably reachable, and might			 * affect the status of associated prefixes..			 */			pfxlist_onlink_check();		}	} else {		int llchange;		/*		 * Check if the link-layer address has changed or not.		 */		if (!lladdr)			llchange = 0;		else {			if (sdl->sdl_alen) {				if (bcmp(lladdr, LLADDR(sdl), ifp->if_addrlen))					llchange = 1;				else					llchange = 0;			} else				llchange = 1;		}		/*		 * This is VERY complex.  Look at it with care.		 *		 * override solicit lladdr llchange	action		 *					(L: record lladdr)		 *		 *	0	0	n	--	(2c)		 *	0	0	y	n	(2b) L		 *	0	0	y	y	(1)    REACHABLE->STALE		 *	0	1	n	--	(2c)   *->REACHABLE		 *	0	1	y	n	(2b) L *->REACHABLE		 *	0	1	y	y	(1)    REACHABLE->STALE		 *	1	0	n	--	(2a)		 *	1	0	y	n	(2a) L		 *	1	0	y	y	(2a) L *->STALE		 *	1	1	n	--	(2a)   *->REACHABLE		 *	1	1	y	n	(2a) L *->REACHABLE		 *	1	1	y	y	(2a) L *->REACHABLE		 */		if (!is_override && (lladdr && llchange)) {	   /* (1) */			/*			 * If state is REACHABLE, make it STALE.			 * no other updates should be done.			 */			if (ln->ln_state == ND6_LLINFO_REACHABLE) {				ln->ln_state = ND6_LLINFO_STALE;				ln->ln_expire = time_second + nd6_gctimer;			}			goto freeit;		} else if (is_override				   /* (2a) */			|| (!is_override && (lladdr && !llchange)) /* (2b) */			|| !lladdr) {				   /* (2c) */			/*			 * Update link-local address, if any.			 */			if (lladdr) {				sdl->sdl_alen = ifp->if_addrlen;				bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen);			}			/*			 * If solicited, make the state REACHABLE.			 * If not solicited and the link-layer address was			 * changed, make it STALE.			 */			if (is_solicited) {				ln->ln_state = ND6_LLINFO_REACHABLE;				ln->ln_byhint = 0;				if (ln->ln_expire) {					ln->ln_expire = time_second +					    nd_ifinfo[ifp->if_index].reachable;				}			} else {				if (lladdr && llchange) {					ln->ln_state = ND6_LLINFO_STALE;					ln->ln_expire = time_second + nd6_gctimer;				}			}		}		if (ln->ln_router && !is_router) {			/*			 * The peer dropped the router flag.			 * Remove the sender from the Default Router List and			 * update the Destination Cache entries.			 */			struct nd_defrouter *dr;			struct in6_addr *in6;			int s;			in6 = &((struct sockaddr_in6 *)rt_key(rt))->sin6_addr;			/*			 * Lock to protect the default router list.			 * XXX: this might be unnecessary, since this function			 * is only called under the network software interrupt			 * context.  However, we keep it just for safety.  			 */#ifdef __NetBSD__			s = splsoftnet();#else			s = splnet();#endif			dr = defrouter_lookup(in6, rt->rt_ifp);			if (dr)				defrtrlist_del(dr);			else if (!ip6_forwarding && ip6_accept_rtadv) {				/*				 * Even if the neighbor is not in the default				 * router list, the neighbor may be used				 * as a next hop for some destinations				 * (e.g. redirect case). So we must				 * call rt6_flush explicitly.				 */				rt6_flush(&ip6->ip6_src, rt->rt_ifp);			}			splx(s);		}		ln->ln_router = is_router;	}	rt->rt_flags &= ~RTF_REJECT;	ln->ln_asked = 0;	if (ln->ln_hold) {		/*		 * we assume ifp is not a p2p here, so just set the 2nd		 * argument as the 1st one.		 */		nd6_output(ifp, ifp, ln->ln_hold,			   (struct sockaddr_in6 *)rt_key(rt), rt);		ln->ln_hold = 0;	} freeit:	m_freem(m);	return; bad:	icmp6stat.icp6s_badna++;	m_freem(m);}/* * Neighbor advertisement output handling. * * Based on RFC 2461 * * the following items are not implemented yet: * - proxy advertisement delay rule (RFC2461 7.2.8, last paragraph, SHOULD) * - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD) */voidnd6_na_output(ifp, daddr6, taddr6, flags, tlladdr, sdl0)	struct ifnet *ifp;	const struct in6_addr *daddr6, *taddr6;	u_long flags;	int tlladdr;		/* 1 if include target link-layer address */	struct sockaddr *sdl0;	/* sockaddr_dl (= proxy NA) or NULL */{	struct mbuf *m;	struct ip6_hdr *ip6;	struct nd_neighbor_advert *nd_na;	struct in6_ifaddr *ia = NULL;	struct ip6_moptions im6o;	int icmp6len;	int maxlen;	caddr_t mac = NULL;	/* estimate the size of message */	maxlen = sizeof(*ip6) + sizeof(*nd_na);	maxlen += (sizeof(struct nd_opt_hdr) + ifp->if_addrlen + 7) & ~7;#ifdef DIAGNOSTIC	if (max_linkhdr + maxlen >= MCLBYTES) {		printf("nd6_na_output: max_linkhdr + maxlen >= MCLBYTES "		    "(%d + %d > %d)\n", max_linkhdr, maxlen, MCLBYTES);		panic("nd6_na_output: insufficient MCLBYTES");		/* NOTREACHED */	}#endif	MGETHDR(m, M_DONTWAIT, MT_DATA);	if (m && max_linkhdr + maxlen >= MHLEN) {		MCLGET(m, M_DONTWAIT);		if ((m->m_flags & M_EXT) == 0) {			m_free(m);			m = NULL;		}	}	if (m == NULL)		return;	m->m_pkthdr.rcvif = NULL;	if (IN6_IS_ADDR_MULTICAST(daddr6)) {		m->m_flags |= M_MCAST;		im6o.im6o_multicast_ifp = ifp;		im6o.im6o_multicast_hlim = 255;		im6o.im6o_multicast_loop = 0;	}	icmp6len = sizeof(*nd_na);	m->m_pkthdr.len = m->m_len = sizeof(struct ip6_hdr) + icmp6len;	m->m_data += max_linkhdr;	/* or MH_ALIGN() equivalent? */	/* fill neighbor advertisement packet */	ip6 = mtod(m, struct ip6_hdr *);	ip6->ip6_flow = 0;	ip6->ip6_vfc &= ~IPV6_VERSION_MASK;	ip6->ip6_vfc |= IPV6_VERSION;	ip6->ip6_nxt = IPPROTO_ICMPV6;	ip6->ip6_hlim = 255;	if (IN6_IS_ADDR_UNSPECIFIED(daddr6)) {		/* reply to DAD */		ip6->ip6_dst.s6_addr16[0] = IPV6_ADDR_INT16_MLL;		ip6->ip6_dst.s6_addr16[1] = htons(ifp->if_index);		ip6->ip6_dst.s6_addr32[1] = 0;		ip6->ip6_dst.s6_addr32[2] = 0;		ip6->ip6_dst.s6_addr32[3] = IPV6_ADDR_INT32_ONE;		flags &= ~ND_NA_FLAG_SOLICITED;	} else		ip6->ip6_dst = *daddr6;	/*	 * Select a source whose scope is the same as that of the dest.	 */	ia = in6_ifawithifp(ifp, &ip6->ip6_dst);	if (ia == NULL) {		m_freem(m);		return;	}	ip6->ip6_src = ia->ia_addr.sin6_addr;	nd_na = (struct nd_neighbor_advert *)(ip6 + 1);	nd_na->nd_na_type = ND_NEIGHBOR_ADVERT;	nd_na->nd_na_code = 0;	nd_na->nd_na_target = *taddr6;	if (IN6_IS_SCOPE_LINKLOCAL(&nd_na->nd_na_target))		nd_na->nd_na_target.s6_addr16[1] = 0;	/*	 * "tlladdr" indicates NS's condition for adding tlladdr or not.	 * see nd6_ns_input() for details.	 * Basically, if NS packet is sent to unicast/anycast addr,	 * target lladdr option SHOULD NOT be included.	 */	if (tlladdr) {		mac = NULL;		/*		 * sdl0 != NULL indicates proxy NA.  If we do proxy, use		 * lladdr in sdl0.  If we are not proxying (sending NA for		 * my address) use lladdr configured for the interface.		 */		if (sdl0 == NULL)			mac = nd6_ifptomac(ifp);		else if (sdl0->sa_family == AF_LINK) {			struct sockaddr_dl *sdl;			sdl = (struct sockaddr_dl *)sdl0;			if (sdl->sdl_alen == ifp->if_addrlen)				mac = LLADDR(sdl);		}	}	if (tlladdr && mac) {		int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen;		struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_na + 1);				/* roundup to 8 bytes alignment! */		optlen = (optlen + 7) & ~7;		m->m_pkthdr.len += optlen;		m->m_len += optlen;		icmp6len += optlen;		bzero((caddr_t)nd_opt, optlen);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?