Esempio n. 1
0
static void
parse_input(T_NET_BUF *input)
{
	T_PPP_PROTENT	*entry;
	int_t		ix;
	uint16_t	proto;

	NET_COUNT_PPP(net_count_ppp.in_octets,  input->len);
	NET_COUNT_PPP(net_count_ppp.in_packets, 1);

	/* フレーム長をチェックする */
	if (input->len < sizeof(T_PPP_HDR)) {	/* FreeBSD では PPP_HDRLEN は 4 */
		syslog(LOG_NOTICE, "[PPP] recv short frame.");
		goto buf_rel;
		}

	proto = ntohs(*GET_PPP_HDR(input));

	/* プロトコルが LCP 以外で、LCP がオープンされていなければエラー */
	if (proto != PPP_LCP && lcp_fsm.state != PPP_FSM_OPENED) {
		syslog(LOG_NOTICE, "[PPP] LCP not open.");
		goto buf_rel;
		}

	/* リンクが認証状態以前は、LCP、LQR、PAP、CHAP のみ受け付ける */
	if (ppp_phase <= PPP_PHASE_AUTHENTICATE &&
	    !(proto == PPP_LCP || proto == PPP_LQR ||
	      proto == PPP_PAP || proto == PPP_CHAP)) {
		syslog(LOG_NOTICE, "[PPP] disc proto: 0x%04x, phase: %d.", proto, ppp_phase);
		goto buf_rel;
		}

	/* 上位プロトコルの入力関数を呼出す */
	for (ix = 0; (entry = protocols[ix]) != NULL; ix ++) {
		if (entry->proto == proto && entry->input != NULL) {
			(*entry->input)(input);
			syscall(rel_net_buf(input));
			return;
			}
		if ((entry->proto & ~0x8000) == proto && entry->datainput != NULL) {
			(*entry->datainput)(input);
			return;
			}
		}

	syslog(LOG_INFO, "[PPP] unexp proto: 0x%04x.", proto);
	lcp_sprotrej(input);

buf_rel:
	NET_COUNT_PPP(net_count_ppp.in_err_packets, 1);
	syscall(rel_net_buf(input));
	}
Esempio n. 2
0
ER
nd6_output_hold (T_IFNET *ifp, T_LLINFO_ND6 *ln)
{
	SYSTIM now;

	/* アドレス解決待ちのデータグラムがあれば送信する。*/
	if (ln->hold) {

		/* タイムアウトの処理 */
		if (ln->tmout != TMO_FEVR) {

			/*
			 *  タイムアウトの指定が永久でなく、時間を過ぎていれば、
			 *  出力しないで終了する。
			 */
			syscall(get_tim(&now));
			if ((int32_t)(ln->tmout - now) < 0) {
			           /* ln->tmout < now */
				syscall(rel_net_buf(ln->hold));
				ln->hold = NULL;
				return E_TMOUT;
				}
			}
				
		nd6_output(ifp, ln->hold, &ln->addr, ln, TMO_FEVR);
		ln->hold = NULL;
		}
	return E_OK;
	}
Esempio n. 3
0
void
arp_input (T_IF_ADDR *ifaddr, T_NET_BUF *input)
{
	T_ARP_HDR	*arph;

	NET_COUNT_ARP(net_count_arp.in_octets , input->len - IF_HDR_SIZE);
	NET_COUNT_ARP(net_count_arp.in_packets, 1);

	/* ARP ヘッダの長さをチェックする。*/
	if (input->len < IF_ARP_HDR_SIZE)
		goto buf_rel;

	arph = GET_ARP_HDR(input);

	/*
	 *  物理アドレスフォーマットが Ehternet 以外
	 *  または、プロトコルが IP 以外はエラー。
	 */
	if (ntohs(arph->hrd_addr) != ARPHRD_ETHER ||
	    ntohs(arph->proto)    != ETHER_TYPE_IP)
		goto buf_rel;

	/* ARP ヘッダ + Ether ARP ヘッダの長さをチェックする。*/
	if (input->len < IF_ARP_ETHER_HDR_SIZE)
		goto buf_rel;

	in_arpinput(ifaddr, input);
	return;

buf_rel:
	NET_COUNT_ARP(net_count_arp.in_err_packets, 1);
	syscall(rel_net_buf(input));
	}
