Exemple #1
0
void
if_loop_input_task (intptr_t exinf)
{
	T_NET_BUF	*input;
	ID		tskid;

	get_tid(&tskid);
	syslog(LOG_NOTICE, "[LOOP INPUT:%d] started.", tskid);
	while (true) {
		if (rcv_dtq(DTQ_LOOP_INPUT, (intptr_t)&input) == E_OK) {
			NET_COUNT_LOOP(net_count_loop.in_octets,  input->len);
			NET_COUNT_LOOP(net_count_loop.in_packets, 1);

#if defined(SUPPORT_INET4)

			/* IPv4 入力関数を呼び出す */
			if (IP4_VHL_V(GET_IP4_HDR(input)->vhl) == IPV4_VERSION)
				ip_input(input);

#endif	/* of #if defined(SUPPORT_INET4) */

#if defined(SUPPORT_INET6)

			/* IPv6 入力関数を呼び出す */
			if (IP6_VCF_V(ntohl(GET_IP6_HDR(input)->vcf)) == IPV6_VERSION)
				ip6_input(input);

#endif	/* of #if defined(SUPPORT_INET6) */

			}
		}
	}
Exemple #2
0
uint16_t
in6_cksum (T_NET_BUF *nbuf, uint8_t proto, uint_t off, uint_t len)
{
	uint32_t	sum;
	uint_t		align;

	/* 4 オクテット境界のデータ長 */
	align = (len + 3) >> 2 << 2;

	/* 4 オクテット境界までパディングで埋める。*/
	if (align > len)
		memset((uint8_t*)nbuf->buf + off + len, 0, align - len);

	sum = in_cksum_sum(nbuf->buf + off, align)
	    + in_cksum_sum(&GET_IP6_HDR(nbuf)->src, sizeof(T_IN6_ADDR) * 2)
	    + len + proto;
	sum = in_cksum_carry(sum);

	return ~htons((uint16_t)sum);
	}
Exemple #3
0
ER_UINT
ip6_unknown_opt (T_NET_BUF *input, uint8_t *opt)
{
	switch (IP6OPT_TYPE(*opt)) {
	case IP6OPT_TYPE_SKIP:		/* 無視する。			*/
		return *(opt + 1);
		break;
	case IP6OPT_TYPE_ICMP:		/* 破棄して、マルチキャストでなければ ICMP を返送する。	*/
		if (IN6_IS_ADDR_MULTICAST(&GET_IP6_HDR(input)->dst))
			return IP6_OPT_RET_ERR;
		/* no break; */
	case IP6OPT_TYPE_FORCEICMP:	/* 破棄して、ICMP を返送する。	*/
		icmp6_error(input, ICMP6_PARAM_PROB,
		                   ICMP6_PARAMPROB_OPTION,
		                   (opt - input->buf) - IF_HDR_SIZE);
		return IP6_OPT_RET_REL;
		break;
	/*case IP6OPT_TYPE_DISCARD:	データグラムを破棄する。	*/
		}

	return IP6_OPT_RET_ERR;
	}
Exemple #4
0
uint8_t *
ip6_get_prev_hdr (T_NET_BUF *nbuf, uint_t off)
{
	T_IP6_HDR	*ip6h;
	T_IP6_EXT_HDR	*ip6eh;
	uint_t		len;
	uint8_t		next;

	ip6h = GET_IP6_HDR(nbuf);
	if (off == IF_HDR_SIZE + sizeof(T_IP6_HDR))
		return &ip6h->next;
	else {
		next  = ip6h->next;
		len   = IF_HDR_SIZE + sizeof(T_IP6_HDR);
		ip6eh = NULL;

		while (len < off) {
			ip6eh = (T_IP6_EXT_HDR *)(nbuf->buf + len);

			switch (next) {
			case IPPROTO_FRAGMENT:
				len += sizeof(T_IP6_FRAG_HDR);
				break;
			case IPPROTO_AH:
				len += (ip6eh->len + 2) << 2;
				break;
			default:
				len += (ip6eh->len + 2) << 3;
				break;
				}
			next  = ip6eh->next;
			}

		if (ip6eh == NULL)
			return NULL;
		else
			return &ip6eh->next;
		}
	}
