From 4f0e3348174dce4fe4bc51c9b4b4fc4fa7430409 Mon Sep 17 00:00:00 2001
From: Jondy Zhao <jondy.zhao@gmail.com>
Date: Thu, 28 Mar 2013 10:15:26 +0800
Subject: [PATCH] Use other interface instead loopback interface in the
 blackhole route entry.

---
 component/babeld/README.cygwin   |  21 +++++-
 component/babeld/cyginet.c       | 107 ++++++++++++++++++++++++++++++-
 component/babeld/cyginet.h       |   2 +
 component/babeld/kernel_cygwin.c |  30 +++++----
 4 files changed, 143 insertions(+), 17 deletions(-)

diff --git a/component/babeld/README.cygwin b/component/babeld/README.cygwin
index c6cc1283f..b4e101e6e 100644
--- a/component/babeld/README.cygwin
+++ b/component/babeld/README.cygwin
@@ -68,4 +68,23 @@ Notes
    "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters",
    but says nothing for Ipv6. Does it work for ipv6 in the registry:
    "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters"
-   ?
\ No newline at end of file
+   ?
+
+8. There is no RTM_BLACKHOLE, RTM_REJECT, NULLROUTE in the windows, in
+   the kernel_cygwin.c!kernel_route, we only use local address as
+   gateway in the BLACKHOLE route entry. Is it OK?
+
+   A: It's not OK actually. In the Windows, you can't use 127.0.0.1 as
+      gateway when add route entry. So the solution is find one
+      interface other than loopback interface, then use its
+      uni-address as gateway, it will be a blackhole route entry. For
+      example, if you have an interface which ip addr is
+      192.168.128.100, and ifindex is 5, then
+
+      $ route add 100.28.0.0 mask 255.255.0.0 192.168.128.100 if 5
+
+      Destination network 100.28.0.0/16 will be unreachable.
+      
+
+9. IN6_LINKLOCAL_IFINDEX && SET_IN6_LINKLOCAL_IFINDEX, do both of them
+   work in the Windows?
\ No newline at end of file
diff --git a/component/babeld/cyginet.c b/component/babeld/cyginet.c
index 6a80e31da..6771ec04e 100644
--- a/component/babeld/cyginet.c
+++ b/component/babeld/cyginet.c
@@ -229,6 +229,15 @@ libwinet_refresh_interface_map_table()
   return (NO_ERROR == dwRet);
 }
 
