diff mbox series

[iproute2-next,2/2] tc: add em_ipt ematch for calling xtables matches from tc matching context

Message ID 1519384345-17115-3-git-send-email-eyal.birger@gmail.com
State Accepted, archived
Delegated to: David Ahern
Headers show
Series tc: add ipt ematch | expand

Commit Message

Eyal Birger Feb. 23, 2018, 11:12 a.m. UTC
The commit calls a new tc ematch for using netfilter xtable matches.

This allows early classification as well as mirroning/redirecting traffic
based on logic implemented in netfilter extensions.

Current supported use case is classification based on the incoming IPSec
state used during decpsulation using the 'policy' iptables extension
(xt_policy).

The matcher uses libxtables for parsing the input parameters.

Example use for matching an IPSec state with reqid 1:

tc qdisc add dev eth0 ingress
tc filter add dev eth0 protocol ip parent ffff: \
    basic match 'ipt(-m policy --dir in --pol ipsec --reqid 1)' \
    action drop

This is the user-space counter part of kernel commit ccc007e4a746
("net: sched: add em_ipt ematch for calling xtables matches")

Signed-off-by: Eyal Birger <eyal.birger@gmail.com>
---
 etc/iproute2/ematch_map |   1 +
 man/man8/tc-ematch.8    |  15 ++++
 tc/Makefile             |   7 ++
 tc/em_ipt.c             | 208 ++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 231 insertions(+)
 create mode 100644 tc/em_ipt.c
diff mbox series

Patch

diff --git a/etc/iproute2/ematch_map b/etc/iproute2/ematch_map
index 1823983..4d6bb2f 100644
--- a/etc/iproute2/ematch_map
+++ b/etc/iproute2/ematch_map
@@ -5,3 +5,4 @@ 
 4	meta
 7	canid
 8	ipset
+9	ipt
diff --git a/man/man8/tc-ematch.8 b/man/man8/tc-ematch.8
index b9bf70c..042f840 100644
--- a/man/man8/tc-ematch.8
+++ b/man/man8/tc-ematch.8
@@ -98,6 +98,17 @@  When using the ipset ematch with the "ip_set_hash:net,iface" set type,
 the interface can be queried using "src,dst (source ip address, outgoing interface) or
 "src,src" (source ip address, incoming interface) syntax.
 
+.SS ipt
+test packet against xtables matches
+
+.IR ipt "( " [-6] " "-m " " MATCH_NAME " " FLAGS " )
+
+.IR MATCH_NAME " := " string
+
+.IR FLAGS " := { " FLAG " [, " FLAGS "] }
+
+The flag options are the same as those used by the xtable match used.
+
 .SH CAVEATS
 
 The ematch syntax uses '(' and ')' to group expressions. All braces need to be
@@ -127,6 +138,10 @@  Check if packet source ip and the interface the packet arrived on is member of "
 
 # 'ipset(interactive src,src)'
 
+Check if packet matches an IPSec state with reqid 1:
+
+# 'ipt(-m policy --dir in --pol ipsec --reqid 1)'
+
 .SH "AUTHOR"
 
 The extended match infrastructure was added by Thomas Graf.
diff --git a/tc/Makefile b/tc/Makefile
index 3716dd6..dfd0026 100644
--- a/tc/Makefile
+++ b/tc/Makefile
@@ -80,6 +80,7 @@  endif
 ifneq ($(TC_CONFIG_NO_XT),y)
   ifeq ($(TC_CONFIG_XT),y)
     TCSO += m_xt.so
+    TCMODULES += em_ipt.o
     ifeq ($(TC_CONFIG_IPSET),y)
       TCMODULES += em_ipset.o
     endif
@@ -163,6 +164,12 @@  m_xt_old.so: m_xt_old.c
 
 em_ipset.o: CFLAGS += $$($(PKG_CONFIG) xtables --cflags)
 
+em_ipt.o: CFLAGS += $$($(PKG_CONFIG) xtables --cflags)
+
+ifeq ($(TC_CONFIG_XT),y)
+  LDFLAGS += $$($(PKG_CONFIG) xtables --libs)
+endif
+
 %.yacc.c: %.y
 	$(QUIET_YACC)$(YACC) $(YACCFLAGS) -o $@ $<
 
