open linux udp posix broadcast

linux - open - wildfly admin console



UDP-Broadcast en todas las interfaces (3)

De la solución de Jeremy en UNIX Socket Preguntas frecuentes:

#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; }

En un sistema Linux con una interfaz cableada e inalámbrica (p. Ej., 192.168.1.xy subredes 192.168.2.x) quiero enviar una transmisión UDP que se transmite a través de TODAS las interfaces disponibles (es decir, tanto a través de la interfaz cableada como la inalámbrica) ).

Actualmente envío a () a INADDR_BROADCAST, sin embargo, parece que la transmisión solo se envía a través de una de las interfaces (no siempre la misma y las transmisiones posteriores pueden usar la otra interfaz).

¿Hay alguna manera de enviar una transmisión UDP que se transmita a través de cada interfaz?


No puede tener un solo sendto() generar un paquete en cada interfaz; en general (a pesar de la fragmentación) es un paquete transmitido para cada sendto() .

Deberá transmitir el paquete una vez para cada interfaz y:

  1. usar llamadas de bajo nivel ( setsockopt() ?) para seleccionar la interfaz de salida

  2. Enviar a la dirección de difusión específica para cada interfaz conocida

sin embargo, esto último no es adecuado si está tratando de hacer algún tipo de mecanismo de descubrimiento, de modo que los dispositivos a los que espera responder no están configurados correctamente con una dirección IP en la misma subred que la interfaz a la que están conectados a.


En primer lugar, debe considerar la transmisión como obsoleta, especialmente INADDR_BROADCAST (255.255.255.255). Su pregunta resalta exactamente una de las razones por las que la transmisión no es adecuada. Debería morir junto con IPv4 (con suerte). Tenga en cuenta que IPv6 ni siquiera tiene un concepto de transmisión (en su lugar se utiliza multidifusión).

INADDR_BROADCAST está limitado al enlace local. Hoy en día, su uso solo es visible para la configuración automática de DHCP, ya que en ese momento, el cliente no sabrá en qué red está conectado.

Con un solo sendto() , solo se genera un único paquete, y la interfaz de salida está determinada por la tabla de enrutamiento del sistema operativo ( ip route en Linux). No puede tener un solo sendto() generar más de un paquete, tendría que recorrer en iteración todas las interfaces y usar sockets sin formato o enlazar el socket a un dispositivo usando setsockopt(..., SOL_SOCKET, SO_BINDTODEVICE, "ethX") para enviar cada paquete sin pasar por la tabla de enrutamiento del sistema operativo (esto requiere privilegios de root). No es una buena solución.

En cambio, dado que INADDR_BROADCAST no se enruta de todos modos, puede lograr casi lo mismo al iterar sobre cada interfaz y enviar el paquete a su dirección de transmisión. Por ejemplo, suponiendo que sus redes tienen máscaras 255.255.255.0 (/ 24), las direcciones de transmisión son 192.168.1.255 y 192.168.2.255 . Llame a sendto() una vez para cada una de estas direcciones y habrá cumplido su objetivo.

Edición: se corrigió la información relacionada con INADDR_BROADCAST y se complementó la respuesta con información sobre SO_BINDTODEVICE .