このページの目次
ネットワークプロトコルスタックの概要
カバー範囲: sk_buff → NAPI → softirq (NET_RX/NET_TX) → プロトコルスタックの階層化 → socket API → net_device → マルチキュー カーネルバージョン: 2.6 ~ 6.x
概要
Linux のネットワークスタックは、世界中で最も広く使用されている TCP/IP 実装です。NIC の DMA によるパケット受信からユーザー空間での read() 呼び出しの返却まで、パケットは 10 層以上の処理を経由します。本稿では、この完全なパスを追跡し、中核となるデータ構造と設計上の判断に焦点を当てます。
sk_buff: パケット記述子
// include/linux/skbuff.h
;
sk_buff の中核的な設計は、head/data/tail/end の4ポインタ配置です。データは head~end で確保されたバッファ内にあり、data~tail が有効なデータ領域、head~data と tail~end がそれぞれ headroom/tailroom となります。プロトコルスタックが各層のヘッダを追加する際、data ポインタを前方にシフトさせることで既存の headroom を再利用し、コピーを不要にしています。
NAPI: 割込みとポーリングのハイブリッド
// net/core/dev.c
// 問題: 高速 NIC (10Gbps+) では毎秒数百万パケットを受信し、
// パケットごとに1回の割込みが発生すると、割込み嵐が発生する
// NAPI: 最初のパケットで割込み通知 → 割込みを無効化 → softirq 内でパケットをポーリング
// NIC ドライバ:
// 1. パケット受信 → RX 割込みを無効化 → napi_schedule(&napi)
// 2. NET_RX_SOFTIRQ 発生 → napi_poll()
// → バッチ処理でパケットを受信 (1バジェット、通常300パケット)
// → 1バッチ処理完了 → まだパケットが残っている場合 → ポーリングを継続
// → パケットが枯渇した場合 → RX 割込みを有効化 → ステップ1に戻る
パケット受信の完全なパス
flowchart TD
DMA["NIC DMA<br/>→ リングバッファ (RX ring)"]
DMA -->|"ハードウェア割込み"| IRQ["ドライバ割込みハンドラ<br/>napi_schedule()"]
IRQ -->|"NET_RX_SOFTIRQ"| POLL["napi_poll()<br/>バッチ受信、最大300パケット"]
POLL -->|"パケットごと"| NETIF["__netif_receive_skb()"]
NETIF --> XDP["XDPフック (bpf)<br/>最も早期の処理ポイント"]
NETIF --> RXH{"RXハンドラ<br/>リストのチェック"}
RXH -->|"tcpdump"| AF_PACKET["AF_PACKET<br/>パケットソケットへコピー"]
RXH -->|"bridge/bonding/VLAN"| REDIR["リダイレクトの可能性あり"]
NETIF --> DELIVER["deliver_skb()"]
DELIVER --> IP_RCV["ip_rcv()<br/>L3 (IPv4)"]
IP_RCV --> IP_LOCAL["ip_local_deliver()"]
IP_LOCAL --> NF{"netfilter<br/>PREROUTING"}
NF -->|"FORWARD"| FWD["他のインターフェースへ転送"]
NF -->|"INPUT"| IP_FINISH["ip_local_deliver_finish()"]
IP_FINISH --> TCP_RCV["tcp_v4_rcv()<br/>L4 (TCP)"]
TCP_RCV --> TCP_DO["tcp_v4_do_rcv()"]
TCP_DO --> STATE{"接続状態は?"}
STATE -->|"ESTABLISHED"| FAST["tcp_rcv_established()<br/>高速パス ⚡"]
STATE -->|"LISTEN"| SLOW["tcp_rcv_state_process()"]
SLOW --> QUEUE["データを受信キューに格納"]
FAST --> QUEUE
QUEUE --> WAKE["sk_data_ready()<br/>ブロック中の read() をウェイクアップ ✅"]
classDef hw fill:#fff3e0,stroke:#ef6c00
classDef irq fill:#ffebee,stroke:#c62828
classDef l2 fill:#e3f2fd,stroke:#1565c0
classDef l3 fill:#f3e5f5,stroke:#7b1fa2
classDef l4 fill:#e8f5e9,stroke:#2e7d32
classDef decision fill:#fff8e1,stroke:#f9a825
class DMA,IRQ hw
class POLL,NETIF,XDP,RXH,AF_PACKET,REDIR l2
class IP_RCV,IP_LOCAL,IP_FINISH,DELIVER,NF,FWD l3
class TCP_RCV,TCP_DO,FAST,SLOW,QUEUE,WAKE l4
class STATE decision
マルチキューと RSS
RSS (Receive Side Scaling):
NIC は hash(src_ip, dst_ip, src_port, dst_port) % N を使用
→ N 個の受信キュー → N 個の CPU
NAPI の napi_struct はキュー単位 (per-queue) で存在する:
→ 各キューが独立した NAPI を持つ → パケット受信の並列化
→ 同一フローのパケットは常に同一キューへ → TCP の順序保持
XPS (Transmit Packet Steering):
送信側のマルチキュー: 同様だが TX 向けに適用される
net_device: ネットワークデバイス抽象化
// include/linux/netdevice.h
;
デバッグ
# パケットの送受信統計
# ソフト割込み統計 (NET_RX/NET_TX)
|
# NAPI 統計 (キュー単位でのポーリング)
|
# パケットドロップの原因 (ドロップカウンター)
参考
- ソースコード:
net/core/dev.c,include/linux/skbuff.h,include/linux/netdevice.h - カーネルドキュメント:
Documentation/networking/ - LWN: "The Linux networking stack", "NAPI design"
キーワード: sk_buff, NAPI, NET_RX_SOFTIRQ, マルチキュー, RSS, net_device, XDP