简体   繁体   English

IPv4 链路本地多播未通过多个网络接口传输

[英]IPv4 link-local multicast not transmitted over multiple network interfaces

Many users have a computer with a Wifi connection to the internet and a wired connection to a local network.许多用户拥有一台带有 Wifi 连接到互联网和有线连接到本地网络的计算机。 I'm trying to use IPv4 link-local multicast to discover devices on the wired network but this does not work, apparently because the automatically generated routing tables only transmit the multicast frames via the Wifi interface.我正在尝试使用 IPv4 链路本地多播来发现有线网络上的设备,但这不起作用,显然是因为自动生成的路由表仅通过 Wifi 接口传输多播帧。

My Java code tries to specify both interfaces:我的 Java 代码尝试指定两个接口:

sock.setNetworkInterface(ifc);
sock.send(new DatagramPacket(...));

but this appears to have no effect.但这似乎没有效果。

I can use我可以用

sudo route -n change -host 224.0.0.189 -interface en4

to change the routing table, and this works.更改路由表,这有效。 But it's obviously is not practical for an app.但这对于应用程序显然不实用。

Any idea how to solve this?知道如何解决这个问题吗?

(Ideally, I'd like the multicasts to be transmitted from all interfaces.) (理想情况下,我希望从所有接口传输多播。)

It all works using plain old BSD Sockets code:这一切都使用普通的旧 BSD Sockets 代码:

static const uint8_t discoveryEchoReq[] = { 0, 0, 0, 2, 0, 0, 0, 0xff, 0, 0, 0, 0, 0, 0, 0, 0 };

int main(int argc, char **argv) {
    
    //setup socket addr
    struct sockaddr_in mcastSockAddr;
    memset((char *)&mcastSockAddr, 0, sizeof(mcastSockAddr));
    mcastSockAddr.sin_family = AF_INET;
    mcastSockAddr.sin_addr.s_addr = inet_addr("224.0.0.189");
    mcastSockAddr.sin_port = htons(48556);

    //look for interfaces
    struct ifaddrs * interfaces = NULL;
    struct ifaddrs * ifc = NULL;
    if (getifaddrs(&interfaces) < 0) exit(-1);
    ifc = interfaces;
    while(ifc != NULL) {
        if ( (ifc->ifa_addr->sa_family == AF_INET) && ((ifc->ifa_flags & IFF_MULTICAST) != 0) ) {
            int sock = socket(AF_INET, SOCK_DGRAM, 0); //create socket
            if (sock < 0) exit(-2);
            if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, &((struct sockaddr_in *)ifc->ifa_addr)->sin_addr, sizeof(struct in_addr)) < 0) exit(-3);
            if (sendto(sock, discoveryEchoReq, sizeof(discoveryEchoReq), 0, (struct sockaddr*)&mcastSockAddr, sizeof(mcastSockAddr)) < 0) exit(-5);
            if (close(sock) < 0) exit(-6);
        }
        ifc = ifc->ifa_next; //follow linked list
    }
    freeifaddrs(interfaces); //free memory
    
    exit(0);
}

but similar Java code does not work:但类似的 Java 代码不起作用:

for (Enumeration ifcs = NetworkInterface.getNetworkInterfaces() ; ifcs.hasMoreElements() ; ) {
    NetworkInterface ifc = (NetworkInterface)ifcs.nextElement();
    if (ifc.supportsMulticast()) {
        for (Enumeration addrs = ifc.getInetAddresses() ; addrs.hasMoreElements() ; ) {
            InetAddress addr = (InetAddress)addrs.nextElement();
            if (addr instanceof Inet4Address) {
                MulticastSocket sock = new MulticastSocket();
                sock.setNetworkInterface(ifc);
                sock.send(new DatagramPacket(Discoverer.ECHO_REQUEST, Discoverer.ECHO_REQUEST.length, Daemon.SKT_MCAST));
            }
        }
    }
}

ie in the BSD case, the multicast packets are emitted from both network interfaces, but in the Java case they are emitted from only one interface.即在 BSD 情况下,多播数据包从两个网络接口发出,但在 Java 情况下,它们仅从一个接口发出。

Using NIO fixes the problem, so my guess is that there's maybe a bug in the Java runtime?使用 NIO 解决了这个问题,所以我的猜测是 Java 运行时可能存在错误?

ie this works:即这有效:

for (Enumeration ifcs = NetworkInterface.getNetworkInterfaces() ; ifcs.hasMoreElements() ; ) {
    NetworkInterface ifc = (NetworkInterface)ifcs.nextElement();
        if (ifc.supportsMulticast()) {
        for (Enumeration addrs = ifc.getInetAddresses() ; addrs.hasMoreElements() ; ) {
            InetAddress addr = (InetAddress)addrs.nextElement();
            if (addr instanceof Inet4Address) {
                DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET).setOption(StandardSocketOptions.IP_MULTICAST_IF, ifc);
                DatagramSocket sock = dc.socket();
                sock.send(new DatagramPacket(Discoverer.ECHO_REQUEST, Discoverer.ECHO_REQUEST.length, Daemon.SKT_MCAST));
            }
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM