kernel-netlink: Avoid route dump if routing rule excludes traffic with a certain...
authorTobias Brunner <tobias@strongswan.org>
Wed, 5 Aug 2015 14:51:38 +0000 (16:51 +0200)
committerTobias Brunner <tobias@strongswan.org>
Tue, 18 Aug 2015 10:06:08 +0000 (12:06 +0200)
If the routing rule we use to direct traffic to our own routing table
excludes traffic with a certain mark (fwmark = !<mark>) we can simplify
the route lookup and avoid dumping all routes by passing the mark to the
request.  That way our own routes are ignored and we get the preferred
route back without having to dump and analyze all routes, which is quite a
burden on hosts with lots of routes.

src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c

index 1515b01..4e5e02d 100644 (file)
@@ -491,6 +491,16 @@ struct private_kernel_netlink_net_t {
        bool rta_prefsrc_for_ipv6;
 
        /**
+        * whether marks can be used in route lookups
+        */
+       bool rta_mark;
+
+       /**
+        * the mark excluded from the routing rule used for virtual IPs
+        */
+       mark_t routing_mark;
+
+       /**
         * whether to prefer temporary IPv6 addresses over public ones
         */
        bool prefer_temporary_addrs;
@@ -1676,18 +1686,25 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest,
        family = dest->get_family(dest);
        hdr = &request.hdr;
        hdr->nlmsg_flags = NLM_F_REQUEST;
-       if (family == AF_INET || this->rta_prefsrc_for_ipv6 ||
-               this->routing_table || match_net)
-       {       /* 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;
        hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
 
        msg = NLMSG_DATA(hdr);
        msg->rtm_family = family;
+       if (!match_net && this->rta_mark && this->routing_mark.value)
+       {
+               /* if our routing rule excludes packets with a certain mark we can
+                * get the preferred route without having to dump all routes */
+               chunk = chunk_from_thing(this->routing_mark.value);
+               netlink_add_attribute(hdr, RTA_MARK, chunk, sizeof(request));
+       }
+       else if (family == AF_INET || this->rta_prefsrc_for_ipv6 ||
+                        this->routing_table || match_net)
+       {       /* 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;
+       }
        if (candidate)
        {
                chunk = candidate->get_address(candidate);
@@ -2412,6 +2429,10 @@ static status_t manage_rule(private_kernel_netlink_net_t *this, int nlmsg_type,
                        netlink_add_attribute(hdr, FRA_FWMARK, chunk, sizeof(request));
                        chunk = chunk_from_thing(mark.mask);
                        netlink_add_attribute(hdr, FRA_FWMASK, chunk, sizeof(request));
+                       if (msg->rtm_flags & FIB_RULE_INVERT)
+                       {
+                               this->routing_mark = mark;
+                       }
                }
 #else
                DBG1(DBG_KNL, "setting firewall mark on routing rule is not supported");
@@ -2435,6 +2456,10 @@ static void check_kernel_features(private_kernel_netlink_net_t *this)
                        case 3:
                                if (a == 2)
                                {
+                                       if (b == 6 && c >= 36)
+                                       {
+                                               this->rta_mark = TRUE;
+                                       }
                                        DBG2(DBG_KNL, "detected Linux %d.%d.%d, no support for "
                                                 "RTA_PREFSRC for IPv6 routes", a, b, c);
                                        break;
@@ -2443,6 +2468,7 @@ static void check_kernel_features(private_kernel_netlink_net_t *this)
                        case 2:
                                /* only 3.x+ uses two part version numbers */
                                this->rta_prefsrc_for_ipv6 = TRUE;
+                               this->rta_mark = TRUE;
                                break;
                        default:
                                break;