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技術ブログ