Exemple #5
0
ER
in6_set_header (T_NET_BUF *nbuf, uint_t len,
                T_IN6_ADDR *dstaddr, T_IN6_ADDR *srcaddr,
                uint8_t next, uint8_t hlim)
{
	T_IFNET		*ifp = IF_GET_IFNET();
	T_IP6_HDR	*ip6h;
	T_IN6_IFADDR	*ia;

	/*
	 *  宛先アドレスにふさわしい送信元アドレスを、
	 *  ネットワークインタフェースから探索して利用する。
	 */
	if (srcaddr == NULL || !IN6_IS_ADDR_UNSPECIFIED(srcaddr))
		;
	else if ((ia = in6_ifawithifp(ifp, dstaddr)) == NULL)
		return E_SYS;
	else
		srcaddr = &ia->addr;

	/* IPv6 ヘッダを設定する。*/
	ip6h		= GET_IP6_HDR(nbuf);
	ip6h->vcf	= htonl(IP6_MAKE_VCF(IPV6_VERSION, 0));
	ip6h->plen	= htons(len);
	ip6h->next	= next;
	ip6h->hlim	= hlim;

	if (dstaddr == NULL)
		memset(&ip6h->dst, 0, sizeof(T_IN6_ADDR));
	else
		ip6h->dst = *dstaddr;

	if (srcaddr == NULL)
		memset(&ip6h->src, 0, sizeof(T_IN6_ADDR));
	else
		ip6h->src = *srcaddr;

	return E_OK;
	}
Exemple #6
0
void
ip6_input (T_NET_BUF *input)
{
	T_IP6_HDR	*ip6h;
	T_IN6_IFADDR	*ia6;
	T_IFNET		*ifp;
	ER_UINT		noff;
	uint_t		next, nest, plen, offp, nextp;
	uint_t		(*func)(T_NET_BUF **, uint_t *, uint_t *);

	NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_OCTETS], input->len - IF_HDR_SIZE);
	NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_PACKETS], 1);
	NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInReceives, 1);

	/* IP ヘッダの長さをチェックする。*/
	if (input->len < IF_IP6_HDR_SIZE) {
		NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_SHORT], 1);
		NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInHdrErrors, 1);
		goto buf_rel;
		}

	ip6h = GET_IP6_HDR(input);

	/* バージョンをチェックする。*/
	if (IP6_VCF_V(ntohl(ip6h->vcf)) != IPV6_VERSION) {
		NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_VER], 1);
		NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInHdrErrors, 1);
		goto buf_rel;
		}

	/*
	 *  次のデータグラムは破棄する。
	 *
	 *    ・始点アドレスがマルチキャスト
	 *    ・終点アドレスが未定義
	 *
	 *  ネットワークインタフェースがループバックでないとき、
	 *  次のデータグラムも破棄する。
	 *
	 *    ・始点アドレスがループバック
	 *    ・終点アドレスがループバック
	 *
	 *  悪意のあるユーザが TCP/UDP スタックの混乱や
	 *  セキュリティチェックをバイパスするため、IPv4
	 *  マップアドレスを悪用することを防ぐため
	 *  以下のデータグラムは破棄する。  
	 *
	 *    ・始点アドレスが ::ffff:127.0.0.1
	 *    ・終点アドレスが ::ffff:127.0.0.1
	 */

	if (IN6_IS_ADDR_MULTICAST(&ip6h->src) ||
	    IN6_IS_ADDR_UNSPECIFIED(&ip6h->dst)) {
		NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_ADDR], 1);
		NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInAddrErrors, 1);
		goto buf_rel;
		}

	if (IN6_IS_ADDR_V4MAPPED(&ip6h->src) ||
	    IN6_IS_ADDR_V4MAPPED(&ip6h->dst)) {
		NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_ADDR], 1);
		NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInAddrErrors, 1);
		goto buf_rel;
		}

#ifdef SUPPORT_LOOP

#else	/* of #ifdef SUPPORT_LOOP */

	if (IN6_IS_ADDR_LOOPBACK(&ip6h->src) ||
	    IN6_IS_ADDR_LOOPBACK(&ip6h->dst)) {
		NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_ADDR], 1);
		NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInAddrErrors, 1);
		goto buf_rel;
		}

