[英]UDP-Broadcast on all interfaces
在具有有線和無線接口(例如 192.168.1.x 和 192.168.2.x 子網)的 Linux 系統上,我想發送一個通過所有可用接口(即通過有線和無線接口)發出的 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")
繞過操作系統路由表發送每個數據包(這需要 root 權限)。 不是一個好的解決方案。
相反,由於INADDR_BROADCAST
都不會路由INADDR_BROADCAST
,因此您可以通過遍歷每個接口並將數據包發送到其廣播地址來實現幾乎相同的事情。 例如,假設您的網絡有 255.255.255.0 (/24) 掩碼,廣播地址為192.168.1.255和192.168.2.255 。 為這些地址中的每一個調用sendto()
一次,您將完成您的目標。
編輯:關於INADDR_BROADCAST
固定信息,並用關於SO_BINDTODEVICE
信息補充答案。
您不能讓單個sendto()
在每個接口上生成一個數據包 - 通常(盡管存在碎片)它是為每個sendto()
傳輸的一個數據包。
您需要為每個接口傳輸一次數據包,並且:
使用低級 ( setsockopt()
?) 調用來選擇出站接口
發送到每個已知接口的特定廣播地址
但是,如果您嘗試執行某種發現機制,則后者不適合,這樣您期望響應的設備實際上並未正確配置與它們連接的接口位於同一子網中的 IP 地址到。
來自Jeremy在 UNIX 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;
}
我在github上找到了一個解決方案: https : //github.com/dechamps/WinIPBroadcast 。 它在所有接口上發送廣播,並且自述文件解釋了原因。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.