【図解】Linux Networkの全体像|Socket・TCP/IP・NICとPacket送受信の流れ

はじめに

Linux上のApplicationがNetworkへDataを送るとき、DataはApplicationから直接NICへ渡されるわけではありません。ApplicationはSocketを通じてKernelへ送信を依頼し、KernelのTCP/IP StackがHeaderの付与、経路選択、送信単位の調整などを行い、Device DriverとNICを通じてNetworkへ送り出します。

受信時は反対です。NICがEthernet Frameを受け取り、Device DriverとKernelのNetwork Stackが内容を処理し、最終的に対応するSocketへDataを届けます。Applicationはread()やrecv()などを使って、そのDataを読み取ります。

Network障害を調査するときは、Application、Socket、TCP、IP、Network Interface、NICというLayerを分けて考えることが重要です。「通信できない」という同じ現象でも、Listenしていない、TCP Connectionを確立できない、Routingがない、Neighborを解決できない、InterfaceがDownしているなど、原因はLayerごとに異なります。

本記事ではLinux Networkについて、ApplicationからSocket、TCP/IP Stack、Device Driver、NICまでの全体像、送信と受信の基本フロー、Packetを観察するための確認コマンドを順番に解説します。

Linux Networkの全体像

Linux Networkは、User Spaceで動くApplicationと、Kernel Spaceで動くNetwork Stack、HardwareであるNICによって構成されます。送信処理を単純化すると、次のように表せます。

ApplicationはHTTP、SSH、DNS、Database Protocolなど、用途に応じたDataを作ります。Socket APIは、そのDataをKernelへ渡す入口です。TCPやUDPはTransport Layer、IPはNetwork LayerとしてDataを処理します。

IP Layerで送信先までの経路が選択されると、Packetは送信先に対応するNetwork Interfaceへ渡されます。Ethernetを利用する場合は、Neighbor Tableを使ってNext HopのMAC Addressを決定し、Ethernet Headerを付けてNICから送信します。

Layer主な役割Linuxで確認する例
Application通信内容とApplication Protocolを処理するProcess、Log、curl
SocketApplicationとKernelの通信Endpointになるss、lsof
TCP / UDPPort、Connection、再送、Flow Controlなどを処理するss、nstat、tcpdump
IPIP AddressとRoutingに基づいてPacketを届けるip address、ip route
Network InterfacePacketを送受信する論理的なInterfaceになるip link、ip -s link
Driver / NICKernelとNetwork Hardwareの間でPacketを転送するethtool、ethtool -S

User SpaceとKernel Space

LinuxではApplicationは通常User Spaceで動作し、TCP/IP StackやDevice DriverはKernel Spaceで動作します。User SpaceのProcessは、Kernel MemoryやNICを直接操作できません。System Callを通じてKernelへ処理を依頼します。

Network通信ではsocket()、bind()、listen()、accept()、connect()、send()、recv()、read()、write()などが主なSystem Callです。Applicationから見ると、SocketはFile Descriptorとして扱われます。そのため、Fileに対して利用するread()やwrite()をSocketにも利用できます。

System CallによってCPUの実行ModeはUser ModeからKernel Modeへ移り、Kernelが引数とSocketの状態を確認して処理します。ただし、send()を呼び出した時点で必ずDataが物理Networkへ出たとは限りません。多くの場合、DataはまずKernel内のSocket BufferへCopyされ、その後の処理はKernelによって非同期に進められます。

ApplicationとApplication Protocol

Applicationは、相手と交換するDataの意味や形式を決めます。Web通信ではHTTP、Remote LoginではSSH、名前解決ではDNSなどが利用されます。これらはApplication Protocolであり、TCPやUDPより上位に位置します。

たとえばcurlでWeb Serverへ接続すると、curlはURLを解釈し、必要に応じてDNSでServerのIP Addressを調べ、TCP Connectionを確立します。HTTPSではさらにTLS Handshakeを行い、その上でHTTP Requestを送信します。

各Layerは上位Layerから受け取ったDataへHeaderなどのControl情報を付けます。この処理をEncapsulationと呼びます。受信側では外側のHeaderから順に確認して取り除き、最終的にApplication DataをSocketへ届けます。この逆方向の処理はDecapsulationと呼ばれます。