#endif	/* of #ifdef SUPPORT_LOOP */

	ifp = IF_GET_IFNET();

	if (IN6_IS_ADDR_MULTICAST(&ip6h->dst)) {
		/* 宛先がマルチキャストの場合の処理 */
		NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInMcastPkts, 1);
		if (!in6_lookup_multi(ifp, &ip6h->dst)) {
			NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_ADDR], 1);
			NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInAddrErrors, 1);
			goto buf_rel;
			}
		}
	else {
		if ((ia6 = in6_lookup_ifaddr(ifp, &ip6h->dst)) == NULL) {
			NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_ADDR], 1);
			NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInAddrErrors, 1);
			goto buf_rel;
			}

		/*  アドレスが未解決の場合はデータグラムを破棄する。*/
		if (IFA6_IS_NOTREADY(ia6)) {
			NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_ADDR], 1);
			NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInAddrErrors, 1);
			goto buf_rel;
			}
		}

	/* オフセットを設定する。*/
	offp  = GET_IP6_NEXT_HDR(input) - input->buf;

	/* ペイロード長を取り出す。*/
	plen = ntohs(ip6h->plen);


	/* 次ヘッダの位置を初期化する。*/
	nextp = offsetof(T_IP6_HDR, next);

	/*
	 *  中継点 (Hop-by-Hop) オプションヘッダのチェック
	 */
	if (ip6h->next == IPPROTO_HOPOPTS) {

		/* 中継点ヘッダの長さをチェックする。*/
		if (plen < sizeof(T_IP6_HBH_HDR)) {
			NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_SHORT], 1);
			NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInHdrErrors, 1);
			goto buf_rel;
			}

		if ((noff = ip6_hopopts_input(input, &offp, &nextp)) < 0) {
			if (noff == IP6_OPT_RET_ERR) {
				NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_PACKETS], 1);
				NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInHdrErrors, 1);
				goto buf_rel;
				}
			else if (noff == IP6_OPT_RET_REL) {
				NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_PACKETS], 1);
				NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInHdrErrors, 1);
				return;
				}
			}

		/*
		 *  ペイロード長が 0 で、中継点オプションがあるときは、
		 *  巨大ペイロード・オプションが含まれなければならない。
		 *  このとき、巨大ペイロード・オプションにある
		 *  ペイロード長をチェックする必要があるが、本実装では、
		 *  巨大ペイロード・オプションを未知のオプションとして
		 *  扱うので、何もしない。
		 */
		next = ((T_IP6_HBH_HDR *)(ip6h + 1))->next;
		}
	else
		next = ip6h->next;

	/* IPv6 データグラム長をチェックする。*/
	if (input->len - IF_IP6_HDR_SIZE < plen) {
		NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_SHORT], 1);
		NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInTruncatedPkts, 1);
		goto buf_rel;
		}
	else if (input->len > IF_IP6_HDR_SIZE + plen)
		input->len = IF_IP6_HDR_SIZE + plen;

	/* 上位プロトコル処理関数を呼び出す。*/
	nest = 0;
	while (next != IPPROTO_DONE) {
		if (++ nest > IP6_HDR_NEST_LIMIT) {
			NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_PROTO], 1);
			NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInUnknownProtos, 1);
			goto buf_rel;
			}
		else if ((func = get_upper_proto(next)) == NULL) {
			NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_PROTO], 1);
			NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_PACKETS], 1);
			NET_COUNT_MIB(in6_ifstat.ipv6IfStatsInUnknownProtos, 1);

			/* icmp6_error でネットワークバッファを開放する。*/
			icmp6_error(input, ICMP6_PARAM_PROB,
			                   ICMP6_PARAMPROB_NEXT_HEADER, nextp);
			return;
			}
		else {
			next = (*func)(&input, &offp, &nextp);
			}
		}
	return;

buf_rel:
	NET_COUNT_IP6(net_count_ip6[NC_IP6_IN_ERR_PACKETS], 1);
	syscall(rel_net_buf(input));
	}
