[PATCH nft 5/5] src: add ipsec (xfrm) expression

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Máté Eckl <ecklm94@xxxxxxxxx>

This allows matching on ipsec tunnel/beet addresses in xfrm state
associated with a packet, ipsec request id and the SPI.

Examples:

 ipsec in ip saddr 192.168.1.0/24
 ipsec out ip6 daddr @endpoints
 ipsec in spi 1-65536

Joint work with Máté Eckl.

Cc: Máté Eckl <ecklm94@xxxxxxxxx>
Signed-off-by: Florian Westphal <fw@xxxxxxxxx>
---
 doc/primary-expression.txt          |  34 +++++++++
 include/expression.h                |   8 +++
 include/json.h                      |   2 +
 include/linux/netfilter/nf_tables.h |  29 ++++++++
 include/xfrm.h                      |  16 +++++
 src/Makefile.am                     |   1 +
 src/evaluate.c                      |  19 +++++
 src/json.c                          |  46 ++++++++++++
 src/netlink_delinearize.c           |  21 ++++++
 src/netlink_linearize.c             |  16 +++++
 src/parser_bison.y                  |  66 +++++++++++++++++
 src/parser_json.c                   | 101 +++++++++++++++++++++++---
 src/scanner.l                       |   9 +++
 src/xfrm.c                          | 120 +++++++++++++++++++++++++++++++
 tests/py/inet/ipsec.t               |  21 ++++++
 tests/py/inet/ipsec.t.json          | 136 ++++++++++++++++++++++++++++++++++++
 tests/py/inet/ipsec.t.payload       |  40 +++++++++++
 17 files changed, 674 insertions(+), 11 deletions(-)
 create mode 100644 include/xfrm.h
 create mode 100644 src/xfrm.c
 create mode 100644 tests/py/inet/ipsec.t
 create mode 100644 tests/py/inet/ipsec.t.json
 create mode 100644 tests/py/inet/ipsec.t.payload

diff --git a/doc/primary-expression.txt b/doc/primary-expression.txt
index e404e5423a01..eb0438b2f440 100644
--- a/doc/primary-expression.txt
+++ b/doc/primary-expression.txt
@@ -284,3 +284,37 @@ ip6 filter output rt nexthop fd00::1
 inet filter output rt ip nexthop 192.168.0.1
 inet filter output rt ip6 nexthop fd00::1
 -------------------------- 
+
+IPSEC EXPRESSIONS
+~~~~~~~~~~~~~~~~~
+
+[verse]
+*ipsec* {in | out} [ spnum 'NUM' ]  {reqid | spi }
+*ipsec* {in | out} [ spnum 'NUM' ]  {ip | ip6 } { saddr | daddr }
+
+A ipsec expression refers to ipsec data associated with a packet.
+
+The 'in' or 'out' keyword needs to be used to specify if the expression should
+examine inbound or outbound policies. The 'in' keyword can be used in the
+prerouting, input and forward hooks.  The 'out' keyword applies to forward,
+output and postrouting hooks.
+The optional keyword spnum can be used to match a specific state in a chain,
+it defaults to 0.
+
+.Ipsec expression types
+[options="header"]
+|=======================
+|Keyword| Description| Type
+|reqid|
+Request ID|
+integer (32 bit)
+|spi|
+Security Parameter Index|
+integer (32 bit)
+|saddr|
+Source address of the tunnel|
+ipv4_addr/ipv6_addr
+|daddr|
+Destination address of the tunnel|
+ipv4_addr/ipv6_addr
+|=================================
diff --git a/include/expression.h b/include/expression.h
index f2c5c1ad7a04..fb52abfea76e 100644
--- a/include/expression.h
+++ b/include/expression.h
@@ -69,6 +69,7 @@ enum expr_types {
 	EXPR_HASH,
 	EXPR_RT,
 	EXPR_FIB,
+	EXPR_XFRM,
 };
 
 enum ops {
@@ -194,6 +195,7 @@ enum expr_flags {
 #include <ct.h>
 #include <socket.h>
 #include <osf.h>
+#include <xfrm.h>
 
 /**
  * struct expr
@@ -337,6 +339,12 @@ struct expr {
 			uint32_t		flags;
 			uint32_t		result;
 		} fib;
+		struct {
+			/* EXPR_XFRM */
+			enum nft_xfrm_keys	key;
+			uint8_t		direction;
+			uint8_t		spnum;
+		} xfrm;
 	};
 };
 
diff --git a/include/json.h b/include/json.h
index 66f60e76aa83..99b676446516 100644
--- a/include/json.h
+++ b/include/json.h
@@ -43,6 +43,7 @@ json_t *fib_expr_json(const struct expr *expr, struct output_ctx *octx);
 json_t *constant_expr_json(const struct expr *expr, struct output_ctx *octx);
 json_t *socket_expr_json(const struct expr *expr, struct output_ctx *octx);
 json_t *osf_expr_json(const struct expr *expr, struct output_ctx *octx);
