ikev1: Send and verify IPv6 addresses correctly
[strongswan.git] / src / libhydra / plugins / kernel_netlink / kernel_netlink_net.c
index b8cd397..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;
@@ -1538,6 +1548,7 @@ typedef struct {
        u_int8_t dst_len;
        u_int32_t table;
        u_int32_t oif;
+       u_int32_t priority;
 } rt_entry_t;
 
 /**
@@ -1550,6 +1561,26 @@ static void rt_entry_destroy(rt_entry_t *this)
 }
 
 /**
+ * Check if the route received with RTM_NEWROUTE is usable based on its type.
+ */
+static bool route_usable(struct nlmsghdr *hdr)
+{
+       struct rtmsg *msg;
+
+       msg = NLMSG_DATA(hdr);
+       switch (msg->rtm_type)
+       {
+               case RTN_BLACKHOLE:
+               case RTN_UNREACHABLE:
+               case RTN_PROHIBIT:
+               case RTN_THROW:
+                       return FALSE;
+               default:
+                       return TRUE;
+       }
+}
+
+/**
  * Parse route received with RTM_NEWROUTE. The given rt_entry_t object will be
  * reused if not NULL.
  *
@@ -1573,6 +1604,7 @@ static rt_entry_t *parse_route(struct nlmsghdr *hdr, rt_entry_t *route)
                route->dst_len = msg->rtm_dst_len;
                route->table = msg->rtm_table;
                route->oif = 0;
+               route->priority = 0;
        }
        else
        {
@@ -1601,6 +1633,12 @@ static rt_entry_t *parse_route(struct nlmsghdr *hdr, rt_entry_t *route)
                                        route->oif = *(u_int32_t*)RTA_DATA(rta);
                                }
                                break;
+                       case RTA_PRIORITY:
+                               if (RTA_PAYLOAD(rta) == sizeof(route->priority))
+                               {
+                                       route->priority = *(u_int32_t*)RTA_DATA(rta);
+                               }
+                               break;
 #ifdef HAVE_RTA_TABLE
                        case RTA_TABLE:
                                if (RTA_PAYLOAD(rta) == sizeof(route->table))
@@ -1648,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);
@@ -1692,6 +1737,10 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest,
                                rt_entry_t *other;
                                uintptr_t table;
 
+                               if (!route_usable(current))
+                               {
+                                       continue;
+                               }
                                route = parse_route(current, route);
 
                                table = (uintptr_t)route->table;
@@ -1724,11 +1773,16 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest,
                                        }
                                        route->src_host = src;
                                }
-                               /* insert route, sorted by decreasing network prefix */
+                               /* insert route, sorted by priority and network prefix */
                                enumerator = routes->create_enumerator(routes);
                                while (enumerator->enumerate(enumerator, &other))
                                {
-                                       if (route->dst_len > other->dst_len)
+                                       if (route->priority < other->priority)
+                                       {
+                                               break;
+                                       }
+                                       if (route->priority == other->priority &&
+                                               route->dst_len > other->dst_len)
                                        {
                                                break;
                                        }
@@ -2375,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");
@@ -2398,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;
@@ -2406,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;