Socketとは

Socketは、ApplicationがLinux KernelのNetwork機能を利用するための通信のEndpointです。ApplicationはSocketを通じて、send()やrecv()などでDataを送受信します。

LinuxではSocketはFile Descriptorとして扱われるため、read()やwrite()を使用することもできます。

Socketには、通信に応じて以下の情報が関連付けられます。

要素意味
Protocol FamilyAddress体系AF_INET、AF_INET6
Socket Type通信方式SOCK_STREAM、SOCK_DGRAM
Local Address自Host側のIP Address192.0.2.10
Local Port自Host側のPort443
Remote Address通信相手のIP Address198.51.100.20
Remote Port通信相手のPort53000

TCP Connectionは、Source IP Address、Source Port、Destination IP Address、Destination Port、Protocolの組み合わせで識別できます。そのため、同じServer Port 443でも複数のClientとのConnectionを区別できます。

Server Applicationでは、socket()でSocketを作成し、bind()でLocal AddressとPortを割り当て、listen()で接続を待ちます。accept()によってClientとの通信用のConnected Socketを取得し、Listening Socketは引き続き新しい接続を待ちます。

TCPとUDPの役割

TCPとUDPはTransport LayerのProtocolです。どちらもPort Numberなどの情報を使って、受信したDataを対応するSocketへ届けます。一方、通信の性質は大きく異なります。

項目TCPUDP
通信方式Connection-orientedConnectionless
Dataの見え方連続したByte StreamDatagram単位
到達確認ACKを利用するProtocol自体では行わない
再送失われたDataを再送するProtocol自体では行わない
順序制御Sequence Numberで順序を管理するProtocol自体では保証しない
主な利用例HTTP、SSH、DatabaseDNS、NTP、音声・映像通信

TCPは送信DataをByte列として扱います。Applicationが一度のsend()で渡した単位と、受信側が一度のrecv()で取得する単位が一致する保証はありません。TCPは必要に応じてDataを複数のSegmentへ分け、ACK、再送、Flow Control、Congestion Controlを使って通信を制御します。

UDPはDatagram単位を保ち、TCPより単純なHeaderを持ちます。ただし、Packet Loss、重複、順序の入れ替わりが起きた場合にどう扱うかは、Application Protocol側で考慮する必要があります。

IP AddressとRoutingの役割

IP Layerは、送信元と送信先のIP Addressに基づいてPacketをNetwork上で届けます。LinuxはRouting Tableを参照し、送信先に対してどのRouteを利用するか、どのNetwork Interfaceから送るか、必要であればどのGatewayへ渡すかを決定します。

同じSubnet内の相手へ送る場合は、一般に相手自身がNext Hopになります。別Networkの相手へ送る場合は、Default GatewayなどのRouterがNext Hopになります。LinuxはIPv4ではARP、IPv6ではNeighbor Discoveryを利用し、Next HopのIP Addressに対応するLink Layer Addressを解決します。

一台のLinux Hostに複数のInterfaceや複数のRouteがある場合、経路選択は単純なDefault Routeだけでは決まりません。Prefix Length、Metric、Policy Routing Ruleなども関係します。

Network Interfaceとは

LinuxのNetwork Interfaceは、KernelがPacketを送受信するための論理的なNetwork Deviceです。物理NICに対応するenp1s0などのInterfaceだけでなく、Loopback、Bridge、Bond、VLAN、Tunnel、vethなどもNetwork Interfaceとして扱われます。

Interfaceの例役割
lo同一Host内のLoopback通信
enp1s0、eth0物理NICに対応するEthernet Interface
bond0複数Interfaceを束ねるBonding Interface
br0Layer 2 Bridge
vlan100802.1Q VLAN Interface
vethContainerやNetwork Namespace間を接続する仮想Interface

InterfaceにはUP / DOWNの管理状態、MTU、MAC Address、IP Address、送受信Packet数、Error数などがあります。ただし、InterfaceがUPであることと、物理Linkが正常であることは同じではありません。管理状態、Carrier、Driver、Cable、Switch Portなどを分けて確認します。

Device DriverとNICの役割

Device Driverは、Linux KernelのNetwork SubsystemとNIC Hardwareの間を接続します。Kernel内のPacket表現をNICが送信できる形でQueueへ登録し、受信時にはNICから受け取ったDataをKernelのNetwork Stackへ渡します。

