web-dev-qa-db-ja.com

Linux:特定のネットワークインターフェイスを強制的に使用するにはどうすればよいですか?

これは これ以前のSO質問 の続きと見なすことができます。

理想的には、プロセスをジェイルして、何があっても特定のインターフェイスのみを使用するようにします。 TCP接続を確立し、UDPデータグラムを送信し、UDPブロードキャストをリッスンします。現在、私が行っているのは次のとおりです。

  1. 使用するインターフェースのIPを決定します。
  2. インターフェイスからのすべてのパケットをそのIPにルーティングするIPポリシールールを作成します
  3. 別のIPポリシールールを作成して、そのIPからのすべてのパケットをそのインターフェイスにルーティングします
  4. ルールごとにデフォルトのルーティングテーブルを設定する

現在、これはほとんど機能しますが、クライアントプロセスも進んで実行する必要があります。つまり、使用したいインターフェースの特定のIPにバインドする必要があり、SO_BINDTODEVICEも設定する必要があると思います。 (ただし、TCPまたはUDP)を使用するときにSO_BINDTODEVICEが実際に機能するかどうかについての矛盾する情報を読み続けます。)幸い、クライアントアプリケーションはPythonであり、ソケットクラスを拡張して実行できます。これらすべてが透過的に行われますが、特にブロードキャストの受信に関して、それが完全なソリューションであるかどうかはわかりません。

私の質問は次のとおりです。

  1. SO_BINDTODEVICEは私がここでやりたいことをしますか?それとも、rawソケットに対してのみ有効ですか?誰かが、「ソケットのSO_BINDTODEVICEは、ソケットがその物理インターフェイスのワイヤ/アンテナに到着したパケットのみを受信することを保証するものではありません」とコメントしました。これが本当に本当なら、何 しますかSO_BINDTODEVICEしますか?

  2. ローカルIPが一意である必要がないようにこれを行う方法はありますか?これは、あるインターフェイス上のDHCPサーバーが別のインターフェイスで使用されているIPを割り当てて、ルーティングテーブルを混乱させる可能性があるという事実以外は問題にはなりません。

  3. 特定のインターフェイスからのみブロードキャストを受信するにはどうすればよいですか?特定のIPにバインドすると、ブロードキャストが無視されるように見えます。これは理にかなっていますが、私が探しているものではありません。

Linuxカーネル2.6.26を搭載したUbuntu8.04で実行しています。 2つの異なるインターフェイスを介して2つの異なるネットワーク上の同じサブネットに同時にアクセスできることは、交渉の余地のない要件であるため、(ほとんどの場合)「そうしないでください」の影響を受けません。 :)

18
Matt Green

激しい週末を過ごした後、私は以前に議論したことのほとんどにほとんど手間をかけずに対処するソリューションを提示できることを嬉しく思います。

Net.ipv4.conf.all.rp_filterと呼ばれるsysctlがあり、これを0に設定してソース検証を無効にすることができます。

 rp_filter --INTEGER 
 2-RFC1812 
で指定されているように、リバースパスによるソース検証を実行します。シングルホームホストおよびスタブネットワーク
ルーターに推奨されるオプション。低速で信頼性の低いプロトコル(RIPの一種)を実行している、または静的ルートを使用している複雑な(ループフリーではない)
ネットワークで問題が発生する可能性があります。
 
 1-(デフォルト)RPフィルタリングのより弱い形式:直接接続されたインターフェースで送信されたように見えるすべてのパケット
をドロップしますが、
は別のインターフェースから入力されました。
 
 0 -ソースの検証はありません。

これは、/ proc/sys/net/ipv4/conf/<interface>/rp_filterを使用してインターフェースごとに設定することもできます。

あるポスターが説明しているように、1つのサブネットからのパケットが常に同じインターフェイスから送信されるとは限らないという意味で、IPルーティングは「決定論的ではありません」。この場合、これはまさに必要なものです。これが本当にあなたが望むものであるかどうかを決定するために追加の調査をしてください。

放送は私にはわからない理由でまだ問題がありますが、私はついにこの問題に満足し、それが他の人に役立つことを願っています。

3
Matt Green

私の一般的な質問に関しては、それを行うにはいくつかの方法があるようです。

  • ルーティングテーブルの変更と各プロセスからの協力を伴う複雑な方法。これは私が上で説明した方法です。それが持つ1つの利点は、ユーザースペースから機能します。私はそれにいくつかの追加のメモを置き、以下の私の特定の質問に答えました。

  • _SO_BINDTODEVICE_が設定されている場合、ルーティングテーブルを完全に無視するカスタムカーネルモデルを記述します。ただし、クライアントプロセスはsetsockopt(SOL_SOCKET, SO_BINDTODEVICE, dev)を呼び出す必要があります。このオプションは、心の弱い人には絶対に向いていません。

  • プロセスを仮想化します。これはおそらく多くの人にとって適切ではなく、主に構成に伴う独自の頭痛の種をもたらします。しかし、それは言及する価値があります。

