GNU/Linux >> Znalost Linux >  >> Linux

UDP-Broadcast na všech rozhraních

Z Jeremyho řešení na 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;
}

Nejprve byste měli považovat vysílání za zastaralé, zvláště INADDR_BROADCAST (255.255.255.255). Váš dotaz zdůrazňuje přesně jeden z důvodů, proč je vysílání nevhodné. Měl by zemřít spolu s IPv4 (doufejme). Všimněte si, že IPv6 nemá ani koncept vysílání (místo toho se používá vícesměrové vysílání).

INADDR_BROADCAST je omezena na místní odkaz. V dnešní době je viditelné použití pouze pro automatickou konfiguraci DHCP, protože v tu chvíli klient ještě nebude vědět, do jaké sítě je připojen.

S jediným sendto() , vygeneruje se pouze jeden paket a odchozí rozhraní je určeno směrovací tabulkou operačního systému (ip route na linuxu). Nemůžete mít jeden sendto() generovat více než jeden paket, museli byste iterovat všechna rozhraní a buď použít nezpracované sokety, nebo svázat soket se zařízením pomocí setsockopt(..., SOL_SOCKET, SO_BINDTODEVICE, "ethX") pro odeslání každého paketu, který obchází směrovací tabulku OS (to vyžaduje oprávnění root). Není to dobré řešení.

Místo toho od INADDR_BROADCAST není stejně směrován, můžete dosáhnout téměř stejné věci iterací přes každé rozhraní a odesláním paketu na jeho broadcast adresu. Za předpokladu, že vaše sítě mají například masky 255.255.255.0 (/24), jsou adresy vysílání 192.168.1.255 a 192.168.2.255 . Zavolejte na číslo sendto() jednou pro každou z těchto adres a dosáhnete svého cíle.

Upravit: opravené informace týkající se INADDR_BROADCAST a doplnění odpovědi o informace o SO_BINDTODEVICE .


Nemůžete mít jeden sendto() vygenerovat paket na každém rozhraní - obecně (bez ohledu na fragmentaci) je to jeden paket přenesený pro každé sendto() .

Budete muset odeslat paket jednou pro každé rozhraní a buď:

  1. použijte nízkoúrovňový (setsockopt() ?) volání pro výběr odchozího rozhraní

  2. odeslat na konkrétní broadcast adresu pro každé známé rozhraní

posledně jmenovaný však není vhodný, pokud se pokoušíte provést nějaký mechanismus zjišťování, že zařízení, u kterých očekáváte odpověď, nejsou ve skutečnosti správně nakonfigurována s IP adresou ve stejné podsíti jako rozhraní, ke kterému jsou připojena. do.


Linux
  1. Malá písmena ve všech adresářích v adresáři?

  2. Tisknout všechny soubory ve složce?

  3. Jaký proces používá celý můj disk IO

  1. C/C++ Linux MAC adresy všech rozhraní

  2. Jak odstranit všechny kontejnery Docker

  3. Uveďte pouze názvy zařízení všech dostupných síťových rozhraní

  1. Seznam všech dostupných manuálových stránek

  2. Jak zablokovat veškerý provoz mezi dvěma rozhraními?

  3. Propojení ethernetových rozhraní