入口Linuxのネットワークプロトコルスタック処理サブシステム

メッセージネットワークドライバを受信した後、skb->プロトコルフィールドを初期化します。リンク層機能を受信Netif_receive_skbがフィールドに従ってさらに処理するために、そのパケット・プロトコル・モジュールに決定することができます。

イーサネットデバイスが呼び出さ  eth_type_transを()に与えるskb-> プロトコル割り当て。
__be16  eth_type_trans(DEV *、*構造体net_deviceをsk_buff SKBを構造体)
{
    構造体ethhdr *のEth;
    unsigned char型* rawp、NET
    / *受信net_deviceを割り当てる* SKB /
    skb-> DEV = DEV;
    SKBの先頭に/ *ポインタMAC割り当て* /
    skb_reset_mac_header(SKB);
    / * skb->下りデータ、スキップイーサネットヘッダ* /
    skb_pull(SKB、ETH_HLEN)
    のEth = eth_hdr(SKB);
    IF(そうもない(is_multicast_ether_addr(エタ- > h_dest)) )
    {
        / * *ブロードキャストおよびマルチキャストパケットが与えるskb-> pkt_type割り当てであるか否かを決定することです/
        IF(compare_ether_addr_64bits( -エタ> h_dest、DEV->放送)!)
            skb-> pkt_type = PACKET_BROADCAST;
        他
            skb-> pkt_type = PACKET_MULTICAST;
    }
               
    / *パケットは、宛先MACアドレスではない場合はMACの受信装置に、skb-を提供> pkt_type * /
    他IF(1 / * DEV->&IFF_PROMISCフラグ* /)
    {
        IF(そうもない(compare_ether_addr_64bits(エタ- > h_dest、
                                               DEV-> dev_addr)))
            skb-> pkt_type = PACKET_OTHERHOST;
    }
    / *マーベルチップ交換DSAヘッド* /
    IF(netdev_uses_dsa_tags(DEV))
        htons(ETH_P_DSA)を返す;
    IF(netdev_uses_trailer_tags(DEV))は
        htons(ETH_P_TRAILER)を返します。
               
    / *イーサネットヘッダこのリターンで* /
    IF(ntohs(エタ- > h_proto)> = 1536)
        リターンエタ- > h_proto;
               
    / *以下のような処理の非イーサネットパケットではなく、議論* /
    rawp skb- =>データ;
    IF(*(*はunsigned short)rawp == 0xFFFFの)
        htons(ETH_P_802_3)を返す;
    / *実802.2 LLC * /。
    htons(ETH_P_802_2)を返す;
}
ネットワークデバイスドライバコールnetif_receive_skb()コールまたはnetif_rx eth_type_trans()(前):
    skb-> = eth_type_transプロトコル(SKB、DEV);

各ネットワーク層プロトコルは、パケットを受信した初期設定の関数です。Linuxカーネルのデータ構造は、単一構造体packet_typeネットワーク層プロトコルを記述するために使用されます。
構造体packet_type
{
    例えばIP(0x0800で)、VLANなどの/ *プロトコルタイプ、(0x8100)* /
    / *これは本当にhtons(ETHER_TYPE)IS * /
    __be16タイプ;  
  
    / *指定され、それが空である場合、ネットワーク装置は、それが全てである受信ネットワークデバイスを受け、
      指定した場合、指定されたデバイス* /でのみ受信されたデータは、
    / * NULLここにワイルドカードIS壁紙* /; net_device * DEVストラクト

    / *プロトコル受信機能* /
    INT(* FUNC)(sk_buff *を構造体を
                  net_device *、ストラクト
                  構造体packet_typeを* 、
                  構造体net_deviceは*);
  
    / *ここでGSO機能の使用は、独自の機能を実現するために使用GSO機能への各プロトコルは、* /
    構造体sk_buff *(* gso_segment)(構造体sk_buff * SKB、INT機能)、
    INT(* gso_send_check )(* sk_buff SKBを構造体);
                                                             
    / *ここでGROを使用する関数であり、独自の機能を達成するために、各プロトコルの機能を使用GRO * / 
    )sk_buff **(* gro_receiveをストラクト(sk_buffをストラクト**ヘッド、sk_buff *構造体SKB );
    INT(* gro_complete)(*)sk_buff SKBをストラクト、
  
    使用中のプライベートデータプロトコル/ *ポインタは、一般的に*使用されていません/
    * af_packet_priv無効;                                        
    / *この構造体は、それぞれのハッシュリンクリストに接続されている* /
    LIST_HEADリストをストラクト;
};
のLinuxカーネルは、ハッシュテーブルの定義  ptype_baseを、キーハッシュは、構造体packet_typeにタイプフィールドです。構造体packet_typeのリンクリストにテーブルポイントの各要素。

