hana_shinのLinux技術ブログ

Linuxの技術情報を掲載しています。特にネットワークをメインに掲載していきます。

TSO(TCP Segmentation Offload)について



1 TSO(TCP Segmentation Offload)とは?

TSOは、NICが送信パケットをMTU長に分割する仕組みです。以前は、カーネルで分割していましたが、最近はNICでパケットを分割することができます。NICでパケットを分割することで、カーネルの負荷を低減することができます。ここでは、仮想NICのTSOが有効/無効の時、TCPパケットがどのように分割されるかを確認してみます。

2 検証環境

2.1 ネットワーク構成

サーバとクライアントの2台構成です。図中のens33はNICの名前です。

                          192.168.2.0/24
client(ens33) -------------------------------------(ens33) server
        .105                                       .100

2.2 版数

サーバ、クライアントともに下記版数です。

[root@server ~]# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)

カーネル版数は以下のとおりです。

[root@server ~]# uname  -r
3.10.0-1160.el7.x86_64

3 事前準備

サーバのTCP/11111番ポートを使用するので、TCP/11111番ポートへのアクセスを許可します。firewall-cmdコマンドの使い方は、firewall-cmdの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# firewall-cmd --add-port=11111/tcp
success

ポートの状態を確認します。11111番ポートが開放されたことがわかります。

[root@server ~]# firewall-cmd --list-ports
11111/tcp

4 TSO有効時

仮想NICのTSOを確認します。TSOが有効(★)であることがわかります。なお、指定するオプション-kは小文字です。

[root@client ~]# ethtool -k ens33 | grep tcp
tcp-segmentation-offload: on ★
        tx-tcp-segmentation: on
        tx-tcp-ecn-segmentation: off [fixed]
        tx-tcp6-segmentation: off [fixed]
        tx-tcp-mangleid-segmentation: off

クライアントからサーバに送信する送信データを作成します。データサイズは2000(byte)とします。送信データの作成方法は、ファイルの作り方 - hana_shinのLinux技術ブログを参照してください。

[root@client ~]# fallocate -l 2000 test.dat
[root@client ~]# ls -l test.dat
-rw-r--r--. 1 root root 2000  1月 14 21:25 test.dat

クライアントでtcpdumpを実行します。tcpdumpの使い方は、tcpdumpの使い方(基本編) - hana_shinのLinux技術ブログを参照してください。

[root@client ~]# tcpdump -i ens33 port 11111
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes

サーバでncコマンドを実行します。ncプロセスがTCPの11111番ポートでListenするようにします。ncコマンドは、ncコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# nc -klv 11111 > test.dat
Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Listening on :::11111
Ncat: Listening on 0.0.0.0:11111

ncプロセスがListenしているポート番号を確認します。TCPの11111番ポートでListenしていることがわかります。lsofコマンドは、lsofコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# lsof -c nc -a -i4 -a -P
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nc      3964 root    4u  IPv4  55566      0t0  TCP *:11111 (LISTEN)

クライアントからサーバにデータを送信します。

[root@client ~]# nc server 11111 < test.dat

tcpdumpの実行結果を確認します。tcpdumpは、カーネルNICの間でパケットをキャプチャします。TSOが有効なので、TCP層ではなくNICでパケットの分割をするので、TCPパケットのペイロードサイズが2000(byte)と表示されています。

[root@client ~]# tcpdump -i ens33 port 11111 -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
21:32:08.742695 IP 192.168.2.105.57310 > 192.168.2.100.11111: Flags [S], seq 3702008934, win 29200, options [mss 1460,sackOK,TS val 474675 ecr 0,nop,wscale 7], length 0
21:32:08.743483 IP 192.168.2.100.11111 > 192.168.2.105.57310: Flags [S.], seq 2914285991, ack 3702008935, win 28960, options [mss 1460,sackOK,TS val 9891545 ecr 474675,nop,wscale 7], length 0
21:32:08.743560 IP 192.168.2.105.57310 > 192.168.2.100.11111: Flags [.], ack 1, win 229, options [nop,nop,TS val 474676 ecr 9891545], length 0
21:32:08.743731 IP 192.168.2.105.57310 > 192.168.2.100.11111: Flags [P.], seq 1:2001, ack 1, win 229, options [nop,nop,TS val 474676 ecr 9891545], length 2000 ★
21:32:08.743861 IP 192.168.2.105.57310 > 192.168.2.100.11111: Flags [F.], seq 2001, ack 1, win 229, options [nop,nop,TS val 474676 ecr 9891545], length 0
21:32:08.744240 IP 192.168.2.100.11111 > 192.168.2.105.57310: Flags [.], ack 2001, win 258, options [nop,nop,TS val 9891546 ecr 474676], length 0
21:32:08.744287 IP 192.168.2.100.11111 > 192.168.2.105.57310: Flags [F.], seq 1, ack 2002, win 258, options [nop,nop,TS val 9891546 ecr 474676], length 0
21:32:08.744394 IP 192.168.2.105.57310 > 192.168.2.100.11111: Flags [.], ack 2, win 229, options [nop,nop,TS val 474677 ecr 9891546], length 0