一般的なNICはDMAを利用して、Main Memory上のBufferとNICの間でPacket Dataを転送します。送受信DescriptorはRing Bufferとして管理されることが多く、TX Ringは送信、RX Ringは受信に利用されます。

NICやDriverはChecksum Offload、TCP Segmentation Offload、Generic Receive Offload、複数Queueなどの機能を利用し、CPU負荷を減らしてThroughputを高めます。このため、tcpdumpで観察するPacket SizeやChecksum表示が、Wire上の実際のFrameと一致しない場合があります。

Packet送信の全体フロー

TCPを利用するApplicationがDataを送るときの基本的な流れを確認します。実際のKernel内部では多くの処理と最適化が行われますが、全体像は次のように整理できます。

① ApplicationがDataを送信する

Applicationはsend()やwrite()を呼び出し、SocketのFile Descriptorと送信DataをKernelへ渡します。DataはまずKernel内のSocket Send BufferへCopyされ、その後の送信処理はKernelによって進められます。

Blocking Socketでは、Socket Send Bufferに十分な空きがない場合、空きができるまでProcessが待機することがあります。

② TCPがSegmentを作る

TCP Layerでは、Connectionの状態を確認しながら送信Dataを処理します。TCP HeaderにはSequence Number、ACK Number、Flag、Windowなど、TCPによる信頼性のある通信に必要な情報が設定されます。

送信するDataが大きい場合は、MSSやTSOなども関係し、実際にどの段階でDataを分割するかはNICのOffload機能などによって異なります。

③ IPが経路を選択する

IP Layerでは、送信元と送信先のIP Addressなどを含むIP Headerを付けます。

LinuxはRouting Tableを参照し、Destination IP Addressに対するRouteを検索します。その結果から、Packetを送信するOutput InterfaceやNext Hop / Gatewayなどを決定します。

また、FirewallやNATなどが構成されている場合は、NetfilterのHookを通過する過程でPacketが検査・変更されることがあります。

④ qdiscとDriverを通る

PacketはNetwork Interfaceの送信処理へ進み、qdiscを経由してDevice Driverへ渡されます。

qdiscはLinuxのTraffic Controlに関係する仕組みで、構成に応じてPacketのQueueing、優先制御、帯域制御などを行います。

Device DriverはNICへ送信するためのDescriptorをTX Ringへ登録し、NICが送信処理を開始できるようにします。

⑤ NICがFrameを送信する

NICはTX RingのDescriptorを参照し、DMAを利用してMain Memory上のPacket Dataを読み取ります。そのDataをEthernet Frameとして物理Networkへ送信します。

送信が完了すると、Device Driverは送信完了を処理し、使用済みのDescriptorやBufferを再利用できるように回収します。

このように、Applicationがsend()を呼び出してから実際にNetworkへDataが出るまでには、Socket、TCP/IP、qdisc、Device Driver、NICという複数のLayerを通過します。

Packet受信の全体フロー

TCPを利用するPacket受信では、送信とは反対方向に処理が進みます。NICが受信したFrameはDriverからKernelのNetwork Stackへ渡され、Headerの内容に基づいて対応するSocketまで届けられます。

① NICがFrameを受信する

NICはNetworkからEthernet Frameを受信し、DMAを使ってDataをMemory上の受信Bufferへ格納します。RX RingのDescriptorには、受信したDataの位置や状態などの情報が記録されます。

② Driverが受信処理を開始する

NICはInterruptを利用してPacketの受信をKernelへ通知します。Packetが大量に届く場合、PacketごとにInterruptを発生させるとCPU負荷が高くなります。

LinuxではNAPIを利用し、InterruptとPollingを組み合わせることで、複数のPacketを効率的に処理します。

③ Ethernet / IP Layerで処理する

Device Driverから渡されたFrameはEthernet Layerで処理され、EtherTypeに基づいてIPv4やIPv6などの上位Protocolへ渡されます。IP LayerではIP HeaderやDestination IP Addressなどを確認し、Protocol Numberから上位Protocolを判断します。

TCP Packetの場合は、その後TCP Layerへ渡されます。

④ TCPで処理する

