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)); }
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; }
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)); }
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; } }
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); }
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; }
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; }
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); } }
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)); } } }
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; }
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); }
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); } } }
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)); }
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; }
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); }
uint_t ah6_input (T_NET_BUF **input, uint_t *offp, uint_t *nextp) { syscall(rel_net_buf(*input)); return IPPROTO_DONE; }
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)); }
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; }
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)); }
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; }
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); }
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); }
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; }