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>
This commit is contained in:
Eyal Birger 2018-02-23 13:12:25 +02:00 committed by David Ahern
parent 526862038e
commit dd29621578
4 changed files with 231 additions and 0 deletions

View File

@ -5,3 +5,4 @@
4 meta
7 canid
8 ipset
9 ipt

View File

@ -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.

View File

@ -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 $@ $<

208
tc/em_ipt.c Normal file
View File

@ -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
};