Esempio n. 4
0
bool_t
arp_resolve (T_IF_ADDR *ifaddr, T_NET_BUF *output, T_IN4_ADDR gw)
{
	T_ARP_ENTRY	*ent;
	T_ETHER_HDR	*eth;
	T_IFNET		*ifp = IF_GET_IFNET();

	eth = GET_ETHER_HDR(output);

	/*
	 *  次の場合は、イーサネットのブロードキャストアドレスを返す。
	 *
	 *    ・全ビットが 1
	 *    ・ホスト部の全ビットが 1 で、ネットワーク部がローカルアドレス
	 */
	if (gw == IPV4_ADDR_BROADCAST ||
	    gw == ((ifp->in_ifaddr.addr & ifp->in_ifaddr.mask) | ~ifp->in_ifaddr.mask)) {
		memcpy(eth->dhost, ether_broad_cast_addr, ETHER_ADDR_LEN);
		return true;
		}

	/* 送信先 GW の IP アドレスが ARP キャッシュにあるか調べる。*/
	syscall(wai_sem(SEM_ARP_CACHE_LOCK));
	ent = arp_lookup(gw, true);
	if (ent->expire) {
		memcpy(eth->dhost, ent->mac_addr, ETHER_ADDR_LEN);
		syscall(sig_sem(SEM_ARP_CACHE_LOCK));
		return true;
		}
	else {
	 	/* 送信がペンデングされているフレームがあれば捨てる。*/
		if (ent->hold) {
			NET_COUNT_IP4(net_count_ip4[NC_IP4_OUT_ERR_PACKETS], 1);
			syscall(rel_net_buf(ent->hold));
			}

		/*
		 *  送信をペンディングする。
		 *  IF でネットワークバッファを開放しないフラグが設定されているときは、
		 *  送信をペンディングしない。
		 */
		if ((output->flags & NB_FLG_NOREL_IFOUT) == 0)
			ent->hold = output;
		else {
			output->flags &= (uint8_t)~NB_FLG_NOREL_IFOUT;
			ent->hold = NULL;
			}
		syscall(sig_sem(SEM_ARP_CACHE_LOCK));

		/* アドレス解決要求を送信する。*/
		arp_request(ifaddr, gw);
		return false;
		}
	}
Esempio n. 5
0
void
tcp_free_reassq (T_TCP_CEP *cep)
{
	T_NET_BUF	*q, *nq;

	for (q = cep->reassq; q != NULL; q = nq) {
		nq = GET_TCP_Q_HDR(q, GET_TCP_IP_Q_HDR(q)->thoff)->next;
		syscall(rel_net_buf(q));
		}
	cep->reassq  = NULL;
	NET_COUNT_TCP(net_count_tcp[NC_TCP_FREE_RCV_QUEUE],   1);
	}
Esempio n. 6
0
static void
ip_freef (int_t ix)
{
	T_NET_BUF	*frag, *next;

	frag = ip_frag_queue[ix];
	while (frag != NULL) {
		next = GET_QIP4_HDR(frag)->next;
		syscall(rel_net_buf(frag));
		frag = next;
		}
	ip_frag_queue[ix] = NULL;
	}
Esempio n. 7
0
ER
ppp_output (T_NET_BUF *output, TMO tmout)
{
	ER error = E_OK;

#ifdef PPP_CFG_MODEM

#if 0	/* 保留 */
	/* モデムの接続完了まで待つ。*/
	if ((error = wait_modem()) != E_OK)
		goto buf_ret;
#endif

#endif	/* of #ifdef PPP_CFG_MODEM */

	/* IPCP の接続完了まで待つ。*/
	if ((error = wait_ipcp()) != E_OK)
		goto buf_ret;

#ifdef PPP_IDLE_TIMEOUT
	wai_sem(SEM_IDLE_TIMEOUT);
	if (idle) {
		untimeout((FP)idle_timeout, NULL);
		idle = false;
		}
	sig_sem(SEM_IDLE_TIMEOUT);
#endif	/* of #ifdef PPP_IDLE_TIMEOUT */

	/* PPP 出力キューに投入する。*/
	if ((error = tsnd_dtq(DTQ_PPP_OUTPUT, output, tmout)) != E_OK)
		goto buf_ret;

#ifdef PPP_IDLE_TIMEOUT
	wai_sem(SEM_IDLE_TIMEOUT);
	if (!idle && ppp_phase == PPP_PHASE_NETWORK) {
		timeout((FP)idle_timeout, NULL, PPP_IDLE_TIMEOUT);
		idle = true;
		}
	sig_sem(SEM_IDLE_TIMEOUT);
#endif	/* of #ifdef PPP_IDLE_TIMEOUT */

	return error;

buf_ret:
	syscall(rel_net_buf(output));
	NET_COUNT_PPP(net_count_ppp.out_err_packets, 1);
	return error;
	}
Esempio n. 8
0
void
ppp_cp_output (uint8_t code, uint8_t id, T_NET_BUF *output)
{
	T_PPP_CP_HDR	*cph;

	/* CP ヘッダを設定する */
	cph = GET_PPP_CP_HDR(output);
	cph->code = code;
	cph->id   = id;
	cph->len  = htons(output->len - sizeof(T_PPP_HDR));

	/* PPP 出力キューに投入する。*/
	if (snd_dtq(DTQ_PPP_OUTPUT, output) != E_OK) {
		syscall(rel_net_buf(output));
		NET_COUNT_PPP(net_count_ppp.out_err_packets, 1);
		}
	}
