2009-03-25 19 views
16

En un sistema Linux con una interfaz cableada y una inalámbrica (por ejemplo, subredes 192.168.1.x y 192.168.2.x) deseo enviar una transmisión UDP que se apaga a través de TODAS las interfaces disponibles (es decir, tanto a través de la interfaz alámbrica como inalámbrica).UDP-Broadcast en todas las interfaces

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

¿Hay alguna manera de que pueda enviar una transmisión UDP que sale por cada interfaz?

Respuesta

27

En primer lugar, se debe considerar la emisión obsoletos, especialmente INADDR_BROADCAST (255.255.255.255). Su pregunta resalta exactamente una de las razones por las cuales 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 difusión (en su lugar, se usa multidifusión).

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

Con un solo sendto(), solo se genera un paquete y la interfaz de salida se determina mediante la tabla de enrutamiento del sistema operativo (ip route en Linux). No puede tener un solo sendto() generar más de un paquete, debe iterar sobre todas las interfaces y usar sockets sin procesar 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 root privilegios). No es una buena solución.

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


Editar: información fija con respecto a INADDR_BROADCAST, y complementando la respuesta con información sobre SO_BINDTODEVICE.

+1

si INADDR_BROADCAST alguna vez significó que fue hace mucho tiempo. Le preguntaré al autor de RFC 1 cuando lo vea esta noche :) En la memoria reciente, siempre se ha asignado a la dirección MAC del segmento de red local. _Las transmisiones dirigidas_ solían ser manejadas por enrutadores, pero ahora están bloqueadas por seguridad. – Alnitak

+0

Ok, revisé (RFC 919) y parece que la primera definición de 255.255.255.255 fue "vecinos inmediatos". Por alguna razón, creí que todo el internet estaba representado por 0.0.0.0/0, lo que resulta en una red = 0.0.0.0 y brdcast = 255.255.255.255. Se corrigió la respuesta. – Juliano

+0

ok, me alegro de que esté arreglado. Ahora, en el comentario sobre IPv6, no olvide que una transmisión es semánticamente equivalente a una multidifusión TTL 1, excepto que no usa tramas de difusión en la capa 2. – Alnitak

3

No puede tener un solo sendto() generar un paquete en cada interfaz - en general (a pesar de la fragmentación) se transmite un paquete por cada sendto().

Tendrá que transmitir el paquete una vez para cada interfaz y ya sea: (? setsockopt())

  1. uso bajo nivel de llamadas para seleccionar la interfaz de salida

  2. enviar a la emisión específica dirección para cada interfaz conocida

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

3

De Jeremy's solution en UNIX Socket Preguntas:

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

El código vinculado anteriormente muestra cómo enumerar todas las interfaces usando getifaddrs() y GetIpAddrTable() de Win32() –

Cuestiones relacionadas