Fixed IPv6 source address lookup
authorTobias Brunner <tobias@strongswan.org>
Mon, 18 Jun 2012 10:01:10 +0000 (12:01 +0200)
committerTobias Brunner <tobias@strongswan.org>
Mon, 25 Jun 2012 14:29:59 +0000 (16:29 +0200)
Because Linux kernels prior to 3.0 do not support RTA_PREFSRC for
IPv6 routes we didn't use NLM_F_DUMP to get all routes.
Still routes installed with policies are installed also for IPv6.
So since only one route is returned without DUMP, and we ignore
all routes from our own routing table, no source address was found
during roaming if DST of the installed route included the IKE peer.

With newer kernels we can now use DUMP as we did for IPv4 already,
for older kernels we do so if our own routes are installed in a
separate routing table, otherwise we still use GET.

src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c

index e23cf44..2f2167a 100644 (file)
@@ -38,6 +38,7 @@
  */
 
 #include <sys/socket.h>
+#include <sys/utsname.h>
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
 #include <unistd.h>
@@ -313,6 +314,11 @@ struct private_kernel_netlink_net_t {
        bool install_virtual_ip;
 
        /**
+        * whether preferred source addresses can be specified for IPv6 routes
+        */
+       bool rta_prefsrc_for_ipv6;
+
+       /**
         * list with routing tables to be excluded from route lookup
         */
        linked_list_t *rt_exclude;
@@ -1130,11 +1136,11 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest,
 
        hdr = (struct nlmsghdr*)request;
        hdr->nlmsg_flags = NLM_F_REQUEST;
-       if (dest->get_family(dest) == AF_INET)
-       {
-               /* We dump all addresses for IPv4, as we want to ignore IPsec specific
-                * routes installed by us. But the kernel does not return source
-                * addresses in a IPv6 dump, so fall back to get() for v6 routes. */
+       if (dest->get_family(dest) == AF_INET || this->rta_prefsrc_for_ipv6 ||
+               this->routing_table)
+       {       /* kernels prior to 3.0 do not support RTA_PREFSRC for IPv6 routes.
+                * as we want to ignore routes with virtual IPs we cannot use DUMP
+                * if these routes are not installed in a separate table */
                hdr->nlmsg_flags |= NLM_F_DUMP;
        }
        hdr->nlmsg_type = RTM_GETROUTE;
@@ -1745,6 +1751,36 @@ static status_t manage_rule(private_kernel_netlink_net_t *this, int nlmsg_type,
        return this->socket->send_ack(this->socket, hdr);
 }
 
+/**
+ * check for kernel features (currently only via version number)
+ */
+static void check_kernel_features(private_kernel_netlink_net_t *this)
+{
+       struct utsname utsname;
+       int a, b, c;
+
+       if (uname(&utsname) == 0)
+       {
+               switch(sscanf(utsname.release, "%d.%d.%d", &a, &b, &c))
+               {
+                       case 3:
+                               if (a == 2)
+                               {
+                                       DBG2(DBG_KNL, "detected Linux %d.%d.%d, no support for "
+                                                "RTA_PREFSRC for IPv6 routes", a, b, c);
+                                       break;
+                               }
+                               /* fall-through */
+                       case 2:
+                               /* only 3.x+ uses two part version numbers */
+                               this->rta_prefsrc_for_ipv6 = TRUE;
+                               break;
+                       default:
+                               break;
+               }
+       }
+}
+
 METHOD(kernel_net_t, destroy, void,
        private_kernel_netlink_net_t *this)
 {
@@ -1835,6 +1871,8 @@ kernel_netlink_net_t *kernel_netlink_net_create()
        timerclear(&this->last_route_reinstall);
        timerclear(&this->last_roam);
 
+       check_kernel_features(this);
+
        if (streq(hydra->daemon, "starter"))
        {       /* starter has no threads, so we do not register for kernel events */
                register_for_events = FALSE;