Esempio n. 9
0
void
ppp_output_task(intptr_t exinf)
{
	T_NET_BUF	*output;
	ID		tskid;

	get_tid(&tskid);
	syslog(LOG_NOTICE, "[PPP OUTPUT:%d] started.", tskid);

	while (true) {
		while (rcv_dtq(DTQ_PPP_OUTPUT, (intptr_t*)&output) == E_OK) {
			NET_COUNT_PPP(net_count_ppp.out_octets,  output->len);
			NET_COUNT_PPP(net_count_ppp.out_packets, 1);
			syscall(HDLC_write(output));
			syscall(rel_net_buf(output));
			}
		}
	}
Esempio n. 10
0
T_LLINFO_ND6 *
nd6_lookup (T_IN6_ADDR *addr, bool_t create)
{
	SYSTIM	min = 0xffffffff;
	int_t	ix, fix = -1, mix = -1;

	for (ix = NUM_ND6_CACHE_ENTRY; ix -- > 0; ) {
		if (nd6_cache[ix].state == 0) {
			/* 未使用エントリのインデックスを記録する。*/
			if (fix == -1)
				fix = ix;
			}
		else if (IN6_ARE_ADDR_EQUAL(addr, &nd6_cache[ix].addr)) {
			return &nd6_cache[ix];
			}
		else if ((int32_t)(nd6_cache[ix].expire - min) < 0) {	/* nd6_cache[ix].expire < min */
			/* 有効期限が最短エントリのインデックスを記録する。*/
			min = nd6_cache[ix].expire;
			mix = ix;
			}
		}

	if (create) {
		if (fix == -1)  {
			if (nd6_cache[mix].hold != NULL) {
				syscall(rel_net_buf(nd6_cache[mix].hold));
				}
			fix = mix;
			}
		syscall(get_tim(&nd6_cache[fix].expire));
		nd6_cache[fix].addr   = *addr;
		nd6_cache[fix].state  = ND6_LLINFO_NO_STATE;
		return &nd6_cache[fix];
		}
	else
		return NULL;
	}
Esempio n. 11
0
static void
arp_timer (void *ignore)
{
	int_t	ix;

	syscall(wai_sem(SEM_ARP_CACHE_LOCK));

	for (ix = NUM_ARP_ENTRY; ix -- > 0; ) {
		if (arp_cache[ix].expire) {
			arp_cache[ix].expire -= ARP_TIMER_TMO;
			if (arp_cache[ix].expire == 0) {
				/* 送信がペンデングされているフレームがあれば捨てる。*/
				if (arp_cache[ix].hold) {
					NET_COUNT_IP4(net_count_ip4[NC_IP4_OUT_ERR_PACKETS], 1);
					syscall(rel_net_buf(arp_cache[ix].hold));
					}
				memset(&arp_cache[ix], 0, sizeof(T_ARP_ENTRY));
				}
			}
		}

	syscall(sig_sem(SEM_ARP_CACHE_LOCK));
	timeout(arp_timer, NULL, ARP_TIMER_TMO);
	}
Esempio n. 12
0
void
ppp_input_task(intptr_t exinf)
{
	T_NET_BUF	*input;
	T_PPP_PROTENT	*proto;
	ID		tskid;
	int_t		ix;

	/* ポートを初期設定する */
	syscall(serial_opn_por(HDLC_PORTID));
	syscall(serial_ctl_por(HDLC_PORTID, IOCTL_FCSND | IOCTL_FCRCV));

	get_tid(&tskid);

#ifdef PPP_CFG_MODEM

	init_modem();
	syslog(LOG_NOTICE, "[PPP INPUT:%d] started with modem on port %d.", tskid, HDLC_PORTID);

#else	/* of #ifdef PPP_CFG_MODEM */

	syslog(LOG_NOTICE, "[PPP INPUT:%d] started on port %d.", tskid, HDLC_PORTID);

#endif	/* of #ifdef PPP_CFG_MODEM */

	/* ネットワークタイマタスクを起動する */
	syscall(act_tsk(NET_TIMER_TASK));

	/* 上位プロトコルを初期化する */
	for (ix = 0; (proto = protocols[ix]) != NULL; ix ++)
		if (proto->init != NULL)
			(*proto->init)();

	lcp_lowerup();
	lcp_open(PPP_OPEN_PASSIVE);

	/* PPP 出力タスクを起動する */
	syscall(act_tsk(PPP_OUTPUT_TASK));

	while (true) {
		if (tget_net_buf(&input, IF_PDU_SIZE, TMO_PPP_GET_NET_BUF) == E_OK) {
			while (HDLC_read(input, IF_PDU_SIZE) != E_OK)
				;
			if (input->len > 0) {

	#ifdef PPP_IDLE_TIMEOUT
				wai_sem(SEM_IDLE_TIMEOUT);
				if (idle && ntohs(*GET_PPP_HDR(input)) != PPP_LCP) {
					untimeout((FP)idle_timeout, NULL);
					idle = false;
					}
				sig_sem(SEM_IDLE_TIMEOUT);
	#endif	/* of #ifdef PPP_IDLE_TIMEOUT */

				parse_input(input);
				}
			else
				syscall(rel_net_buf(input));

	#ifdef PPP_IDLE_TIMEOUT
			wai_sem(SEM_IDLE_TIMEOUT);
			if (!idle && ppp_phase == PPP_PHASE_NETWORK) {
				timeout((FP)idle_timeout, NULL, PPP_IDLE_TIMEOUT);
				idle = true;
				}
			else if (idle && ppp_phase != PPP_PHASE_NETWORK) {
				untimeout((FP)idle_timeout, NULL);
				idle = false;
				}
			sig_sem(SEM_IDLE_TIMEOUT);
	#endif	/* of #ifdef PPP_IDLE_TIMEOUT */

			}
		else {
			HDLC_dummy_read();
			NET_COUNT_PPP(net_count_ppp.in_err_packets, 1);
			NET_COUNT_PPP(net_count_ppp_no_bufs, 1);
			}
		}
	}