+/* Map ifindex belong to family to index of another family,
+   Ipv4 -> Ipv6 or
+   Ipv6 -> Ipv4
+
+   Return 0, if the interface only binds one ip version.
+
+   Special case:
+       If the interface is loopback, it will always return 1. 
+   */
 static int
 libwinet_map_ifindex(int family, int ifindex)
 {
@@ -239,7 +248,8 @@ libwinet_map_ifindex(int family, int ifindex)
   DWORD dwReturn = 0;
 
   dwRet = GetAdaptersAddresses(AF_UNSPEC,
-                               GAA_FLAG_SKIP_ANYCAST            \
+                               GAA_FLAG_SKIP_UNICAST            \
+                               | GAA_FLAG_SKIP_ANYCAST          \
                                | GAA_FLAG_SKIP_MULTICAST        \
                                | GAA_FLAG_SKIP_DNS_SERVER       \
                                | GAA_FLAG_SKIP_FRIENDLY_NAME,
@@ -252,7 +262,8 @@ libwinet_map_ifindex(int family, int ifindex)
     if (NULL == (pAdaptAddr = (IP_ADAPTER_ADDRESSES*)MALLOC(dwSize)))
       return 0;
     dwRet = GetAdaptersAddresses(AF_UNSPEC,
-                                 GAA_FLAG_SKIP_ANYCAST            \
+                                 GAA_FLAG_SKIP_UNICAST            \
+                                 | GAA_FLAG_SKIP_ANYCAST          \
                                  | GAA_FLAG_SKIP_MULTICAST        \
                                  | GAA_FLAG_SKIP_DNS_SERVER       \
                                  | GAA_FLAG_SKIP_FRIENDLY_NAME,
@@ -268,7 +279,8 @@ libwinet_map_ifindex(int family, int ifindex)
     while (pTmpAdaptAddr) {
       if (family == AF_INET ? pTmpAdaptAddr -> IfIndex == ifindex
           : pTmpAdaptAddr -> Ipv6IfIndex == ifindex) {
-        dwReturn = family == AF_INET ?
+        dwReturn = (pTmpAdaptAddr -> IfType == IF_TYPE_SOFTWARE_LOOPBACK) ?
+          1 : (family == AF_INET) ?
           pTmpAdaptAddr -> Ipv6IfIndex : pTmpAdaptAddr -> IfIndex;
         break;
       }
@@ -750,6 +762,76 @@ int cyginet_stop_monitor_route_changes()
   return rc;
 }
 
+/* Find an interface which binds both of ipv4 and ipv6, and configured
+   with at least one unicast address.
+
+   Return ipv6 ifindex of this interface, set addr6 and addr as the
+   ipv4 and ipv6 address respectively.
+
+   Return 0 if there is no matched interface found.
+   */
+int
+cyginet_blackhole_index(struct in6_addr* addr6, char * addr)
+{
+  IP_ADAPTER_ADDRESSES *pAdaptAddr = NULL;
+  IP_ADAPTER_ADDRESSES *pTmpAdaptAddr = NULL;
+  DWORD dwRet = 0;
+  DWORD dwSize = 0x10000;
+  DWORD dwReturn = 0;
+
+  dwRet = GetAdaptersAddresses(AF_UNSPEC,
+                               GAA_FLAG_SKIP_ANYCAST            \
+                               | GAA_FLAG_SKIP_MULTICAST        \
+                               | GAA_FLAG_SKIP_DNS_SERVER       \
+                               | GAA_FLAG_SKIP_FRIENDLY_NAME,
+                               NULL,
+                               pAdaptAddr,
+                               &dwSize
+                               );
+  if (ERROR_BUFFER_OVERFLOW == dwRet) {
+    FREE(pAdaptAddr);
+    if (NULL == (pAdaptAddr = (IP_ADAPTER_ADDRESSES*)MALLOC(dwSize)))
+      return 0;
+    dwRet = GetAdaptersAddresses(AF_UNSPEC,
+                                 GAA_FLAG_SKIP_ANYCAST            \
+                                 | GAA_FLAG_SKIP_MULTICAST        \
+                                 | GAA_FLAG_SKIP_DNS_SERVER       \
+                                 | GAA_FLAG_SKIP_FRIENDLY_NAME,
+                                 NULL,
+                                 pAdaptAddr,
+                                 &dwSize
+                                 );
+  }
+
+  if (NO_ERROR == dwRet) {
+    pTmpAdaptAddr = pAdaptAddr;
+    while (pTmpAdaptAddr) {
+      if (pTmpAdaptAddr -> IfIndex
+          && pTmpAdaptAddr -> Ipv6IfIndex
+          && (pTmpAdaptAddr -> OperStatus == IfOperStatusUp)
+          && (pTmpAdaptAddr -> IfType != IF_TYPE_SOFTWARE_LOOPBACK)) {
+        
+        PIP_ADAPTER_UNICAST_ADDRESS p = pTmpAdaptAddr -> FirstUnicastAddress;
+        while (p) {
+          SOCKADDR *s;
+          s =  (p -> Address).lpSockaddr;
+          if (s -> sa_family == AF_INET)
+            memcpy(addr, &(((struct sockaddr_in *)s) -> sin_addr), 4);
+          else if (s -> sa_family == AF_INET6)
+            memcpy(addr6, &(((struct sockaddr_in6 *)s) -> sin6_addr), 16);
+          p = p -> Next;
+        }
+        dwReturn = pTmpAdaptAddr -> Ipv6IfIndex;
+        break;
+      }
+      pTmpAdaptAddr = pTmpAdaptAddr->Next;
+    }
+    FREE(pAdaptAddr);
+  }
+
+  return dwReturn;
+}
+
 /*
  * There are 3 ways to change a route:
  *
@@ -759,6 +841,16 @@ int cyginet_stop_monitor_route_changes()
  *                DeleteIpForwardEntry
  *                SetIpForwardEntry
  *
+ *    Or route command
+ *    
+ *    Or netsh routing add persistentroute
+ *    
+ *    Or netsh routing add rtmroute
+ *
+ *    it need "Routing and Remote Access Service" running on the local
+ *    machine. Use 'net start remoteaccess' on the local machine to
+ *    start the service.
+ *    
  * 2. IPv6 route: command "netsh"
  *
  *    C:/> netsh interface ipv6 add route
@@ -2366,6 +2458,15 @@ runTestCases()
     }
   }
 
+  printf("\n\nTest cyginet_blackhole_index:\n\n");
+  {
+    struct in6_addr addr6;
+    char addr[4];
+    printf("The blackhole ifindex is %d\n",
+           cyginet_blackhole_index(&addr6, addr)
+           );
+  }
+
 #if _WIN32_WINNT < _WIN32_WINNT_VISTA
 
   printf("\n\nTest libwinet_dump_ipv6_route_table:\n\n");
diff --git a/component/babeld/cyginet.h b/component/babeld/cyginet.h
index 6b844e5fd..49051f338 100755
--- a/component/babeld/cyginet.h
+++ b/component/babeld/cyginet.h
@@ -176,4 +176,6 @@ char * cyginet_ifname(const char *);
 char * cyginet_guidname(const char *);
 char * cyginet_ipv4_index2ifname(int);
 
+int cyginet_blackhole_index(struct in6_addr*, char *);
+
 #endif  /* __CYGIFNET_H__ */
diff --git a/component/babeld/kernel_cygwin.c b/component/babeld/kernel_cygwin.c
index aa19e305d..fecdc50da 100644
--- a/component/babeld/kernel_cygwin.c
+++ b/component/babeld/kernel_cygwin.c
@@ -69,6 +69,12 @@ static int get_sdl(struct sockaddr_dl *sdl, char *guidname);
 static const unsigned char v4prefix[16] =
     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0 };
 
+static int ifindex_blackhole = -1;
+static struct in6_addr blackhole_addr6 = {{IN6ADDR_LOOPBACK_INIT}};
+static char blackhole_addr[1][1][16] = 
+        {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x01 }}};
+
 int export_table = -1, import_table = -1;
 
 int
