hana_shinのLinux技術ブログ

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

ソケットオプションの使い方(SO_REUSEPORT)



1 はじめに

SO_REUSEPORTは、同じ名前(ローカルIPアドレスとローカルポート番号の組)のソケットを複数のプロセスで使用できるソケットオプションです。SO_REUSEPORTを使用する場合/しない場合について、どのような違いがあるかについてncコマンドとiperf3コマンドを使って確認してみます。

ちなみに、それぞれのコマンドがSO_REUSEPORTを使用しているかどうかは以下のとおりです。あとで説明しますが、SO_REUSEPORTを使用しているかどうかはstraceコマンドを使うとわかります。

コマンド SO_REUSEPORTの使用有無
nc 使用する
iperf3 使用しない

1.1 ncコマンドの場合(SO_REUSEPORTを使用する)

ncコマンドを実行します。なお、ncコマンドのインストール方法、使い方は、ncコマンドの使い方(ネットワーク実験の幅が広がるなぁ~) - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# nc -kl 11111

もう1つターミナル開いて、lsofコマンドを実行します。ncプロセスが、TCPの11111番ポートでListenしていることがわかります。なお、lsofコマンドのインストール方法、使い方は、lsofコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# lsof -c nc -a -i -a -nP
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nc      19818 root    3u  IPv4 115846      0t0  TCP *:11111 (LISTEN)

