diff options
Diffstat (limited to '')
-rw-r--r-- | tc/em_ipt.c | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/tc/em_ipt.c b/tc/em_ipt.c new file mode 100644 index 0000000..69efefd --- /dev/null +++ b/tc/em_ipt.c @@ -0,0 +1,207 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * em_ipt.c IPtables extensions matching Ematch + * + * (C) 2018 Eyal Birger <eyal.birger@gmail.com> + */ + +#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, +#if (XTABLES_VERSION_CODE >= 11) + .compat_rev = xtables_compatible_revision, +#endif +}; + +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 +}; |