JavaでUDPマルチキャストプログラムの送信を行ったところ、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)と同じ種類のアドレスが割当てられているインタフェースにのみ送信するといった絞込みをかける必要がありそうです。