もう1つncコマンドを実行します。このとき、ncプロセスがどのようなシステムコールを実行しているかを確認するため、straceコマンドを実行します。実行結果を確認すると、ncコマンドはSO_REUSEPORTを使用していることがわかります。なお、straceコマンドのインストール方法、使い方は、straceコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# strace -e trace=network nc -kl 11111
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_REUSEPORT, [1], 4) = 0
bind(3, {sa_family=AF_INET, sin_port=htons(11111), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
listen(3, 1)                            = 0
accept4(3,

lsofコマンドを実行します。SO_REUSEPORTを使用しているため、2つのncプロセスが同じ名前(ローカルIPアドレスは任意、ローカルポート番号は11111)のソケットを使用していることがわかります。

[root@server ~]# lsof -c nc -a -i -a -nP
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nc      19818 root    3u  IPv4 115846      0t0  TCP *:11111 (LISTEN)
nc      19835 root    3u  IPv4 115085      0t0  TCP *:11111 (LISTEN)

1.2 iperf3コマンドの場合(SO_REUSEPORTを使用しない)

iperf3コマンドを実行します。なお、iperf3のインストール方法、使い方は、iperf3コマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# iperf3 -s
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------

lsofコマンドを実行します。iperf3プロセスは、TCPの5201番ポートでListenしていることがわかります。

[root@server ~]# lsof -c iperf3 -a -i -a -nP
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
iperf3  19790 root    3u  IPv6 121979      0t0  TCP *:5201 (LISTEN)

もう1つiperf3コマンドを実行します。このとき、iperf3がどのようなシステムコールを実行しているかを確認するため、straceコマンドを実行します。実行結果を確認すると、iperf3はSO_REUSEPORTを使用していません。その結果、bindシステムコールがEADDRINUSEでエラー復帰していることがわかります。

[root@server ~]# strace -e trace=network iperf3 -s
socket(AF_INET6, SOCK_STREAM, IPPROTO_IP) = 3
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
setsockopt(3, SOL_IPV6, IPV6_V6ONLY, [0], 4) = 0
bind(3, {sa_family=AF_INET6, sin6_port=htons(5201), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "::", &sin6_addr), sin6_scope_id=0}, 28) = -1 EADDRINUSE (アドレスは既に使用中です)
iperf3: error - unable to start listener for connections: Address already in use
iperf3: exiting
+++ exited with 1 +++

1.3 その他

ちなみに、ローカルIPアドレスが異なれば、同じローカルポート番号を使うことができます。iperf3コマンドを使って、そのことを確認してみます。

iperf3コマンドを実行します。このとき、ローカルIPアドレスに10.0.0.10を指定します。

[root@server ~]# iperf3 -s -B 10.0.0.10
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------

もう1つ、iperf3コマンドを実行します。このとき、ローカルIPアドレスに192.168.122.68を指定します。

[root@server ~]# iperf3 -s -B 192.168.122.68
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------

ssコマンドを実行します。2つのiperf3プロセスが、同じローカルポート番号(5201)を使うことができることが分かります。なお、ssコマンドの使い方は、ssコマンドの使い方 - hana_shinのLinux技術ブログを参照してください。

[root@server ~]# ss -antop4 'sport == :5201'
State     Recv-Q    Send-Q          Local Address:Port         Peer Address:Port    Process
LISTEN    0         128                 10.0.0.10:5201              0.0.0.0:*        users:(("iperf3",pid=1688,fd=3))
LISTEN    0         128            192.168.122.68:5201              0.0.0.0:*        users:(("iperf3",pid=1686,fd=3))

2 検証環境

2.1 ネットワーク構成

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

                          192.168.2.0/24
client(eth0) -------------------------------------(eth0) server
        .115                                       .120

2.2 版数

サーバとクライアントのAlmaLinuxの版数は以下のとおりです。

[root@server ~]# cat /etc/redhat-release
AlmaLinux release 8.6 (Sky Tiger)

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

[root@server ~]# uname -r
4.18.0-372.9.1.el8.x86_64

3 事前準備

クライアントからサーバの11111番ポートにアクセスできるようにするため、TCPの11111番ポートを解放します。なお、firewall-cmdの使い方は、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 動作確認

ncコマンドを使うと、複数のncプロセスで同じ名前(ローカルIPアドレスとローカルポート番号の組)のソケットを使用できることがわかりました。次は、ncコマンドを使ってTCPコネクションの確立を試してみたいと思います。

サーバでncコマンドを実行します。

[root@server ~]# nc -kl 11111

サーバでもう1つターミナルを開いて、ncコマンドを実行します。

[root@server ~]# nc -kl 11111

サーバでもう1つターミナルを開いて、ssコマンドを実行します。2つのncプロセスが同じ名前のソケットを使用していることがわかります。この理由は、ncプロセスが、ソケットに対してSO_REUSEPORTを設定しているからです。

[root@server ~]# ss -antop4  'sport == :11111'
State         Recv-Q         Send-Q                 Local Address:Port                  Peer Address:Port        Process
LISTEN        0              1                            0.0.0.0:11111                      0.0.0.0:*            users:(("nc",pid=1628,fd=3))
LISTEN        0              1                            0.0.0.0:11111                      0.0.0.0:*            users:(("nc",pid=1626,fd=3))

次に、クライアントでncコマンドを実行します。

[root@client ~]# nc 192.168.122.68 11111

クライアントでもう1つターミナルを開いて、ncコマンドを実行します。

[root@client ~]# nc 192.168.122.68 11111

サーバでssコマンドを実行します。サーバとクライアントの間でTCPコネクションが2つ生成されていることがわかります。サーバ側のncプロセスは、同じ名前(ローカルIPアドレスが192.168.122.68、ローカルポート番号が11111)のソケットを使用していることがわかります。

[root@server ~]# ss -antop4  'sport == :11111'
State         Recv-Q        Send-Q                Local Address:Port                   Peer Address:Port         Process
LISTEN        0             1                           0.0.0.0:11111                       0.0.0.0:*             users:(("nc",pid=1628,fd=3))
LISTEN        0             1                           0.0.0.0:11111                       0.0.0.0:*             users:(("nc",pid=1626,fd=3))
ESTAB         0             0                    192.168.122.68:11111               192.168.122.177:49876         users:(("nc",pid=1628,fd=4))
ESTAB         0             0                    192.168.122.68:11111               192.168.122.177:49874         users:(("nc",pid=1626,fd=4))

Z 参考情報

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