From 536028a6e2539d84edbf1ff77bdb3c147ea57211 Mon Sep 17 00:00:00 2001 From: Nguyen Ngoc Nam Date: Thu, 11 Jul 2019 10:46:03 +0700 Subject: [PATCH 1/2] To support NAT --- src/libcharon/plugins/kernel_vpp/README.md | 2 +- .../plugins/kernel_vpp/kernel_vpp_ipsec.c | 42 +++++- .../plugins/socket_vpp/socket_vpp_socket.c | 137 ++++++++++++------ 3 files changed, 129 insertions(+), 52 deletions(-) diff --git a/src/libcharon/plugins/kernel_vpp/README.md b/src/libcharon/plugins/kernel_vpp/README.md index 003c95f3072..8ff6e4d7bed 100644 --- a/src/libcharon/plugins/kernel_vpp/README.md +++ b/src/libcharon/plugins/kernel_vpp/README.md @@ -2,7 +2,7 @@ ## Overview ## The kernel-vpp plugin is an interface to the IPsec and networking backend for [**VPP**](https://wiki.fd.io/view/VPPhttps://wiki.fd.io/view/VPP) platform using the [**VPP C API**](https://wiki.fd.io/view/VPP/How_To_Use_The_C_API). It provides address and routing lookup functionality and installs routes for IPsec traffic. It installs and maintains Security Associations and Policies to the [**VPP IPsec**](https://wiki.fd.io/view/VPP/IPSec_and_IKEv2#IPSec). -The socket-vpp plugin is a replacement for socket-default for the VPP. It provides an IPv4/IPv6 IKE socket backend based on the VPP UDP punt socket. The plugin initialize VPP UDP IPv4 and IPv6 punt socket for IKE ports 500. Custom port can be specified using the charon.port strongswan.conf option. charon.plugins.socket-vpp.path configures custom path for read socket. Write socket path is configured in VPP startup configuration punt { socket }, VPP returns this path in punt_socket_register API reply. +The socket-vpp plugin is a replacement for socket-default for the VPP. It provides an IPv4/IPv6 IKE socket backend based on the VPP UDP punt socket. The plugin initialize VPP UDP IPv4 and IPv6 punt socket for IKE ports 500 and 4500 (NAT-T IKE). To have VPP punt IKE packets to strongswan, the VPP command `vppctl set punt udp 500 4500` must be executed. Custom port can be specified using the `charon.port` and `charon.port_nat_t` options in `strongswan.conf`. `charon.plugins.socket-vpp.path` configures custom path for read socket. Write socket path is configured in VPP startup configuration `punt { socket }`, VPP returns this path in punt_socket_register API reply. The read socket path and write socket path must be different, otherwise, VPP plugin cannot be loaded properly. ## How to build strongswan for VPP ## Install vpp-lib and vpp-dev packages. The plugins are disabled by default and can be enabled by adding: diff --git a/src/libcharon/plugins/kernel_vpp/kernel_vpp_ipsec.c b/src/libcharon/plugins/kernel_vpp/kernel_vpp_ipsec.c index 07f57914c43..4f267d8d1e9 100644 --- a/src/libcharon/plugins/kernel_vpp/kernel_vpp_ipsec.c +++ b/src/libcharon/plugins/kernel_vpp/kernel_vpp_ipsec.c @@ -447,19 +447,13 @@ static status_t interface_add_del_spd(bool add, uint32_t spd_id, uint32_t sw_if_ return rv; } -/** - * Add or remove a bypass policy - */ -static status_t manage_bypass(bool add, uint32_t spd_id) +static int bypass_port(bool add, uint32_t spd_id, uint16_t port) { vl_api_ipsec_spd_add_del_entry_t *mp; vl_api_ipsec_spd_add_del_entry_reply_t *rmp; char *out = NULL; int out_len; status_t rv = FAILED; - uint16_t port; - - port = lib->settings->get_int(lib->settings, "%s.port", CHARON_UDP_PORT, lib->ns); mp = vl_msg_api_alloc (sizeof (*mp)); memset(mp, 0, sizeof(*mp)); @@ -550,12 +544,46 @@ static status_t manage_bypass(bool add, uint32_t spd_id) goto error; } rv = SUCCESS; + error: free(out); vl_msg_api_free(mp); + return rv; } +/** + * Add or remove a bypass policy + */ +static status_t manage_bypass(bool add, uint32_t spd_id) +{ + uint16_t port; + status_t rv; + + port = lib->settings->get_int(lib->settings, "%s.port", IKEV2_UDP_PORT, lib->ns); + + if (port) + { + rv = bypass_port(add, spd_id, port); + if (rv != SUCCESS) + { + return rv; + } + } + + port = lib->settings->get_int(lib->settings, "%s.port_nat_t", IKEV2_NATT_PORT, lib->ns); + if (port) + { + rv = bypass_port(add, spd_id, port); + if (rv != SUCCESS) + { + return rv; + } + } + + return SUCCESS; +} + /** * Add or remove a policy */ diff --git a/src/libcharon/plugins/socket_vpp/socket_vpp_socket.c b/src/libcharon/plugins/socket_vpp/socket_vpp_socket.c index 77d0751b277..1f4e9266edd 100644 --- a/src/libcharon/plugins/socket_vpp/socket_vpp_socket.c +++ b/src/libcharon/plugins/socket_vpp/socket_vpp_socket.c @@ -55,6 +55,11 @@ struct private_socket_vpp_socket_t { */ uint16_t port; + /** + * Configured port for NAT-T + */ + uint16_t natt; + /** * maximum packet size to receive */ @@ -272,41 +277,13 @@ METHOD(socket_t, destroy, void, free(this); } -/* - * See header for description - */ -socket_vpp_socket_t *socket_vpp_socket_create() +static int register_punt_port(private_socket_vpp_socket_t *this, uint16_t port, char *read_path) { - private_socket_vpp_socket_t *this; - char *read_path, *out; + char *out; int out_len; vl_api_punt_socket_register_t *mp; vl_api_punt_socket_register_reply_t *rmp; - INIT(this, - .public = { - .socket = { - .send = _sender, - .receive = _receiver, - .get_port = _get_port, - .supported_families = _supported_families, - .destroy = _destroy, - }, - }, - .max_packet = lib->settings->get_int(lib->settings, - "%s.max_packet", PACKET_MAX_DEFAULT, lib->ns), - .port = lib->settings->get_int(lib->settings, "%s.port", - CHARON_UDP_PORT, lib->ns), - ); - - read_path = lib->settings->get_str(lib->settings, - "%s.plugins.socket-vpp.path", READ_PATH, lib->ns); - - this->vac = lib->get(lib, "kernel-vpp-vac"); - if (!this->vac) - { - DBG1(DBG_LIB, "no vac available (plugin missing?)"); - } /* Register IPv4 punt socket for IKEv2 port in VPP */ mp = vl_msg_api_alloc(sizeof(*mp)); memset(mp, 0, sizeof(*mp)); @@ -314,45 +291,117 @@ socket_vpp_socket_t *socket_vpp_socket_create() mp->header_version = ntohl(1); mp->is_ip4 = 1; mp->l4_protocol = IPPROTO_UDP; - mp->l4_port = ntohs(this->port); + mp->l4_port = ntohs(port); strncpy(mp->pathname, read_path, 107); if (this->vac->send(this->vac, (char*)mp, sizeof(*mp), &out, &out_len)) { - DBG1(DBG_LIB, "send register vpp ip4 punt socket faield"); - return NULL; + DBG1(DBG_LIB, "send register vpp ip4 punt socket fail on port %d", port); + return -1; } rmp = (void *)out; if (rmp->retval) { - DBG1(DBG_LIB, "register vpp ip4 punt socket faield %d", ntohl(rmp->retval)); - return NULL; + DBG1(DBG_LIB, "register vpp ip4 punt socket fail on port %d with ret %d", port, ntohl(rmp->retval)); + return -1; } /* Register IPv6 punt socket for IKEv2 port in VPP */ - mp->is_ip4 = 0; + mp->is_ip4=0; if (this->vac->send(this->vac, (char*)mp, sizeof(*mp), &out, &out_len)) { - DBG1(DBG_LIB, "send register vpp ip6 punt socket faield"); - return NULL; + DBG1(DBG_LIB, "send register vpp ip6 punt socket fail on port %d", port); + return -1; } rmp = (void *)out; if (rmp->retval) { - DBG1(DBG_LIB, "register vpp ip6 punt socket faield %d", ntohl(rmp->retval)); - return NULL; + DBG1(DBG_LIB, "register vpp ip6 punt socket fail on port %d with ret %d", port, ntohl(rmp->retval)); + return -1; } - DBG3(DBG_LIB, "vpp punt socket %s", rmp->pathname); + DBG3(DBG_LIB, "Registered vpp punt socket on port %d successfully with returned write path: %s", port, rmp->pathname); this->sock = socket(AF_UNIX, SOCK_DGRAM, 0); if (this->sock < 0) { DBG1(DBG_LIB, "opening vpp socket failed: %m"); - return NULL; + return -1; + } + + /* There should be only one write path returned by VPP */ + if (this->write_addr.sun_family == 0) + { + strncpy(this->write_addr.sun_path, rmp->pathname, sizeof(this->write_addr.sun_path)); + this->write_addr.sun_family = AF_UNIX; + } + else if (strncmp(this->write_addr.sun_path, rmp->pathname, sizeof(this->write_addr.sun_path)) != 0) + { + DBG1(DBG_LIB, "More than one write path returned by VPP. Previous one is: %s, now is: %s", this->write_addr.sun_path, rmp->pathname); + return -1; } + return 0; +} + +/* + * See header for description + */ +socket_vpp_socket_t *socket_vpp_socket_create() +{ + private_socket_vpp_socket_t *this; + char *read_path, *out; + int out_len; + vl_api_punt_socket_register_t *mp; + vl_api_punt_socket_register_reply_t *rmp; + + INIT(this, + .public = { + .socket = { + .send = _sender, + .receive = _receiver, + .get_port = _get_port, + .supported_families = _supported_families, + .destroy = _destroy, + }, + }, + .max_packet = lib->settings->get_int(lib->settings, + "%s.max_packet", PACKET_MAX_DEFAULT, lib->ns), + .port = lib->settings->get_int(lib->settings, "%s.port", + IKEV2_UDP_PORT, lib->ns), + .natt = lib->settings->get_int(lib->settings, "%s.port_nat_t", + IKEV2_NATT_PORT, lib->ns), + ); + + this->vac = lib->get(lib, "kernel-vpp-vac"); + if (!this->vac) + { + DBG1(DBG_LIB, "no vac available (plugin missing?)"); + } + + read_path = lib->settings->get_str(lib->settings, + "%s.plugins.socket-vpp.path", READ_PATH, lib->ns); memset(&this->write_addr, 0, sizeof(this->write_addr)); - strncpy(this->write_addr.sun_path, rmp->pathname, sizeof(this->write_addr.sun_path)); - this->write_addr.sun_family = AF_UNIX; + if (this->port && (register_punt_port(this, this->port, read_path) != 0)) + { + DBG1(DBG_LIB, "Register punt port %d fail", this->port); + return NULL; + } + + if (this->natt) + { + if (this->natt == this->port) + { + DBG1(DBG_LIB, "IKE NAT Port (%d) cannot be the same as IKE Port (%d)", this->natt, this->port); + return NULL; + } + + if (register_punt_port(this, this->natt, read_path) != 0) + { + DBG1(DBG_LIB, "Register punt port %d fail", this->port); + return NULL; + } + } + + /* Bind read path */ memset(&this->read_addr, 0, sizeof(this->read_addr)); strncpy(this->read_addr.sun_path, read_path, sizeof(this->read_addr.sun_path)); this->read_addr.sun_family = AF_UNIX; From 1910ffa0749164f60f18f562b127eda913c06bcc Mon Sep 17 00:00:00 2001 From: Nguyen Duc Cong Binh Date: Thu, 11 Jul 2019 17:20:09 +0700 Subject: [PATCH 2/2] Support AES GCM, AES CTR --- src/libcharon/plugins/kernel_vpp/README.md | 2 + .../plugins/kernel_vpp/kernel_vpp_ipsec.c | 78 ++++++++++++++++--- 2 files changed, 68 insertions(+), 12 deletions(-) diff --git a/src/libcharon/plugins/kernel_vpp/README.md b/src/libcharon/plugins/kernel_vpp/README.md index 8ff6e4d7bed..997acd0e4fa 100644 --- a/src/libcharon/plugins/kernel_vpp/README.md +++ b/src/libcharon/plugins/kernel_vpp/README.md @@ -4,6 +4,8 @@ The kernel-vpp plugin is an interface to the IPsec and networking backend for [**VPP**](https://wiki.fd.io/view/VPPhttps://wiki.fd.io/view/VPP) platform using the [**VPP C API**](https://wiki.fd.io/view/VPP/How_To_Use_The_C_API). It provides address and routing lookup functionality and installs routes for IPsec traffic. It installs and maintains Security Associations and Policies to the [**VPP IPsec**](https://wiki.fd.io/view/VPP/IPSec_and_IKEv2#IPSec). The socket-vpp plugin is a replacement for socket-default for the VPP. It provides an IPv4/IPv6 IKE socket backend based on the VPP UDP punt socket. The plugin initialize VPP UDP IPv4 and IPv6 punt socket for IKE ports 500 and 4500 (NAT-T IKE). To have VPP punt IKE packets to strongswan, the VPP command `vppctl set punt udp 500 4500` must be executed. Custom port can be specified using the `charon.port` and `charon.port_nat_t` options in `strongswan.conf`. `charon.plugins.socket-vpp.path` configures custom path for read socket. Write socket path is configured in VPP startup configuration `punt { socket }`, VPP returns this path in punt_socket_register API reply. The read socket path and write socket path must be different, otherwise, VPP plugin cannot be loaded properly. +GCM is also supported and VPP must run with DPDK with crypto device or device that support RTE Security. + ## How to build strongswan for VPP ## Install vpp-lib and vpp-dev packages. The plugins are disabled by default and can be enabled by adding: diff --git a/src/libcharon/plugins/kernel_vpp/kernel_vpp_ipsec.c b/src/libcharon/plugins/kernel_vpp/kernel_vpp_ipsec.c index 4f267d8d1e9..ab00f95b4e6 100644 --- a/src/libcharon/plugins/kernel_vpp/kernel_vpp_ipsec.c +++ b/src/libcharon/plugins/kernel_vpp/kernel_vpp_ipsec.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -803,6 +804,15 @@ METHOD(kernel_ipsec_t, add_sa, status_t, chunk_t src, dst; kernel_ipsec_sa_id_t *sa_id; sa_t *sa; + int key_len = data->enc_key.len; + + if ((data->enc_alg == ENCR_AES_CTR) || + (data->enc_alg == ENCR_AES_GCM_ICV8) || + (data->enc_alg == ENCR_AES_GCM_ICV12) || + (data->enc_alg == ENCR_AES_GCM_ICV16)){ + static const int SALT_SIZE = 4; /* See how enc_size is calculated at keymat_v2.derive_child_keys */ + key_len = key_len - SALT_SIZE; + } mp = vl_msg_api_alloc(sizeof(*mp)); memset(mp, 0, sizeof(*mp)); @@ -814,27 +824,68 @@ METHOD(kernel_ipsec_t, add_sa, status_t, switch (data->enc_alg) { case ENCR_NULL: - ca = 0; + ca = IPSEC_CRYPTO_ALG_NONE; break; case ENCR_AES_CBC: - switch (data->enc_key.len * 8) + switch (key_len * 8) + { + case 128: + ca = IPSEC_CRYPTO_ALG_AES_CBC_128; + break; + case 192: + ca = IPSEC_CRYPTO_ALG_AES_CBC_192; + break; + case 256: + ca = IPSEC_CRYPTO_ALG_AES_CBC_256; + break; + default: + DBG1(DBG_KNL, "Key length %d is not supported by VPP!", key_len * 8); + break; + } + break; + case ENCR_AES_CTR: + switch (key_len * 8) + { + case 128: + ca = IPSEC_CRYPTO_ALG_AES_CTR_128; + break; + case 192: + ca = IPSEC_CRYPTO_ALG_AES_CTR_192; + break; + case 256: + ca = IPSEC_CRYPTO_ALG_AES_CTR_256; + break; + default: + DBG1(DBG_KNL, "Key length %d is not supported by VPP!", key_len * 8); + goto error; + break; + } + break; + case ENCR_AES_GCM_ICV8: + case ENCR_AES_GCM_ICV12: + case ENCR_AES_GCM_ICV16: + switch (key_len * 8) { case 128: - ca = 1; + ca = IPSEC_CRYPTO_ALG_AES_GCM_128; break; case 192: - ca = 2; + ca = IPSEC_CRYPTO_ALG_AES_GCM_192; break; case 256: - ca = 3; + ca = IPSEC_CRYPTO_ALG_AES_GCM_256; break; default: + DBG1(DBG_KNL, "Key length %d is not supported by VPP!", key_len * 8); goto error; break; } break; + case ENCR_DES: + ca = IPSEC_CRYPTO_ALG_DES_CBC; + break; case ENCR_3DES: - ca = 4; + ca = IPSEC_CRYPTO_ALG_3DES_CBC; break; default: DBG1(DBG_KNL, "algorithm %N not supported by VPP!", @@ -849,22 +900,25 @@ METHOD(kernel_ipsec_t, add_sa, status_t, switch (data->int_alg) { case AUTH_UNDEFINED: - ia = 0; + ia = IPSEC_INTEG_ALG_NONE; break; case AUTH_HMAC_MD5_96: - ia = 1; + ia = IPSEC_INTEG_ALG_MD5_96; break; case AUTH_HMAC_SHA1_96: - ia = 2; + ia = IPSEC_INTEG_ALG_SHA1_96; + break; + case AUTH_HMAC_SHA2_256_96: + ia = IPSEC_INTEG_ALG_SHA_256_96; break; case AUTH_HMAC_SHA2_256_128: - ia = 4; + ia = IPSEC_INTEG_ALG_SHA_256_128; break; case AUTH_HMAC_SHA2_384_192: - ia = 5; + ia = IPSEC_INTEG_ALG_SHA_384_192; break; case AUTH_HMAC_SHA2_512_256: - ia = 6; + ia = IPSEC_INTEG_ALG_SHA_512_256; break; default: DBG1(DBG_KNL, "algorithm %N not supported by VPP!",