Linuxを使用して、どのイーサネットインターフェイスデータが送信されるかを指定する方法
-
05-07-2019 - |
質問
同じサブネット上に2つのネットワークインターフェイスがあるLinuxベースのサーバーシステムで作業しています(現時点では、 172.17.32.10
& 172.17.32.11
)。ネットワーク上のホストにデータを送信するとき、データが送信されるサーバー上のインターフェイスを指定したいと思います。ソフトウェアで1つのインターフェイスから別のインターフェイスに切り替えることができます(または、両方で送信することもできます)(このアプリケーションでは静的ルーティングルールは機能しません)。
StackOverflowで、netlinkライブラリを使用してその場でルートを変更することを提案する関連質問を見つけました。これは直観的には機能するように見えますが、この同じ結果を達成する他のオプションがあるかどうか疑問に思っていました。
解決
意図的な違反はありませんが、bind()の使用に関する答えはまったく間違っています。 bind()は、パケットIPヘッダー内に配置されたソースIPアドレスを制御します。どのインターフェイスを使用してパケットを送信するかは制御しません。特定の宛先に到達するためのコストが最も低いインターフェイスを判断するために、カーネルのルーティングテーブルが調べられます。 (*注を参照)
代わりに、 SO_BINDTODEVICE
sockoptを使用する必要があります。これは2つのことを行います。
- カーネルルーティングテーブルの内容に関係なく、指定したインターフェイスからパケットが常に出力されます。
- 指定されたインターフェースに到着したパケットのみがソケットに渡されます。他のインターフェースに到着するパケットはそうではありません。
複数のインターフェイスを切り替えたい場合は、インターフェイスごとに1つのソケットを作成することをお勧めします。また、バインドしたインターフェイスへのパケットのみを受信するため、これらのすべてのソケットを select()
/ poll()
に追加する必要があります。 /使用するものは何でも。
#include <net/if.h>
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, "eth1", sizeof(ifr.ifr_name));
if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE,
(void *)&ifr, sizeof(ifr)) < 0) {
perror("SO_BINDTODEVICE failed");
}
(*注)
インターフェイスIPアドレスへの Bind()
は、混乱を招く可能性がありますが、それでも正しい動作になります。たとえば、eth1のIPアドレスに bind()
しますが、ルーティングテーブルがeth0からパケットを送信する場合、eth0ワイヤにパケットが表示されますが、eth1インターフェイスのソースIPアドレスを伝送します。 。これは奇妙ですが許可されていますが、eth1 IPアドレスに返送されたパケットはeth1にルーティングされます。 2つのiPインターフェイスを備えたLinuxシステムを使用して、これをテストできます。私はそれを持っており、それをテストしましたが、 bind()
は物理インターフェースからパケットを誘導するのに効果的ではありません。
技術的には許可されていますが、トポロジによっては動作しない場合があります。攻撃者が偽造IPソースアドレスを使用する分散型サービス拒否攻撃を抑えるために、多くのルーターは現在、リバースパス転送(RPF)チェックを実行しています。送信元IPアドレスが「間違った」パケット。パスがドロップされる可能性があります。