Pregunta

En un sistema Linux con una interfaz inalámbrica y por cable (por ejemplo 192.168.1.xy 192.168.2.x subredes) Quiero enviar una difusión UDP que sale a través de todas las interfaces disponibles (es decir, tanto a través del cable y la interfaz inalámbrica).

Actualmente me SENDTO () para INADDR_BROADCAST, sin embargo, parece que la transmisión sólo se envía a través de una de las interfaces (no siempre los mismos y posteriores transmisiones pueden usar la otra interfaz).

¿Hay una manera que puedo enviar una difusión UDP que sale a través de cada interfaz única?

¿Fue útil?

Solución

En primer lugar, se debe considerar la emisión obsoletos, especialmente INADDR_BROADCAST (255.255.255.255). Su duda destaca precisamente una de las razones por las que la emisión no es adecuado. Se debe morir junto con IPv4 (es de esperar). Tenga en cuenta que IPv6 no tiene ni siquiera un concepto de difusión (multicast es utilizado, en su lugar).

INADDR_BROADCAST se limita al enlace local. Hoy en día, sólo es visible es el uso de DHCP para la configuración automática, ya que en ese momento, el cliente no sabe todavía en qué red está conectado a.

Con un solo sendto(), solamente 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 se puede tener una sola sendto() generar más de un paquete, que tendría que iterar sobre todas las interfaces, y, o bien utilizar sockets crudos o enlazar el conector a un dispositivo mediante setsockopt(..., SOL_SOCKET, SO_BINDTODEVICE, "ethX") para enviar cada paquete evitando la tabla de enrutamiento OS (esto requiere la raíz privilegios). No es una buena solución.

En lugar de ello, ya que INADDR_BROADCAST no se enruta todos modos, se puede lograr casi lo mismo por iteración sobre cada interfaz, y enviar 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 . sendto() llama una vez para cada una de estas direcciones y se han logrado su objetivo.


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

.

Otros consejos

No se puede tener una sola sendto() generar un paquete en cada interfaz -. En (a pesar de la fragmentación) en general es un paquete transmitido por cada sendto()

Usted necesitará para transmitir el paquete una vez para cada interfaz y, o bien:

  1. El uso de bajo nivel (setsockopt()?) Llama para seleccionar la interfaz de salida

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

Sin embargo este último no es conveniente si usted está tratando de hacer algún tipo de mecanismo de descubrimiento, de manera que los dispositivos se está esperando para responder en realidad no están configurados correctamente con una dirección IP en la misma subred que la interfaz se estés conectado con.

solución de Jeremy 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;
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top