From 08f6b21ce7cc4aa4069b4461785fc7173bed2998 Mon Sep 17 00:00:00 2001
From: Josh Blum <josh@joshknows.com>
Date: Fri, 26 Mar 2010 11:45:56 -0700
Subject: added interface address discovery

---
 host/include/uhd/transport/CMakeLists.txt    |   1 +
 host/include/uhd/transport/if_addrs.hpp      |  47 ++++
 host/include/uhd/transport/smart_buffer.hpp  |   2 +-
 host/include/uhd/transport/udp_simple.hpp    |   2 +-
 host/include/uhd/transport/udp_zero_copy.hpp |   2 +-
 host/lib/CMakeLists.txt                      |  15 +-
 host/lib/transport/if_addrs.cpp              | 343 +++++++++++++++++++++++++++
 host/lib/transport/udp_simple.cpp            |   1 +
 host/lib/transport/udp_zero_copy_asio.cpp    |   1 +
 host/lib/usrp/usrp2/usrp2_impl.cpp           |  20 +-
 host/lib/usrp/usrp2/usrp2_impl.hpp           |   1 +
 11 files changed, 429 insertions(+), 6 deletions(-)
 create mode 100644 host/include/uhd/transport/if_addrs.hpp
 create mode 100644 host/lib/transport/if_addrs.cpp

(limited to 'host')

