]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
kernel-pfkey: Added IPComp support
authorFrancois ten Krooden <ftk@nanoteq.com>
Tue, 22 Apr 2014 13:34:41 +0000 (15:34 +0200)
committerTobias Brunner <tobias@strongswan.org>
Thu, 24 Apr 2014 15:36:17 +0000 (17:36 +0200)
- get_cpi function was implemented to retrieve a CPI from the kernel.
- add_sa/update_sa/del_sa were updated to accommodate for IPComp SA.
- Updated add_policy_internal to update the SPD to support IPComp.

src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c

index 4704d419f3aecc99495ada732bc7116c44fbb173..d82d6720dfce05a617ecf01d33d887a32a536ca3 100644 (file)
  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  * for more details.
  */
+/*
+ * Copyright (C) 2014 Nanoteq Pty Ltd
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
 
 #include <sys/types.h>
 #include <sys/socket.h>
@@ -825,18 +846,20 @@ static kernel_algorithm_t integrity_algs[] = {
        {END_OF_LIST,                           0,                                                      },
 };
 
-#if 0
 /**
  * Algorithms for IPComp, unused yet
  */
 static kernel_algorithm_t compression_algs[] = {
 /*     {IPCOMP_OUI,                            0                                                       }, */
        {IPCOMP_DEFLATE,                        SADB_X_CALG_DEFLATE                     },
+#ifdef SADB_X_CALG_LZS
        {IPCOMP_LZS,                            SADB_X_CALG_LZS                         },
+#endif
+#ifdef SADB_X_CALG_LZJH
        {IPCOMP_LZJH,                           SADB_X_CALG_LZJH                        },
+#endif
        {END_OF_LIST,                           0                                                       },
 };
-#endif
 
 /**
  * Look up a kernel algorithm ID and its key size
@@ -854,6 +877,9 @@ static int lookup_algorithm(transform_type_t type, int ikev2)
                case INTEGRITY_ALGORITHM:
                        list = integrity_algs;
                        break;
+               case COMPRESSION_ALGORITHM:
+                       list = compression_algs;
+                       break;
                default:
                        return 0;
        }
@@ -1483,9 +1509,13 @@ static bool receive_events(private_kernel_pfkey_ipsec_t *this, int fd,
        return TRUE;
 }
 
-METHOD(kernel_ipsec_t, get_spi, status_t,
-       private_kernel_pfkey_ipsec_t *this, host_t *src, host_t *dst,
-       u_int8_t protocol, u_int32_t reqid, u_int32_t *spi)
+/**
+ * Get an SPI for a specific protocol from the kernel.
+ */
+
+static status_t get_spi_internal(private_kernel_pfkey_ipsec_t *this,
+       host_t *src, host_t *dst, u_int8_t proto, u_int32_t min, u_int32_t max,
+       u_int32_t reqid, u_int32_t *spi)
 {
        unsigned char request[PFKEY_BUFFER_SIZE];
        struct sadb_msg *msg, *out;
@@ -1500,7 +1530,7 @@ METHOD(kernel_ipsec_t, get_spi, status_t,
        msg = (struct sadb_msg*)request;
        msg->sadb_msg_version = PF_KEY_V2;
        msg->sadb_msg_type = SADB_GETSPI;
-       msg->sadb_msg_satype = proto2satype(protocol);
+       msg->sadb_msg_satype = proto2satype(proto);
        msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg));
 
        sa2 = (struct sadb_x_sa2*)PFKEY_EXT_ADD_NEXT(msg);
