Linux Kernel(2.6)の実装に関するメモ書き

IPv4


Rev.18を表示中。最新版はこちら

IP層(IPv4)に関するメモ

1. 受信処理

IPv4受信処理のエントリルーチンはip_rcv()。

ip_rcv()の処理概要
/* IPヘッダのバージョン値をチェックしてIPv4パケットかチェック */
if (iph->ihl < 5 || iph->version != 4)
    goto inhdr_error;
:

/* チェックサムを確認 */
if (ip_fast_csum((u8 *)iph, iph->ihl))
    goto inhdr_error;

 /* 受信パケットの長さをチェック */
if (skb->len < len || len < (iph->ihl*4))
    goto inhdr_error;
:

/* フィルタ処理をしてip_rcv_finish()へ */
return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,
               ip_rcv_finish);


ip_rcv()の最後でフィルタ処理を行ない、フィルタを通過したパケットは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;


1.1 自システム宛パケットの受信

受信したIPパケットが自分宛だった場合は、dst_input()からip_local_deliver()が呼び出される。このルーチンでは、パケットがフラグメントされていた場合はリアセンブル処理を行ないip_local_deliver_finish()を呼び出す。ip_local_deliver_finish()ではIPパケットの上位プロトコルに応じて、上位プロトコル(TCP,UDP,...)の受信ルーチンを呼び出す。

ip_local_deliver_finish()の処理概要
{

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);
    }
}


1.2 中継処理

受信したIPパケットが中継対象だった場合は、dst_input()からip_forward()が呼び出される。

ip_forwar()の処理概要
:
/* 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()の処理概要
/* IPオプションがあれば処理 */
if (opt->optlen)
    ip_forward_options(skb);

/* ルーティングエントリ登録されている
 * 送信ルーチン(ip_output())へ
 */
return dst_output(skb);

2. 送信処理

IP層のパケット送信エントリルーチンはip_output()。ip_output()はフィルタ処理(NF_IP_POST_ROUTING)を行なった後、ip_finish_output()を呼び出す。ip_finish_output()はそのままip_finish_output2()を呼び出すが、パケット長がMTUを超えている場合は、ip_fragment()でフラグメントを行なった後、ip_finish_output2()が呼び出される。

ip_finish_output2()の処理概要
:
if (hh) {
    /* dstにHardHeaderCacheが存在する */

    /* HardHeaderCacheからレイヤ2ヘッダをパケットにコピー */

    /* パケットを送信 */
    return hh->hh_output(skb);
} else if (dst->neighbour)   (*1)
    /* HardHeaderCacheがない場合の送信 */
    return dst->neighbour->output(skb);  (*2)

(*1) dst->neighbourはrt_intern_hash()でルーティングエントリをキャッシュする時にarp_bind_neighbour()をコールしてARPエントリを指すようにしている。ARPエントリがないときは空のエントリが作成されそこを指す。
(*2) アドレス解決プロトコル参照。ARP未解決ならこの中でアドレス解決処理が開始される。

[関連ルーチン]
dst_input()
ヒットしたルーティングエントリに登録されている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()

dst_out()
ヒットしたルーティングエントリに登録されているinputハンドラを呼び出して送信処理を行なう。

このマクロはskbに登録されているskb->dst->output(skb)を呼び出す。IPv4ルーティングエントリのstruct dst_entryのoutputハンドラはip_output()が登録されている。


[関連ページ]
ルーティングテーブル
アドレス解決プロトコル


最終更新 2006/08/07 16:55:22 - kztomita
(2006/08/01 19:51:49 作成)


リンク
最近更新したページ
検索