5 TSO無効時

ens33のTSOを無効にします。つまり、カーネルで送信パケットを分割することになります。なお、指定するオプション-Kは大文字です。

[root@client ~]# ethtool -K ens33 tso off

TSOの設定有無を確認します。TSOが無効(★)になったことがわかります。なお、指定するオプション-kは小文字です。

[root@client ~]# ethtool -k ens33|grep tcp
tcp-segmentation-offload: off ★
        tx-tcp-segmentation: off
        tx-tcp-ecn-segmentation: off [fixed]
        tx-tcp6-segmentation: off [fixed]
        tx-tcp-mangleid-segmentation: off

クライアントでtcpdumpを実行します。

[root@client ~]# tcpdump -i ens33 port 11111 -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes

サーバでncコマンドを実行します。ncプロセスがTCPの11111番ポートでListenするようにします。

[root@server ~]# nc -klv 11111 > test.dat
Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Listening on :::11111
Ncat: Listening on 0.0.0.0:11111

クライアントからサーバにデータを送信します。

[root@client ~]# nc server 11111 < test.dat

tcpdumpの実行結果を確認します。tcpdumpは、カーネルNICの間でパケットをキャプチャします。TSOが無効なので、TCP層でパケットを分割をするので、TCPパケットのペイロードサイズが1448(byte)と552(byte)と表示されています。

[root@client ~]# tcpdump -i ens33 port 11111 -nn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
21:36:20.217351 IP 192.168.2.105.57312 > 192.168.2.100.11111: Flags [S], seq 3888141037, win 29200, options [mss 1460,sackOK,TS val 726136 ecr 0,nop,wscale 7], length 0
21:36:20.217964 IP 192.168.2.100.11111 > 192.168.2.105.57312: Flags [S.], seq 4173351009, ack 3888141038, win 28960, options [mss 1460,sackOK,TS val 10143024 ecr 726136,nop,wscale 7], length 0
21:36:20.218192 IP 192.168.2.105.57312 > 192.168.2.100.11111: Flags [.], ack 1, win 229, options [nop,nop,TS val 726150 ecr 10143024], length 0
21:36:20.218659 IP 192.168.2.105.57312 > 192.168.2.100.11111: Flags [.], seq 1:1449, ack 1, win 229, options [nop,nop,TS val 726151 ecr 10143024], length 1448 ★
21:36:20.218666 IP 192.168.2.105.57312 > 192.168.2.100.11111: Flags [P.], seq 1449:2001, ack 1, win 229, options [nop,nop,TS val 726151 ecr 10143024], length 552 ★
21:36:20.218673 IP 192.168.2.105.57312 > 192.168.2.100.11111: Flags [F.], seq 2001, ack 1, win 229, options [nop,nop,TS val 726151 ecr 10143024], length 0
21:36:20.219287 IP 192.168.2.100.11111 > 192.168.2.105.57312: Flags [.], ack 2001, win 258, options [nop,nop,TS val 10143025 ecr 726151], length 0
21:36:20.219782 IP 192.168.2.100.11111 > 192.168.2.105.57312: Flags [F.], seq 1, ack 2002, win 258, options [nop,nop,TS val 10143025 ecr 726151], length 0
21:36:20.219874 IP 192.168.2.105.57312 > 192.168.2.100.11111: Flags [.], ack 2, win 229, options [nop,nop,TS val 726152 ecr 10143025], length 0

6 まとめ

・TSOが有効の場合:NICでパケット分割
・TSOが無効の場合:カーネルでパケット分割

カーネルが分割するパケットサイズは、MTUからTCP/IPヘッダのサイズを引いた値になります。MTUはイーサネット環境では1500(byte)です。TCP/IPヘッダサイズは、各々20(byte)です。したがって、分割するパケットサイズは1460(byte)になります。しかし、最近はTCPヘッダに、TCPタイムスタンプオプション(12byte)が付くのが一般的です。したがって、分割するパケットサイズは、1448(byte)単位になります。なお、オプションヘッダを付けるかどうかは、3 way handshakeの時に決定します。TCPの両端がTCPオプションを付けることに合意すれば、TCPオプションが付加されます。

Z 参考情報

私が業務や記事執筆で参考にした書籍を以下のページに記載します。
Linux技術のスキルアップをしよう! - hana_shinのLinux技術ブログ