IPv4 受信処理
1. 概要
IPv4の受信処理と中継処理のメモ。
2. 処理の流れ
IPパケットを受信した時の処理の流れを図1に示す。
図1 受信時の流れ
3. 受信処理
3.1 受信開始
受信したIPv4パケットはip_rcv()に渡される。ip_rcv()ではIPヘッダの内容をチェックした後、NF_IP_PRE_ROUTINGフック処理を行った後、ip_rcv_finish()を呼び出す。
ip_rcv_finish()では、IPパケットのDstIPでルーティングテーブルを検索を行なう。ルーティングテーブルにエントリがあった場合は、最後にdst_input()を呼ぶ。
dst_input()はヒットしたルーティングエントリに登録されている受信ハンドラを呼び出して、自システム宛の受信か中継処理を行なう。自システム 宛の経路にはip_local_deliver()、その他の経路にはip_forward()が設定されている。
ip_rcv_finish()の処理概要
if (likely(skb->dst == NULL)) { /* ルーティングテーブルを検索 */ int err = ip_route_input() if (unlikely(err)) { /* 経路がないなどの理由でエラーが返った場合は廃棄 */ goto drop; } } /* IPヘッダにオプションがあれば処理をする * ここではSourceRoutingオプションを処理する */ if (iph->ihl > 5 && ip_rcv_options(skb)) goto drop; /* ヒットしたルーティングエントリに対応するハンドラへ * 自システム宛ならip_local_deliver() * 中継ならip_forward() */ return dst_input(skb); drop: kfree_skb(skb); return NET_RX_DROP;
3.2 自システム宛のケース - ip_local_deliver()
受信したパケットが自システム宛だった場合は、dst_input()からip_local_deliver()が呼び出される。
ip_local_deliver()では、パケットがフラグメントされていた場合はリアセンブル処理を行ない、ip_local_deliver_finish()を呼び出す。
ip_local_deliver_finish()ではIPパケットの上位プロトコルに応じて、上位プロトコル(TCP,UDP,...)の受信ルーチ ンを呼び出す。また、この際RAWソケットが使用されているのであれば、RAWソケットにも受信データを渡す。
{ resubmit: /* Rawソケットが使用されていた場合、 * Rawソケットの受信ルーチンを呼ぶ */ if (raw_sk && !raw_v4_input(skb, skb->nh.iph, hash)) raw_sk = NULL; if ((ipprot = rcu_dereference(inet_protos[hash])) != NULL) { /* * 上位層の受信ルーチンを呼び出し * TCP tcp_v4_rcv() * UDP udp_rcv() * ICMP icmp_rcv() */ ret = ipprot->handler(skb); } else { /* 該当プロトコルが登録されていない */ if (!raw_sk) { /* ICMPエラーを返す(DstUnreachable,ProtocolUnReachable) */ } else IP_INC_STATS_BH(IPSTATS_MIB_INDELIVERS); kfree_skb(skb); } }
3.3 中継のケース - ip_forward()
受信したパケットが中継対象だった場合は、dst_input()からip_forward()が呼び出される。
ip_forward()はTTLの減算を行い、NF_IP_FORWARDフック処理を行った後、ip_forward_finish()を呼び出す。
: /* Router Alertオプションが付いていたら * ip_call_ra_chain()を呼び出し */ if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb)) return NET_RX_SUCCESS; : /* TTLチェック */ if (skb->nh.iph->ttl <= 1) goto too_many_hops; : /* TTL減算 */ ip_decrease_ttl(iph); /* Redirect対象ならICMPリダイレクト送信 * RTCF_DOREDIRECTフラグはルートキャッシュ作成時に * フローがリダイレクト対象であればセットされる。 */ if (rt->rt_flags&RTCF_DOREDIRECT && !opt->srr) ip_rt_send_redirect(skb); : /* フィルタ処理後、ip_forward_finish()へ */ return NF_HOOK(PF_INET, NF_IP_FORWARD, skb, skb->dev, rt->u.dst.dev, ip_forward_finish); too_many_hops: /* ICMPエラー送信(TTL Over) */ drop /* パケット廃棄 */ }
ip_forward_finish()ではoptionの処理を行った後、dst_output()を呼び出す。この後は送信処理と同じとなる。
4. その他のメモ
4.1 dst_output()
ヒットしたルーティングエントリに登録されているinputハンドラを呼び出して、自システム宛の受信処理か中継処理を行なう。
このマクロはskbに登録されているskb->dst->input(skb)を呼び出しているだけ。skb->dstには ip_route_input()でルーティングキャッシュを検索した時に、ヒットしたルーティングエントリ(struct rtable)のstruct dst_entryへのポインタがセットされる。IPv4ルーティングエントリのstruct dst_entryのinputハンドラは以下のハンドラが登録されている。
- 自システム宛の経路 rth->u.dst.input = ip_local_deliver()
- 中継用の経路 rth->u.dst.input = ip_forward()