diff options
author | Eyal Birger <eyal.birger@gmail.com> | 2018-02-23 13:12:25 +0200 |
---|---|---|
committer | David Ahern <dsahern@gmail.com> | 2018-02-27 09:43:16 -0800 |
commit | dd29621578d29881d42018a4fd9dad364e66baf3 (patch) | |
tree | 2c96dff6d0e45d73354d6e98c35b1a65939897a1 | |
parent | 526862038e92afaed4378e64b3a7f2b501ffbf2f (diff) |
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 <eyal.birger@gmail.com>
Signed-off-by: David Ahern <dsahern@gmail.com>
-rw-r--r-- | etc/iproute2/ematch_map | 1 | ||||
-rw-r--r-- | man/man8/tc-ematch.8 | 15 | ||||
-rw-r--r-- | tc/Makefile | 7 | ||||
-rw-r--r-- | tc/em_ipt.c | 208 |
4 files changed, 231 insertions, 0 deletions
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 <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 +}; |