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) */ } } }
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); }
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; }
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; } }
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; }
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)); }
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_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)); }