[ovs-dev] [PATCH] ARP lookup and next hop functionality on windows

Alin Serdean aserdean at cloudbasesolutions.com
Mon May 4 15:34:58 UTC 2015


This patch implements two functionalities needed for an active manager:
1. ARP lookup
2. Next hop

The first functionality relies on the internal Windows API:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa365956%28v=vs.85%29.aspx

The second one:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa365915%28v=vs.85%29.aspx

Both API's are found in the Iphlpapi library. We need to add this library
when compiling.

Documentation and appveyor config has been updated to match the use of
the new library.

Tested using opendaylight.

Signed-off-by: Alin Gabriel Serdean <aserdean at cloudbasesolutions.com>
Reported-by: Alin Gabriel Serdean <aserdean at cloudbasesolutions.com>
Reported-at: https://github.com/openvswitch/ovs-issues/issues/63
---
 INSTALL.Windows.md   |  25 ++++++-----
 appveyor.yml         |   2 +-
 lib/netdev-windows.c | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 139 insertions(+), 13 deletions(-)

diff --git a/INSTALL.Windows.md b/INSTALL.Windows.md
index 78af0a1..0ec0af0 100644
--- a/INSTALL.Windows.md
+++ b/INSTALL.Windows.md
@@ -62,9 +62,10 @@ or from a distribution tar ball.
   the right compiler, linker, libraries, Open vSwitch component installation
   directories, etc. For example,
 
-    % ./configure CC=./build-aux/cccl LD="`which link`" LIBS="-lws2_32" \
-      --prefix="C:/openvswitch/usr" --localstatedir="C:/openvswitch/var" \
-      --sysconfdir="C:/openvswitch/etc" --with-pthread="C:/pthread"
+    % ./configure CC=./build-aux/cccl LD="`which link`" \
+      LIBS="-lws2_32 -liphlpapi" --prefix="C:/openvswitch/usr" \
+      --localstatedir="C:/openvswitch/var" --sysconfdir="C:/openvswitch/etc" \
+       --with-pthread="C:/pthread"
 
     By default, the above enables compiler optimization for fast code.
     For default compiler optimization, pass the "--with-debug" configure
@@ -114,10 +115,10 @@ Note down the directory where OpenSSL is installed (e.g.: C:/OpenSSL-Win32).
 * While configuring the package, specify the OpenSSL directory path.
 For example,
 
-    % ./configure CC=./build-aux/cccl LD="`which link`" LIBS="-lws2_32" \
-    --prefix="C:/openvswitch/usr" --localstatedir="C:/openvswitch/var" \
-    --sysconfdir="C:/openvswitch/etc" --with-pthread="C:/pthread" \
-    --enable-ssl --with-openssl="C:/OpenSSL-Win32"
+    % ./configure CC=./build-aux/cccl LD="`which link`"  \
+    LIBS="-lws2_32 -liphlpapi" --prefix="C:/openvswitch/usr" \
+    --localstatedir="C:/openvswitch/var" --sysconfdir="C:/openvswitch/etc" \
+    --with-pthread="C:/pthread" --enable-ssl --with-openssl="C:/OpenSSL-Win32"
 
 * Run make for the ported executables.
 
@@ -131,11 +132,11 @@ level 'make' will invoke building the kernel datapath, if the
 '--with-vstudioddk' argument is specified while configuring the package.
 For example,
 
-    % ./configure CC=./build-aux/cccl LD="`which link`" LIBS="-lws2_32" \
-    --prefix="C:/openvswitch/usr" --localstatedir="C:/openvswitch/var" \
-    --sysconfdir="C:/openvswitch/etc" --with-pthread="C:/pthread" \
-    --enable-ssl --with-openssl="C:/OpenSSL-Win32" \
-    --with-vstudioddk="<WDK to use>"
+    % ./configure CC=./build-aux/cccl LD="`which link`" \
+    LIBS="-lws2_32 -liphlpapi" --prefix="C:/openvswitch/usr" \
+    --localstatedir="C:/openvswitch/var" --sysconfdir="C:/openvswitch/etc" \
+    --with-pthread="C:/pthread" --enable-ssl \
+    --with-openssl="C:/OpenSSL-Win32" --with-vstudioddk="<WDK to use>"
 
     Possible values for "<WDK to use>" are:
     "Win8.1 Debug", "Win8.1 Release", "Win8 Debug" and "Win8 Release".
diff --git a/appveyor.yml b/appveyor.yml
index a14f0fc..863b561 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -39,5 +39,5 @@ build_script:
 - C:\MinGW\msys\1.0\bin\bash -lc "cp /c/pthreads-win32/Pre-built.2/dll/x86/*.dll /c/openvswitch/."
 - C:\MinGW\msys\1.0\bin\bash -lc "mv /bin/link.exe /bin/link_copy.exe"
 - C:\MinGW\msys\1.0\bin\bash -lc "cd /c/openvswitch && ./boot.sh"
-- C:\MinGW\msys\1.0\bin\bash -lc "cd /c/openvswitch && ./configure CC=build-aux/cccl LD=\"`which link`\" LIBS=-lws2_32 --with-pthread=C:/pthreads-win32/Pre-built.2 --with-openssl=C:/OpenSSL-Win32 --with-vstudioddk=\"Win8.1 Debug\""
+- C:\MinGW\msys\1.0\bin\bash -lc "cd /c/openvswitch && ./configure CC=build-aux/cccl LD=\"`which link`\" LIBS=\"-lws2_32 -liphlpapi\" --with-pthread=C:/pthreads-win32/Pre-built.2 --with-openssl=C:/OpenSSL-Win32 --with-vstudioddk=\"Win8.1 Debug\""
 - C:\MinGW\msys\1.0\bin\bash -lc "cd /c/openvswitch && make"