Esempio n. 13
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));
	}
Esempio n. 14
0
ER
nd6_output (T_IFNET *ifp, T_NET_BUF *output, T_IN6_ADDR *dst, T_LLINFO_ND6 *ln, TMO tmout)
{
	ER	error = E_OK;
	SYSTIM	now;

	/*
	 *  あて先アドレスがマルチキャストか、近隣キャッシュが
	 *  不要の場合は直ちに送信する。
	 */
	if (!(IN6_IS_ADDR_MULTICAST(dst) || !IF_IN6_NEED_CACHE(ifp))) {
		if (ln == NULL) {
			/* 近隣キャッシュが未定義の場合 */
			if (nd6_is_addr_neighbor(ifp, dst)) {
				/*
				 *  指定されたアドレスのノードが同一リンクにある時は、
				 *  近隣キャッシュを探索し、未定義の場合は登録する。
				 */
				ln = nd6_lookup(dst, true);
				}
			else {
				error = E_CLS;
				goto err_ret;
				}
			}

		/*
		 *  近隣キャッシュの状態が、データリンク層のアドレスが分かっていても、
		 *  到達可能性が確認されていないときは、到達不能通知を開始する。
		 */
		if (ln->state == ND6_LLINFO_STALE) {
			syscall(get_tim(&now));
			ln->expire = now + ND6_DELAY_TIME;
			ln->asked = 0;
			ln->state = ND6_LLINFO_DELAY;
			}

		/*
		 *  近隣キャッシュの状態が、データリンク層のアドレス未解決の場合は
		 *  近隣探索を行う。
		 */
		if (ln->state <= ND6_LLINFO_INCOMPLETE) {

			/* タイムアウトの指定がポーリングならタイムアウトで終了する。*/
			if (tmout == TMO_POL) {
				error = E_TMOUT;
				goto err_ret;
				}

			if (ln->state == ND6_LLINFO_NO_STATE)
				ln->state = ND6_LLINFO_INCOMPLETE;

			/* すでに、未解決のネットワークバッファがあれば、開放する。*/
			if (ln->hold != NULL) {
				syscall(rel_net_buf(ln->hold));
				}

			/*
			 *  送信をペンディングする。
			 *  IF でネットワークバッファを開放しないフラグが設定されているときは、
			 *  送信をペンディングしない。
			 */
			if ((output->flags & NB_FLG_NOREL_IFOUT) == 0) {
				ln->hold = output;

				/* タイムアウトの設定 */
				if (tmout == TMO_FEVR)
					ln->tmout = tmout;
				else {
					syscall(get_tim(&now));
					ln->tmout = now + tmout;
					}
				}
			else {
				output->flags &= ~NB_FLG_NOREL_IFOUT;
				syscall(get_tim(&now));
				ln->tmout = now + tmout;
				}

			if (ln->expire) {
				if (ln->asked < ND6_MAX_MCAST_QUERY) {
					syscall(get_tim(&now));
					if ((int32_t)(ln->expire - now) < 0) {
					           /* ln->expire < now */
						ln->expire += ND6_RETRANS_TIME;	/* 近隣探索送信間隔 */
						ln->asked ++;
						nd6_ns_output(ifp, &in6_addr_unspecified, dst, ln, false);
						}
					}
				}
			return error;
			}
		}

	/*
	 *  重複が検出されたインタフェースには出力しない。
	 */
	if ((ifp->flags & ND6_IFF_IFDISABLED) != 0) {
		return E_OK;
		}

	IF_SET_PROTO(output, IF_PROTO_IPV6);
	error = IF_OUTPUT(output, dst, ln ? & ln->ifaddr : NULL, tmout);
	return error;

err_ret:
	syscall(rel_net_buf(output));
	return error;
	}
