すでに確立されている接続の送信元ポートとしてリストされているポートをリッスンしますか?

すでに確立されている接続の送信元ポートとしてリストされているポートをリッスンしますか?

ローカルアドレス127.0.0.1:45000の接続がESTABLISHED状態の場合、ローカルプロセスは同じポート45000でバインドしてリッスンできますか?または、ポートが使用中と見なされ、バインディング要求がブロックされますか?

答え1

bindに設定すると失敗します。本当にこれを行うには、他のパケットキャプチャ方法を使用して何が起こっているのかを確認する必要があります。進行中の情報やその他のカーネル診断もオプションです。errnoEINVALtcpdumpstrace

答え2

全体の話は「あなたが意味するものによって異なります」です。制御できない既存のプログラムとまったく同じIPとポートでリッスンしたい場合は、他の人が指摘したように幸運ではないかもしれません。ただし、次の場合

  • 他のIPアドレスを受信できます。
  • 元のアプリケーションをある程度制御できます。

もしそうなら、この記事の残りの部分にも興味があるでしょう。

同じポート、異なるインターネットプロトコル(IP)アドレス

プログラムを使用するときバインディング(2)AF_INET ソケットの場合、ポートと IP アドレスを指定するソケットにアドレスを割り当てるシステムコールです。したがって、ポートは同じですが、IPアドレスが異なる2つのアドレスは異なるため、競合なしに別々に割り当てることができます。例えばソカットシェルのループバックインターフェイスのIPアドレスからポート9000にバインドできます。

 socat TCP4-LISTEN:9000,bind=127.0.0.1 STDOUT

同じポートにバインドしますが、外部IPアドレスは別のポートにあります。

socat TCP4-LISTEN:9000,bind=10.0.2.15 STDOUT

どちらのプロセスもリッスンしているIPアドレスとポートの接続を受け入れることができます。ただし、誰かがワイルドカードアドレス0.0.0.0を受信して​​いる場合、最初のプロセスはシステムのすべてのIPにバインドされるため、より具体的なアドレスにバインドできません。

同じポート、同じIP

デフォルトでは、2つのプロセスは2つの異なるファイル記述子を同じアドレスにバインドできません。 Linux でこの操作を試みると、次から EADDRINUSE が返されますbind(2)

socat TCP4-LISTEN:9000,bind=127.0.0.1 STDOUT
2014/11/07 00:10:13 socat[21202] E bind(3, {AF=2 127.0.0.1:9000}, 16): Address already in use

あなたの質問とフォローアップを見ると、現在必要なポートを使用しているプログラムには多くのコントロールがありません。ただし、これにより、あるプロセスはポート+ IPアドレスに接続され、他のプロセスは同じアドレスでリッスンできます。たとえば、多くのサーバーアプリケーションは次のことを行います。

  • 主なプロセスには、バインド()、リスニング()、および受け入れ()接続があります。
  • 許可された接続を処理するために新しいプロセスをフォークすると、デフォルトのプロセスが返され、新しく着信接続をaccept()しようとします。

この場合、そのポートにはESTABLISHED接続を持つ子プロセスが表示されますが、親プロセスには同じポートにLISTENingソケットがあります。

最近のLinuxカーネルでは、完全に関連していない2つのプロセスがSO_REUSEPORTソケットオプションを使用して同じアドレスにバインドされる可能性があります。あるプロセスがソケットにSO_REUSEPORTオプションを設定すると、最初のプロセスと同じ有効なUIDを持つ別のプロセスもSO_REUSEPORTオプションを設定して同じアドレスにバインドできます。

残念ながら、私のバージョンにはsocat単純なTCP例を提供するのが難しいバグがあるようです。しかし、以下に短く誤って書かれたサンプルプログラムを提供しました。 2つの異なるシェルで同じユーザーでプログラムを実行すると、両方が問題なくバインドされます。

State    Recv-Q Send-Q  Local Address:Port  Peer Address:Port
LISTEN   0      128     127.0.0.1:9000        *:*     users:(("bind_tcp_reusep",pid=21254,fd=3))
LISTEN   0    128 127.0.0.1:9000           *:*      users:(("bind_tcp_reusep",pid=21253,fd=3))

アイデアは、ネットワークサーバーを作成する人に多数の同時接続を処理できるアプリケーションを作成するための新しいツールを提供することです。

次のLWN文書は、このオプションのユースケースの良い概要を提供します。

http://lwn.net/Articles/542629/

確立された接続のトラフィックの表示

robbat2が述べたように、tcpdump既存のトラフィックを監視したい場合は、これが最良のオプションです。

SO_REUSEPORTプログラムの例

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <string.h>
#include <errno.h>


int main() {
  struct sockaddr_in bind_addr;
  struct sockaddr peer_addr;
  int optval = 1;
  int tcp_socket;
  int err;
  int addr_len = sizeof(struct sockaddr);

  memset(&bind_addr, 0, sizeof(struct sockaddr_in));
  memset(&peer_addr, 0, sizeof(struct sockaddr));
  bind_addr.sin_family = AF_INET;
  bind_addr.sin_port = htons(9000);

  if (inet_pton(AF_INET, "127.0.0.1", &(bind_addr.sin_addr)) != 1) {
    perror("inet_pton");
    exit(1);
  }

  tcp_socket = socket(AF_INET, SOCK_STREAM, 0);
  setsockopt(tcp_socket, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
  err = bind(tcp_socket, (const struct sockaddr *)&bind_addr, sizeof(struct sockaddr));

  if (err != 0) {
    perror("bind failed");
    exit(1);
  }

  err = listen(tcp_socket, 256);
  if (err != 0) {
    perror("listen failed");
    exit(1);
  }
  accept(tcp_socket, &peer_addr, &addr_len);
}

答え3

私はローカルのTomcatサーバーを起動し、設定された接続の送信元アドレスとしてリストされているポートにバインドするように設定を調整することで直接試しました。これにより、他のESTABLISHED接続がすでにそのポートをローカルアドレスとして使用している間、サーバーはLISTEN状態のポートにバインドできます。

関連情報