2009-03-25 18 views
16

Sur un système Linux avec une interface filaire et une interface sans fil (par exemple, sous-réseaux 192.168.1.x et 192.168.2.x), je souhaite envoyer une diffusion UDP diffusée via TOUTES les interfaces disponibles (c'est-à-dire à la fois via l'interface filaire et sans fil).UDP-Diffusion sur toutes les interfaces

Actuellement je sendto() à INADDR_BROADCAST, cependant il semble que la diffusion seulement est envoyée par l'intermédiaire de l'une des interfaces (pas toujours la même chose et les diffusions suivantes peuvent utiliser l'autre interface).

Y a-t-il un moyen d'envoyer une diffusion UDP qui passe par chaque interface?

Répondre

27

Tout d'abord, vous devriez considérer la diffusion obsolète, spécialement INADDR_BROADCAST (255.255.255.255). Votre question met en évidence l'une des raisons pour lesquelles la diffusion est inadaptée. Il devrait mourir avec IPv4 (espérons-le). Notez que IPv6 n'a même pas de concept de diffusion (la multidiffusion est utilisée à la place).

INADDR_BROADCAST est limitée à la liaison locale. De nos jours, seule l'utilisation visible est pour la configuration automatique DHCP, car à ce moment-là, le client ne sait pas encore dans quel réseau il est connecté.

Avec un seul sendto(), un seul paquet est généré et l'interface sortante est déterminée par la table de routage du système d'exploitation (ip route sous linux). Vous ne pouvez pas avoir un seul sendto() générer plus d'un paquet, vous devez parcourir toutes les interfaces, et soit utiliser des sockets raw ou lier le socket à un périphérique en utilisant setsockopt(..., SOL_SOCKET, SO_BINDTODEVICE, "ethX") pour envoyer chaque paquet en contournant la table de routage OS (cela nécessite root privilèges). Pas une bonne solution. Au lieu de cela, puisque INADDR_BROADCAST n'est pas routé de toute façon, vous pouvez obtenir presque la même chose en itérant sur chaque interface et en envoyant le paquet à son adresse de diffusion. Par exemple, en supposant que vos réseaux ont des masques 255.255.255.0 (/ 24), les adresses de diffusion sont 192.168.1.255 et 192.168.2.255. Appelez sendto() une fois pour chacune de ces adresses et vous aurez atteint votre objectif.


Edit: informations fixes en ce qui concerne à INADDR_BROADCAST, et en complétant la réponse avec des informations sur SO_BINDTODEVICE.

+1

si INADDR_BROADCAST signifiait jamais qu'il y a longtemps _very_. Je vais demander à l'auteur de RFC 1 quand je le vois ce soir :) Dans la mémoire récente, il a toujours mappé à l'adresse MAC du segment de réseau local. Les diffusions _Directed_ étaient auparavant gérées par les routeurs, mais sont désormais bloquées pour des raisons de sécurité. – Alnitak

+0

Ok, j'ai vérifié (RFC 919) et il semble que la première définition de 255.255.255.255 était "voisins immédiats". Pour une raison quelconque, je croyais que l'ensemble de l'Internet était représenté par 0.0.0.0/0, résultant en réseau = 0.0.0.0 et brdcast = 255.255.255.255. Correction de la réponse – Juliano

+0

ok, content que ce soit corrigé. Maintenant, sur le commentaire sur IPv6, n'oubliez pas qu'une diffusion est sémantiquement équivalente à une multidiffusion TTL 1, sauf qu'elle n'utilise pas de trames de diffusion au niveau de la couche 2. – Alnitak

3

Vous ne pouvez pas avoir un seul sendto() générer un paquet sur chaque interface - en général (fragmentation nonobstant) c'est un paquet transmis pour chaque sendto().

Vous devrez transmettre le paquet une fois pour chaque interface et soit: (setsockopt())

  1. utilisation de bas niveau appels pour sélectionner l'interface de sortie

  2. envoyer à la diffusion spécifique adresse pour chaque interface connue

ce dernier n'est cependant pas approprié si vous essayez de faire une sorte de mécanisme de découverte, tel sur les périphériques que vous attendez de répondre ne sont pas correctement configurés avec une adresse IP dans le même sous-réseau que l'interface à laquelle ils sont connectés.

3

De Jeremy's solution sous 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; 
} 
+1

Le code lié ci-dessus montre comment énumérer toutes les interfaces utilisant à la fois getifaddrs() et le GetIpAddrTable() spécifique à Win32() –