diff --git a/lib/netdev-windows.c b/lib/netdev-windows.c
index 1fc1da7..47e12df 100644
--- a/lib/netdev-windows.c
+++ b/lib/netdev-windows.c
@@ -17,6 +17,7 @@
 #include <stdlib.h>
 #include <config.h>
 #include <errno.h>
+#include <iphlpapi.h>
 
 #include <net/if.h>
 
@@ -373,6 +374,128 @@ netdev_windows_update_flags(struct netdev *netdev_,
     return 0;
 }
 
+/* Number of times to retry the doubling of the length of the buffer which is
+ * used to get all interfaces/ARP table */
+#define MAX_RETRIES 10
+
+/* Looks up in the ARP table entry for a given 'ip'. If it is found, the
+ * corresponding MAC address will be copied in 'mac' and return 0. If no
+ * matching entry is found or an error occurs it will log it and return ENXIO.
+ */
+static int
+netdev_windows_arp_lookup(const struct netdev *netdev,
+                          ovs_be32 ip, uint8_t mac[ETH_ADDR_LEN])
+{
+    PMIB_IPNETTABLE arp_table = NULL;
+    /* The buffer length of all ARP entries */
+    uint32_t buffer_length = 1000;
+    uint32_t ret_val = 0;
+    uint32_t counter = 0;
+    uint8_t retries = 0;
+
+    do {
+        arp_table = (MIB_IPNETTABLE *) malloc(buffer_length);
+
+        if (arp_table == NULL) {
+            VLOG_ERR("Could not allocate memory for all the interfaces");
+            return ENXIO;
+        }
+
+        ret_val = GetIpNetTable(arp_table, &buffer_length, false);
+
+        if (ret_val == ERROR_INSUFFICIENT_BUFFER ) {
+            free(arp_table);
+            arp_table = NULL;
+            buffer_length = buffer_length * 2;
+        }
+
+        retries++;
+    } while ((ret_val == ERROR_INSUFFICIENT_BUFFER ) &&
+             (retries < MAX_RETRIES));
+
+    if (ret_val == NO_ERROR) {
+        for (counter = 0; counter < arp_table->dwNumEntries; counter++) {
+            if (arp_table->table[counter].dwAddr == ip) {
+                memcpy(mac, arp_table->table[counter].bPhysAddr, ETH_ADDR_LEN);
+
+                free(arp_table);
+                return 0;
+            }
+        }
+    } else {
+        VLOG_ERR("Call to GetAdaptersAddresses failed with error: %d",
+                 ret_val);
+    }
+
+    free(arp_table);
+    return ENXIO;
+}
+
+static int
+netdev_windows_get_next_hop(const struct in_addr *host,
+                            struct in_addr *next_hop,
+                            char **netdev_name)
+{
+    uint32_t ret_val = 0;
+    /* The buffer length of all addresses */
+    uint32_t buffer_length = 1000;
+    uint8_t retries = 0;
+    PIP_ADAPTER_ADDRESSES all_addr = NULL;
+    PIP_ADAPTER_ADDRESSES cur_addr = NULL;
+
+    do {
+        all_addr = (IP_ADAPTER_ADDRESSES *) malloc(buffer_length);
+
+        if (all_addr == NULL) {
+            VLOG_ERR("Could not allocate memory for all the interfaces");
+            return ENXIO;
+        }
+
+        ret_val = GetAdaptersAddresses(AF_INET,
+                                       GAA_FLAG_INCLUDE_PREFIX |
+                                       GAA_FLAG_INCLUDE_GATEWAYS,
+                                       NULL, all_addr, &buffer_length);
+
+        if (ret_val == ERROR_BUFFER_OVERFLOW) {
+            ovs_assert(all_addr);
+            free(all_addr);
+            all_addr = NULL;
+            buffer_length = buffer_length * 2;
+        }
+
+        retries++;
+
+    } while ((ret_val == ERROR_BUFFER_OVERFLOW) && (retries < MAX_RETRIES));
+
+    if (ret_val == NO_ERROR) {
+        cur_addr = all_addr;
+        while (cur_addr) {
+            if(cur_addr->FirstGatewayAddress &&
+               cur_addr->FirstGatewayAddress->Address.lpSockaddr) {
+                struct sockaddr_in *ipv4 = (struct sockaddr_in *)
+                                           cur_addr->FirstGatewayAddress->Address.lpSockaddr;
+                next_hop->s_addr = ipv4->sin_addr.S_un.S_addr;
+                *netdev_name = xstrdup((char *)cur_addr->FriendlyName);
+
+                if (all_addr) {
+                    free(all_addr);
+                }
+                return 0;
+            }
+
+            cur_addr = cur_addr->Next;
+        }
+    } else {
+        VLOG_ERR("Call to GetAdaptersAddresses failed with error: %d",
+                 ret_val);
+    }
+
+    if (all_addr) {
+        free(all_addr);
+    }
+    return ENXIO;
+}
+
 static int
 netdev_windows_internal_construct(struct netdev *netdev_)
 {
@@ -390,6 +513,8 @@ netdev_windows_internal_construct(struct netdev *netdev_)
     .get_etheraddr      = netdev_windows_get_etheraddr,                 \
     .set_etheraddr      = netdev_windows_set_etheraddr,                 \
     .update_flags       = netdev_windows_update_flags,                  \
+    .get_next_hop       = netdev_windows_get_next_hop,                  \
+    .arp_lookup         = netdev_windows_arp_lookup,                    \
 }
 
 const struct netdev_class netdev_windows_class =
-- 
1.9.5.msysgit.0



More information about the dev mailing list