kernel-netlink: Make interface ID configurable on SAs and policies
authorTobias Brunner <tobias@strongswan.org>
Fri, 1 Feb 2019 16:29:20 +0000 (17:29 +0100)
committerTobias Brunner <tobias@strongswan.org>
Wed, 3 Apr 2019 10:00:08 +0000 (12:00 +0200)
src/libcharon/kernel/kernel_ipsec.h
src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c

index 4158eb4..70ff2eb 100644 (file)
@@ -55,6 +55,8 @@ struct kernel_ipsec_sa_id_t {
        uint8_t proto;
        /** Optional mark */
        mark_t mark;
+       /** Optional interface ID */
+       uint32_t if_id;
 };
 
 /**
@@ -154,6 +156,8 @@ struct kernel_ipsec_policy_id_t {
        traffic_selector_t *dst_ts;
        /** Optional mark */
        mark_t mark;
+       /** Optional interface ID */
+       uint32_t if_id;
        /** Network interface restricting policy */
        char *interface;
 };
index 2819cbe..205e772 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2018 Tobias Brunner
+ * Copyright (C) 2006-2019 Tobias Brunner
  * Copyright (C) 2005-2009 Martin Willi
  * Copyright (C) 2008-2016 Andreas Steffen
  * Copyright (C) 2006-2007 Fabian Hartmann, Noah Heusser
