【図解】LinuxのPacket送信の仕組みを徹底解説|TX Ring・DMA・qdisc・Device Driver・NICまで

はじめに

Applicationがsend()やwrite()を実行しても、DataがそのままNICへ渡されるわけではありません。DataはSocket Send Bufferで管理され、TCP、IP / Routing、qdisc、Device Driver、TX Ringを通り、最後にNICがDMAでMemory上のDataを読み取ってNetworkへ送信します。

本記事では、Packet受信と対になる送信経路を扱い、ApplicationからNICまでDataがどのように流れるのかを処理順に解説します。あわせて、Linux上で送信処理を確認するための代表的なCommandも紹介します。

Packet送信の全体像

LinuxにおけるPacket送信の基本的な流れは以下の通りです。

実際のLinux Network Stackでは、Netfilter、Network Namespace、Bridge、VLAN、Tunnel、Traffic Control、各種Offloadなどが加わる場合があります。本記事では、Local Applicationが物理Network InterfaceからTCP Dataを送信する基本経路に絞って説明します。

① ApplicationがDataを送る

ApplicationはSocketのFile Descriptorを指定し、send()またはwrite()を実行してDataをKernelへ渡します。

System Callが成功して返したByte数は、Local KernelがそのDataを受け入れた量を表します。相手Hostへの到達や、Remote Applicationによる処理完了を意味するわけではありません。

Blocking Socketで送信Bufferに十分な空きがなければ、Processは空きができるまで待つことがあります。Non-blocking SocketではEAGAINなどが返ることがあり、Application側で再試行します。また、write()やsend()では要求した全Byteが必ず一度に受理されるとは限らないため、戻り値を確認することが重要です。

② TCPで送信Dataを処理する

Applicationから受け取ったDataはSocket Send Bufferで管理されます。TCPでは、まだ送信していないDataだけでなく、送信済みでもACKを受信していないDataなども送信状態として管理します。

TCPはSocket Send BufferのDataをByte StreamとしてSequence Numberで管理し、Source / Destination Port、ACK、Flags、WindowなどのTCP Header情報を設定しながら送信処理を進めます。Receiver WindowとCongestion Windowなどの制約を考慮し、必要に応じて未確認Dataを再送します。

送信単位にはMSSが関係します。ただしGSOやTSOが有効な環境では、KernelやDriverが比較的大きなBufferのまま後段へ渡し、DriverやNIC側でWire上のSegment Sizeへ分割することがあります。このため、Host上のtcpdumpではWire上より大きなTCP Packetに見える場合があります。

なお、Socket Send Bufferを大きくすれば常に高速になるわけではありません。送信性能はApplicationの書き込み方、Receiver Window、Congestion Window、RTT、Packet Lossなどにも左右されます。

③ IP / Routingで送信経路を決める

TCPの処理後、IP LayerではSource / Destination IP Address、Protocol、TTLまたはHop Limitなどの情報を持つIP Headerが関連付けられます。

続いてLinuxはDestination IP Addressに対してRoute Lookupを行い、Output Interface、Source Address、Next Hopなどを決定します。同一Subnet上の宛先ならDestination Host自身がNext Hopとなり、別Subnetなら通常はGatewayがNext Hopになります。

Ethernetで送信するにはNext HopのMAC Addressも必要です。Neighbor Tableに有効なEntryがなければ、IPv4ではARP、IPv6ではNeighbor DiscoveryによってMAC Addressを解決します。

FirewallやNATが構成されている場合は、Netfilter HookなどでPacketが検査・変更されることがあります。PolicyによってDropされたPacketはNICまで到達しません。

④ qdiscで送信PacketをQueueingする

Routing後のPacketは、Network Interfaceの送信処理へ進みます。この経路で重要になるのがqdisc(Queueing Discipline)です。

qdiscは単純にPacketを並べるだけではなく、どのPacketから送るかを決めるScheduling、帯域を調整するShaping、公平性やLatencyの制御、条件に応じたDrop / Markingなどを行えます。

qdiscへPacketが滞留するとQueueing Delayが増え、Queueが上限に達するとDropが発生する場合があります。一方、一部のVirtual Deviceや構成ではnoqueueが表示され、通常のqdisc Queueを経由しない場合もあります。

⑤ Device DriverがTX Descriptorを準備する

qdiscから取り出されたPacketはNetwork Deviceの送信処理を経てDevice Driverへ渡されます。DriverはNICが送信Dataを扱えるように、Memory上のBuffer情報をもとにTX Descriptorを準備します。

Checksum OffloadやTSOなどが使われる場合、DriverはNICへ必要な処理内容もDescriptorなどを通じて伝えます。

⑥ TX RingへDescriptorを登録する

準備したTX DescriptorはTX Ringへ登録されます。TX RingはPacket Dataそのものを並べる場所というより、NICが処理する送信Dataの場所やLengthなどを示すDescriptor Ringです。

TX Ringに空きがなくなると、Driverは送信Queueを一時停止することがあります。NICによる送信処理が進み、CompletionによってDescriptorを再利用できるようになると送信を再開します。

⑦ NICがDMAでDataを読み取って送信する

Driverは送信BufferをDMAで扱える形にし、その情報をDescriptorとしてNICへ示します。NICはTX DescriptorをもとにMain Memory上のDataをDMA Readで取得します。