また、構造体packet_typeのリストを定義ptype_allをこのリスト上のプロトコルハンドラは、そのパケットのプロトコル、ツールを受け、主にネットワークスニファを受信したネットワークパケットに使用します。そして、このようなtcpdumpのキャプチャ生のソケットなどの手続きは、このタイプの構造を使用しpacket_type。

/ IP * 0800
 * 8100 802.1Q VLAN
 * 0001 802.3
 * AX.25 0002
 * 0004 802.2
 * 8035 RARP
 * SNAP 0005
 * 0805 X.25
 * ARP 0806
 * 8137 IPX
 LocalTalkの0009 *
 *のIPv6 86DDは、
 * /
の#define PTYPE_HASH_SIZE(16)
の#define PTYPE_HASH_MASK(PTYPE_HASH_SIZE - 1)
静的DEFINE_SPINLOCK(ptype_lock);
静的構造体LIST_HEADの  ptype_base [PTYPE_HASH_SIZE] __read_mostly;
LIST_HEADの静的構造体  ptype_all  __read_mostly; / *タップ* /
この張ハッシュテーブルは、パケットの種類に応じて対応するプロトコルスタックハンドラを見つけます。

Linuxカーネル以下のプロトコル機能を追加使用して削除
プロトコルを追加します:
無効  dev_add_pack(構造体packet_type * Pt)が
{
    ハッシュをint型;                                                                      
    プロテクトスピンロックに使用/ *リストptype_all ptype_lock。
     ptype_bashハッシュテーブルを保護するスピンロックを書き込む際に、
    本質的には、保護するためのRCUロックを読んだとき、ときプリエンプション* /禁止読み
    ; spin_lock_bh(&ptype_lock)
    IF(PT->タイプ== htonsの(ETH_P_ALL))
        list_add_rcu(&PT->リスト、&​​ptype_all);
    他{
        ハッシュ= ntohs(PT->型)&PTYPE_HASH_MASK;
        list_add_rcu(PT-&>リスト、&​​ptype_base [ハッシュ]);
    }
    spin_unlock_bh(&ptype_lock);
}

プロトコルタイプがETH_P_ALLとして定義されている場合、それはすべてのメッセージタイプを受信することです。これはptyte_all packet_typeリストを置くために追加されました。

各プロトコルは独自のデータの初期化、独自のpaket_type変数を定義します。プロトコルモジュール初期化のときに呼び出すため
dev_add_packetは自分packet_typeがpacket_baseハッシュテーブルに加えます。
 