TCP LayerではTCP Headerを確認し、IP AddressやPort、Connectionの状態などから対応するSocketを検索します。

さらに、Sequence Numberの確認やDataの並べ替え、重複Dataの処理、ACKの生成、Receive Windowの管理などを行います。Applicationが読み取れる状態になったDataはSocket Receive Bufferへ格納されます。

⑤ Socket Receive Bufferへ格納する

TCPによって処理されたDataは、対応するSocketのReceive Bufferへ格納されます。

ApplicationのProcessがDataの到着を待っている場合は、Dataが読み取り可能になるとProcessがWake Upされます。

⑥ ApplicationがDataを読み取る

Wake UpされたProcessがCPUにSchedulingされ、recv()やread()の処理が再開されると、Socket Receive BufferのDataがKernel SpaceからUser Spaceへ渡されます。

これによって、Networkから受信したDataをApplicationが利用できるようになります。

Packet、Segment、Datagram、Frameの違い

Networkの説明ではPacket、Segment、Datagram、Frameという用語が使われます。会話ではPacketが広い意味で使われることもありますが、LayerごとのData単位を区別すると処理を理解しやすくなります。

名称主なLayer主なHeader情報
TCP SegmentTransport LayerPort、Sequence Number、ACK、Flag、Window
UDP DatagramTransport LayerPort、Length、Checksum
IP PacketNetwork LayerSource / Destination IP Address、Protocol、TTLなど
Ethernet FrameData Link LayerSource / Destination MAC Address、EtherType

Ethernet Frameは通常同じLayer 2 Network内で転送され、Routerを越えるたびにLink Layer Headerが作り直されます。一方、IPのSource AddressとDestination Addressは、NATなどで変更されない限りEnd-to-Endで利用されます。TTLはRouterを通過するたびに減少します。

MTU、MSS、Packet分割の基本

MTU(Maximum Transmission Unit)は、Network Interfaceで一度に送信できるIP Packetの最大Sizeを示します。一般的なEthernet Interfaceでは1500 Byteが利用されますが、環境によって異なります。VLANやTunnelなどを重ねると追加Headerが必要になり、実際に運べる上位DataのSizeへ影響します。

TCPのMSSは、一つのTCP Segmentに含めるTCP Payloadの最大Sizeです。IPv4でOptionのない20 ByteのIP Headerと20 ByteのTCP Headerを仮定し、MTUが1500 ByteならMSSは一般に1460 Byteになります。

ただし、TCP Option、IPv6、Tunnel、Offloadなどによって条件は変わります。送信Dataが大きくてもApplicationがPacket単位へ分割するとは限りません。TCP/IP StackやNIC Offloadが適切な単位へ処理します。

Linux Networkの全体フロー

送信時はApplication DataにTCPまたはUDP、IP、EthernetのControl情報が加えられ、外側へ向かってEncapsulationされます。受信時は各Layerが自分のHeaderを処理し、内側のDataを上位Layerへ渡します。

ただし、実際のLinux Kernelでは、Netfilter、Network Namespace、Bridge、VLAN、Bonding、Tunnel、Traffic Control、NIC Offloadなどが経路へ加わる場合があります。ContainerやVirtual Machineを利用する環境では、複数の仮想InterfaceとNetwork Stackを通ることもあります。

まず基本となるApplication、Socket、TCP / UDP、IP、Interface、Driver、NICの関係を理解し、その後に必要な機能を追加して考えると、複雑なNetwork構成でもDataの流れを追跡しやすくなります。

まとめ

LinuxのNetwork通信では、ApplicationがSocketを通じてKernelへDataを渡し、TCP / UDP、IP、Network Interface、Device Driver、NICを経由してNetworkへ送信されます。

受信時は反対に、NICが受信したFrameをKernelがEthernet、IP、TCP / UDPの順に処理し、対応するSocket Receive BufferへDataを届けます。

Network障害を調査するときは、この処理経路をLayerごとに分け、Application、Socket、Routing、Network Interface、Device Driver、NICなどを順番に確認することが重要です。

まず基本となるDataの流れを理解しておくことで、Netfilter、VLAN、Bonding、Bridge、Tunnel、Containerなどが加わる複雑なNetwork構成でも、問題が発生しているLayerを切り分けやすくなります。

コメント