GNU/Linux >> Tutoriales Linux >  >> Linux

UDP-Broadcast en todas las interfaces

De la solución de Jeremy en preguntas frecuentes sobre sockets de UNIX:

#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 primer lugar, debe considerar la transmisión obsoleta, especialmente INADDR_BROADCAST (255.255.255.255). Su pregunta destaca 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 usa multidifusión).

INADDR_BROADCAST se limita al enlace local. Actualmente, su único uso visible es para la autoconfiguración de DHCP, ya que en ese momento, el cliente aún 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 puedes tener un solo sendto() generar más de un paquete, tendría que iterar sobre todas las interfaces y usar sockets sin procesar o vincular 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, desde INADDR_BROADCAST no está enrutado de todos modos, puede lograr casi lo mismo iterando sobre cada interfaz y enviando el paquete a su dirección de transmisión. Por ejemplo, suponiendo que sus redes tengan 255.255.255.0 (/24) máscaras, las direcciones de transmisión son 192.168.1.255 y 192.168.2.255 . Llamar sendto() una vez por cada una de estas direcciones y habrás logrado tu objetivo.

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


No puedes tener un solo sendto() generar un paquete en cada interfaz - en general (a pesar de la fragmentación) es un paquete transmitido por cada sendto() .

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

  1. usar bajo nivel (setsockopt() ?) llamadas 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 adecuado si está tratando de hacer algún tipo de mecanismo de descubrimiento, de modo que los dispositivos que espera que respondan no estén configurados correctamente con una dirección IP en la misma subred que la interfaz a la que están conectados. a.


Linux
  1. Qué proceso está usando todo mi disco IO

  2. C/C++ Linux Dirección MAC de todas las interfaces

  3. Enumere solo los nombres de dispositivos de todas las interfaces de red disponibles

  4. Lista de todas las páginas man disponibles

  5. ¿Cómo bloquear todo el tráfico entre dos interfaces?

Cómo enumerar todas las bases de datos en PostgreSQL

[Revisión] Pear OS 8:Todo se trata de la apariencia

¿Eliminar todos los comentarios de C con Sed?

¿Escribir todo el desplazamiento hacia atrás de Tmux en un archivo?

Todo sobre el disco web

Cree una matriz con todas las interfaces de red en bash