/* SPDX-License-Identifier: GPL-2.0 */ /* * em_ipt.c IPtables extensions matching Ematch * * (C) 2018 Eyal Birger */ #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, #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 };