+json_t *xfrm_expr_json(const struct expr *expr, struct output_ctx *octx);
 
 json_t *integer_type_json(const struct expr *expr, struct output_ctx *octx);
 json_t *string_type_json(const struct expr *expr, struct output_ctx *octx);
@@ -123,6 +124,7 @@ EXPR_PRINT_STUB(fib_expr)
 EXPR_PRINT_STUB(constant_expr)
 EXPR_PRINT_STUB(socket_expr)
 EXPR_PRINT_STUB(osf_expr)
+EXPR_PRINT_STUB(xfrm_expr)
 
 EXPR_PRINT_STUB(integer_type)
 EXPR_PRINT_STUB(string_type)
diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h
index 1a63bd1e32f4..169c2abcfcff 100644
--- a/include/linux/netfilter/nf_tables.h
+++ b/include/linux/netfilter/nf_tables.h
@@ -1501,6 +1501,35 @@ enum nft_devices_attributes {
 };
 #define NFTA_DEVICE_MAX		(__NFTA_DEVICE_MAX - 1)
 
+/*
+ * enum nft_xfrm_attributes - nf_tables xfrm expr netlink attributes
+ *
+ * @NFTA_XFRM_DREG: destination register (NLA_U32)
+ * @NFTA_XFRM_KEY: enum nft_xfrm_keys (NLA_U32)
+ * @NFTA_XFRM_DIR: direction (NLA_U8)
+ * @NFTA_XFRM_SPNUM: index in secpath array (NLA_U32)
+ */
+enum nft_xfrm_attributes {
+	NFTA_XFRM_UNSPEC,
+	NFTA_XFRM_DREG,
+	NFTA_XFRM_KEY,
+	NFTA_XFRM_DIR,
+	NFTA_XFRM_SPNUM,
+	__NFTA_XFRM_MAX
+};
+#define NFTA_XFRM_MAX (__NFTA_XFRM_MAX - 1)
+
+enum nft_xfrm_keys {
+	NFT_XFRM_KEY_UNSPEC,
+	NFT_XFRM_KEY_DADDR_IP4,
+	NFT_XFRM_KEY_DADDR_IP6,
+	NFT_XFRM_KEY_SADDR_IP4,
+	NFT_XFRM_KEY_SADDR_IP6,
+	NFT_XFRM_KEY_REQID,
+	NFT_XFRM_KEY_SPI,
+	__NFT_XFRM_KEY_MAX,
+};
+#define NFT_XFRM_KEY_MAX (__NFT_XFRM_KEY_MAX - 1)
 
 /**
  * enum nft_trace_attributes - nf_tables trace netlink attributes
diff --git a/include/xfrm.h b/include/xfrm.h
new file mode 100644
index 000000000000..ea7d322c6df9
--- /dev/null
+++ b/include/xfrm.h
@@ -0,0 +1,16 @@
+#ifndef NFTABLES_XFRM_H
+#define NFTABLES_XFRM_H
+
+struct xfrm_template {
+	const char		*token;
+	const struct datatype	*dtype;
+	unsigned int		len;
+	enum byteorder		byteorder;
+};
+
+extern const struct xfrm_template xfrm_templates[__NFT_XFRM_KEY_MAX];
+
+extern struct expr *xfrm_expr_alloc(const struct location *loc,
+				    uint8_t direction, uint8_t spnum,
+				    enum nft_xfrm_keys key);
+#endif
diff --git a/src/Makefile.am b/src/Makefile.am
index 8e69232fd0b7..307bab108cca 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -43,6 +43,7 @@ libnftables_la_SOURCES =			\
 		rt.c				\
 		numgen.c			\
 		ct.c				\
+		xfrm.c				\
 		netlink.c			\
 		netlink_linearize.c		\
 		netlink_delinearize.c		\
diff --git a/src/evaluate.c b/src/evaluate.c
index 195508236e1e..fb3b4e56fdcb 100644
--- a/src/evaluate.c
+++ b/src/evaluate.c
@@ -1751,6 +1751,23 @@ static int expr_evaluate_variable(struct eval_ctx *ctx, struct expr **exprp)
 	return expr_evaluate(ctx, exprp);
 }
 
+static int expr_evaluate_xfrm(struct eval_ctx *ctx, struct expr **exprp)
+{
+	struct expr *expr = *exprp;
+
+	switch (ctx->pctx.family) {
+	case NFPROTO_IPV4:
+	case NFPROTO_IPV6:
+	case NFPROTO_INET:
+		break;
+	default:
+		return expr_error(ctx->msgs, expr, "ipsec expression is only"
+				  " valid in ip/ip6/inet tables");
+	}
+
+	return expr_evaluate_primary(ctx, exprp);
+}
+
 static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr)
 {
 	if (ctx->debug_mask & NFT_DEBUG_EVALUATION) {
@@ -1816,6 +1833,8 @@ static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr)
 		return expr_evaluate_numgen(ctx, expr);
 	case EXPR_HASH:
 		return expr_evaluate_hash(ctx, expr);
+	case EXPR_XFRM:
+		return expr_evaluate_xfrm(ctx, expr);
 	default:
 		BUG("unknown expression type %s\n", (*expr)->ops->name);
 	}
diff --git a/src/json.c b/src/json.c
index 84bdaa39c0d3..7cd4355a218a 100644
--- a/src/json.c
+++ b/src/json.c
@@ -16,6 +16,7 @@
 #include <linux/netfilter/nf_log.h>
 #include <linux/netfilter/nf_nat.h>
 #include <linux/netfilter/nf_tables.h>
+#include <linux/xfrm.h>
 #include <pwd.h>
 #include <grp.h>
 #include <jansson.h>
@@ -806,6 +807,51 @@ json_t *osf_expr_json(const struct expr *expr, struct output_ctx *octx)
 	return json_pack("{s:{s:s}}", "osf", "key", "name");
 }
 
+json_t *xfrm_expr_json(const struct expr *expr, struct output_ctx *octx)
+{
+	const char *name = xfrm_templates[expr->xfrm.key].token;
+	const char *family = NULL;
+	const char *dirstr;
+	json_t *root;
+
+	switch (expr->xfrm.direction) {
+	case XFRM_POLICY_IN:
+		dirstr = "in";
+		break;
+	case XFRM_POLICY_OUT:
+		dirstr = "out";
+		break;
+	default:
+		return NULL;
+	}
+
+	switch (expr->xfrm.key) {
+	case NFT_XFRM_KEY_UNSPEC:
+	case NFT_XFRM_KEY_SPI:
+	case NFT_XFRM_KEY_REQID:
+	case __NFT_XFRM_KEY_MAX:
+		break;
+	case NFT_XFRM_KEY_DADDR_IP4:
+	case NFT_XFRM_KEY_SADDR_IP4:
+		family = "ip";
+		break;
+	case NFT_XFRM_KEY_DADDR_IP6:
+	case NFT_XFRM_KEY_SADDR_IP6:
+		family = "ip6";
+		break;
+	}
+
+	root = json_pack("{s:s}", "key", name);
+
+	if (family)
+		json_object_set_new(root, "family", json_string(family));
+
+	json_object_set_new(root, "dir", json_string(dirstr));
+	json_object_set_new(root, "spnum", json_integer(expr->xfrm.spnum));
+
+	return json_pack("{s:o}", "ipsec", root);
+}
+
 json_t *integer_type_json(const struct expr *expr, struct output_ctx *octx)
 {
 	char buf[1024] = "0x";
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
index 6c5188cd9b5f..0a6ebe05ca7c 100644
--- a/src/netlink_delinearize.c
+++ b/src/netlink_delinearize.c
@@ -189,6 +189,25 @@ static void netlink_parse_immediate(struct netlink_parse_ctx *ctx,
 		netlink_set_register(ctx, dreg, expr);
 }
 
+static void netlink_parse_xfrm(struct netlink_parse_ctx *ctx,
+			       const struct location *loc,
+			       const struct nftnl_expr *nle)
+{
+	enum nft_registers dreg;
+	enum nft_xfrm_keys key;
+	struct expr *expr;
+	uint32_t spnum;
+	uint8_t dir;
+
+	key = nftnl_expr_get_u32(nle, NFTNL_EXPR_XFRM_KEY);
+	dir = nftnl_expr_get_u8(nle, NFTNL_EXPR_XFRM_DIR);
+	spnum = nftnl_expr_get_u32(nle, NFTNL_EXPR_XFRM_SPNUM);
+	expr = xfrm_expr_alloc(loc, dir, spnum, key);
+
+	dreg = netlink_parse_register(nle, NFTNL_EXPR_XFRM_DREG);
+	netlink_set_register(ctx, dreg, expr);
+}
+
 static enum ops netlink_parse_range_op(const struct nftnl_expr *nle)
 {
 	switch (nftnl_expr_get_u32(nle, NFTNL_EXPR_RANGE_OP)) {
@@ -1441,6 +1460,7 @@ static const struct {
 	{ .name = "fib",	.parse = netlink_parse_fib },
 	{ .name = "tcpopt",	.parse = netlink_parse_exthdr },
 	{ .name = "flow_offload", .parse = netlink_parse_flow_offload },
+	{ .name = "xfrm",	.parse = netlink_parse_xfrm },
 };
 
 static int netlink_parse_expr(const struct nftnl_expr *nle,
@@ -2106,6 +2126,7 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp)
 	case EXPR_FIB:
 	case EXPR_SOCKET:
 	case EXPR_OSF:
+	case EXPR_XFRM:
 		break;
 	case EXPR_HASH:
 		if (expr->hash.expr)
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
index 0bd946a1cf80..0ac51bd0d710 100644
--- a/src/netlink_linearize.c
+++ b/src/netlink_linearize.c
@@ -679,6 +679,20 @@ static void netlink_gen_immediate(struct netlink_linearize_ctx *ctx,
 	nftnl_rule_add_expr(ctx->nlr, nle);
 }
 
+static void netlink_gen_xfrm(struct netlink_linearize_ctx *ctx,
+			     const struct expr *expr,
+			     enum nft_registers dreg)
+{
+	struct nftnl_expr *nle;
+
+	nle = alloc_nft_expr("xfrm");
+	netlink_put_register(nle, NFTNL_EXPR_XFRM_DREG, dreg);
+	nftnl_expr_set_u32(nle, NFTNL_EXPR_XFRM_KEY, expr->xfrm.key);
+	nftnl_expr_set_u8(nle, NFTNL_EXPR_XFRM_DIR, expr->xfrm.direction);
+	nftnl_expr_set_u32(nle, NFTNL_EXPR_XFRM_SPNUM, expr->xfrm.spnum);
+	nftnl_rule_add_expr(ctx->nlr, nle);
+}
+
 static void netlink_gen_expr(struct netlink_linearize_ctx *ctx,
 			     const struct expr *expr,
 			     enum nft_registers dreg)
@@ -721,6 +735,8 @@ static void netlink_gen_expr(struct netlink_linearize_ctx *ctx,
 		return netlink_gen_socket(ctx, expr, dreg);
 	case EXPR_OSF:
 		return netlink_gen_osf(ctx, expr, dreg);
+	case EXPR_XFRM:
+		return netlink_gen_xfrm(ctx, expr, dreg);
 	default:
 		BUG("unknown expression type %s\n", expr->ops->name);
 	}
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 5fd304a9381f..1c68b4f4420e 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -21,6 +21,7 @@
 #include <linux/netfilter/nf_conntrack_tuple_common.h>
 #include <linux/netfilter/nf_nat.h>
 #include <linux/netfilter/nf_log.h>
+#include <linux/xfrm.h>
 #include <netinet/ip_icmp.h>
 #include <netinet/icmp6.h>
 #include <libnftnl/common.h>
@@ -511,6 +512,15 @@ int nft_lex(void *, void *, void *);
 %token EXTHDR			"exthdr"
 
 %token IPSEC		"ipsec"
+%token MODE			"mode"
+%token REQID		"reqid"
+%token SPNUM		"spnum"
+%token TRANSPORT	"transport"
+%token TUNNEL		"tunnel"
+
+%token IN			"in"
+%token OUT			"out"
+
 %type <string>			identifier type_identifier string comment_spec
 %destructor { xfree($$); }	identifier type_identifier string comment_spec
 
@@ -759,6 +769,10 @@ int nft_lex(void *, void *, void *);
 %type <list>			timeout_states timeout_state
 %destructor { xfree($$); }	timeout_states timeout_state
 
+%type <val>			xfrm_state_key	xfrm_state_proto_key xfrm_dir	xfrm_spnum
+%type <expr>			xfrm_expr
+%destructor { expr_free($$); }	xfrm_expr
+
 %%
 
 input			:	/* empty */
