Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/vpp ike nat gcm #1

Open
wants to merge 2 commits into
base: vpp-1810
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/libcharon/plugins/kernel_vpp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

## 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 <socket_path> }, 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 <socket_path> }`, 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:
Expand Down
120 changes: 101 additions & 19 deletions src/libcharon/plugins/kernel_vpp/kernel_vpp_ipsec.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <vlibapi/api.h>
#include <vlibmemory/api.h>
#include <vpp/api/vpe_msg_enum.h>
#include <vnet/ipsec/ipsec.h>
#include <collections/hashtable.h>
#include <threading/mutex.h>

Expand Down Expand Up @@ -447,19 +448,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));
Expand Down Expand Up @@ -550,12 +545,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
*/
Expand Down Expand Up @@ -775,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));
Expand All @@ -786,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 = 1;
ca = IPSEC_CRYPTO_ALG_AES_CTR_128;
break;
case 192:
ca = 2;
ca = IPSEC_CRYPTO_ALG_AES_CTR_192;
break;
case 256:
ca = 3;
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 = IPSEC_CRYPTO_ALG_AES_GCM_128;
break;
case 192:
ca = IPSEC_CRYPTO_ALG_AES_GCM_192;
break;
case 256:
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!",
Expand All @@ -821,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!",
Expand Down
137 changes: 93 additions & 44 deletions src/libcharon/plugins/socket_vpp/socket_vpp_socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -272,87 +277,131 @@ 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));
mp->_vl_msg_id = ntohs(VL_API_PUNT_SOCKET_REGISTER);
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;
Expand Down