受信時はNICがPacket DataをMain MemoryへDMA Writeするのに対し、送信時はNICがMain Memory上の送信DataをDMA Readする、という対称関係で考えると分かりやすくなります。

NICはDMA Readで取得した送信Dataを、Ethernet FrameとしてNetworkへ送出します。Checksum Offload、TSO、VLAN Offloadなどが有効な場合は、NICがChecksum計算やSegment分割、VLAN Tag処理などの一部を担当します。

NICからFrameが送信されたことは、Remote ApplicationがDataを処理したことを意味しません。TCPではRemote TCPからACKを受けることでByteの到達を確認し、Application Levelの処理完了はHTTP ResponseなどApplication Protocol側の応答で確認します。

⑧ 送信完了後にDescriptorを回収する

NICがTX Descriptorに対応する送信処理を完了すると、DriverはTX Completionを確認し、送信に使用したDescriptorやKernel内のPacket Bufferを再利用できる状態にします。必要に応じてDMA Mappingも解除します。

ここで重要なのは、TX CompletionとTCP ACKは別の処理だという点です。TX CompletionはNIC側の送信処理が完了したことを示しますが、Remote HostへのTCP Data到達を示すものではありません。

LinuxでPacket送信を確認する

Packet送信で問題が発生した場合も、Socket → Routing → Neighbor → qdisc → Device Driver → TX Ring → NICという送信経路に沿って確認すると、どこで処理が滞っているのかを切り分けやすくなります。

確認箇所コマンド主に確認できること
Socket / TCPss -ntmiSend-Q、Socket Memory、RTT、cwndなどTCP内部情報
Routingip route get 198.51.100.20Output Interface、Source Address、Next Hopなど
Neighborip neigh showNext HopのIP AddressとMAC Address、Neighbor State
qdisctc -s qdisc show dev enp1s0qdiscの種類、送信量、Drop、Overlimit、Backlogなど
NIC / Interfaceip -s link show dev enp1s0TX Packet数、Bytes、Error、Dropなど
TX Ringethtool -g enp1s0RX / TX Ringの現在値・最大値
Offloadethtool -k enp1s0TSO、GSO、Checksum Offloadなどの設定
NIC / Driver統計ethtool -S enp1s0NIC / Driver固有のTX Counter、Drop、Errorなど
NIC設定ethtool enp1s0Link Up / Down、Speed、Duplex、Auto-negotiationなど
Network Trafficsar -n DEV 1InterfaceごとのPacket数、Bytes、Traffic量
Network Errorsar -n EDEV 1InterfaceごとのError、Drop、FIFO Errorなど
Packettcpdump -i enp1s0 -nnKernelのCapture Pointを通過する送受信Packet

例えばSend-Qが継続的に増えている場合は、Applicationが書き込む速度に対してTCPの送信が追いついていない可能性があります。RTT、cwnd、Receiver Window、Packet Lossなどとあわせて確認します。

qdiscのBacklogやDropが増えている場合は、Network Interfaceへ送り出す前のQueueでPacketが滞留している可能性があります。

さらにNIC / Driver固有のTX DropやError Counterが増えている場合は、Device Driver、TX Ring、NIC付近まで確認範囲を広げます。Counter名はNICやDriverによって異なるため、ethtool -Sの複数のCounterをTraffic量とあわせて確認することが重要です。

また、tcpdumpでPacketが確認できても、実際にNICからWireへ正常に送信されたことを直接保証するものではありません。tcpdumpのCapture PointとNIC Hardware Counterは観測している位置が異なるため、必要に応じてRemote Host側のCaptureやNetwork機器のCounterなどと比較します。

まとめ

LinuxのPacket送信では、Applicationがsend()やwrite()を実行すると、DataはSocket Send Bufferへ渡され、TCP / IP、Routing、qdiscなどの処理を経てDevice Driverへ渡されます。

Device Driverは送信Dataを参照するTX Descriptorを準備してTX Ringへ登録し、NICはTX DescriptorをもとにMain Memory上のDataをDMA Readで取得してNetworkへ送信します。

送信後はDriverがTX Completionを確認し、使用済みのDescriptorやPacket Bufferを回収して再利用できる状態にします。

ここで重要なのは、send()の成功、TX Completion、TCP ACKの受信、Remote Applicationの処理完了は、それぞれ別の段階だという点です。send()が成功しただけでは、相手HostへのData到達を意味しません。

Packet送信で問題が発生した場合は、Socket、Routing、Neighbor、qdisc、Device Driver、TX Ring、NICと送信経路に沿って確認すると、Packetがどこで滞留・Dropしているのかを切り分けやすくなります。

ss、ip route、ip neigh、tc、ethtool、sar、tcpdumpなどを組み合わせ、各Layerの状態を確認することが重要です。

Linuxにおけるネットワークの仕組みについては、以下の書籍にまとめています。興味がありましたら、ぜひ読んでみてください。

Linux入門・図解

Linuxのネットワークの仕組みを
図解で理解したい方へ

Socket・TCP・ルーティング・NIC 現場で役立つ通信処理の基礎知識など Linuxにおけるネットワークの仕組みを図解を中心にわかりやすく解説しています。

Kindleで書籍を見る →

コメント