@@ -3043,6 +3057,7 @@ primary_expr		:	symbol_expr			{ $$ = $1; }
 			|	hash_expr			{ $$ = $1; }
 			|	fib_expr			{ $$ = $1; }
 			|	osf_expr			{ $$ = $1; }
+			|	xfrm_expr			{ $$ = $1; }
 			|	'('	basic_expr	')'	{ $$ = $2; }
 			;
 
@@ -3785,6 +3800,57 @@ numgen_expr		:	NUMGEN	numgen_type	MOD	NUM	offset_opt
 			}
 			;
 
+xfrm_spnum		:	SPNUM	NUM { $$ = $2; }
+			|		    { $$ = 0; }
+			;
+
+xfrm_dir		:	IN	{ $$ = XFRM_POLICY_IN; }
+			|	OUT	{ $$ = XFRM_POLICY_OUT; }
+			;
+
+xfrm_state_key		:	SPI { $$ = NFT_XFRM_KEY_SPI; }
+			|	REQID { $$ = NFT_XFRM_KEY_REQID; }
+			;
+
+xfrm_state_proto_key	:	DADDR		{ $$ = NFT_XFRM_KEY_DADDR_IP4; }
+			|	SADDR		{ $$ = NFT_XFRM_KEY_SADDR_IP4; }
+			;
+
+xfrm_expr		:	IPSEC	xfrm_dir	xfrm_spnum	xfrm_state_key
+			{
+				if ($3 > 255) {
+					erec_queue(error(&@3, "value too large"), state->msgs);
+					YYERROR;
+				}
+				$$ = xfrm_expr_alloc(&@$, $2, $3, $4);
+			}
+			|	IPSEC	xfrm_dir	xfrm_spnum	nf_key_proto	xfrm_state_proto_key
+			{
+				enum nft_xfrm_keys xfrmk = $5;
+
+				switch ($4) {
+				case NFPROTO_IPV4:
+					break;
+				case NFPROTO_IPV6:
+					if ($5 == NFT_XFRM_KEY_SADDR_IP4)
+						xfrmk = NFT_XFRM_KEY_SADDR_IP6;
+					else if ($5 == NFT_XFRM_KEY_DADDR_IP4)
+						xfrmk = NFT_XFRM_KEY_DADDR_IP6;
+					break;
+				default:
+					YYERROR;
+					break;
+				}
+
+				if ($3 > 255) {
+					erec_queue(error(&@3, "value too large"), state->msgs);
+					YYERROR;
+				}
+
+				$$ = xfrm_expr_alloc(&@$, $2, $3, xfrmk);
+			}
+			;
+
 hash_expr		:	JHASH		expr	MOD	NUM	SEED	NUM	offset_opt
 			{
 				$$ = hash_expr_alloc(&@$, $4, true, $6, $7, NFT_HASH_JENKINS);
diff --git a/src/parser_json.c b/src/parser_json.c
index 3f0ab0ac1993..9aadc33ed93a 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -16,6 +16,8 @@
 #include <netinet/icmp6.h>
 #include <netinet/ip.h>
 #include <netinet/ip_icmp.h>
+#include <linux/xfrm.h>
+
 #include <linux/netfilter.h>
 #include <linux/netfilter/nf_conntrack_tuple_common.h>
 #include <linux/netfilter/nf_log.h>
@@ -679,12 +681,32 @@ static bool ct_key_is_dir(enum nft_ct_keys key)
 	return false;
 }
 