@@ -430,6 +430,9 @@ struct ipsec_sa_t {
        /** Optional mark */
        mark_t mark;
 
+       /** Optional mark */
+       uint32_t if_id;
+
        /** Description of this SA */
        ipsec_sa_cfg_t cfg;
 
@@ -445,7 +448,8 @@ static u_int ipsec_sa_hash(ipsec_sa_t *sa)
        return chunk_hash_inc(sa->src->get_address(sa->src),
                                                  chunk_hash_inc(sa->dst->get_address(sa->dst),
                                                  chunk_hash_inc(chunk_from_thing(sa->mark),
-                                                 chunk_hash(chunk_from_thing(sa->cfg)))));
+                                                 chunk_hash_inc(chunk_from_thing(sa->if_id),
+                                                 chunk_hash(chunk_from_thing(sa->cfg))))));
 }
 
 /**
@@ -457,6 +461,7 @@ static bool ipsec_sa_equals(ipsec_sa_t *sa, ipsec_sa_t *other_sa)
                   sa->dst->ip_equals(sa->dst, other_sa->dst) &&
                   sa->mark.value == other_sa->mark.value &&
                   sa->mark.mask == other_sa->mark.mask &&
+                  sa->if_id == other_sa->if_id &&
                   ipsec_sa_cfg_equals(&sa->cfg, &other_sa->cfg);
 }
 
@@ -465,13 +470,14 @@ static bool ipsec_sa_equals(ipsec_sa_t *sa, ipsec_sa_t *other_sa)
  */
 static ipsec_sa_t *ipsec_sa_create(private_kernel_netlink_ipsec_t *this,
                                                                   host_t *src, host_t *dst, mark_t mark,
-                                                                  ipsec_sa_cfg_t *cfg)
+                                                                  uint32_t if_id, ipsec_sa_cfg_t *cfg)
 {
        ipsec_sa_t *sa, *found;
        INIT(sa,
                .src = src,
                .dst = dst,
                .mark = mark,
+               .if_id = if_id,
                .cfg = *cfg,
        );
        found = this->sas->get(this->sas, sa);
@@ -546,7 +552,7 @@ struct policy_sa_out_t {
 static policy_sa_t *policy_sa_create(private_kernel_netlink_ipsec_t *this,
        policy_dir_t dir, policy_type_t type, host_t *src, host_t *dst,
        traffic_selector_t *src_ts, traffic_selector_t *dst_ts, mark_t mark,
-       ipsec_sa_cfg_t *cfg)
+       uint32_t if_id, ipsec_sa_cfg_t *cfg)
 {
        policy_sa_t *policy;
 
@@ -564,7 +570,7 @@ static policy_sa_t *policy_sa_create(private_kernel_netlink_ipsec_t *this,
                INIT(policy, .priority = 0);
        }
        policy->type = type;
-       policy->sa = ipsec_sa_create(this, src, dst, mark, cfg);
+       policy->sa = ipsec_sa_create(this, src, dst, mark, if_id, cfg);
        return policy;
 }
 
@@ -610,6 +616,9 @@ struct policy_entry_t {
        /** Optional mark */
        uint32_t mark;
 
+       /** Optional interface ID */
+       uint32_t if_id;
+
        /** Associated route installed for this policy */
        route_entry_t *route;
 
@@ -651,7 +660,8 @@ static void policy_entry_destroy(private_kernel_netlink_ipsec_t *this,
 static u_int policy_hash(policy_entry_t *key)
 {
        chunk_t chunk = chunk_from_thing(key->sel);
-       return chunk_hash_inc(chunk, chunk_hash(chunk_from_thing(key->mark)));
+       return chunk_hash_inc(chunk, chunk_hash_inc(chunk_from_thing(key->mark),
+                                                 chunk_hash(chunk_from_thing(key->if_id))));
 }
 
 /**
@@ -661,6 +671,7 @@ static bool policy_equals(policy_entry_t *key, policy_entry_t *other_key)
 {
        return memeq(&key->sel, &other_key->sel, sizeof(struct xfrm_selector)) &&
                   key->mark == other_key->mark &&
+                  key->if_id == other_key->if_id &&
                   key->direction == other_key->direction;
 }
 
@@ -1577,6 +1588,7 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
                        .spi = htonl(ntohs(data->cpi)),
                        .proto = IPPROTO_COMP,
                        .mark = id->mark,
+                       .if_id = id->if_id,
                };
                kernel_ipsec_add_sa_t ipcomp_sa = {
                        .reqid = data->reqid,
@@ -1902,6 +1914,11 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
                goto failed;
        }
 
+       if (id->if_id && !add_uint32(hdr, sizeof(request), XFRMA_IF_ID, id->if_id))
+       {
+               goto failed;
+       }
+
        if (ipcomp == IPCOMP_NONE && (data->mark.value | data->mark.mask))
        {
                if (!add_uint32(hdr, sizeof(request), XFRMA_SET_MARK,
@@ -2034,6 +2051,10 @@ static void get_replay_state(private_kernel_netlink_ipsec_t *this,
        {
                return;
        }
+       if (sa->if_id && !add_uint32(hdr, sizeof(request), XFRMA_IF_ID, sa->if_id))
+       {
+               return;
+       }
 
        if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS)
        {
@@ -2132,6 +2153,10 @@ METHOD(kernel_ipsec_t, query_sa, status_t,
        {
                return FAILED;
        }
+       if (id->if_id && !add_uint32(hdr, sizeof(request), XFRMA_IF_ID, id->if_id))
+       {
+               return FAILED;
+       }
 
        if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS)
        {
@@ -2236,6 +2261,10 @@ METHOD(kernel_ipsec_t, del_sa, status_t,
        {
                return FAILED;
        }
+       if (id->if_id && !add_uint32(hdr, sizeof(request), XFRMA_IF_ID, id->if_id))
+       {
+               return FAILED;
+       }
 
        switch (this->socket_xfrm->send_ack(this->socket_xfrm, hdr))
        {
@@ -2282,6 +2311,7 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
                        .spi = htonl(ntohs(data->cpi)),
                        .proto = IPPROTO_COMP,
                        .mark = id->mark,
+                       .if_id = id->if_id,
                };
                kernel_ipsec_update_sa_t ipcomp = {
                        .new_src = data->new_src,
@@ -2312,6 +2342,10 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
        {
                return FAILED;
        }
+       if (id->if_id && !add_uint32(hdr, sizeof(request), XFRMA_IF_ID, id->if_id))
+       {
+               return FAILED;
+       }
 
        if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS)
        {
@@ -2786,6 +2820,12 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this,
                policy_change_done(this, policy);
                return FAILED;
        }
+       if (ipsec->if_id &&
+               !add_uint32(hdr, sizeof(request), XFRMA_IF_ID, ipsec->if_id))
+       {
+               policy_change_done(this, policy);
+               return FAILED;
+       }
        this->mutex->unlock(this->mutex);
 
        status = this->socket_xfrm->send_ack(this->socket_xfrm, hdr);
@@ -2837,6 +2877,7 @@ METHOD(kernel_ipsec_t, add_policy, status_t,
        INIT(policy,
                .sel = ts2selector(id->src_ts, id->dst_ts, id->interface),
                .mark = id->mark.value & id->mark.mask,
+               .if_id = id->if_id,
                .direction = id->dir,
                .reqid = data->sa->reqid,
        );
@@ -2882,7 +2923,8 @@ METHOD(kernel_ipsec_t, add_policy, status_t,
 
        /* cache the assigned IPsec SA */
        assigned_sa = policy_sa_create(this, id->dir, data->type, data->src,
-                                               data->dst, id->src_ts, id->dst_ts, id->mark, data->sa);
+                                                                  data->dst, id->src_ts, id->dst_ts, id->mark,
+                                                                  id->if_id, data->sa);
        assigned_sa->auto_priority = get_priority(policy, data->prio, id->interface);
        assigned_sa->priority = this->get_priority ? this->get_priority(id, data)
                                                                                           : data->manual_prio;
@@ -2980,6 +3022,10 @@ METHOD(kernel_ipsec_t, query_policy, status_t,
        {
                return FAILED;
        }
+       if (id->if_id && !add_uint32(hdr, sizeof(request), XFRMA_IF_ID, id->if_id))
+       {
+               return FAILED;
+       }
 
        if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS)
        {
@@ -3048,6 +3094,7 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
                .src = data->src,
                .dst = data->dst,
                .mark = id->mark,
+               .if_id = id->if_id,
                .cfg = *data->sa,
        };
        char markstr[32] = "";
@@ -3063,6 +3110,7 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
        memset(&policy, 0, sizeof(policy_entry_t));
        policy.sel = ts2selector(id->src_ts, id->dst_ts, id->interface);
        policy.mark = id->mark.value & id->mark.mask;
+       policy.if_id = id->if_id;
        policy.direction = id->dir;
 
        /* find the policy */
@@ -3153,6 +3201,11 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
                policy_change_done(this, current);
                return FAILED;
        }
+       if (id->if_id && !add_uint32(hdr, sizeof(request), XFRMA_IF_ID, id->if_id))
+       {
+               policy_change_done(this, current);
+               return FAILED;
+       }
 
        if (current->route)
        {