Esempio n. 15
0
void
tcp_respond (T_NET_BUF *output, T_TCP_CEP *cep,
             T_TCP_SEQ ack, T_TCP_SEQ seq, uint_t rbfree, uint8_t flags)
{
	T_IP_HDR	*iph;
	T_TCP_HDR	*tcph;
	uint_t		win = 0;

	if ((flags & TCP_FLG_RST) == 0)
		win = rbfree;

	/*
	 *  output が NULL でなければ、これは入力したセグメントの
	 *  net_buf で、そのまま再利用する。
	 */
	if (output != NULL) {
		T_IN_ADDR	ipaddr;
		uint16_t		portno;

		/*
		 * IPv4 では、IP ヘッダのオプションを削除する。
		 * IPv6 では、拡張ヘッダを削除する。
		 */
		if (IP_REMOVE_OPTIONS(output) != E_OK) {
			syscall(rel_net_buf(output));
			return;
			}

		iph  = GET_IP_HDR(output);

		/* IP アドレスを交換する。*/
		ipaddr = iph->src;
		iph->src = iph->dst;
		iph->dst = ipaddr;

#if defined(SUPPORT_INET6)

		/* トラヒッククラスとフローラベルをクリアする。*/
		iph->vcf = htonl(IP6_MAKE_VCF(IP6_VCF_V(ntohl(iph->vcf)), 0));

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

		/* TCP SDU 長を 0 にする。*/
		SET_IP_SDU_SIZE(iph, TCP_HDR_SIZE);

		tcph = GET_TCP_HDR(output, IF_IP_TCP_HDR_OFFSET);

		/* ポート番号を交換する。*/
		portno = tcph->sport;
		tcph->sport = tcph->dport;
		tcph->dport = portno;

		/* TCP ヘッダに情報を設定する。*/
		tcph->doff = TCP_MAKE_DATA_OFF(TCP_HDR_SIZE);
		}
	
	/* cep が NULL であれば、何もしないで終了する。*/
	else if (cep == NULL)
		return;
	else {
		if (tcp_get_segment(&output, cep, 0,
		                    0, (uint_t)(net_buf_max_siz() - IF_IP_TCP_HDR_SIZE),
		                    NBA_SEARCH_ASCENT, TMO_TCP_GET_NET_BUF) != E_OK)
			return;
		tcph = GET_TCP_HDR(output, IF_IP_TCP_HDR_OFFSET);
		flags |= TCP_FLG_ACK;
		}

	tcph->seq   = htonl(seq);
	tcph->ack   = htonl(ack);
	tcph->win   = htons(win);
	tcph->flags = flags;
	tcph->urp   = tcph->sum = 0;

	/*
	 *  チェックサムを設定する。
	 */
	tcph->sum = IN_CKSUM(output, IPPROTO_TCP, IF_IP_TCP_HDR_OFFSET, 
	                     (uint_t)GET_TCP_HDR_SIZE2(output, IF_IP_TCP_HDR_OFFSET));

	/* ネットワークバッファ長を調整する。*/
	output->len = (uint16_t)GET_IF_IP_TCP_HDR_SIZE2(output, IF_IP_TCP_HDR_OFFSET);

#ifdef TCP_CFG_TRACE

	tcp_output_trace(output, cep);

#endif	/* of #ifdef TCP_CFG_TRACE */

	/* ネットワーク層 (IP) の出力関数を呼び出す。*/
	IP_OUTPUT(output, TMO_TCP_OUTPUT);
	}
Esempio n. 16
0
uint_t
ah6_input (T_NET_BUF **input, uint_t *offp, uint_t *nextp)
{
	syscall(rel_net_buf(*input));
	return IPPROTO_DONE;
	}
Esempio n. 17
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));
	}
