UNIX Socket FAQ

A forum for questions and answers about network programming on Linux and all other Unix-like systems

You are not logged in.

#1 2007-04-25 06:58 PM

jfriesne
Administrator
From: California
Registered: 2005-07-06
Posts: 348
Website

Re: UDP broadcasts vs multiple interfaces?

Hi all,

I have a program that uses UDP broadcast to compile a list of available servers on the LAN... it's pretty straightforward:  the user clicks the "Query Available Servers" button on the client, the client sends out a single broadcast UDP packet on a well-known port, and any servers that receive the UDP packet reply with a (unicast) UDP packet back to the client's IP address (as reported by recvfrom()).  When the client receives a UDP packet from a given IP address, it adds that IP address to its list of available servers in the GUI.

This all works fine, but I have some users with multiple network interfaces claiming that they only see responses from servers on one of the network interfaces.  In particular, laptop users with both a 802.11 wireless connection and a hard-wired Ethernet connection say they can "see" the servers on the hard-wired LAN but not those on the 802.11 wireless LAN using this technique.  However, if they can still connect to the servers on the Wireless LAN (after they've added the appropriate IP addresses by hand, of course).

So my question is, is there some trick I need to do in order to get UDP broadcast to work on all available interfaces, and not just "the default interface"?  It may be that this problem is Windows-specific, but I'm not sure about that either, since I don't have any multiple-network-interface machines (Windows, Linux, or MacOS/X) to test with.

Thanks for any hints you can give..... for reference, I'm including some relevant code snippets from my program below.

-Jeremy

--------------------------------------

// The function I use to create UDP sockets (on both client and server)
int CreateUDPSocket()
{
   return socket(AF_INET, SOCK_DGRAM, 0);
}

// The function I use to bind my UDP socket to a particular port to
// listen for incoming UDP responses.  This function is used by both
// the client and the server
status_t BindUDPSocket(int sock, uint16 port)
{
   struct sockaddr_in saSocket;
   memset(&saSocket, 0, sizeof(saSocket));
   saSocket.sin_family      = AF_INET;
   saSocket.sin_addr.s_addr = htonl(INADDR_ANY);
   saSocket.sin_port        = htons(port);

   if (bind(sock, (struct sockaddr *) &saSocket, sizeof(saSocket)) == 0)
   {
      return B_NO_ERROR;
   }
   else return B_ERROR;
}

// The function the client uses to send our the broadcast "query" UDP packet
// Note that I pass in ((uint32)-1) as the ip argument (e.g. 255.255.255.255)
int32 SendDataUDP(int sock, const void * buffer, uint32 size, uint32 ip, uint16 port)
{
    struct sockaddr_in toAddr;
    memset(&toAddr, 0, sizeof(toAddr));

    toAddr.sin_family = AF_INET;
    toAddr.sin_addr.s_addr = htonl(destIP);
    toAddr.sin_port        = htons(port);
    return sendto(sock, (const char *)buffer, size, 0L, (struct sockaddr *)&toAddr, sizeof(toAddr));
}

// This is the function I call to receive UDP packets, on both client and server
int32 ReceiveDataUDP(int sock, void * buffer, uint32 size, uint32 * optFromIP, uint16 * optFromPort)
{
   struct sockaddr_in fromAddr;
   int fromAddrLen = sizeof(fromAddr);
   int r = recvfrom(sock, (char *)buffer, size, 0L, (struct sockaddr *) &fromAddr, &fromAddrLen);
   if (r >= 0)
   {
      if (optFromIP)   *optFromIP   = ntohl(fromAddr.sin_addr.s_addr);
      if (optFromPort) *optFromPort = ntohs(fromAddr.sin_port);
   }
   return r;
}

Offline

#2 2007-04-25 07:35 PM

RobSeace
Administrator
From: Boston, MA
Registered: 2002-06-12
Posts: 3,839
Website

Re: UDP broadcasts vs multiple interfaces?

I think the only choice is to query the local interfaces and send separate broadcast
packets to each of their real broadcast IPs, rather than just sending to the limited
broadcast IP (255.255.255.255) as you do now...  Look for a function like getifaddrs(),
which will make your life easier, if you've got it...  If not, you'll have to go messing
with ioctl(SIOCGIF*)...  You might still be able to pull the interface list via some
other function like if_nameindex(), rather than needing to do SIOCGIFCONF, which
is a pain in the ass...  But, in that case, you'd still at least need to do SIOCGIFBRDADDR
to get the broadcast IP...  If you can use getifaddrs(), that supplies the broadcast IP
to you as part of its returned list of structs, so no further work necessary...  (You
mention you're writing this on Windoze?  Take a look at a recent version of the
libpcap code; they have code for getting at the interface list on Windoze, along
with various other systems...  Specifically, it's in "fad-win32.c" on v0.9.4, that I have
lying around...)

Offline

#3 2007-04-25 11:50 PM

jfriesne
Administrator
From: California
Registered: 2005-07-06
Posts: 348
Website

Re: UDP broadcasts vs multiple interfaces?

Hi Rob,

Thanks for the info... yeesh, that sounds like a pain.  :^P  At least now I know where to look!

This program needs to run correctly on Windows, Linux, and MacOS/X (it would be nice if it ran correctly in other OS's as well, but it's not required).

Am I correct in thinking that it's only the UDP-broadcast-send that I need to change to send one packet per interface, and that the UDP receiving code (both on the server and client sides) will "just work", as is, once that is fixed?

Thanks again,
Jeremy

Offline

#4 2007-04-26 01:00 PM

RobSeace
Administrator
From: Boston, MA
Registered: 2002-06-12
Posts: 3,839
Website

Re: UDP broadcasts vs multiple interfaces?

Offline

#5 2007-04-27 11:55 PM

jfriesne
Administrator
From: California
Registered: 2005-07-06
Posts: 348
Website

Re: UDP broadcasts vs multiple interfaces?

Hi all,

Okay, I think I have it all figured out now.  Thanks for the help, Rob!  In case anyone might find it useful, I'm including some example code that compiles and runs under MacOS/X, Linux, and Win32 (possibly other places too, I don't know).  When it runs, it prints out information about all the available IPv4 network interfaces on the host machine, including the interface name, description (Windows only), IP address, netmask, and broadcast IP address.

-Jeremy

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

Offline

#6 2007-04-28 06:07 PM

RobSeace
Administrator
From: Boston, MA
Registered: 2002-06-12
Posts: 3,839
Website

Re: UDP broadcasts vs multiple interfaces?

Minor point: you technically should be using "ifa_broadaddr" to get the broadcast
address...  Now, it seems most systems define the "ifaddrs" struct such that
"ifa_dstaddr" and "ifa_broadaddr" share the same exact space (either via union or
via #define'ing one as the other), so your method will work fine in practice...  But,
it's just kind of conceptually ugly...  You really shouldn't know about such ugly
internal details like that...  Better to reference the proper "ifa_broadaddr" name,
instead...  (And, technically, you should only do so if "ifa_flags" has IFF_BROADCAST
set, too...)

Offline

#7 2007-05-01 02:43 AM

jfriesne
Administrator
From: California
Registered: 2005-07-06
Posts: 348
Website

Re: UDP broadcasts vs multiple interfaces?

Offline

#8 2007-05-01 01:01 PM

RobSeace
Administrator
From: Boston, MA
Registered: 2002-06-12
Posts: 3,839
Website

Re: UDP broadcasts vs multiple interfaces?

Offline

Board footer

Powered by FluxBB