History

net: sched: add em_ipt ematch for calling xtables matches

The commit 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 module dynamically fetches the netfilter match module and calls it using a fake xt_action_param structure based on validated userspace provided parameters. As the xt_policy match does not access skb->data, no skb modifications are needed on match.

Eyal Birger committed 8 years ago

Commit ccc007e1 parent 022ddbc5 files · +292 −1Browse files

Unified · Split

5 files
  1. include/uapi/linux/pkt_cls.h +2 −1
  2. include/uapi/linux/tc_ematch/tc_em_ipt.h +20 −0
  3. net/sched/Kconfig +12 −0
  4. net/sched/Makefile +1 −0
  5. net/sched/em_ipt.c +257 −0
include/uapi/linux/pkt_cls.h+2−1
@@ -555,7 +555,8 @@ enum { · 554 unmodified lines
555555 #define TCF_EM_VLAN 6
556556 #define TCF_EM_CANID 7
557557 #define TCF_EM_IPSET 8
558−#define TCF_EM_MAX 8
558+#define TCF_EM_IPT 9
559+#define TCF_EM_MAX 9
559560
560561 enum {
561562 TCF_EM_PROG_TC
include/uapi/linux/tc_ematch/tc_em_ipt.h+20−0
@@ -0,0 +1,20 @@
1+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
2+#ifndef __LINUX_TC_EM_IPT_H
3+#define __LINUX_TC_EM_IPT_H
4+
5+#include <linux/types.h>
6+#include <linux/pkt_cls.h>
7+
8+enum {
9+ TCA_EM_IPT_UNSPEC,
10+ TCA_EM_IPT_HOOK,
11+ TCA_EM_IPT_MATCH_NAME,
12+ TCA_EM_IPT_MATCH_REVISION,
13+ TCA_EM_IPT_NFPROTO,
14+ TCA_EM_IPT_MATCH_DATA,
15+ __TCA_EM_IPT_MAX
16+};
17+
18+#define TCA_EM_IPT_MAX (__TCA_EM_IPT_MAX - 1)
19+
20+#endif
net/sched/Kconfig+12−0
@@ -658,6 +658,18 @@ config NET_EMATCH_IPSET · 657 unmodified lines
658658 To compile this code as a module, choose M here: the
659659 module will be called em_ipset.
660660
661+config NET_EMATCH_IPT
662+ tristate "IPtables Matches"
663+ depends on NET_EMATCH && NETFILTER && NETFILTER_XTABLES
664+ ---help---
665+ Say Y here to be able to classify packets based on iptables
666+ matches.
667+ Current supported match is "policy" which allows packet classification
668+ based on IPsec policy that was used during decapsulation
669+
670+ To compile this code as a module, choose M here: the
671+ module will be called em_ipt.
672+
661673 config NET_CLS_ACT
662674 bool "Actions"
663675 select NET_CLS
net/sched/Makefile+1−0
@@ -75,3 +75,4 @@ obj-$(CONFIG_NET_EMATCH_META) += em_meta.o · 74 unmodified lines
7575 obj-$(CONFIG_NET_EMATCH_TEXT) += em_text.o
7676 obj-$(CONFIG_NET_EMATCH_CANID) += em_canid.o
7777 obj-$(CONFIG_NET_EMATCH_IPSET) += em_ipset.o
78+obj-$(CONFIG_NET_EMATCH_IPT) += em_ipt.o
net/sched/em_ipt.c+257−0
@@ -0,0 +1,257 @@
1+/*
2+ * net/sched/em_ipt.c IPtables matches Ematch
3+ *
4+ * (c) 2018 Eyal Birger <eyal.birger@gmail.com>
5+ *
6+ * This program is free software; you can redistribute it and/or
7+ * modify it under the terms of the GNU General Public License
8+ * as published by the Free Software Foundation; either version
9+ * 2 of the License, or (at your option) any later version.
10+ */
11+
12+#include <linux/gfp.h>
13+#include <linux/module.h>
14+#include <linux/types.h>
15+#include <linux/kernel.h>
16+#include <linux/string.h>
17+#include <linux/skbuff.h>
18+#include <linux/tc_ematch/tc_em_ipt.h>
19+#include <linux/netfilter.h>
20+#include <linux/netfilter/x_tables.h>
21+#include <linux/netfilter_ipv4/ip_tables.h>
22+#include <linux/netfilter_ipv6/ip6_tables.h>
23+#include <net/pkt_cls.h>
24+
25+struct em_ipt_match {
26+ const struct xt_match *match;
27+ u32 hook;
28+ u8 match_data[0] __aligned(8);
29+};
30+
31+struct em_ipt_xt_match {
32+ char *match_name;
33+ int (*validate_match_data)(struct nlattr **tb, u8 mrev);
34+};
35+
36+static const struct nla_policy em_ipt_policy[TCA_EM_IPT_MAX + 1] = {
37+ [TCA_EM_IPT_MATCH_NAME] = { .type = NLA_STRING,
38+ .len = XT_EXTENSION_MAXNAMELEN },
39+ [TCA_EM_IPT_MATCH_REVISION] = { .type = NLA_U8 },
40+ [TCA_EM_IPT_HOOK] = { .type = NLA_U32 },
41+ [TCA_EM_IPT_NFPROTO] = { .type = NLA_U8 },
42+ [TCA_EM_IPT_MATCH_DATA] = { .type = NLA_UNSPEC },
43+};
44+
45+static int check_match(struct net *net, struct em_ipt_match *im, int mdata_len)
46+{
47+ struct xt_mtchk_param mtpar = {};
48+ union {
49+ struct ipt_entry e4;
50+ struct ip6t_entry e6;
51+ } e = {};
52+
53+ mtpar.net = net;
54+ mtpar.table = "filter";
55+ mtpar.hook_mask = 1 << im->hook;
56+ mtpar.family = im->match->family;
57+ mtpar.match = im->match;
58+ mtpar.entryinfo = &e;
59+ mtpar.matchinfo = (void *)im->match_data;
60+ return xt_check_match(&mtpar, mdata_len, 0, 0);
61+}
62+
63+static int policy_validate_match_data(struct nlattr **tb, u8 mrev)
64+{
65+ if (mrev != 0) {
66+ pr_err("only policy match revision 0 supported");
67+ return -EINVAL;
68+ }
69+
70+ if (nla_get_u32(tb[TCA_EM_IPT_HOOK]) != NF_INET_PRE_ROUTING) {
71+ pr_err("policy can only be matched on NF_INET_PRE_ROUTING");
72+ return -EINVAL;
73+ }
74+
75+ return 0;
76+}
77+
78+static const struct em_ipt_xt_match em_ipt_xt_matches[] = {
79+ {
80+ .match_name = "policy",
81+ .validate_match_data = policy_validate_match_data
82+ },
83+ {}
84+};
85+
86+static struct xt_match *get_xt_match(struct nlattr **tb)
87+{
88+ const struct em_ipt_xt_match *m;
89+ struct nlattr *mname_attr;
90+ u8 nfproto, mrev = 0;
91+ int ret;
92+
93+ mname_attr = tb[TCA_EM_IPT_MATCH_NAME];
94+ for (m = em_ipt_xt_matches; m->match_name; m++) {
95+ if (!nla_strcmp(mname_attr, m->match_name))
96+ break;
97+ }
98+
99+ if (!m->match_name) {
100+ pr_err("Unsupported xt match");
101+ return ERR_PTR(-EINVAL);
102+ }
103+
104+ if (tb[TCA_EM_IPT_MATCH_REVISION])
105+ mrev = nla_get_u8(tb[TCA_EM_IPT_MATCH_REVISION]);
106+
107+ ret = m->validate_match_data(tb, mrev);
108+ if (ret < 0)
109+ return ERR_PTR(ret);
110+
111+ nfproto = nla_get_u8(tb[TCA_EM_IPT_NFPROTO]);
112+ return xt_request_find_match(nfproto, m->match_name, mrev);
113+}
114+
115+static int em_ipt_change(struct net *net, void *data, int data_len,
116+ struct tcf_ematch *em)
117+{
118+ struct nlattr *tb[TCA_EM_IPT_MAX + 1];
119+ struct em_ipt_match *im = NULL;
120+ struct xt_match *match;
121+ int mdata_len, ret;
122+
123+ ret = nla_parse(tb, TCA_EM_IPT_MAX, data, data_len, em_ipt_policy,
124+ NULL);
125+ if (ret < 0)
126+ return ret;
127+
128+ if (!tb[TCA_EM_IPT_HOOK] || !tb[TCA_EM_IPT_MATCH_NAME] ||
129+ !tb[TCA_EM_IPT_MATCH_DATA] || !tb[TCA_EM_IPT_NFPROTO])
130+ return -EINVAL;
131+
132+ match = get_xt_match(tb);
133+ if (IS_ERR(match)) {
134+ pr_err("unable to load match\n");
135+ return PTR_ERR(match);
136+ }
137+
138+ mdata_len = XT_ALIGN(nla_len(tb[TCA_EM_IPT_MATCH_DATA]));
139+ im = kzalloc(sizeof(*im) + mdata_len, GFP_KERNEL);
140+ if (!im) {
141+ ret = -ENOMEM;
142+ goto err;
143+ }
144+
145+ im->match = match;
146+ im->hook = nla_get_u32(tb[TCA_EM_IPT_HOOK]);
147+ nla_memcpy(im->match_data, tb[TCA_EM_IPT_MATCH_DATA], mdata_len);
148+
149+ ret = check_match(net, im, mdata_len);
150+ if (ret)
151+ goto err;
152+
153+ em->datalen = sizeof(*im) + mdata_len;
154+ em->data = (unsigned long)im;
155+ return 0;
156+
157+err:
158+ kfree(im);
159+ module_put(match->me);
160+ return ret;
161+}
162+
163+static void em_ipt_destroy(struct tcf_ematch *em)
164+{
165+ struct em_ipt_match *im = (void *)em->data;
166+
167+ if (!im)
168+ return;
169+
170+ if (im->match->destroy) {
171+ struct xt_mtdtor_param par = {
172+ .net = em->net,
173+ .match = im->match,
174+ .matchinfo = im->match_data,
175+ .family = im->match->family
176+ };
177+ im->match->destroy(&par);
178+ }
179+ module_put(im->match->me);
180+ kfree((void *)im);
181+}
182+
183+static int em_ipt_match(struct sk_buff *skb, struct tcf_ematch *em,
184+ struct tcf_pkt_info *info)
185+{
186+ const struct em_ipt_match *im = (const void *)em->data;
187+ struct xt_action_param acpar = {};
188+ struct net_device *indev = NULL;
189+ struct nf_hook_state state;
190+ int ret;
191+
192+ rcu_read_lock();
193+
194+ if (skb->skb_iif)
195+ indev = dev_get_by_index_rcu(em->net, skb->skb_iif);
196+
197+ nf_hook_state_init(&state, im->hook, im->match->family,
198+ indev ?: skb->dev, skb->dev, NULL, em->net, NULL);
199+
200+ acpar.match = im->match;
201+ acpar.matchinfo = im->match_data;
202+ acpar.state = &state;
203+
204+ ret = im->match->match(skb, &acpar);
205+
206+ rcu_read_unlock();
207+ return ret;
208+}
209+
210+static int em_ipt_dump(struct sk_buff *skb, struct tcf_ematch *em)
211+{
212+ struct em_ipt_match *im = (void *)em->data;
213+
214+ if (nla_put_string(skb, TCA_EM_IPT_MATCH_NAME, im->match->name) < 0)
215+ return -EMSGSIZE;
216+ if (nla_put_u32(skb, TCA_EM_IPT_HOOK, im->hook) < 0)
217+ return -EMSGSIZE;
218+ if (nla_put_u8(skb, TCA_EM_IPT_MATCH_REVISION, im->match->revision) < 0)
219+ return -EMSGSIZE;
220+ if (nla_put_u8(skb, TCA_EM_IPT_NFPROTO, im->match->family) < 0)
221+ return -EMSGSIZE;
222+ if (nla_put(skb, TCA_EM_IPT_MATCH_DATA,
223+ im->match->usersize ?: im->match->matchsize,
224+ im->match_data) < 0)
225+ return -EMSGSIZE;
226+
227+ return 0;
228+}
229+
230+static struct tcf_ematch_ops em_ipt_ops = {
231+ .kind = TCF_EM_IPT,
232+ .change = em_ipt_change,
233+ .destroy = em_ipt_destroy,
234+ .match = em_ipt_match,
235+ .dump = em_ipt_dump,
236+ .owner = THIS_MODULE,
237+ .link = LIST_HEAD_INIT(em_ipt_ops.link)
238+};
239+
240+static int __init init_em_ipt(void)
241+{
242+ return tcf_em_register(&em_ipt_ops);
243+}
244+
245+static void __exit exit_em_ipt(void)
246+{
247+ tcf_em_unregister(&em_ipt_ops);
248+}
249+
250+MODULE_LICENSE("GPL");
251+MODULE_AUTHOR("Eyal Birger <eyal.birger@gmail.com>");
252+MODULE_DESCRIPTION("TC extended match for IPtables matches");
253+
254+module_init(init_em_ipt);
255+module_exit(exit_em_ipt);
256+
257+MODULE_ALIAS_TCF_EMATCH(TCF_EM_IPT);