SO_REUSEPORTが盛り上がっているみたいですね。
私はコミットログを読んで性能向上のための機能だと認識していて、親プロセスが開いたソケットのfdを子プロセスが使うpreforkのモデルに代わって、子プロセスがそれぞれソケットを開いて使うものだと思い込んでいました。そのため、まったく関係のないプロセスがbindしているポートに対して追加でbindできてしまうというのはなかなか驚きでした。
ただ、何も制限なしにbindできてしまったら、悪意のある何者かが同じ計算機内のすでに使われているポートにbindすることで、一定確率で外部からの通信を奪い取ることができてしまいます。何か制限があるはずだと思い、ソースコードを眺めてみました。
真面目に追い掛けたわけではありませんが、ざっと見たところ、おそらくこの関数で判定しているはず。
sock
構造体のsk_reuseport
はsetsockopt
でSO_REUSEPORT
を有効にした場合に真になります (net/core/sock.c
)。というわけで、以下の行が関係していそうです。
ちょっと読みにくいですが、おそらく以下のような意味でしょう。これ (とその手前にある条件) を満たす場合には2つのsock
のアドレスを確認し、一致するか一方でも0である場合にはbindを禁止しています。
- すでにbindされた
sock
と新しくbindしようとするsock
のどちらか一方でもSO_REUSEPORT
が有効でない
- すでにbindされた
sock
がTCP_TIME_WAIT
状態でなく、かつすでにbindされたsock
と新しくbindしようとするsock
のuidが一致する
要するに、SO_REUSEPORT
で同じポートにbindできるのは、同じuidのプロセスだけということになります。
実際に試してみたところ、同じuidであればbindできましたが、uidが異なる場合には、例え後からbindするのがrootであってもbindできませんでした。
当たり前かもしれませんが、よく考えられていると思いました。そもそも、あるソフトウェアの親プロセスだとか子プロセスだとかのセマンティクスはカーネルからしたら知ったことではないので、よく考えればuidくらいしかやりようがないよね、という話かもしれませんが。