GNU/Linux >> Linux の 問題 >  >> Linux

Linux:UDP リッスン ソケットを特定のインターフェイスにバインドしますか (または、データグラムが入ってきたインターフェイスを見つけますか)?

私がうまくいった解決策は次のとおりです。まず、ARP と RP の設定を変更する必要があります。 /etc/sysctl.conf に以下を追加して再起動します (これを動的に設定するコマンドもあります):

net.ipv4.conf.default.arp_filter = 1
net.ipv4.conf.default.rp_filter = 2
net.ipv4.conf.all.arp_filter = 1
net.ipv4.conf.all.rp_filter = 2

arp フィルターは、eth0 からの応答を WAN 経由でルーティングできるようにするために必要でした。 rp フィルター オプションは、受信パケットを受信した NIC に厳密に関連付けるために必要でした (サブネットに一致する任意の NIC にパケットを関連付ける弱いモデルとは対照的です)。 EJP からのコメントにより、この重要なステップに進みました。

その後、SO_BINDTODEVICE が動作するようになりました。 2 つのソケットのそれぞれが独自の NIC にバインドされているため、メッセージの送信元のソケットに基づいて、メッセージがどの NIC から送信されたかを知ることができました。

s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
rc=setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, nic, IF_NAMESIZE);
memset((char *) &si_me, 0, sizeof(si_me));
si_me.sin_family = AF_INET;
si_me.sin_port = htons(LISTEN_PORT);
si_me.sin_addr.s_addr = htonl(INADDR_ANY);
rc=bind(s, (struct sockaddr *)&si_me, sizeof(si_me))

次に、送信元アドレスが元の要求の送信元である NIC のアドレスであるデータグラムを使用して、着信データグラムに応答したいと考えました。その答えは、その NIC のアドレスを調べて、発信ソケットをそのアドレスにバインドすることです (bind を使用) )。

s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
get_nic_addr(nics, (struct sockaddr *)&sa)
sa.sin_port = 0;
rc = bind(s, (struct sockaddr *)&sa, sizeof(struct sockaddr));
sendto(s, ...);

int get_nic_addr(const char *nic, struct sockaddr *sa)
{
    struct ifreq ifr;
    int fd, r;
    fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd < 0) return -1;
    ifr.ifr_addr.sa_family = AF_INET;
    strncpy(ifr.ifr_name, nic, IFNAMSIZ);
    r = ioctl(fd, SIOCGIFADDR, &ifr);
    if (r < 0) { ... }
    close(fd);
    *sa = *(struct sockaddr *)&ifr.ifr_addr;
    return 0;
}

(毎回 NIC のアドレスを調べるのは無駄に思えるかもしれませんが、アドレスが変更されたときに通知を受けるには、コードがかなり多くなります。これらのトランザクションは、バッテリで動作しないシステムでは数秒に 1 回しか発生しません。)


IP_RECVDSTADDR を介して、送信者が使用する宛先アドレスを取得できます。 recvmsg() を使用して、プラットフォームがサポートしている場合はオプション . Unix Network Programming で説明されているように、かなり複雑です。 第 1 巻、第 3 版、#22.2、および ページ。

あなたの編集によれば、あなたは TCP/IP の「ウィーク エンド システム モデル」として知られているものに反対しています。基本的に、パケットが到着すると、システムは正しいポートをリッスンする適切なインターフェイスを介して配信することを選択できます。どこかの TCP/IP RFC で議論されています。


setsockopt に不正な値を渡しています .

rc=setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, nic, strlen(nic));

マニュアルページには SO_BIND_TO_DEVICE と書かれています :

<ブロック引用>

渡されたオプションは可変長のヌル終端です 最大サイズ IFNAMSIZ のインターフェイス名文字列

strlen 終端の null は含まれません。試すことができます:

rc=setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, nic, 1 + strlen(nic));

dnsmasq これは正しく機能しており、使用しています

setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, intname, IF_NAMESIZE)

Linux
  1. Linux –システムに搭載されているハードディスクを確認する方法は?

  2. Linuxのソケットバッファサイズを見つける方法

  3. インターネットへの接続に使用しているインターフェイスを確認するにはどうすればよいですか?

  1. Linuxで特定のファイルを提供するパッケージを見つける方法

  2. Linux:サーバーにログインしているユーザーを見つける

  3. Linux VPS の仮想化タイプを調べる方法は?

  1. raw ソケットを特定のインターフェイスにバインドする方法

  2. Linux 端末から特定のファイルを見つけるにはどうすればよいですか?

  3. Linux のコマンドラインから socket.io websocket にメッセージを送信するにはどうすればよいですか?