所有接口上的UDP广播

在有线和无线接口的Linux系统(例如192.168.1.x和192.168.2.x子网)上,我想发送一个UDP广播,通过所有可用的接口(即通过有线和无线接口)。

目前我sendto()INADDR_BROADCAST,但似乎广播只通过其中一个接口发送(不总是相同的,随后的广播可能会使用其他接口)。

有没有一种方法可以发送通过每个接口传出的UDP广播?

首先,你应该考虑广播过时,特别是INADDR_BROADCAST (255.255.255.255)。 你的问题恰好突出了广播不适合的原因之一。 它应该与IPv4(希望)一起死亡。 请注意,IPv6甚至没有广播的概念(相反,使用多播)。

INADDR_BROADCAST仅限于本地链接。 现在,只能看到用于DHCP自动配置,因为在这个时候,客户端不知道它连接到什么网络。

使用单个sendto() ,只生成一个数据包,而出接口则由操作系统的路由表(linux上的ip route )决定。 你不能有一个sendto()生成多个数据包,你必须迭代所有的接口,使用原始套接字或使用setsockopt(..., SOL_SOCKET, SO_BINDTODEVICE, "ethX")发送每个数据包绕过OS路由表(这需要root权限)。 不是一个好的解决方案

相反,因为INADDR_BROADCAST不是路由,所以通过在每个接口上迭代并发送数据包到它的广播地址,可以实现几乎相同的目的。 例如,假设您的网络具有255.255.255.0(/ 24)掩码,广播地址是192.168.1.255192.168.2.255 。 给这些地址中的每一个调用sendto()一次,你就完成了你的目标。


编辑:修复关于INADDR_BROADCAST信息,并用关于SO_BINDTODEVICE信息补充答案。

你不能有一个单一的sendto()在每个接口上生成一个数据包 – 一般来说(尽管有碎片),它是每个sendto()发送的一个数据包。

您需要为每个接口传输一次数据包,并且:

  1. 使用低级( setsockopt() ?)调用来选择出站接口

  2. 发送到每个已知接口的特定广播地址

但是如果你想要做某种发现机制,那么后者是不适合的,例如你希望响应的设备实际上没有正确地配置IP地址与它们连接的接口在同一个子网中至。