@@ -355,11 +361,6 @@ kernel_route(int operation, const unsigned char *dest, unsigned short plen,
     int route_ifindex;
     int prefix_len;
 
-    struct in6_addr local6 = {{IN6ADDR_LOOPBACK_INIT}};
-    char local4[1][1][16] =
-        {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-            0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x01 }}};
-
     /* Check that the protocol family is consistent. */
     if(plen >= 96 && v4mapped(dest)) {
         if(!v4mapped(gate)) {
@@ -413,11 +414,14 @@ kernel_route(int operation, const unsigned char *dest, unsigned short plen,
     if(metric == KERNEL_INFINITY) {
         /* RTF_BLACKHOLE; */
         /* ==> Set gateway to an unused ip address in the Windows */
-        if (ifindex_lo < 0) {
-            ifindex_lo = cyginet_loopback_index(AF_UNSPEC);
-            if(ifindex_lo <= 0)
+        if (ifindex_blackhole < 0) {
+            ifindex_blackhole = cyginet_blackhole_index(&blackhole_addr6,
+                                                        blackhole_addr[0][0]+12
+                                                        );
+            if(ifindex_blackhole <= 0)
                 return -1;
         }
+        route_ifindex = ifindex_blackhole;
     }
 
 #define PUSHADDR(dst, src)                                              \
@@ -438,20 +442,20 @@ kernel_route(int operation, const unsigned char *dest, unsigned short plen,
 
         PUSHADDR(destination, dest);
         if (metric == KERNEL_INFINITY)
-            PUSHADDR(gateway, **local4);
+            PUSHADDR(gateway, **blackhole_addr);
         else
             PUSHADDR(gateway, gate);
 
     } else {
         PUSHADDR6(destination, dest);
         if (metric == KERNEL_INFINITY)
-            PUSHADDR6(gateway, &local6);
+            PUSHADDR6(gateway, &blackhole_addr6);
         else
             PUSHADDR6(gateway, gate);
     }
 #undef PUSHADDR
 #undef PUSHADDR6
-
+    /* What if route_ifindex == 0 */
     switch(operation) {
     case ROUTE_FLUSH:
         rc = cyginet_delete_route_entry(&destination,
@@ -484,8 +488,8 @@ kernel_route(int operation, const unsigned char *dest, unsigned short plen,
     /* Monitor thread will write data to kernel pipe when any change
        in the route table is happened. Here it's babeld itself to
        change the route table, so kernel pipe need to be clean. */
-    int ch;
-    while (read(kernel_pipe_handles[0], &ch, 1) > 0);
+    /* int ch; */
+    /* while (read(kernel_pipe_handles[0], &ch, 1) > 0); */
     return rc;
 }
 
-- 
2.30.9