実際のエントリ関数プロトコルスタック  netif_receive_skb()
のint  netif_receive_skb(* SKB sk_buffの構造体)
{
    構造体packet_typeは* PTYPE、* pt_prev;
    ストラクトnet_device * orig_dev;
    ストラクトnet_deviceは* null_or_origは、
    INT RET =はNET_RX_DROP;
    __be16型;
                                                  
    / *には、SKBの割り当てがかかりタイムスタンプ* /
    IF(skb-> tstamp.tv64!)
        net_timestamp(SKB);
                                                  
    バンドVLANタグは、パケットネットワークカードは、一般的に解決ハードウェアサポートのVLANにここに来れば/ *、ドライブが出て解析する必要があります
      に割り当てられたVLANタグをSBKは、ネイティブVLANポート層パケットにするかどうか、判断する
      ことである場合、* DEV VLAN /受信DEVを割り当てる
    IF(skb-> vlan_tci vlan_hwaccel_do_receive &&(SKB))
        戻りNET_RX_SUCCESS;
                                                  
    / *にnetpoll用ネットワーク機器やI / Oの初期化ではない良いとき
      、パケットを処理する仮想ネットワークカードの受信を実現するために行うことができます
      * /主にリモートデバッグおよびネットワーク制御端子用
    WEはNAPIを通じて得ているかどうかを確認にnetpoll * /、ここでは/ *壁紙を
    IF(netpoll_receive_skb(SKB))
        のリターンは、 NET_RX_DROP;
    IF(skb-> IIF!)
        skb-> IIF = skb-> DEV->のifIndex;
                                                  
    / *リンク集約処理、受信した集約DEVを追加した場合、ポートを受信重合DEVを交換する* /
    null_or_orig = NULL ;
    orig_dev = skb-> DEV;
    IF(orig_dev->マスタ)
    {
        IF(skb_bond_should_drop(SKB))
            null_or_orig = orig_dev; / *配信のみ完全一致* /
        他
            skb-> DEV = orig_dev->マスター;
    }
                                                 
     / *増加パケットカウント統計* /受信
    __get_cpu_var(netdev_rx_stat).total ++を。
                                                  
    / *初期以下SKBポインタ* /
    skb_reset_network_header(SKB);
    skb_reset_transport_header(SKB);
    skb-> mac_len = skb-> network_header - skb-> mac_header;
    pt_prev = NULL;
                                                 
    / *検索とptype_base ptype_base、RCUを使用しますロックを読む* /クリティカルセクションを保護
    )(rcu_read_lock;
  
                                                   
    カーネルプロトコルスニファが登録されている場合/ *、それがSKBコピー処理に渡されます。
      
      サイクルのこのラウンドの後、機能スニファ最後の実行プロトコルは、呼び出されていない
      最後のパケットハンドラが呼び出されているので、次の呼び出しにプロセスは、
      SKBのユーザー参照カウントを必要とプラス1しませんしたがって、次のハンドラはなり
      ますが、SKBを処分したい機能は、あなたが最初に実行する必要がある場合は、渡されたp型続く
      、独自のハンドラを実装した後、p型ハンドラを* /
    list_for_each_entry_rcu(p型、&ptype_all、リスト)
    {
        IF(ptype-> DEV == null_or_orig ||
            ptype-> DEV skb- ==> DEV ||
            ptype-> == orig_dev DEV)
        {
            IF(pt_prev)
                RET = deliver_skb(SKB、pt_prev、orig_dev);
            pt_prev = PTYPE;
        }
    }
                                                 
    / *処理し、レイヤ2ブリッジにもしリターンSKB == NULL、説明SKB直接層
      、ネットワーク層を送信せずに行って転送され、関数が直接返す* /
    SKB = handle_bridge(SKB、&pt_prev、&RET、orig_dev);
    IF(SKB!)
        GOTO OUT。
                                                  
    / *プロセスのVLAN * /
    SKB = handle_macvlan(SKB、&pt_prev、&RET、orig_dev);
    IF(SKB!)
        GOTO OUT;
                                                 
    / *上記の工程の後に、メッセージがptype_baseハッシュテーブルに、消化されていない
      メッセージを見つけるために、プロトコル機能を受信し、プロトコル処理に対応する* /
    タイプ= skb->プロトコル
    list_for_each_entry_rcu(p型、
            &ptype_base [ntohs(タイプ)&PTYPE_HASH_MASK]、リスト)
    {
        IF(ptype->タイプ==タイプ&&
            (ptype-> DEV == null_or_orig
            || ptype-> DEV == skb-> DEV
            || p型- > DEV == orig_dev))
        {
            IF(pt_prev)
                RET = deliver_skb(SKB、pt_prev、orig_dev)。
            pt_prev = p型;
        }
    }
    もし(pt_prev)
    {
        RET = pt_prev-> FUNC(SKB、skb-> DEV、pt_prev、orig_dev)。
    }
    他
    {
        kfree_skb(SKB)。
       / *ジャマル、
        どのようにあなたは私を使用するには、*つもりだったこの:-)。
        * /
         RET = NET_RX_DROP;
    }
OUT:
    rcu_read_unlock();
    戻りRET;
}
EXPORT_SYMBOL(netif_receive_skb);

静的インラインint型  deliver_skb(SKBストラクトsk_buff *、
                  構造体* pt_prevのpacket_type、
                  orig_dev net_device *構造体)
{
  対応プロトコル機能に受信された1つによってSKB参照カウントへ/ *は、
    リリースが使用されている防ぎます。プロトコルの複数のパケットをすることができるので、
    受信機の機能で処理されます。最後のではなく、必要なプロトコル機能は、SKB受け取る
    最後に受信したプロトコル機能が解放する責任があるので、1で参照カウントを
    パケットがメモリを占有* /
    atomic_inc(&skb-を>ユーザー);
    リターンpt_prev-> FUNC(SKB、SKB - > DEV、pt_prev、orig_dev);
}

发布了64 篇原创文章 · 获赞 171 · 访问量 22万+

おすすめ

転載: blog.csdn.net/alpha_love/article/details/104580349