torutkのブログ

ソフトウェア・エンジニアのブログ

マルチホームでのUDP送信口

JavaUDPマルチキャストプログラムの送信を行ったところ、LAN上の別PCで受信できず、WiresharkでのパケットキャプチャでもUDPパケットが確認できませんでした。

このPC、VMware Playerをインストールしていたため、仮想的にLANインタフェースが複数存在するマルチホームな設定となっています。Wiresharkで各インタフェースのパケットを調べると、実のLAN側ではなく、内部の仮想LANインタフェースに送信していました。

JavaのDatagramSocketクラスのsendで送信されるデータは、ネットワークが複数ある場合ルーティングテーブルによって決定されるインタフェース1つにだけ出力されます。ルーティングテーブルはWindowsの場合、route PRINTコマンドで確認できます。

C:\>route PRINT
中略
   224.0.0.0   240.0.0.0   リンク上         127.0.0.1   306
   224.0.0.0   240.0.0.0   リンク上     192.168.187.1   276
   224.0.0.0   240.0.0.0   リンク上      192.168.48.1   276
   224.0.0.0   240.0.0.0   リンク上      192.168.10.1   276

実際のNICに割当てたアドレス192.168.10.1と、VMware Virtual Ethernet Adapterに割当てられたアドレス192.168.48.1と192.168.187.1が同じメトリクス値で並んでいます。そして、UDPパケットは最初の192.168.187.1に送信されていました。

マルチホームでのUDPパケット送信対策

マルチキャストの場合は、存在するインタフェースのそれぞれからUDPパケットを送信するのが望ましい場合が多いので、プログラム中でインタフェース一覧を取得し、それぞれごとに送信するのがよいようです。

しかし、インタフェース一覧からUDPパケット送信可能なインタフェースを分別するのは困難です。
また、プログラムを変更できないとか、特定のインタフェースにだけ出せればよい場合もあります。ルーティングテーブルのメトリクス値を変更して制御する方法もあります。

ルーティングテーブルの変更による制御

例えば上記の場合、実際のNICである192.168.10.1から送信されるようメトリクス値を変更します。

C:>route CHANGE 224.0.0.0 MASK 240.0.0.0 192.168.10.1 METRIC 200 IF 8

メトリクス値は数値が小さい方が優先度が高いので、これでデフォルトの送信口が実際のNICになります。

routeコマンドの設定ミスには要注意

IF(インタフェース)を指定しなかったり、ゲートウェイを指定しないとき、224.0.0.0に関する他のルーティング情報が消失してしまいました。このあたりの設定については奥が深いようです。

インタフェース一覧を取得し送信する

Javaネットワークプログラミングの真髄のp.298にある「すべてのインタフェイスからマルチキャストを送信する」のコード片(一部修正)

    MulticastSocket socket = ...
    DatagramPacket packet = ...

    Enumeration<NetworkInterface> intfs =
        NetworkInterface.getNetworkInterfaces();
    while (intfs.hasMoreElements()) {
        NetworkInterface intf = intfs.nextElement();
        socket.setNetworkInterface(intf);
        socket.send(packet);
    }

この方法だと、socket.send()の内部で例外が発生することがあります。Enumerationで取得したインタフェースが指定したpacketを送信できないもの等です。

そこで、送信先アドレスの種類(IPv4,IPv6)と同じ種類のアドレスが割当てられているインタフェースにのみ送信するといった絞込みをかける必要がありそうです。