Exemple #7
0
ER
ip6_output (T_NET_BUF *output, uint16_t flags, TMO tmout)
{
	T_IP6_HDR	*ip6h;
	T_IN6_ADDR	*gw;
	T_IFNET		*ifp = IF_GET_IFNET();
	ER		error = E_OK;

#ifdef IP6_CFG_FRAGMENT

	T_IP6_HDR	*fip6h;
	T_IP6_FRAG_HDR	*fip6f;
	T_NET_BUF	*frag;
	uint32_t	mtu, id;
	uint16_t	plen, foff, flen;
	uint8_t		nextproto;
	bool_t		alwaysfrag = false;

#endif	/* of #ifdef IP6_CFG_FRAGMENT */

	ip6h = GET_IP6_HDR(output);

	NET_COUNT_IP6(net_count_ip6[NC_IP6_OUT_OCTETS],
	              GET_IP_HDR_SIZE(ip6h) + GET_IP_SDU_SIZE(ip6h));
	NET_COUNT_IP6(net_count_ip6[NC_IP6_OUT_PACKETS], 1);
	NET_COUNT_MIB(in6_ifstat.ipv6IfStatsOutRequests, 1);

	/*
	 *  重複アドレス検出要請以外には、送信元アドレスに
	 *  無指定を指定できない。
	 */
	if (IN6_IS_ADDR_UNSPECIFIED(&ip6h->src) && (flags & IPV6_OUT_FLG_DAD) == 0) {
		error = E_PAR;
		goto drop;
		}

	/* 送信元アドレスにはマルチキャストアドレスを指定できない。*/
	if (IN6_IS_ADDR_MULTICAST(&ip6h->src)) {
		error = E_PAR;
		goto drop;
		}

	/*
	 *  経路選択を行う。
	 */
	if ((gw = in6_rtalloc(ifp, &ip6h->dst)) == NULL) {
		error = E_PAR;
		goto drop;
		}

	if (!IN6_IS_ADDR_MULTICAST(&ip6h->dst)) {
		/* ユニキャストアドレスの処理 */
		}
	else {
		/* マルチキャストアドレスの処理 */
		NET_COUNT_MIB(in6_ifstat.ipv6IfStatsOutMcastPkts, 1);
		if (flags & IPV6_OUT_FLG_HOP_LIMIT)
			ip6h->hlim = IPV6_OUT_GET_HOP_LIMIT(flags);
		else
			ip6h->hlim = IPV6_DEFAULT_MULTICAST_HOPS;
		}

	/* Traffic Class と Flow Label をサポートしないので 0 に設定する。*/
	ip6h->vcf = htonl(IP6_MAKE_VCF(IPV6_VERSION, 0));

#ifdef IP6_CFG_FRAGMENT


#if NUM_IN6_HOSTCACHE_ENTRY > 0

	mtu = in6_hostcache_getmtu(&ip6h->dst);

	if (0 < mtu && mtu <= IPV6_MMTU) {

		/*
		 *  Path MTU が登録されている場合、
		 *  MTU が IPv6 MMTU(1280 オクテット)以下の場合は、
		 *  MTU を IPv6 MMTU に設定し分割して送信する。
		 */
		mtu = IPV6_MMTU;
		alwaysfrag = true;
		}
	else if (mtu == 0)
		mtu  = linkmtu;

#else	/* of #if NUM_IN6_HOSTCACHE_ENTRY > 0 */

	mtu  = linkmtu;

#endif	/* of #if NUM_IN6_HOSTCACHE_ENTRY > 0 */

	plen = ntohs(ip6h->plen);
	if (plen + IP6_HDR_SIZE <= mtu && !alwaysfrag) {

		/* 分割する必要がないときの処理 */
		if ((error = nd6_output(ifp, output, gw, NULL, tmout)) != E_OK) {
			NET_COUNT_IP6(net_count_ip6[NC_IP6_OUT_ERR_PACKETS], 1);
			NET_COUNT_MIB(in6_ifstat.ipv6IfStatsOutDiscards, 1);
			}
		}
	else {

		/* 断片 ID を設定する。*/
		id = ip6_id ++;

		/* MTU が IPv6 の最大パケットサイズを超えないようにする。*/
		if (mtu > IPV6_MAXPACKET)
			mtu = IPV6_MAXPACKET;

		/*
		 *  MTU から、分割不能部分(TINET-1.4 では、分割ヘッダ以外の
		 *  拡張ヘッダの出力は未実装のため 0 オクテット)
		 *  と断片ヘッダのサイズを引いたとき、8 オクテット以上なければ
		 *  分割して送信できないためエラーにする。
		 */
		if (((mtu - sizeof(T_IP6_FRAG_HDR)) & ~7) < 8) {
			error = E_PAR;
			goto drop;
			}

		/*
		 *  IPv6 ヘッダの next フィールドに設定する値は、断片ヘッダ。
		 *  断片ヘッダの  next フィールドに設定する値は、
		 *  元のデータグラムの next フィールドの値。
		 */
		nextproto  = ip6h->next;
		ip6h->next = IPPROTO_FRAGMENT;

		/* 分割して送信する。*/
		NET_COUNT_IP6(net_count_ip6[NC_IP6_FRAG_OUT], 1);
		for (foff = 0; foff < plen; foff += flen) {

			/* 断片の長さを計算し、8 オクテット境界に調整する。*/
			if (foff + (mtu - (IP6_HDR_SIZE + sizeof(T_IP6_FRAG_HDR))) < plen)
				flen = (mtu - (IP6_HDR_SIZE + sizeof(T_IP6_FRAG_HDR))) >> 3 << 3;
			else
				flen = plen - foff;

			/* ネットワークバッファを獲得する。*/
			if (tget_net_buf(&frag, flen + IF_IP6_HDR_SIZE + sizeof(T_IP6_FRAG_HDR), TMO_IP6_FRAG_GET_NET_BUF) == E_OK) {

				/* IPv6 ヘッダをコピーする。*/
				fip6h = GET_IP6_HDR(frag);
				*fip6h = *ip6h;

				/* IPv6 ヘッダを埋める。*/
				fip6h->plen	= htons(flen + sizeof(T_IP6_FRAG_HDR));

				/* 断片ヘッダを埋める。*/
				fip6f = (T_IP6_FRAG_HDR *)GET_IP6_NEXT_HDR(frag);
				fip6f->off_flag	= htons((uint16_t)(foff & ~7));
				if (foff + (mtu - (IP6_HDR_SIZE + sizeof(T_IP6_FRAG_HDR))) < plen)
					fip6f->off_flag	|= IP6F_MORE_FRAG;
				fip6f->reserved	= 0;
				fip6f->ident	= htonl(id);
				fip6f->next	= nextproto;

				/* SDU をコピーする。*/
				memcpy((uint8_t*)(fip6f + 1), GET_IP6_NEXT_HDR(output) + foff, flen);
				NET_COUNT_IP6(net_count_ip6[NC_IP6_FRAG_OUT_FRAGS], 1);
				NET_COUNT_MIB(in6_ifstat.ipv6IfStatsOutFragCreates, 1);

				/* 送信する。*/
				if ((error = nd6_output(ifp, frag, gw, NULL, tmout)) != E_OK) {
					NET_COUNT_MIB(in6_ifstat.ipv6IfStatsOutFragFails, 1);
					goto drop;
					}
				}
			else {
				/* ネットワークバッファが獲得できなければ、送信をあきらめる。*/
				error = E_NOMEM;
				goto drop;
				}
			}

		/* IF でネットワークバッファを開放しないフラグをチェックする。*/
		if ((output->flags & NB_FLG_NOREL_IFOUT) == 0) {
			syscall(rel_net_buf(output));
			}
		else
			output->flags &= (uint8_t)~NB_FLG_NOREL_IFOUT;

		NET_COUNT_MIB(in6_ifstat.ipv6IfStatsOutFragOKs, 1);
		}
Exemple #8
0
void
nd6_ns_input (T_NET_BUF *input, uint_t off)
{
	T_IFNET			*ifp = IF_GET_IFNET();
	T_IP6_HDR		*ip6h;
	T_NEIGHBOR_SOLICIT_HDR	*nsh;
	T_ND_OPT_HDR		*opth;
	T_IN6_IFADDR		*ifa;
	bool_t			tlladdr;
	uint_t			lladdr_len = 0;
	uint32_t		flags;
	uint8_t			*lladdr = NULL;
	uint8_t			nd_opt_off[ND_OPT_OFF_ARRAY_SIZE];

	NET_COUNT_ICMP6(net_count_nd6[NC_ND6_NS_IN_PACKETS], 1);

	ip6h = GET_IP6_HDR(input);

	/*
	 *  ヘッダのチェック、以下の場合は破棄する。
	 *    ・ホップリミットが IPV6_MAXHLIM (255) 以外
	 *    ・ヘッダ長が短い
	 */
	if (ip6h->hlim != IPV6_MAXHLIM || input->len - off < NEIGHBOR_SOLICIT_HDR_SIZE)
		goto err_ret;

	nsh  = (T_NEIGHBOR_SOLICIT_HDR *)(input->buf + off);

	if (IN6_IS_ADDR_UNSPECIFIED(&ip6h->src)) {
		/*
		 *  始点アドレスが無指定なら、重複アドレス検出
		 *  あて先アドレスは、要請マルチキャストでなければならない。
		 */
		if (!IN6_IS_ADDR_NS_MULTICAST(&ip6h->dst))
			goto err_ret;
		}

	/* 目的アドレスがマルチキャストならエラー */
	if (IN6_IS_ADDR_MULTICAST(&nsh->target))
		goto err_ret;

	/* 近隣探索オプションのオフセットを記録する。*/
	if (nd6_options(nd_opt_off, input->buf + (off + NEIGHBOR_SOLICIT_HDR_SIZE),
	                            input->len - (off + NEIGHBOR_SOLICIT_HDR_SIZE)) != E_OK)
		goto err_ret;

	/* 近隣探索オプション (始点リンクアドレス) */
	if (nd_opt_off[ND_OPT_OFF_ARRAY_IX(ND_OPT_SOURCE_LINKADDR)]) {
		opth = (T_ND_OPT_HDR *)((uint8_t *)(input->buf + off + NEIGHBOR_SOLICIT_HDR_SIZE) +
		                        nd_opt_off[ND_OPT_OFF_ARRAY_IX(ND_OPT_SOURCE_LINKADDR)] - 8);
	 	/* 注意: オプションオフセット配列には、オフセット + 8 が設定されている。*/
		lladdr     = (uint8_t *)(opth + 1);
		lladdr_len = (opth->len << 3);
		}

	if (IN6_IS_ADDR_UNSPECIFIED(&ip6h->src) && lladdr != NULL)
		goto err_ret;

	/* 宛先アドレスがマルチキャストなら近隣通知にデータリンク層のアドレスを付ける。*/
	if (IN6_IS_ADDR_MULTICAST(&ip6h->dst))
		tlladdr = true;
	else
		tlladdr = false;

	/*
	 *  目的アドレスが、自分のネットワークインタフェースに
	 *  割り当てられているアドレスか調べる。
	 *  なお、代理サービスは実装していない。
	 */
	ifa = in6_lookup_ifaddr(ifp, &nsh->target);

	if (ifa == NULL)
		goto free_ret;

	/* 探索結果アドレスが重複していれば応答しないで終了する。*/
	if (ifa->flags & IN6_IFF_DUPLICATED)
		goto err_ret;

	/*
	 *  ネットワークインタフェースのアドレス長が一致しなければエラー
	 */
	if (lladdr && lladdr_len != ((sizeof(T_IF_ADDR) + sizeof(T_ND_OPT_HDR) + 7) & ~7))
		goto err_ret;

	/*
	 *  始点アドレスが自分のアドレスと一致すれば重複している。
	 */
	if (IN6_ARE_ADDR_EQUAL(&ifa->addr, &ip6h->src))
		goto free_ret;

	/* 重複検出中に、近隣要請を受信したときの処理 */
	if (ifa->flags & IN6_IFF_TENTATIVE) {
		if (IN6_IS_ADDR_UNSPECIFIED(&ip6h->src))
			nd6_dad_ns_input(ifp, ifa);
		goto free_ret;
		}

	if ((ifa->flags & IN6_IFF_ANYCAST) || !tlladdr)
		flags = 0;
	else
		flags = ND_NA_FLG_OVERRIDE;

	/*
	 *  始点アドレスが無指定なら、送信相手は重複アドレス検出中で、
	 *  直接送信することはできないので、全ノードマルチキャスト
	 *  アドレスに送信する。
	 */
	if (IN6_IS_ADDR_UNSPECIFIED(&ip6h->src)) {
		nd6_na_output(ifp, &in6_addr_linklocal_allnodes, &nsh->target, flags, tlladdr);
		goto free_ret;
		}

	/* 近隣キャッシュに登録する。*/
	nd6_cache_lladdr(ifp, &ip6h->src, (T_IF_ADDR *)lladdr, ND_NEIGHBOR_SOLICIT, 0);

	nd6_na_output(ifp, &ip6h->src, &nsh->target, flags | ND_NA_FLG_SOLICITED, tlladdr);

free_ret:
	syscall(rel_net_buf(input));
	return;

err_ret:
	NET_COUNT_ICMP6(net_count_nd6[NC_ICMP6_IN_ERR_PACKETS], 1);
	syscall(rel_net_buf(input));
	}