从Jeremy解决方案的UNIX Socket Socket FAQ:

 #include <stdio.h> #ifdef WIN32 # include <windows.h> # include <winsock.h> # include <iphlpapi.h> #else # include <unistd.h> # include <stdlib.h> # include <sys/socket.h> # include <netdb.h> # include <netinet/in.h> # include <net/if.h> # include <sys/ioctl.h> #endif #include <string.h> #include <sys/stat.h> typedef unsigned long uint32; #if defined(__FreeBSD__) || defined(BSD) || defined(__APPLE__) || defined(__linux__) # define USE_GETIFADDRS 1 # include <ifaddrs.h> static uint32 SockAddrToUint32(struct sockaddr * a) { return ((a)&&(a->sa_family == AF_INET)) ? ntohl(((struct sockaddr_in *)a)->sin_addr.s_addr) : 0; } #endif // convert a numeric IP address into its string representation static void Inet_NtoA(uint32 addr, char * ipbuf) { sprintf(ipbuf, "%li.%li.%li.%li", (addr>>24)&0xFF, (addr>>16)&0xFF, (addr>>8)&0xFF, (addr>>0)&0xFF); } // convert a string represenation of an IP address into its numeric equivalent static uint32 Inet_AtoN(const char * buf) { // net_server inexplicably doesn't have this function; so I'll just fake it uint32 ret = 0; int shift = 24; // fill out the MSB first bool startQuad = true; while((shift >= 0)&&(*buf)) { if (startQuad) { unsigned char quad = (unsigned char) atoi(buf); ret |= (((uint32)quad) << shift); shift -= 8; } startQuad = (*buf == '.'); buf++; } return ret; } static void PrintNetworkInterfaceInfos() { #if defined(USE_GETIFADDRS) // BSD-style implementation struct ifaddrs * ifap; if (getifaddrs(&ifap) == 0) { struct ifaddrs * p = ifap; while(p) { uint32 ifaAddr = SockAddrToUint32(p->ifa_addr); uint32 maskAddr = SockAddrToUint32(p->ifa_netmask); uint32 dstAddr = SockAddrToUint32(p->ifa_dstaddr); if (ifaAddr > 0) { char ifaAddrStr[32]; Inet_NtoA(ifaAddr, ifaAddrStr); char maskAddrStr[32]; Inet_NtoA(maskAddr, maskAddrStr); char dstAddrStr[32]; Inet_NtoA(dstAddr, dstAddrStr); printf(" Found interface: name=[%s] desc=[%s] address=[%s] netmask=[%s] broadcastAddr=[%s]\n", p->ifa_name, "unavailable", ifaAddrStr, maskAddrStr, dstAddrStr); } p = p->ifa_next; } freeifaddrs(ifap); } #elif defined(WIN32) // Windows XP style implementation // Adapted from example code at http://msdn2.microsoft.com/en-us/library/aa365917.aspx // Now get Windows' IPv4 addresses table. Once again, we gotta call GetIpAddrTable() // multiple times in order to deal with potential race conditions properly. MIB_IPADDRTABLE * ipTable = NULL; { ULONG bufLen = 0; for (int i=0; i<5; i++) { DWORD ipRet = GetIpAddrTable(ipTable, &bufLen, false); if (ipRet == ERROR_INSUFFICIENT_BUFFER) { free(ipTable); // in case we had previously allocated it ipTable = (MIB_IPADDRTABLE *) malloc(bufLen); } else if (ipRet == NO_ERROR) break; else { free(ipTable); ipTable = NULL; break; } } } if (ipTable) { // Try to get the Adapters-info table, so we can given useful names to the IP // addresses we are returning. Gotta call GetAdaptersInfo() up to 5 times to handle // the potential race condition between the size-query call and the get-data call. // I love a well-designed API :^P IP_ADAPTER_INFO * pAdapterInfo = NULL; { ULONG bufLen = 0; for (int i=0; i<5; i++) { DWORD apRet = GetAdaptersInfo(pAdapterInfo, &bufLen); if (apRet == ERROR_BUFFER_OVERFLOW) { free(pAdapterInfo); // in case we had previously allocated it pAdapterInfo = (IP_ADAPTER_INFO *) malloc(bufLen); } else if (apRet == ERROR_SUCCESS) break; else { free(pAdapterInfo); pAdapterInfo = NULL; break; } } } for (DWORD i=0; i<ipTable->dwNumEntries; i++) { const MIB_IPADDRROW & row = ipTable->table[i]; // Now lookup the appropriate adaptor-name in the pAdaptorInfos, if we can find it const char * name = NULL; const char * desc = NULL; if (pAdapterInfo) { IP_ADAPTER_INFO * next = pAdapterInfo; while((next)&&(name==NULL)) { IP_ADDR_STRING * ipAddr = &next->IpAddressList; while(ipAddr) { if (Inet_AtoN(ipAddr->IpAddress.String) == ntohl(row.dwAddr)) { name = next->AdapterName; desc = next->Description; break; } ipAddr = ipAddr->Next; } next = next->Next; } } char buf[128]; if (name == NULL) { sprintf(buf, "unnamed-%i", i); name = buf; } uint32 ipAddr = ntohl(row.dwAddr); uint32 netmask = ntohl(row.dwMask); uint32 baddr = ipAddr & netmask; if (row.dwBCastAddr) baddr |= ~netmask; char ifaAddrStr[32]; Inet_NtoA(ipAddr, ifaAddrStr); char maskAddrStr[32]; Inet_NtoA(netmask, maskAddrStr); char dstAddrStr[32]; Inet_NtoA(baddr, dstAddrStr); printf(" Found interface: name=[%s] desc=[%s] address=[%s] netmask=[%s] broadcastAddr=[%s]\n", name, desc?desc:"unavailable", ifaAddrStr, maskAddrStr, dstAddrStr); } free(pAdapterInfo); free(ipTable); } #else // Dunno what we're running on here! # error "Don't know how to implement PrintNetworkInterfaceInfos() on this OS!" #endif } int main(int, char **) { PrintNetworkInterfaceInfos(); return 0; }