From cb4ade6e38006d16dff74e1c68f771e54fb31e71 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 27 Feb 2018 09:42:23 -0800 Subject: [PATCH 1/3] Import tc_em_ipt.h from kernel at commit 08009a760213 Signed-off-by: David Ahern --- include/uapi/linux/tc_ematch/tc_em_ipt.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 include/uapi/linux/tc_ematch/tc_em_ipt.h diff --git a/include/uapi/linux/tc_ematch/tc_em_ipt.h b/include/uapi/linux/tc_ematch/tc_em_ipt.h new file mode 100644 index 00000000..49a65530 --- /dev/null +++ b/include/uapi/linux/tc_ematch/tc_em_ipt.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __LINUX_TC_EM_IPT_H +#define __LINUX_TC_EM_IPT_H + +#include +#include + +enum { + TCA_EM_IPT_UNSPEC, + TCA_EM_IPT_HOOK, + TCA_EM_IPT_MATCH_NAME, + TCA_EM_IPT_MATCH_REVISION, + TCA_EM_IPT_NFPROTO, + TCA_EM_IPT_MATCH_DATA, + __TCA_EM_IPT_MAX +}; + +#define TCA_EM_IPT_MAX (__TCA_EM_IPT_MAX - 1) + +#endif From 526862038e92afaed4378e64b3a7f2b501ffbf2f Mon Sep 17 00:00:00 2001 From: Eyal Birger Date: Fri, 23 Feb 2018 13:12:24 +0200 Subject: [PATCH 2/3] tc: ematch: add parse_eopt_argv() method for providing ematches with argv parameters ematche uses YACC to parse ematch arguments and places them in struct bstr linked lists. It is useful to be able to receive parameters as argc,argv in order to use getopt (and alike) argument parsers. Signed-off-by: Eyal Birger Signed-off-by: David Ahern --- tc/m_ematch.c | 27 ++++++++++++++++++++++++++- tc/m_ematch.h | 2 ++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/tc/m_ematch.c b/tc/m_ematch.c index 7dbc2e75..61f8fb3f 100644 --- a/tc/m_ematch.c +++ b/tc/m_ematch.c @@ -169,6 +169,31 @@ static struct ematch_util *get_ematch_kind_num(__u16 kind) return get_ematch_kind(name); } +static int em_parse_call(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr, + struct ematch_util *e, struct ematch *t) +{ + if (e->parse_eopt_argv) { + int argc = 0, i = 0, ret; + struct bstr *args; + char **argv; + + for (args = t->args; args; args = bstr_next(args)) + argc++; + argv = calloc(argc, sizeof(char *)); + if (!argv) + return -1; + for (args = t->args; args; args = bstr_next(args)) + argv[i++] = args->data; + + ret = e->parse_eopt_argv(n, hdr, argc, argv); + + free(argv); + return ret; + } + + return e->parse_eopt(n, hdr, t->args->next); +} + static int parse_tree(struct nlmsghdr *n, struct ematch *tree) { int index = 1; @@ -212,7 +237,7 @@ static int parse_tree(struct nlmsghdr *n, struct ematch *tree) } hdr.kind = num; - if (e->parse_eopt(n, &hdr, t->args->next) < 0) + if (em_parse_call(n, &hdr, e, t) < 0) return -1; } diff --git a/tc/m_ematch.h b/tc/m_ematch.h index fa6e214a..f634f191 100644 --- a/tc/m_ematch.h +++ b/tc/m_ematch.h @@ -88,6 +88,8 @@ struct ematch_util int kind_num; int (*parse_eopt)(struct nlmsghdr *,struct tcf_ematch_hdr *, struct bstr *); + int (*parse_eopt_argv)(struct nlmsghdr *, struct tcf_ematch_hdr *, + int, char **); int (*print_eopt)(FILE *, struct tcf_ematch_hdr *, void *, int); void (*print_usage)(FILE *); struct ematch_util *next; From dd29621578d29881d42018a4fd9dad364e66baf3 Mon Sep 17 00:00:00 2001 From: Eyal Birger Date: Fri, 23 Feb 2018 13:12:25 +0200 Subject: [PATCH 3/3] tc: add em_ipt ematch for calling xtables matches from tc matching context 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 Signed-off-by: David Ahern --- 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 --git a/etc/iproute2/ematch_map b/etc/iproute2/ematch_map index 18239837..4d6bb2f0 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 b9bf70ca..042f8404 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 3716dd6a..dfd00267 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 00000000..9d2b2f9b --- /dev/null +++ b/tc/em_ipt.c @@ -0,0 +1,208 @@ +/* + * em_ipt.c IPtables extenstions matching Ematch + * + * (C) 2018 Eyal Birger + * + * 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 + +#include +#include +#include +#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 +};