オプション1と2では、希望どおりに機能するようにオプトインするプロセスが必要です。これは、socket()呼び出しをハイジャックしてソケットを作成し、記述子を返す前にすぐにデバイスにバインドするダイナミックライブラリを作成することで部分的に軽減できます。これについて詳しく説明します ここ

いくつかの調査と多くのグーグルを行った後、Linuxカーネル2.6.26がどのように動作するかについていくつかの結論を引き出すことができます。これらはおそらくすべて実装固有の動作であり、カーネル固有でさえあることに注意してください。私の単一のデータポイントに基づいて機能を実装することを決定する前に、独自のプラットフォームをテストしてください。

  1. _SO_BINDTODEVICE_は、少なくともUDPについては、実際にそれが言うことを実行します。

  2. ルーティングテーブルを使用しているため、インターフェイスごとに一意のIPが必要なようです。カスタムカーネルモジュールは、この制限を回避できます。

  3. 特定のインターフェイスでブロードキャストを受信するには、最初に_SO_BINDTODEVICE_を使用してデバイスにバインドし、次に通常のbind()呼び出しでブロードキャストアドレスにバインドします。デバイスのバインドは、何よりも先に行う必要があります。次に、ソケットはそのインターフェイスに到着するブロードキャストのみを受信します。

最初にソケットを作成してテストしました。次に、setsockopt(SOL_SOCKET, SO_BINDTODEVICE, dev)を使用して特定のインターフェイスにバインドしました。最後に、ブロードキャストアドレスにバインドしました。別のコンピューターから、バインドされていないインターフェイスを介して受信されるブロードキャストを送信しました。デバイスにバインドされたソケットはこのブロードキャストを受信しませんでした。これは理にかなっています。 setsockopt(SOL_SOCKET, SO_BINDTODEVICE, dev)呼び出しを削除すると、ブロードキャストが受信されます。

ここでもsetsockopt(SOL_SOCKET, SO_REUSEADDR, 1)を使用できることにも言及しておく必要があります。 _SO_REUSEADDR_のセマンティクスはブロードキャストアドレスによって変わることに注意してください。具体的には、両方に_SO_REUSEADDR_が設定されている場合、2つのソケットをブロードキャストアドレスと同じマシン上の同じポートにバインドすることは合法です。

更新:_SO_BINDTODEVICE_のブロードキャストは、特にブロードキャストフレームの受信に危険を伴うようです。一方のインターフェイスで受信したブロードキャストフレームを観察していましたが、同時にもう一方のインターフェイスでは消えていました。これらはローカルルーティングテーブルの影響を受けているようですが、IPポリシールールの影響を受けません。しかし、私はこれについて100%確信しているわけではなく、継続したい場合の調査のポイントとしてのみ言及しています。つまり、自己責任で使用してください。時間の都合上、インターフェイスでrawソケットを開き、イーサネットヘッダーとIPヘッダーを自分で解析しました。

4
Matt Green

あなたの質問に対する直接の答えではなく、参考までに。あなたが上で述べたように、この解決策はあなたがする必要がある/したいことにはあまりにも多くの仕事かもしれません。

私は個人的に、これを可能にするネットワークスタックフックカーネルモジュールを作成するというアイデアが好きです。このようにして、ユーザースペースとの間で送受信されるマルチキャストフレームとユニットキャストフレームを完全に制御できます。ドライバーとユーザースペースアプリケーションとの間でデータを送受信するには、 netlink sockets のようなものを使用する必要がありますが、それは非常にうまく機能し、非常に高速です。

また、この方法でスタックの任意のレベルにフックすることができます...イーサネットまたはIP。したがって、送信/受信するものを完全に制御できます。

これが例です article netfilterスタックへのフックについて説明しています。
注:この記事はIPスタックにフックし、古いものでもあります。 APIが変更されたことは知っていますが、この記事の多くは実際的および理論的に適用されます。ブリッジレイヤーにフックしたい場合は、同様のメカニズムを使用しますが、

BR_LOCAL_IN instead of NF_IP_LOCAL_IN

注:これは、インターフェースでrawソケットを開くのと非常によく似ています。フレームは自分で作成する必要があります。

2
Steve Lazaridis

プロセスのネットワーク名前空間を単一のインターフェースに制限してみることができます。 CONFIG_NETNS(最新のディストリビューションのほとんどのカーネル)を使用したカーネルビルドと、割り当てを行うためのスクリプトが必要です。 サンプル構成

2
event