+static int json_parse_family(struct json_ctx *ctx, json_t *root)
+{
+	const char *family;
+
+	if (!json_unpack(root, "{s:s}", "family", &family)) {
+		int familyval = parse_family(family);
+
+		switch (familyval) {
+		case NFPROTO_IPV6:
+		case NFPROTO_IPV4:
+			return familyval;
+		default:
+			json_error(ctx, "Invalid family '%s'.", family);
+			return -1;
+		}
+	}
+
+	return NFPROTO_UNSPEC;
+}
+
 static struct expr *json_parse_ct_expr(struct json_ctx *ctx,
 				       const char *type, json_t *root)
 {
-	const char *key, *dir, *family;
+	const char *key, *dir;
 	unsigned int i;
-	int dirval = -1, familyval = NFPROTO_UNSPEC, keyval = -1;
+	int dirval = -1, familyval, keyval = -1;
 
 	if (json_unpack_err(ctx, root, "{s:s}", "key", &key))
 		return NULL;
@@ -701,14 +723,9 @@ static struct expr *json_parse_ct_expr(struct json_ctx *ctx,
 		return NULL;
 	}
 
-	if (!json_unpack(root, "{s:s}", "family", &family)) {
-		familyval = parse_family(family);
-		if (familyval != NFPROTO_IPV4 &&
-		    familyval != NFPROTO_IPV6) {
-			json_error(ctx, "Invalid CT family '%s'.", family);
-			return NULL;
-		}
-	}
+	familyval = json_parse_family(ctx, root);
+	if (familyval < 0)
+		return NULL;
 
 	if (!json_unpack(root, "{s:s}", "dir", &dir)) {
 		if (!strcmp(dir, "original")) {
@@ -716,7 +733,7 @@ static struct expr *json_parse_ct_expr(struct json_ctx *ctx,
 		} else if (!strcmp(dir, "reply")) {
 			dirval = IP_CT_DIR_REPLY;
 		} else {
-			json_error(ctx, "Invalid ct direction '%s'.", dir);
+			json_error(ctx, "Invalid direction '%s'.", dir);
 			return NULL;
 		}
 
@@ -1167,6 +1184,67 @@ static struct expr *json_parse_set_elem_expr(struct json_ctx *ctx,
 	return expr;
 }
 
+static struct expr *json_parse_xfrm_expr(struct json_ctx *ctx,
+					 const char *type, json_t *root)
+{
+	const char *key, *dir;
+	unsigned int i, spnum;
+	int dirval = -1, familyval, keyval = -1;
+
+	if (json_unpack_err(ctx, root, "{s:s}", "key", &key))
+		return NULL;
+
+	for (i = 1; i < array_size(xfrm_templates); i++) {
+		if (strcmp(key, xfrm_templates[i].token))
+			continue;
+		keyval = i;
+		break;
+	}
+
+	if (keyval == -1) {
+		json_error(ctx, "Unknown xfrm key '%s'.", key);
+		return NULL;
+	}
+
+	familyval = json_parse_family(ctx, root);
+	if (familyval < 0)
+		return NULL;
+
+	if (!json_unpack(root, "{s:s}", "dir", &dir)) {
+		if (!strcmp(dir, "in")) {
+			dirval = XFRM_POLICY_IN;
+		} else if (!strcmp(dir, "out")) {
+			dirval = XFRM_POLICY_OUT;
+		} else {
+			json_error(ctx, "Invalid direction '%s'.", dir);
+			return NULL;
+		}
+	}
+
+	spnum = 0;
+	if (!json_unpack(root, "{s:i}", "spnum", &spnum)) {
+		if (spnum > 255) {
+			json_error(ctx, "Invalid spnum'%d'.", spnum);
+			return NULL;
+		}
+	}
+
+	switch (keyval) {
+	case NFT_XFRM_KEY_SADDR_IP4:
+		if (familyval == NFPROTO_IPV6)
+			keyval = NFT_XFRM_KEY_SADDR_IP6;
+		break;
+	case NFT_XFRM_KEY_DADDR_IP4:
+		if (familyval == NFPROTO_IPV6)
+			keyval = NFT_XFRM_KEY_DADDR_IP6;
+		break;
+	default:
+		break;
+	}
+
+	return xfrm_expr_alloc(int_loc, dirval, spnum, keyval);
+}
+
 static struct expr *json_parse_expr(struct json_ctx *ctx, json_t *root)
 {
 	const struct {
@@ -1185,6 +1263,7 @@ static struct expr *json_parse_expr(struct json_ctx *ctx, json_t *root)
 		{ "tcp option", json_parse_tcp_option_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES },
 		{ "meta", json_parse_meta_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_MAP },
 		{ "osf", json_parse_osf_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_MAP },
+		{ "ipsec", json_parse_xfrm_expr, CTX_F_PRIMARY | CTX_F_MAP },
 		{ "socket", json_parse_socket_expr, CTX_F_PRIMARY },
 		{ "rt", json_parse_rt_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP },
 		{ "ct", json_parse_ct_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_MAP },
diff --git a/src/scanner.l b/src/scanner.l
index 26e63b9bcc0c..4a143b1e5955 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -555,6 +555,15 @@ addrstring	({macaddr}|{ip4addr}|{ip6addr})
 "exthdr"		{ return EXTHDR; }
 
 "ipsec"			{ return IPSEC; }
+"mode"			{ return MODE; }
+"reqid"			{ return REQID; }
+"spnum"			{ return SPNUM; }
+"transport"		{ return TRANSPORT; }
+"tunnel"		{ return TUNNEL; }
+
+"in"			{ return IN; }
+"out"			{ return OUT; }
+
 {addrstring}		{
 				yylval->string = xstrdup(yytext);
 				return STRING;
diff --git a/src/xfrm.c b/src/xfrm.c
new file mode 100644
index 000000000000..0f5818c568fa
--- /dev/null
+++ b/src/xfrm.c
@@ -0,0 +1,120 @@
+/*
+ * XFRM (ipsec) expression
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <nftables.h>
+#include <erec.h>
+#include <expression.h>
+#include <xfrm.h>
+#include <datatype.h>
+#include <gmputil.h>
+#include <utils.h>
+#include <string.h>
+
+#include <netinet/ip.h>
+#include <linux/netfilter.h>
+#include <linux/xfrm.h>
+
+#define XFRM_TEMPLATE_BE(__token, __dtype, __len) {	\
+	.token		= (__token),			\
+	.dtype		= (__dtype),			\
+	.len		= (__len),			\
+	.byteorder	= BYTEORDER_BIG_ENDIAN,		\
+}
+
+#define XFRM_TEMPLATE_HE(__token, __dtype, __len) {	\
+	.token		= (__token),			\
+	.dtype		= (__dtype),			\
+	.len		= (__len),			\
+	.byteorder	= BYTEORDER_HOST_ENDIAN,	\
+}
+
+const struct xfrm_template xfrm_templates[] = {
+	[NFT_XFRM_KEY_DADDR_IP4]	= XFRM_TEMPLATE_BE("daddr", &ipaddr_type, 4 * BITS_PER_BYTE),
+	[NFT_XFRM_KEY_SADDR_IP4]	= XFRM_TEMPLATE_BE("saddr", &ipaddr_type, 4 * BITS_PER_BYTE),
+	[NFT_XFRM_KEY_DADDR_IP6]	= XFRM_TEMPLATE_BE("daddr", &ip6addr_type, 16 * BITS_PER_BYTE),
+	[NFT_XFRM_KEY_SADDR_IP6]	= XFRM_TEMPLATE_BE("saddr", &ip6addr_type, 16 * BITS_PER_BYTE),
+	[NFT_XFRM_KEY_REQID]		= XFRM_TEMPLATE_HE("reqid", &integer_type, 4 * BITS_PER_BYTE),
+	[NFT_XFRM_KEY_SPI]		= XFRM_TEMPLATE_HE("spi", &integer_type, 4 * BITS_PER_BYTE),
+};
+
+static void xfrm_expr_print(const struct expr *expr, struct output_ctx *octx)
+{
+	switch (expr->xfrm.direction) {
+	case XFRM_POLICY_IN:
+		nft_print(octx, "ipsec in");
+		break;
+	case XFRM_POLICY_OUT:
+		nft_print(octx, "ipsec out");
+		break;
+	default:
+		nft_print(octx, "ipsec (unknown dir %d)", expr->xfrm.direction);
+		break;
+	}
+
+	if (expr->xfrm.spnum)
+		nft_print(octx, " spnum %u", expr->xfrm.spnum);
+
+	switch (expr->xfrm.key) {
+	case NFT_XFRM_KEY_DADDR_IP4:
+	case NFT_XFRM_KEY_SADDR_IP4:
+		 nft_print(octx, " ip");
+		 break;
+	case NFT_XFRM_KEY_DADDR_IP6:
+	case NFT_XFRM_KEY_SADDR_IP6:
+		 nft_print(octx, " ip6");
+		 break;
+	case NFT_XFRM_KEY_REQID:
+	case NFT_XFRM_KEY_SPI:
+		 break;
+	default:
+		 nft_print(octx, " (unknown key 0x%x)", expr->xfrm.key);
+		 return;
+	}
+
+	nft_print(octx, " %s", xfrm_templates[expr->xfrm.key].token);
+}
+
+static bool xfrm_expr_cmp(const struct expr *e1, const struct expr *e2)
+{
+	return e1->xfrm.key == e2->xfrm.key &&
+	       e1->xfrm.direction == e2->xfrm.direction &&
+	       e1->xfrm.spnum == e2->xfrm.spnum;
+}
+
+static void xfrm_expr_clone(struct expr *new, const struct expr *expr)
+{
+	memcpy(&new->xfrm, &expr->xfrm, sizeof(new->xfrm));
+}
+
+static const struct expr_ops xfrm_expr_ops = {
+	.type		= EXPR_XFRM,
+	.name		= "xfrm",
+	.print		= xfrm_expr_print,
+	.json		= xfrm_expr_json,
+	.cmp		= xfrm_expr_cmp,
+	.clone		= xfrm_expr_clone,
+};
+
+struct expr *xfrm_expr_alloc(const struct location *loc,
+			     uint8_t direction,
+			     uint8_t spnum,
+			     enum nft_xfrm_keys key)
+{
+	struct expr *expr;
+
+	expr = expr_alloc(loc, &xfrm_expr_ops,
+			  xfrm_templates[key].dtype,
+			  xfrm_templates[key].byteorder,
+			  xfrm_templates[key].len);
+
+	expr->xfrm.direction = direction;
+	expr->xfrm.spnum = spnum;
+	expr->xfrm.key = key;
+
+	return expr;
+}
diff --git a/tests/py/inet/ipsec.t b/tests/py/inet/ipsec.t
new file mode 100644
index 000000000000..e924e9bcbdbc
--- /dev/null
+++ b/tests/py/inet/ipsec.t
@@ -0,0 +1,21 @@
+:ipsec-forw;type filter hook forward priority 0
+
+*ip;ipsec-ip4;ipsec-forw
+*ip6;ipsec-ip6;ipsec-forw
+*inet;ipsec-inet;ipsec-forw
+
+ipsec in reqid 1;ok
+ipsec in spnum 0 reqid 1;ok;ipsec in reqid 1
+
+ipsec out reqid 0xffffffff;ok;ipsec out reqid 4294967295
+ipsec out spnum 0x100000000;fail
+
+ipsec i reqid 1;fail
+
+ipsec out spi 1-561;ok
+
+ipsec in spnum 2 ip saddr { 1.2.3.4, 10.6.0.0/16 };ok
+ipsec in ip6 daddr dead::beef;ok
+ipsec out ip6 saddr dead::feed;ok
+
+ipsec in spnum 256 reqid 1;fail
diff --git a/tests/py/inet/ipsec.t.json b/tests/py/inet/ipsec.t.json
new file mode 100644
index 000000000000..d7d3a03c2113
--- /dev/null
+++ b/tests/py/inet/ipsec.t.json
@@ -0,0 +1,136 @@
+# ipsec in reqid 1
+[
+    {
+        "match": {
+            "left": {
+                "ipsec": {
+                    "dir": "in",
+                    "key": "reqid",
+                    "spnum": 0
+                }
+            },
+            "op": "==",
+            "right": 1
+        }
+    }
+]
+
+# ipsec in spnum 0 reqid 1
+[
+    {
+        "match": {
+            "left": {
+                "ipsec": {
+                    "dir": "in",
+                    "key": "reqid",
+                    "spnum": 0
+                }
+            },
+            "op": "==",
+            "right": 1
+        }
+    }
+]
+
+# ipsec out reqid 0xffffffff
+[
+    {
+        "match": {
+            "left": {
+                "ipsec": {
+                    "dir": "out",
+                    "key": "reqid",
+                    "spnum": 0
+                }
+            },
+            "op": "==",
+            "right": 4294967295
+        }
+    }
+]
+
+# ipsec out spi 1-561
+[
+    {
+        "match": {
+            "left": {
+                "ipsec": {
+                    "dir": "out",
+                    "key": "spi",
+                    "spnum": 0
+                }
+            },
+            "op": "==",
+            "right": {
+                "range": [
+                    1,
+                    561
+                ]
+            }
+        }
+    }
+]
+
+# ipsec in spnum 2 ip saddr { 1.2.3.4, 10.6.0.0/16 }
+[
+    {
+        "match": {
+            "left": {
+                "ipsec": {
+                    "dir": "in",
+                    "family": "ip",
+                    "key": "saddr",
+                    "spnum": 2
+                }
+            },
+            "op": "==",
+            "right": {
+                "set": [
+                    "1.2.3.4",
+                    {
+                        "prefix": {
+                            "addr": "10.6.0.0",
+                            "len": 16
+                        }
+                    }
+                ]
+            }
+        }
+    }
+]
+
+# ipsec in ip6 daddr dead::beef
+[
+    {
+        "match": {
+            "left": {
+                "ipsec": {
+                    "dir": "in",
+                    "family": "ip6",
+                    "key": "daddr",
+                    "spnum": 0
+                }
+            },
+            "op": "==",
+            "right": "dead::beef"
+        }
+    }
+]
+
+# ipsec out ip6 saddr dead::feed
+[
+    {
+        "match": {
+            "left": {
+                "ipsec": {
+                    "dir": "out",
+                    "family": "ip6",
+                    "key": "saddr",
+                    "spnum": 0
+                }
+            },
+            "op": "==",
+            "right": "dead::feed"
+        }
+    }
+]
diff --git a/tests/py/inet/ipsec.t.payload b/tests/py/inet/ipsec.t.payload
new file mode 100644
index 000000000000..dfe0edc387bc
--- /dev/null
+++ b/tests/py/inet/ipsec.t.payload
@@ -0,0 +1,40 @@
+# ipsec in reqid 1
+ip ipsec-ip4 ipsec-input 
+  [ xfrm load in 0 reqid => reg 1 ]
+  [ cmp eq reg 1 0x00000001 ]
+
+# ipsec in spnum 0 reqid 1
+ip ipsec-ip4 ipsec-input 
+  [ xfrm load in 0 reqid => reg 1 ]
+  [ cmp eq reg 1 0x00000001 ]
+
+# ipsec out reqid 0xffffffff
+ip ipsec-ip4 ipsec-input 
+  [ xfrm load out 0 reqid => reg 1 ]
+  [ cmp eq reg 1 0xffffffff ]
+
+# ipsec out spi 1-561
+inet ipsec-inet ipsec-post
+  [ xfrm load out 0 spi => reg 1 ]
+  [ byteorder reg 1 = hton(reg 1, 4, 4) ]
+  [ cmp gte reg 1 0x01000000 ]
+  [ cmp lte reg 1 0x31020000 ]
+
+# ipsec in spnum 2 ip saddr { 1.2.3.4, 10.6.0.0/16 }
+__set%d ipsec-ip4 7 size 5
+__set%d ipsec-ip4 0
+        element 00000000  : 1 [end]     element 04030201  : 0 [end]     element 05030201  : 1 [end]     element 0000060a  : 0 [end]     element 0000070a  : 1 [end]
+ip ipsec-ip4 ipsec-input
+  [ xfrm load in 2 saddr4 => reg 1 ]
+  [ lookup reg 1 set __set%d ]
+
+# ipsec in ip6 daddr dead::beef
+ip ipsec-ip4 ipsec-forw
+  [ xfrm load in 0 daddr6 => reg 1 ]
+  [ cmp eq reg 1 0x0000adde 0x00000000 0x00000000 0xefbe0000 ]
+
+# ipsec out ip6 saddr dead::feed
+ip ipsec-ip4 ipsec-forw
+  [ xfrm load out 0 saddr6 => reg 1 ]
+  [ cmp eq reg 1 0x0000adde 0x00000000 0x00000000 0xedfe0000 ]
+
-- 
2.16.4




[Index of Archives]     [Netfitler Users]     [LARTC]     [Bugtraq]     [Yosemite Forum]

  Powered by Linux