diff --git a/tc/em_ipt.c b/tc/em_ipt.c
new file mode 100644
index 0000000..9d2b2f9
--- /dev/null
+++ b/tc/em_ipt.c
@@ -0,0 +1,208 @@ 
+/*
+ * em_ipt.c		IPtables extenstions matching Ematch
+ *
+ * (C) 2018 Eyal Birger <eyal.birger@gmail.com>
+ *
+ * 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 <getopt.h>
+
+#include <linux/tc_ematch/tc_em_ipt.h>
+#include <linux/pkt_cls.h>
+#include <xtables.h>
+#include "m_ematch.h"
+
+static void em_ipt_print_usage(FILE *fd)
+{
+	fprintf(fd,
+		"Usage: ipt([-6] -m MATCH_NAME [MATCH_OPTS])\n"
+		"Example: 'ipt(-m policy --reqid 1 --pol ipsec --dir in)'\n");
+}
+
+static struct option original_opts[] = {
+	{
+		.name = "match",
+		.has_arg = 1,
+		.val = 'm'
+	},
+	{
+		.name = "ipv6",
+		.val = '6'
+	},
+	{}
+};
+
+static struct xtables_globals em_tc_ipt_globals = {
+	.option_offset = 0,
+	.program_name = "tc-em-ipt",
+	.program_version = "0.1",
+	.orig_opts = original_opts,
+	.opts = original_opts,
+	.compat_rev = xtables_compatible_revision,
+};
+
+static struct xt_entry_match *fake_xt_entry_match(int data_size, void *data)
+{
+	struct xt_entry_match *m;
+
+	m = xtables_calloc(1, XT_ALIGN(sizeof(*m)) + data_size);
+	if (!m)
+		return NULL;
+
+	if (data)
+		memcpy(m->data, data, data_size);
+
+	m->u.user.match_size = data_size;
+	return m;
+}
+
+static void scrub_match(struct xtables_match *match)
+{
+	match->mflags = 0;
+	free(match->m);
+	match->m = NULL;
+}
+
+/* IPv4 and IPv6 share the same hooking enumeration */
+#define HOOK_PRE_ROUTING 0
+#define HOOK_POST_ROUTING 4
+
+static __u32 em_ipt_hook(struct nlmsghdr *n)
+{
+	struct tcmsg *t = NLMSG_DATA(n);
+
+	if (t->tcm_parent != TC_H_ROOT &&
+	    t->tcm_parent == TC_H_MAJ(TC_H_INGRESS))
+		return HOOK_PRE_ROUTING;
+
+	return HOOK_POST_ROUTING;
+}
+
+static int em_ipt_parse_eopt_argv(struct nlmsghdr *n,
+				  struct tcf_ematch_hdr *hdr,
+				  int argc, char **argv)
+{
+	struct xtables_globals tmp_tcipt_globals = em_tc_ipt_globals;
+	struct xtables_match *match = NULL;
+	__u8 nfproto = NFPROTO_IPV4;
+
+	while (1) {
+		struct option *opts;
+		int c;
+
+		c = getopt_long(argc, argv, "6m:", tmp_tcipt_globals.opts,
+				NULL);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'm':
+			xtables_init_all(&tmp_tcipt_globals, nfproto);
+
+			match = xtables_find_match(optarg, XTF_TRY_LOAD, NULL);
+			if (!match || !match->x6_parse) {
+				fprintf(stderr, " failed to find match %s\n\n",
+					optarg);
+				return -1;
+			}
+
+			match->m = fake_xt_entry_match(match->size, NULL);
+			if (!match->m) {
+				printf(" %s error\n", match->name);
+				return -1;
+			}
+
+			if (match->init)
+				match->init(match->m);
+
+			opts = xtables_options_xfrm(tmp_tcipt_globals.orig_opts,
+						    tmp_tcipt_globals.opts,
+						    match->x6_options,
+						    &match->option_offset);
+			if (!opts) {
+				scrub_match(match);
+				return -1;
+			}
+
+			tmp_tcipt_globals.opts = opts;
+			break;
+
+		case '6':
+			nfproto = NFPROTO_IPV6;
+			break;
+
+		default:
+			if (!match) {
+				fprintf(stderr, "failed to find match %s\n\n",
+					optarg);
+				return -1;
+
+			}
+			xtables_option_mpcall(c, argv, 0, match, NULL);
+			break;
+		}
+	}
+
+	if (!match) {
+		fprintf(stderr, " failed to parse parameters (%s)\n", *argv);
+		return -1;
+	}
+
+	/* check that we passed the correct parameters to the match */
+	xtables_option_mfcall(match);
+
+	addraw_l(n, MAX_MSG, hdr, sizeof(*hdr));
+	addattr32(n, MAX_MSG, TCA_EM_IPT_HOOK, em_ipt_hook(n));
+	addattrstrz(n, MAX_MSG, TCA_EM_IPT_MATCH_NAME, match->name);
+	addattr8(n, MAX_MSG, TCA_EM_IPT_MATCH_REVISION, match->revision);
+	addattr8(n, MAX_MSG, TCA_EM_IPT_NFPROTO, nfproto);
+	addattr_l(n, MAX_MSG, TCA_EM_IPT_MATCH_DATA, match->m->data,
+		  match->size);
+
+	xtables_free_opts(1);
+
+	scrub_match(match);
+	return 0;
+}
+
+static int em_ipt_print_epot(FILE *fd, struct tcf_ematch_hdr *hdr, void *data,
+			     int data_len)
+{
+	struct rtattr *tb[TCA_EM_IPT_MAX + 1];
+	struct xtables_match *match;
+	const char *mname;
+	__u8 nfproto;
+
+	if (parse_rtattr(tb, TCA_EM_IPT_MAX, data, data_len) < 0)
+		return -1;
+
+	nfproto = rta_getattr_u8(tb[TCA_EM_IPT_NFPROTO]);
+
+	xtables_init_all(&em_tc_ipt_globals, nfproto);
+
+	mname = rta_getattr_str(tb[TCA_EM_IPT_MATCH_NAME]);
+	match = xtables_find_match(mname, XTF_TRY_LOAD, NULL);
+	if (!match)
+		return -1;
+
+	match->m = fake_xt_entry_match(RTA_PAYLOAD(tb[TCA_EM_IPT_MATCH_DATA]),
+				       RTA_DATA(tb[TCA_EM_IPT_MATCH_DATA]));
+	if (!match->m)
+		return -1;
+
+	match->print(NULL, match->m, 0);
+
+	scrub_match(match);
+	return 0;
+}
+
+struct ematch_util ipt_ematch_util = {
+	.kind = "ipt",
+	.kind_num = TCF_EM_IPT,
+	.parse_eopt_argv = em_ipt_parse_eopt_argv,
+	.print_eopt = em_ipt_print_epot,
+	.print_usage = em_ipt_print_usage
+};