Esempio n. 18
0
uint_t
udp_input (T_NET_BUF **inputp, uint_t *offp, uint_t *nextp)
{
	T_NET_BUF	*input = *inputp;
	T_UDP_CEP	*cep;
	T_UDP_HDR	*udph;
	T_IP_HDR	*iph;
	int_t		ix;
	uint_t		len, hlen, ulen;

	hlen = (uint_t)GET_IF_IP_HDR_SIZE(input);

	NET_COUNT_UDP(net_count_udp.in_octets,  input->len - hlen);
	NET_COUNT_UDP(net_count_udp.in_packets, 1);
	NET_COUNT_MIB(udp_stats.udpInDatagrams, 1);

	/* ヘッダ長をチェックする。*/
	if (input->len < hlen + UDP_HDR_SIZE)
		goto buf_rel;

	udph = (T_UDP_HDR *)(input->buf + *offp);

	/* データグラム長をチェックする */
	ulen  = ntohs(udph->ulen);
	if (ulen != input->len - hlen)
		goto buf_rel;

	/* 宛先ポートが 0 のデータグラムは破棄する。RFC768 */
	if (udph->dport == 0)
		goto buf_rel;

#ifdef UDP_CFG_IN_CHECKSUM

	/* チェックサムをチェックする */
	if (udph->sum && IN_CKSUM(input, IPPROTO_UDP, *offp, ulen) != 0)
		goto buf_rel;

#endif	/* of #ifdef UDP_CFG_IN_CHECKSUM */

	iph = GET_IP_HDR(input);

	/* 宛先アドレスとポートをチェックする */
	for (ix = tmax_udp_ccepid; ix -- > 0; ) {
		cep = &udp_cep[ix];
		if (VALID_UDP_CEP(cep) &&
		    udp_is_dstaddr_accept(&cep->myaddr.ipaddr, &iph->dst) &&
		    ntohs(udph->dport) == cep->myaddr.portno) {
			len = (uint_t)(ntohs(udph->ulen) - UDP_HDR_SIZE);

			if (cep->rcv_tskid != TA_NULL) {	/* 非ノンブロッキングコールでペンディング中 */
				if (psnd_dtq(cep->rcvqid, (intptr_t)input) != E_OK)
					goto buf_rel;
				}

#ifdef UDP_CFG_NON_BLOCKING

			else if (cep->rcv_p_dstaddr != NULL) {	/* ノンブロッキングコールでペンディング中 */

				/* p_dstaddr を設定する。*/
				cep->rcv_p_dstaddr->portno = ntohs(udph->sport);
				IN_COPY_TO_HOST(&cep->rcv_p_dstaddr->ipaddr, &GET_IP_HDR(input)->src);

				/* データをバッファに移す。*/
				memcpy(cep->rcv_data, GET_UDP_SDU(input, *offp),
				       (size_t)(len < cep->rcv_len ? len : cep->rcv_len));
				syscall(rel_net_buf(input));

				if (IS_PTR_DEFINED(cep->callback))

#ifdef TCP_CFG_NON_BLOCKING_COMPAT14

					(*cep->callback)(GET_UDP_CEPID(cep), TFN_UDP_RCV_DAT, (void*)(uint32_t)len);

#else	/* of #ifdef TCP_CFG_NON_BLOCKING_COMPAT14 */

					(*cep->callback)(GET_UDP_CEPID(cep), TFN_UDP_RCV_DAT, (void*)&len);

#endif	/* of #ifdef TCP_CFG_NON_BLOCKING_COMPAT14 */

				else
					syslog(LOG_WARNING, "[UDP] no call back, CEP: %d.", GET_UDP_CEPID(cep));
				cep->rcv_p_dstaddr = NULL;
				}
Esempio n. 19
0
static void
in_arpinput (T_IF_ADDR *ifaddr, T_NET_BUF *input)
{
	T_NET_BUF	*pending;
	T_ETHER_HDR	*eth;
	T_ARP_HDR	*arph;
	T_ETHER_ARP_HDR	*et_arph;
	T_ARP_ENTRY	*ent;
	T_IFNET		*ifp = IF_GET_IFNET();
	T_IN4_ADDR	saddr, taddr;

	et_arph = GET_ETHER_ARP_HDR(input);

	ntoahl(saddr, et_arph->sproto);		/* 送信元 IP アドレス	*/
	ntoahl(taddr, et_arph->tproto);		/* 解決対象 IP アドレス	*/

	/*
	 *  以下の場合はエラー
	 *    ・送信ホストの物理アドレスが自分
	 *    ・送信ホストの物理アドレスがブロードキャスト
	 */
	if (memcmp(et_arph->shost, ifaddr->lladdr,        ETHER_ADDR_LEN) == 0 ||
	    memcmp(et_arph->shost, ether_broad_cast_addr, ETHER_ADDR_LEN) == 0)
		goto err_ret;

	/*
	 *  送信ホストの IP アドレスが自分の場合は、重複しているので
	 *  相手にも知らせる。
	 */
	if (saddr == ifp->in_ifaddr.addr) {

#ifdef ARP_CFG_CALLBACK_DUPLICATED

		if (arp_callback_duplicated(et_arph->shost)) {
			syslog(LOG_ERROR, "[ARP] IP address duplicated: %s", mac2str(NULL, et_arph->shost));
			taddr = saddr;
			goto reply;
			}
		else
			goto buf_rel;

#else	/* of #ifdef ARP_CFG_CALLBACK_DUPLICATED */

		syslog(LOG_ERROR, "[ARP] IP address duplicated: %s", mac2str(NULL, et_arph->shost));
		taddr = saddr;
		goto reply;

#endif	/* of #ifdef ARP_CFG_CALLBACK_DUPLICATED */

		}

	/*
	 *  以下の場合は何もしない。
	 *    ・解決対象 IP アドレスが自分ではない、注: 元の FreeBSD の
	 *      実装では、ARP PROXY 等のため、自分以外の IP アドレスの
	 *      解決も行っているが、本実装では、自分以外の IP
	 *      アドレスの解決は行わない。
	 */
	if (taddr != ifp->in_ifaddr.addr)
		goto buf_rel;

	/*
	 *  送信元 IP アドレスが ARP キャッシュにあるか調べる。
	 *  キャッシュになければ、新たにエントリを登録する。
	 */
	syscall(wai_sem(SEM_ARP_CACHE_LOCK));
	ent = arp_lookup(saddr, true);

	memcpy(ent->mac_addr, et_arph->shost, ETHER_ADDR_LEN);
	ent->expire = ARP_CACHE_KEEP;

	/*
	 *  送信がペンデングされているフレームがあれば送信する。
	 */
	if (ent->hold) {

		/* フレームの Ethernet ヘッダを設定する。*/
		memcpy(GET_ETHER_HDR(ent->hold)->dhost, ent->mac_addr, ETHER_ADDR_LEN);

		pending = ent->hold;
		ent->hold = NULL;
		syscall(sig_sem(SEM_ARP_CACHE_LOCK));

		/* ペンディングされているフレームを送信する。*/
		IF_RAW_OUTPUT(pending, TMO_FEVR);

		}
	else
		syscall(sig_sem(SEM_ARP_CACHE_LOCK));

reply:

	arph = GET_ARP_HDR(input);

	/* アドレス解決要求でなければ終了 */
	if (ntohs(arph->opcode) != ARPOP_REQUEST)
		goto buf_rel;

	/* Ethernet ARP ヘッダを設定する。*/
	memcpy(et_arph->thost, et_arph->shost, ETHER_ADDR_LEN);
	memcpy(et_arph->shost, ifaddr->lladdr, ETHER_ADDR_LEN);
	memcpy(et_arph->tproto, (uint8_t*)&et_arph->sproto, sizeof(T_IN_ADDR));
	ahtonl(et_arph->sproto, taddr);

	/* Ethernet ARP ヘッダを設定する。*/
	arph->opcode = htons(ARPOP_REPLY);

	/* Ethernet ヘッダを設定する。*/
	eth = GET_ETHER_HDR(input);
	memcpy(eth->dhost, eth->shost,     ETHER_ADDR_LEN);
	memcpy(eth->shost, ifaddr->lladdr, ETHER_ADDR_LEN);

	/* ARP 応答を送信する。*/
	IF_RAW_OUTPUT(input, TMO_FEVR);
	return;

err_ret:
	NET_COUNT_ARP(net_count_arp.in_err_packets, 1);
buf_rel:
	syscall(rel_net_buf(input));
	}
Esempio n. 20
0
static uint_t
ip6_no_header_input (T_NET_BUF **input, uint_t *offp, uint_t *nextp)
{
	syscall(rel_net_buf(*input));
	return IPPROTO_DONE;
	}
Esempio n. 21
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);
		}
Esempio n. 22
0
void
nd6_timer (void)
{
	T_IFNET		*ifp = IF_GET_IFNET();
	T_LLINFO_ND6	*ln;
	SYSTIM		now;
	int_t		ix;

	/* 近隣キャッシュの処理 */
	syscall(wai_sem(SEM_ND6_CACHE));
	syscall(get_tim(&now));
	for (ix = NUM_ND6_CACHE_ENTRY; ix -- > 0; ) {
		ln = &nd6_cache[ix];
		if (ln->state >= ND6_LLINFO_NO_STATE && (int32_t)(ln->expire - now) < 0) {
		                                               /* ln->expire < now */

			switch (ln->state) {

			case ND6_LLINFO_INCOMPLETE:	/* 未解決		*/
				if (ln->asked < ND6_MAX_MCAST_QUERY) {
					/*
					 *  マルチキャストアドレス要請の再送回数以下の場合の処理
					 */
					ln->asked ++;
					ln->expire = now + ND6_RETRANS_TIME;	/* 近隣探索送信間隔 */
					nd6_ns_output(ifp, &in6_addr_unspecified, &ln->addr, ln, false);
					}
				else  {
					if (ln->hold != NULL) {
						/*
						 *  最終的には、保留されているネットワークバッファが
						 *  あれば、ICMP6 ERROR を自分自身に返したあと、
						 *  ネットワークバッファを開放する。
						 *  しかし、現在、icmp6_error の実装が不完全のため、
						 *  ネットワークバッファの開放のみ行う。
						 */
#if 0	/* 保留 */
						icmp6_error(ln->hold, ICMP6_DST_UNREACH,
						                      ICMP6_DST_UNREACH_ADDR, 0);
#endif	/* #if 0 */
						syscall(rel_net_buf(ln->hold));
						ln->hold = NULL;
						}
					nd6_free(ln);
					memset((void*)ln, 0, sizeof(*ln));
					}
				break;

			case ND6_LLINFO_REACHABLE:	/* 到達可能		*/
				if (ln->expire) {
					ln->expire = now + ND6_GCOLLECTION_TIME;
					ln->state = ND6_LLINFO_STALE;
					}
				break;

			case ND6_LLINFO_STALE:		/* 到達可能性は未確認	*/
				if (ln->expire) {
					if (ln->hold != NULL) {
						syscall(rel_net_buf(ln->hold));
						ln->hold = NULL;
						}
					nd6_free(ln);
					memset((void*)ln, 0, sizeof(*ln));
					}
				break;

			case ND6_LLINFO_DELAY:		/* 到達可能性の確認待機	*/
				ln->asked = 1;
				ln->expire = now + ND6_RETRANS_TIME;		/* 近隣探索送信間隔 */
				ln->state = ND6_LLINFO_PROBE;
				nd6_ns_output(ifp, &ln->addr, &ln->addr, ln, false);
				break;

			case ND6_LLINFO_PROBE:		/* 到達可能性を確認中	*/
				if (ln->asked < ND6_MAX_UCAST_QUERY) {
					/*
					 *  ユニキャストアドレス要請の再送回数以下の場合の処理
					 */
					ln->asked ++;
					ln->expire += ND6_RETRANS_TIME;	/* 近隣探索送信間隔 */
					nd6_ns_output(ifp, &ln->addr, &ln->addr, ln, false);
					}
				else {
					if (ln->hold != NULL) {
						syscall(rel_net_buf(ln->hold));
						ln->hold = NULL;
						}
					nd6_free(ln);
					memset((void*)ln, 0, sizeof(*ln));
					}
				break;

				}
			}
		}
	syscall(sig_sem(SEM_ND6_CACHE));

	/* ディフォルトルータ・リストの処理 */
	nd6_defrtrlist_timer();

	/* プレフィックス・リストの処理 */
	nd6_prelist_timer();

	/* ネットワークインタフェースのアドレスの処理 */
	in6_ifaddr_timer(ifp);

	/*
	 *  到達可能状態の有効時間の更新
	 */
	if ((int32_t)(nd6_recalc_reachtm_interval - now) < 0) {
	           /* nd6_recalc_reachtm_interval < now */
		nd6_recalc_reachtm_interval	= now + ND6_RECALC_REACHTM_INTERVAL;
		nd6_reachable_time		= ND6_CALC_REACH_TIME(nd6_base_reachable_time);
		}
	timeout((callout_func)nd6_timer, NULL, ND6_TIMER_TMO);
	}
Esempio n. 23
0
uint_t
icmp_input (T_NET_BUF **inputp, uint_t *offp, uint_t *nextp)
{
	T_NET_BUF	*input = *inputp;
	T_ICMP4_HDR	*icmp4h;
	T_IN4_ADDR	addr;
	uint_t		len, align;

	NET_COUNT_ICMP4(net_count_icmp4.in_octets,
	               input->len - GET_IF_IP4_HDR_SIZE(input));
	NET_COUNT_ICMP4(net_count_icmp4.in_packets, 1);

	/* ICMP ヘッダの長さをチェックする。*/
	if (input->len < IF_IP4_ICMP4_HDR_SIZE) {
		NET_COUNT_ICMP4(net_count_icmp4.in_err_packets, 1);
		NET_COUNT_MIB(icmp_stats.icmpInErrors, 1);
		goto buf_rel;
		}

	icmp4h = (T_ICMP4_HDR *)(input->buf + *offp);

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

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

	/* チェックサムを計算する。*/
	if (in_cksum(icmp4h, align) != 0) {
		NET_COUNT_ICMP4(net_count_icmp4.in_err_packets, 1);
		goto buf_rel;
		}

	/* メッセージの型により分岐する。*/
	switch (icmp4h->type) {
	case ICMP4_ECHO_REQUEST:
		icmp_echo(input, *offp);
		return IPPROTO_DONE;
		break;
	case ICMP4_ECHO_REPLY:

#ifdef ICMP_CFG_CALLBACK_ECHO_REPLY

		icmp_echo_reply(input, *offp);

#endif	/* of #ifdef ICMP_CFG_CALLBACK_ECHO_REPLY */

		break;
	case ICMP4_UNREACH:
		icmp_unreach(input, *offp);
		break;
	case ICMP4_REDIRECT:

#if NUM_REDIRECT_ROUTE_ENTRY > 0

		addr = ntohl(icmp4h->data.addr);
		syslog(LOG_INFO, "[ICMP] redirect, addr: %s.", ip2str(NULL, &addr));
		icmp_redirect(input, *offp);

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

		addr = ntohl(icmp4h->data.addr);
		syslog(LOG_INFO, "[ICMP] redirect ignored, addr: %s.", ip2str(NULL, &addr));

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

		break;
	default:
		syslog(LOG_INFO, "[ICMP] unknown type: %d.", icmp4h->type);
		NET_COUNT_ICMP4(net_count_icmp4.in_err_packets, 1);
		break;
		}

buf_rel:
	syscall(rel_net_buf(input));
	return IPPROTO_DONE;
	}