@@ -1515,8 +1545,8 @@ METHOD(kernel_ipsec_t, get_spi, status_t,
        range = (struct sadb_spirange*)PFKEY_EXT_ADD_NEXT(msg);
        range->sadb_spirange_exttype = SADB_EXT_SPIRANGE;
        range->sadb_spirange_len = PFKEY_LEN(sizeof(struct sadb_spirange));
-       range->sadb_spirange_min = 0xc0000000;
-       range->sadb_spirange_max = 0xcFFFFFFF;
+       range->sadb_spirange_min = min;
+       range->sadb_spirange_max = max;
        PFKEY_EXT_ADD(msg, range);
 
        if (pfkey_send(this, msg, &out, &len) == SUCCESS)
@@ -1542,11 +1572,42 @@ METHOD(kernel_ipsec_t, get_spi, status_t,
        return SUCCESS;
 }
 
+METHOD(kernel_ipsec_t, get_spi, status_t,
+       private_kernel_pfkey_ipsec_t *this, host_t *src, host_t *dst,
+       u_int8_t protocol, u_int32_t reqid, u_int32_t *spi)
+{
+       DBG2(DBG_KNL, "getting SPI for reqid {%u}", reqid);
+
+       if (get_spi_internal(this, src, dst, protocol,
+                                                0xc0000000, 0xcFFFFFFF, reqid, spi) != SUCCESS)
+       {
+               DBG1(DBG_KNL, "unable to get SPI for reqid {%u}", reqid);
+               return FAILED;
+       }
+
+       DBG2(DBG_KNL, "got SPI %.8x for reqid {%u}", ntohl(*spi), reqid);
+       return SUCCESS;
+}
+
 METHOD(kernel_ipsec_t, get_cpi, status_t,
        private_kernel_pfkey_ipsec_t *this, host_t *src, host_t *dst,
        u_int32_t reqid, u_int16_t *cpi)
 {
-       return FAILED;
+       u_int32_t received_spi = 0;
+
+       DBG2(DBG_KNL, "getting CPI for reqid {%u}", reqid);
+
+       if (get_spi_internal(this, src, dst, IPPROTO_COMP,
+                                                0x100, 0xEFFF, reqid, &received_spi) != SUCCESS)
+       {
+               DBG1(DBG_KNL, "unable to get CPI for reqid {%u}", reqid);
+               return FAILED;
+       }
+
+       *cpi = htons((u_int16_t)ntohl(received_spi));
+
+       DBG2(DBG_KNL, "got CPI %.4x for reqid {%u}", ntohs(*cpi), reqid);
+       return SUCCESS;
 }
 
 METHOD(kernel_ipsec_t, add_sa, status_t,
@@ -1565,6 +1626,20 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
        struct sadb_key *key;
        size_t len;
 
+       /* if IPComp is used, we install an additional IPComp SA. if the cpi is 0
+        * we are in the recursive call below */
+       if (ipcomp != IPCOMP_NONE && cpi != 0)
+       {
+               lifetime_cfg_t lft = {{0,0,0},{0,0,0},{0,0,0}};
+               add_sa(this, src, dst, htonl(ntohs(cpi)), IPPROTO_COMP, reqid, mark,
+                          tfc, &lft, ENCR_UNDEFINED, chunk_empty, AUTH_UNDEFINED,
+                          chunk_empty, mode, ipcomp, 0, FALSE, FALSE, FALSE, inbound,
+                          NULL, NULL);
+               ipcomp = IPCOMP_NONE;
+               /* use transport mode ESP SA, IPComp uses tunnel mode */
+               mode = MODE_TRANSPORT;
+       }
+
        memset(&request, 0, sizeof(request));
 
        DBG2(DBG_KNL, "adding SAD entry with SPI %.8x and reqid {%u}",
@@ -1595,9 +1670,16 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
        sa->sadb_sa_exttype = SADB_EXT_SA;
        sa->sadb_sa_len = PFKEY_LEN(len);
        sa->sadb_sa_spi = spi;
-       sa->sadb_sa_replay = (protocol == IPPROTO_COMP) ? 0 : 32;
-       sa->sadb_sa_auth = lookup_algorithm(INTEGRITY_ALGORITHM, int_alg);
-       sa->sadb_sa_encrypt = lookup_algorithm(ENCRYPTION_ALGORITHM, enc_alg);
+       if (protocol == IPPROTO_COMP)
+       {
+               sa->sadb_sa_encrypt = lookup_algorithm(COMPRESSION_ALGORITHM, ipcomp);
+       }
+       else
+       {
+               sa->sadb_sa_replay = 32;
+               sa->sadb_sa_auth = lookup_algorithm(INTEGRITY_ALGORITHM, int_alg);
+               sa->sadb_sa_encrypt = lookup_algorithm(ENCRYPTION_ALGORITHM, enc_alg);
+       }
        PFKEY_EXT_ADD(msg, sa);
 
        sa2 = (struct sadb_x_sa2*)PFKEY_EXT_ADD_NEXT(msg);
@@ -1668,11 +1750,6 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
                PFKEY_EXT_ADD(msg, key);
        }
 
-       if (ipcomp != IPCOMP_NONE)
-       {
-               /*TODO*/
-       }
-
 #ifdef HAVE_NATT
        if (encap)
        {
@@ -1719,6 +1796,13 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
                return NOT_SUPPORTED;
        }
 
+       /* if IPComp is used, we first update the IPComp SA */
+       if (cpi)
+       {
+               update_sa(this, htonl(ntohs(cpi)), IPPROTO_COMP, 0,
+                                 src, dst, new_src, new_dst, FALSE, FALSE, mark);
+       }
+
        memset(&request, 0, sizeof(request));
 
        DBG2(DBG_KNL, "querying SAD entry with SPI %.8x", ntohl(spi));
@@ -1919,6 +2003,12 @@ METHOD(kernel_ipsec_t, del_sa, status_t,
        struct sadb_sa *sa;
        size_t len;
 
+       /* if IPComp was used, we first delete the additional IPComp SA */
+       if (cpi)
+       {
+               del_sa(this, src, dst, htonl(ntohs(cpi)), IPPROTO_COMP, 0, mark);
+       }
+
        memset(&request, 0, sizeof(request));
 
        DBG2(DBG_KNL, "deleting SAD entry with SPI %.8x", ntohl(spi));
@@ -2230,6 +2320,7 @@ static status_t add_policy_internal(private_kernel_pfkey_ipsec_t *this,
        ipsec_sa_t *ipsec = mapping->sa;
        pfkey_msg_t response;
        size_t len;
+       ipsec_mode_t proto_mode;
 
        memset(&request, 0, sizeof(request));
 
@@ -2251,15 +2342,43 @@ static status_t add_policy_internal(private_kernel_pfkey_ipsec_t *this,
 
        /* one or more sadb_x_ipsecrequest extensions are added to the
         * sadb_x_policy extension */
+       proto_mode = ipsec->cfg.mode;
+
        req = (struct sadb_x_ipsecrequest*)(pol + 1);
+
+       if (ipsec->cfg.ipcomp.transform != IPCOMP_NONE)
+       {
+               req->sadb_x_ipsecrequest_proto = IPPROTO_COMP;
+
+               /* !!! the length here MUST be in octets instead of 64 bit words */
+               req->sadb_x_ipsecrequest_len = sizeof(struct sadb_x_ipsecrequest);
+               req->sadb_x_ipsecrequest_mode = mode2kernel(ipsec->cfg.mode);
+               req->sadb_x_ipsecrequest_reqid = ipsec->cfg.reqid;
+               req->sadb_x_ipsecrequest_level = (policy->direction == POLICY_OUT) ?
+                                                                                 IPSEC_LEVEL_UNIQUE : IPSEC_LEVEL_USE;
+               if (ipsec->cfg.mode == MODE_TUNNEL)
+               {
+                       len = hostcpy(req + 1, ipsec->src, FALSE);
+                       req->sadb_x_ipsecrequest_len += len;
+                       len = hostcpy((char*)(req + 1) + len, ipsec->dst, FALSE);
+                       req->sadb_x_ipsecrequest_len += len;
+                       /* use transport mode for other SAs */
+                       proto_mode = MODE_TRANSPORT;
+               }
+
+               pol->sadb_x_policy_len += PFKEY_LEN(req->sadb_x_ipsecrequest_len);
+               req = (struct sadb_x_ipsecrequest*)((char*)(req) +
+                                                                                       req->sadb_x_ipsecrequest_len);
+       }
+
        req->sadb_x_ipsecrequest_proto = ipsec->cfg.esp.use ? IPPROTO_ESP
                                                                                                                : IPPROTO_AH;
        /* !!! the length here MUST be in octets instead of 64 bit words */
        req->sadb_x_ipsecrequest_len = sizeof(struct sadb_x_ipsecrequest);
-       req->sadb_x_ipsecrequest_mode = mode2kernel(ipsec->cfg.mode);
+       req->sadb_x_ipsecrequest_mode = mode2kernel(proto_mode);
        req->sadb_x_ipsecrequest_reqid = ipsec->cfg.reqid;
        req->sadb_x_ipsecrequest_level = IPSEC_LEVEL_UNIQUE;
-       if (ipsec->cfg.mode == MODE_TUNNEL)
+       if (proto_mode == MODE_TUNNEL)
        {
                len = hostcpy(req + 1, ipsec->src, FALSE);
                req->sadb_x_ipsecrequest_len += len;