diff mbox series

[v2,5/7] examples/ipsec-secgw: make app to use ipsec library

Message ID 1543597460-6962-6-git-send-email-konstantin.ananyev@intel.com (mailing list archive)
State Superseded, archived
Delegated to: Thomas Monjalon
Headers show
Series examples/ipsec-secgw: make app to use ipsec library | expand

Checks

Context Check Description
ci/Intel-compilation success Compilation OK
ci/checkpatch success coding style OK

Commit Message

Konstantin Ananyev Nov. 30, 2018, 5:04 p.m. UTC
Changes to make ipsec-secgw to utilize librte_ipsec library.
That patch provides:
 - changes in the related data structures.
 - changes in the initialization code.
 - new command-line parameters to enable librte_ipsec codepath
   and related features.

Note that right now by default current (non-librte_ipsec) code-path will
be used. User has to run application with new command-line option ('-l')
to enable new codepath.
The main reason for that:
 - current librte_ipsec doesn't support all ipsec algorithms
   and features that the app does.
 - allow users to run both versions in parallel for some time
   to figure out any functional or performance degradation with the
   new code.

It is planned to deprecate and remove non-librte_ipsec code path
in future releases.

Signed-off-by: Mohammad Abdul Awal <mohammad.abdul.awal@intel.com>
Signed-off-by: Bernard Iremonger <bernard.iremonger@intel.com>
Signed-off-by: Konstantin Ananyev <konstantin.ananyev@intel.com>
---
 examples/ipsec-secgw/ipsec-secgw.c |  44 ++++++-
 examples/ipsec-secgw/ipsec.h       |  14 +++
 examples/ipsec-secgw/meson.build   |   2 +-
 examples/ipsec-secgw/sa.c          | 185 ++++++++++++++++++++++++++++-
 4 files changed, 241 insertions(+), 4 deletions(-)
diff mbox series

Patch

diff --git a/examples/ipsec-secgw/ipsec-secgw.c b/examples/ipsec-secgw/ipsec-secgw.c
index d1da2d5ce..feef865c9 100644
--- a/examples/ipsec-secgw/ipsec-secgw.c
+++ b/examples/ipsec-secgw/ipsec-secgw.c
@@ -155,6 +155,9 @@  static uint32_t single_sa;
 static uint32_t single_sa_idx;
 static uint32_t frame_size;
 
+/* application wide librte_ipsec/SA parameters */
+struct app_sa_prm app_sa_prm = {.enable = 0};
+
 struct lcore_rx_queue {
 	uint16_t port_id;
 	uint8_t queue_id;
@@ -1063,6 +1066,10 @@  print_usage(const char *prgname)
 		" [-P]"
 		" [-u PORTMASK]"
 		" [-j FRAMESIZE]"
+		" [-l]"
+		" [-w REPLAY_WINDOW_SIZE]"
+		" [-e]"
+		" [-a]"
 		" -f CONFIG_FILE"
 		" --config (port,queue,lcore)[,(port,queue,lcore)]"
 		" [--single-sa SAIDX]"
@@ -1073,6 +1080,10 @@  print_usage(const char *prgname)
 		"  -u PORTMASK: Hexadecimal bitmask of unprotected ports\n"
 		"  -j FRAMESIZE: Enable jumbo frame with 'FRAMESIZE' as maximum\n"
 		"                packet size\n"
+		"  -l enables code-path that uses librte_ipsec\n"
+		"  -w REPLAY_WINDOW_SIZE specifies IPsec SQN replay window\n"
+		"     size for each SA\n"
+		"  -a enables SA SQN atomic behaviour\n"
 		"  -f CONFIG_FILE: Configuration file\n"
 		"  --config (port,queue,lcore): Rx queue configuration\n"
 		"  --single-sa SAIDX: Use single SA index for outbound traffic,\n"
@@ -1169,6 +1180,20 @@  parse_config(const char *q_arg)
 	return 0;
 }
 