diff --git a/host/include/uhd/transport/CMakeLists.txt b/host/include/uhd/transport/CMakeLists.txt
index 75b07c540..14b5ccd29 100644
--- a/host/include/uhd/transport/CMakeLists.txt
+++ b/host/include/uhd/transport/CMakeLists.txt
@@ -17,6 +17,7 @@
 
 
 INSTALL(FILES
+    if_addrs.hpp
     smart_buffer.hpp
     udp_simple.hpp
     udp_zero_copy.hpp
diff --git a/host/include/uhd/transport/if_addrs.hpp b/host/include/uhd/transport/if_addrs.hpp
new file mode 100644
index 000000000..fbbb35e1d
--- /dev/null
+++ b/host/include/uhd/transport/if_addrs.hpp
@@ -0,0 +1,47 @@
+//
+// Copyright 2010 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_UHD_IFADDRS_HPP
+#define INCLUDED_UHD_IFADDRS_HPP
+
+#include <uhd/config.hpp>
+#include <string>
+#include <vector>
+
+namespace uhd{ namespace transport{
+
+    /*!
+     * The address for a network interface.
+     */
+    struct UHD_API if_addrs_t{
+        std::string inet;
+        std::string mask;
+        std::string bcast;
+        if_addrs_t(void);
+    };
+
+    /*!
+     * Get a list of network interface addresses.
+     * The internal implementation is system-dependent.
+     * \return a vector of if addrs
+     */
+    UHD_API std::vector<if_addrs_t> get_if_addrs(void);
+
+}} //namespace
+
+
+#endif /* INCLUDED_UHD_IFADDRS_HPP */
diff --git a/host/include/uhd/transport/smart_buffer.hpp b/host/include/uhd/transport/smart_buffer.hpp
index 9e1032feb..a9bc259e9 100644
--- a/host/include/uhd/transport/smart_buffer.hpp
+++ b/host/include/uhd/transport/smart_buffer.hpp
@@ -18,7 +18,7 @@
 #ifndef INCLUDED_UHD_TRANSPORT_SMART_BUFFER_HPP
 #define INCLUDED_UHD_TRANSPORT_SMART_BUFFER_HPP
 
-#include <boost/asio.hpp>
+#include <boost/asio/buffer.hpp>
 #include <boost/utility.hpp>
 #include <boost/shared_ptr.hpp>
 
diff --git a/host/include/uhd/transport/udp_simple.hpp b/host/include/uhd/transport/udp_simple.hpp
index 40e60d091..793ec4fd7 100644
--- a/host/include/uhd/transport/udp_simple.hpp
+++ b/host/include/uhd/transport/udp_simple.hpp
@@ -19,7 +19,7 @@
 #define INCLUDED_UHD_TRANSPORT_UDP_SIMPLE_HPP
 
 #include <uhd/config.hpp>
-#include <boost/asio.hpp>
+#include <boost/asio/buffer.hpp>
 #include <boost/utility.hpp>
 #include <boost/shared_ptr.hpp>
 
diff --git a/host/include/uhd/transport/udp_zero_copy.hpp b/host/include/uhd/transport/udp_zero_copy.hpp
index 03d89b3a5..0441a8e74 100644
--- a/host/include/uhd/transport/udp_zero_copy.hpp
+++ b/host/include/uhd/transport/udp_zero_copy.hpp
@@ -20,7 +20,7 @@
 
 #include <uhd/config.hpp>
 #include <uhd/transport/smart_buffer.hpp>
-#include <boost/asio.hpp>
+#include <boost/asio/buffer.hpp>
 #include <boost/utility.hpp>
 #include <boost/shared_ptr.hpp>
 
diff --git a/host/lib/CMakeLists.txt b/host/lib/CMakeLists.txt
index 22fbde265..4dd638336 100644
--- a/host/lib/CMakeLists.txt
+++ b/host/lib/CMakeLists.txt
@@ -28,6 +28,7 @@ SET(libuhd_sources
     time_spec.cpp
     types.cpp
     wax.cpp
+    transport/if_addrs.cpp
     transport/udp_simple.cpp
     transport/vrt.cpp
     usrp/dboard/basic.cpp
@@ -60,7 +61,6 @@ LIST(APPEND libuhd_sources
 # Setup defines for module loading
 ########################################################################
 INCLUDE(CheckIncludeFileCXX)
-
 CHECK_INCLUDE_FILE_CXX(dlfcn.h HAVE_DLFCN_H)
 CHECK_INCLUDE_FILE_CXX(Winbase.h HAVE_WINBASE_H)
 
@@ -74,6 +74,19 @@ ELSE(HAVE_DLFCN_H)
     MESSAGE(STATUS "Module loading not supported...")
 ENDIF(HAVE_DLFCN_H)
 
+########################################################################
+# Setup defines for interface address discovery
+########################################################################
+INCLUDE(CheckIncludeFileCXX)
+CHECK_INCLUDE_FILE_CXX(ifaddrs.h HAVE_IFADDRS_H)
+
+IF(HAVE_IFADDRS_H)
+    MESSAGE(STATUS "Interface address discovery supported through getifaddrs...")
+    ADD_DEFINITIONS(-DHAVE_IFADDRS_H)
+ELSE(HAVE_IFADDRS_H)
+    MESSAGE(STATUS "Interface address discovery not supported...")
+ENDIF(HAVE_IFADDRS_H)
+
 ########################################################################
 # Setup libuhd library
 ########################################################################
diff --git a/host/lib/transport/if_addrs.cpp b/host/lib/transport/if_addrs.cpp
new file mode 100644
index 000000000..d3ea448fd
--- /dev/null
+++ b/host/lib/transport/if_addrs.cpp
@@ -0,0 +1,343 @@
+//
+// Copyright 2010 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <uhd/transport/if_addrs.hpp>
+
+uhd::transport::if_addrs_t::if_addrs_t(void){
+    /* NOP */
+}
+
+/***********************************************************************
+ * Interface address discovery through ifaddrs api
+ **********************************************************************/
+#ifdef HAVE_IFADDRS_H
+#include <ifaddrs.h>
+#include <boost/asio/ip/address_v4.hpp>
+
+static boost::asio::ip::address_v4 sockaddr_to_ip_addr(sockaddr *addr){
+    if (addr->sa_family == AF_INET) return boost::asio::ip::address_v4(ntohl(
+        reinterpret_cast<sockaddr_in*>(addr)->sin_addr.s_addr
+    ));
+    return boost::asio::ip::address_v4::any();
+}
+
+static bool ifaddrs_valid(const struct ifaddrs *ifaddrs){
+    return (
+        ifaddrs->ifa_addr->sa_family == AF_INET and
+        sockaddr_to_ip_addr(ifaddrs->ifa_addr) != boost::asio::ip::address_v4::loopback()
+    );
+}
+
+std::vector<uhd::transport::if_addrs_t> uhd::transport::get_if_addrs(void){
+    std::vector<if_addrs_t> if_addrs;
+    struct ifaddrs *ifap;
+    if (getifaddrs(&ifap) == 0){
+        for (struct ifaddrs *iter = ifap; iter != NULL; iter = iter->ifa_next){
+            if (not ifaddrs_valid(iter)) continue;
+            if_addrs_t if_addr;
+            if_addr.inet = sockaddr_to_ip_addr(iter->ifa_addr).to_string();
+            if_addr.mask = sockaddr_to_ip_addr(iter->ifa_netmask).to_string();
+            if_addr.bcast = sockaddr_to_ip_addr(iter->ifa_broadaddr).to_string();
+            if_addrs.push_back(if_addr);
+        }
+        freeifaddrs(ifap);
+    }
+    return if_addrs;
+}
+
+/***********************************************************************
+ * Interface address discovery through windows api (TODO)
+ **********************************************************************/
+//#elif HAVE_XXX_H
+
+/***********************************************************************
+ * Interface address discovery not included
+ **********************************************************************/
+#else /* HAVE_IFADDRS_H */
+
+std::vector<uhd::transport::if_addrs_t> uhd::transport::get_if_addrs(void){
+    return std::vector<if_addrs_t>();
+}
+
+#endif /* HAVE_IFADDRS_H */
+
+////////////////////////////////////////////////////////////////////////
+// How to extract the ip address: unix/windows
+// http://www.developerweb.net/forum/showthread.php?t=5085
+////////////////////////////////////////////////////////////////////////
+
+/*
+#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;
+}
+*/
+
+////////////////////////////////////////////////////////////////////////
+// How to extract the mac address: linux/windows
+// http://old.nabble.com/MAC-Address-td19111197.html
+////////////////////////////////////////////////////////////////////////
+
+/*
+Linux:
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netpacket/packet.h>
+#include <ifaddrs.h>
+
+ifaddrs * ifap = 0;
+if(getifaddrs(&ifap) == 0)
+{
+     ifaddrs * iter = ifap;
+     while(iter)
+     {
+         sockaddr_ll * sal =
+                    reinterpret_cast<sockaddr_ll*>(iter->ifa_addr);
+        if(sal->sll_family == AF_PACKET)
+        {
+                 // get the mac bytes
+                // copy(sal->sll_addr,
+                 //      sal->sll_addr+sal->sll_hallen,
+                 //      buffer);
+        }
+         iter = iter->ifa_next;
+     }
+     freeifaddrs(ifap);
+}
+
+Windows:
+#include <winsock2.h>
+#include <iphlpapi.h>
+
+std::vector<boost::uint8_t> buf;
+DWORD bufLen = 0;
+GetAdaptersAddresses(0, 0, 0, 0, &bufLen);
+if(bufLen)
+{
+     buf.resize(bufLen, 0);
+     IP_ADAPTER_ADDRESSES * ptr =
+                     reinterpret_cast<IP_ADAPTER_ADDRESSES*>(&buf[0]);
+     DWORD err = GetAdaptersAddresses(0, 0, 0, ptr, &bufLen);
+     if(err == NO_ERROR)
+     {
+          while(ptr)
+         {
+              if(ptr->PhysicalAddressLength)
+              {
+                 // get the mac bytes
+                // copy(ptr->PhysicalAddress,
+                 //      ptr->PhysicalAddress+ptr->PhysicalAddressLength,
+                 //      buffer);
+              }
+              ptr = ptr->Next;
+          }
+     }
+} 
+*/
diff --git a/host/lib/transport/udp_simple.cpp b/host/lib/transport/udp_simple.cpp
index 3c8ecb70d..f339127ad 100644
--- a/host/lib/transport/udp_simple.cpp
+++ b/host/lib/transport/udp_simple.cpp
@@ -16,6 +16,7 @@
 //
 
 #include <uhd/transport/udp_simple.hpp>
+#include <boost/asio.hpp>
 #include <boost/thread.hpp>
 #include <boost/format.hpp>
 #include <iostream>
diff --git a/host/lib/transport/udp_zero_copy_asio.cpp b/host/lib/transport/udp_zero_copy_asio.cpp
index 219ae8720..63944b2bf 100644
--- a/host/lib/transport/udp_zero_copy_asio.cpp
+++ b/host/lib/transport/udp_zero_copy_asio.cpp
@@ -17,6 +17,7 @@
 
 #include <uhd/transport/udp_zero_copy.hpp>
 #include <boost/cstdint.hpp>
+#include <boost/asio.hpp>
 #include <boost/thread.hpp>
 #include <boost/format.hpp>
 #include <iostream>
diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp
index 85d73e83a..f04ae8d2c 100644
--- a/host/lib/usrp/usrp2/usrp2_impl.cpp
+++ b/host/lib/usrp/usrp2/usrp2_impl.cpp
@@ -15,9 +15,11 @@
 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 
+#include <uhd/transport/if_addrs.hpp>
+#include <uhd/utils.hpp>
 #include <boost/format.hpp>
+#include <boost/foreach.hpp>
 #include <boost/bind.hpp>
-#include <uhd/utils.hpp>
 #include <iostream>
 #include "usrp2_impl.hpp"
 
@@ -36,7 +38,21 @@ STATIC_BLOCK(register_usrp2_device){
 uhd::device_addrs_t usrp2::discover(const device_addr_t &hint){
     device_addrs_t usrp2_addrs;
 
-    if (not hint.has_key("addr")) return usrp2_addrs;
+    //if no address was specified, send a broadcast on each interface
+    if (not hint.has_key("addr")){
+        BOOST_FOREACH(const if_addrs_t &if_addrs, get_if_addrs()){
+            //create a new hint with this broadcast address
+            device_addr_t new_hint = hint;
+            new_hint["addr"] = if_addrs.bcast;
+
+            //call discover with the new hint and append results
+            device_addrs_t new_usrp2_addrs = usrp2::discover(new_hint);
+            usrp2_addrs.insert(usrp2_addrs.begin(),
+                new_usrp2_addrs.begin(), new_usrp2_addrs.end()
+            );
+        }
+        return usrp2_addrs;
+    }
 
     //create a udp transport to communicate
     //TODO if an addr is not provided, search all interfaces?
diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp
index 55ac0b192..a2f454c61 100644
--- a/host/lib/usrp/usrp2/usrp2_impl.hpp
+++ b/host/lib/usrp/usrp2/usrp2_impl.hpp
@@ -22,6 +22,7 @@
 #include <uhd/dict.hpp>
 #include <uhd/types.hpp>
 #include <uhd/time_spec.hpp>
+#include <boost/asio.hpp>
 #include <boost/thread.hpp>
 #include <boost/shared_ptr.hpp>
 #include <boost/function.hpp>
-- 
cgit v1.2.3