+static void
+print_app_sa_prm(const struct app_sa_prm *prm)
+{
+	printf("librte_ipsec usage: %s\n",
+		(prm->enable == 0) ? "disabled" : "enabled");
+
+	if (prm->enable == 0)
+		return;
+
+	printf("replay window size: %u\n", prm->window_size);
+	printf("ESN: %s\n", (prm->enable_esn == 0) ? "disabled" : "enabled");
+	printf("SA flags: %#" PRIx64 "\n", prm->flags);
+}
+
 static int32_t
 parse_args(int32_t argc, char **argv)
 {
@@ -1180,7 +1205,7 @@  parse_args(int32_t argc, char **argv)
 
 	argvopt = argv;
 
-	while ((opt = getopt_long(argc, argvopt, "p:Pu:f:j:",
+	while ((opt = getopt_long(argc, argvopt, "aelp:Pu:f:j:w:",
 				lgopts, &option_index)) != EOF) {
 
 		switch (opt) {
@@ -1236,6 +1261,21 @@  parse_args(int32_t argc, char **argv)
 			}
 			printf("Enabled jumbo frames size %u\n", frame_size);
 			break;
+		case 'l':
+			app_sa_prm.enable = 1;
+			break;
+		case 'w':
+			app_sa_prm.enable = 1;
+			app_sa_prm.window_size = parse_decimal(optarg);
+			break;
+		case 'e':
+			app_sa_prm.enable = 1;
+			app_sa_prm.enable_esn = 1;
+			break;
+		case 'a':
+			app_sa_prm.enable = 1;
+			app_sa_prm.flags |= RTE_IPSEC_SAFLAG_SQN_ATOM;
+			break;
 		case CMD_LINE_OPT_CONFIG_NUM:
 			ret = parse_config(optarg);
 			if (ret) {
@@ -1280,6 +1320,8 @@  parse_args(int32_t argc, char **argv)
 		return -1;
 	}
 
+	print_app_sa_prm(&app_sa_prm);
+
 	if (optind >= 0)
 		argv[optind-1] = prgname;
 
diff --git a/examples/ipsec-secgw/ipsec.h b/examples/ipsec-secgw/ipsec.h
index 2f04b7d68..45485b019 100644
--- a/examples/ipsec-secgw/ipsec.h
+++ b/examples/ipsec-secgw/ipsec.h
@@ -11,6 +11,7 @@ 
 #include <rte_crypto.h>
 #include <rte_security.h>
 #include <rte_flow.h>
+#include <rte_ipsec.h>
 
 #define RTE_LOGTYPE_IPSEC       RTE_LOGTYPE_USER1
 #define RTE_LOGTYPE_IPSEC_ESP   RTE_LOGTYPE_USER2
@@ -70,7 +71,20 @@  struct ip_addr {
 
 #define MAX_KEY_SIZE		32
 
+/*
+ * application wide SA parameters
+ */
+struct app_sa_prm {
+	uint32_t enable; /* use librte_ipsec API for ipsec pkt processing */
+	uint32_t window_size; /* replay window size */
+	uint32_t enable_esn;  /* enable/disable ESN support */
+	uint64_t flags;       /* rte_ipsec_sa_prm.flags */
+};
+
+extern struct app_sa_prm app_sa_prm;
+
 struct ipsec_sa {
+	struct rte_ipsec_session ips; /* one session per sa for now */
 	uint32_t spi;
 	uint32_t cdev_id_qp;
 	uint64_t seq;
diff --git a/examples/ipsec-secgw/meson.build b/examples/ipsec-secgw/meson.build
index 77d8b298f..31f68fee2 100644
--- a/examples/ipsec-secgw/meson.build
+++ b/examples/ipsec-secgw/meson.build
@@ -6,7 +6,7 @@ 
 # To build this example as a standalone application with an already-installed
 # DPDK instance, use 'make'
 
-deps += ['security', 'lpm', 'acl', 'hash']
+deps += ['security', 'lpm', 'acl', 'hash', 'ipsec']
 allow_experimental_apis = true
 sources = files(
 	'esp.c', 'ipsec.c', 'ipsec-secgw.c', 'parser.c',
diff --git a/examples/ipsec-secgw/sa.c b/examples/ipsec-secgw/sa.c
index ff8c4b829..282b40bea 100644
--- a/examples/ipsec-secgw/sa.c
+++ b/examples/ipsec-secgw/sa.c
@@ -19,6 +19,7 @@ 
 #include <rte_ip.h>
 #include <rte_random.h>
 #include <rte_ethdev.h>
+#include <rte_malloc.h>
 
 #include "ipsec.h"
 #include "esp.h"
@@ -694,6 +695,7 @@  print_one_sa_rule(const struct ipsec_sa *sa, int inbound)
 }
 
 struct sa_ctx {
+	void *satbl; /* pointer to array of rte_ipsec_sa objects*/
 	struct ipsec_sa sa[IPSEC_SA_MAX_ENTRIES];
 	union {
 		struct {
@@ -764,7 +766,10 @@  sa_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
 {
 	struct ipsec_sa *sa;
 	uint32_t i, idx;
-	uint16_t iv_length;
+	uint16_t iv_length, aad_length;
+
+	/* for ESN upper 32 bits of SQN also need to be part of AAD */
+	aad_length = (app_sa_prm.enable_esn != 0) ? sizeof(uint32_t) : 0;
 
 	for (i = 0; i < nb_entries; i++) {
 		idx = SPI2IDX(entries[i].spi);
@@ -808,7 +813,7 @@  sa_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
 			sa_ctx->xf[idx].a.aead.iv.offset = IV_OFFSET;
 			sa_ctx->xf[idx].a.aead.iv.length = iv_length;
 			sa_ctx->xf[idx].a.aead.aad_length =
-				sa->aad_len;
+				sa->aad_len + aad_length;
 			sa_ctx->xf[idx].a.aead.digest_length =
 				sa->digest_len;
 
@@ -901,9 +906,169 @@  sa_in_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
 	return sa_add_rules(sa_ctx, entries, nb_entries, 1);
 }
 
+/*
+ * helper function, fills parameters that are identical for all SAs
+ */
+static void
+fill_ipsec_app_sa_prm(struct rte_ipsec_sa_prm *prm,
+	const struct app_sa_prm *app_prm)
+{
+	memset(prm, 0, sizeof(*prm));
+
+	prm->flags = app_prm->flags;
+	prm->ipsec_xform.options.esn = app_prm->enable_esn;
+	prm->replay_win_sz = app_prm->window_size;
+}
+
+static void
+fill_ipsec_sa_prm(struct rte_ipsec_sa_prm *prm, const struct ipsec_sa *ss,
+	const struct ipv4_hdr *v4, struct ipv6_hdr *v6)
+{
+	fill_ipsec_app_sa_prm(prm, &app_sa_prm);
+
+	prm->userdata = (uintptr_t)ss;
+
+	/* setup ipsec xform */
+	prm->ipsec_xform.spi = ss->spi;
+	prm->ipsec_xform.salt = ss->salt;
+	prm->ipsec_xform.direction = ss->direction;
+	prm->ipsec_xform.proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP;
+	prm->ipsec_xform.mode = (ss->flags == TRANSPORT) ?
+		RTE_SECURITY_IPSEC_SA_MODE_TRANSPORT :
+		RTE_SECURITY_IPSEC_SA_MODE_TUNNEL;
+
+	/*
+	 * !!! this has to be changed.
+	 * Below we fill tun.next_proto based on the assumption that
+	 * input packet IP version is equal to tunnel IP version.
+	 * That could not be the case.
+	 * SA should know it's input IP version.
+	 */
+	if (ss->flags == IP4_TUNNEL) {
+		prm->ipsec_xform.tunnel.type = RTE_SECURITY_IPSEC_TUNNEL_IPV4;
+		prm->tun.hdr_len = sizeof(*v4);
+		prm->tun.next_proto = IPPROTO_IPIP;
+		prm->tun.hdr = v4;
+	} else if (ss->flags == IP6_TUNNEL) {
+		prm->ipsec_xform.tunnel.type = RTE_SECURITY_IPSEC_TUNNEL_IPV6;
+		prm->tun.hdr_len = sizeof(*v6);
+		prm->tun.next_proto = IPPROTO_IPV6;
+		prm->tun.hdr = v6;
+	} else {
+		prm->trs.proto = IPPROTO_IPIP;
+	}
+	/* setup crypto section */
+	prm->crypto_xform = ss->xforms;
+}
+
+static void
+fill_ipsec_session(struct rte_ipsec_session *ss, struct rte_ipsec_sa *sa,
+	const struct ipsec_sa *lsa)
+{
+	ss->sa = sa;
+	ss->type = lsa->type;
+
+	/* setup crypto section */
+	if (ss->type == RTE_SECURITY_ACTION_TYPE_NONE) {
+		ss->crypto.ses = lsa->crypto_session;
+	/* setup session action type */
+	} else {
+		ss->security.ses = lsa->sec_session;
+		ss->security.ctx = lsa->security_ctx;
+		ss->security.ol_flags = lsa->ol_flags;
+	}
+}
+
+/*
+ * Initialise related rte_ipsec_sa object.
+ */
+static int
+ipsec_sa_init(struct ipsec_sa *lsa, struct rte_ipsec_sa *sa, uint32_t sa_size)
+{
+	int rc;
+	struct rte_ipsec_sa_prm prm;
+	struct ipv4_hdr v4  = {
+		.version_ihl = IPVERSION << 4 |
+			sizeof(v4) / IPV4_IHL_MULTIPLIER,
+		.time_to_live = IPDEFTTL,
+		.next_proto_id = IPPROTO_ESP,
+		.src_addr = lsa->src.ip.ip4,
+		.dst_addr = lsa->dst.ip.ip4,
+	};
+	struct ipv6_hdr v6 = {
+		.vtc_flow = htonl(IP6_VERSION << 28),
+		.proto = IPPROTO_ESP,
+	};
+
+	if (lsa->flags == IP6_TUNNEL) {
+		memcpy(v6.src_addr, lsa->src.ip.ip6.ip6_b, sizeof(v6.src_addr));
+		memcpy(v6.dst_addr, lsa->dst.ip.ip6.ip6_b, sizeof(v6.dst_addr));
+	}
+
+	fill_ipsec_sa_prm(&prm, lsa, &v4, &v6);
+	rc = rte_ipsec_sa_init(sa, &prm, sa_size);
+	if (rc < 0)
+		return rc;
+
+	fill_ipsec_session(&lsa->ips, sa, lsa);
+	return 0;
+}
+
+/*
+ * Allocate space and init rte_ipsec_sa strcutures,
+ * one per session.
+ */
+static int
+ipsec_satbl_init(struct sa_ctx *ctx, const struct ipsec_sa *ent,
+	uint32_t nb_ent, int32_t socket)
+{
+	int32_t rc, sz;
+	uint32_t i, idx;
+	size_t tsz;
+	struct rte_ipsec_sa *sa;
+	struct ipsec_sa *lsa;
+	struct rte_ipsec_sa_prm prm;
+
+	/* determine SA size */
+	idx = SPI2IDX(ent[0].spi);
+	fill_ipsec_app_sa_prm(&prm, &app_sa_prm);
+	prm.ipsec_xform.direction = ctx->sa[idx].direction;
+	sz = rte_ipsec_sa_size(&prm);
+	if (sz < 0) {
+		RTE_LOG(ERR, IPSEC, "%s(%p, %u, %d): "
+			"failed to determine SA size, error code: %d\n",
+			__func__, ctx, nb_ent, socket, sz);
+		return sz;
+	}
+
+	tsz = sz * nb_ent;
+
+	ctx->satbl = rte_zmalloc_socket(NULL, tsz, RTE_CACHE_LINE_SIZE, socket);
+	if (ctx->satbl == NULL) {
+		RTE_LOG(ERR, IPSEC,
+			"%s(%p, %u, %d): failed to allocate %zu bytes\n",
+			__func__,  ctx, nb_ent, socket, tsz);
+		return -ENOMEM;
+	}
+
+	rc = 0;
+	for (i = 0; i != nb_ent && rc == 0; i++) {
+
+		idx = SPI2IDX(ent[i].spi);
+
+		sa = (struct rte_ipsec_sa *)((uintptr_t)ctx->satbl + sz * i);
+		lsa = ctx->sa + idx;
+
+		rc = ipsec_sa_init(lsa, sa, sz);
+	}
+
+	return rc;
+}
+
 void
 sa_init(struct socket_ctx *ctx, int32_t socket_id)
 {
+	int32_t rc;
 	const char *name;
 
 	if (ctx == NULL)
@@ -926,6 +1091,14 @@  sa_init(struct socket_ctx *ctx, int32_t socket_id)
 				name, socket_id);
 
 		sa_in_add_rules(ctx->sa_in, sa_in, nb_sa_in);
+
+		if (app_sa_prm.enable != 0) {
+			rc = ipsec_satbl_init(ctx->sa_in, sa_in, nb_sa_in,
+				socket_id);
+			if (rc != 0)
+				rte_exit(EXIT_FAILURE,
+					"failed to init inbound SAs\n");
+		}
 	} else
 		RTE_LOG(WARNING, IPSEC, "No SA Inbound rule specified\n");
 
@@ -938,6 +1111,14 @@  sa_init(struct socket_ctx *ctx, int32_t socket_id)
 				name, socket_id);
 
 		sa_out_add_rules(ctx->sa_out, sa_out, nb_sa_out);
+
+		if (app_sa_prm.enable != 0) {
+			rc = ipsec_satbl_init(ctx->sa_out, sa_out, nb_sa_out,
+				socket_id);
+			if (rc != 0)
+				rte_exit(EXIT_FAILURE,
+					"failed to init outbound SAs\n");
+		}
 	} else
 		RTE_LOG(WARNING, IPSEC, "No SA Outbound rule "
 			"specified\n");