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
555#define TCF_EM_VLAN 6555#define TCF_EM_VLAN 6
556#define TCF_EM_CANID 7556#define TCF_EM_CANID 7
557#define TCF_EM_IPSET 8557#define TCF_EM_IPSET 8
558#define TCF_EM_MAX 8558#define TCF_EM_IPT 9
559#define TCF_EM_MAX 9
559 560
560enum {561enum {
561 TCF_EM_PROG_TC562 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
8enum {
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
658 To compile this code as a module, choose M here: the658 To compile this code as a module, choose M here: the
659 module will be called em_ipset.659 module will be called em_ipset.
660 660
661config 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
661config NET_CLS_ACT673config NET_CLS_ACT
662 bool "Actions"674 bool "Actions"
663 select NET_CLS675 select NET_CLS
net/sched/Makefile+1−0
@@ -75,3 +75,4 @@ obj-$(CONFIG_NET_EMATCH_META) += em_meta.o · 74 unmodified lines
75obj-$(CONFIG_NET_EMATCH_TEXT) += em_text.o75obj-$(CONFIG_NET_EMATCH_TEXT) += em_text.o
76obj-$(CONFIG_NET_EMATCH_CANID) += em_canid.o76obj-$(CONFIG_NET_EMATCH_CANID) += em_canid.o
77obj-$(CONFIG_NET_EMATCH_IPSET) += em_ipset.o77obj-$(CONFIG_NET_EMATCH_IPSET) += em_ipset.o
78obj-$(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
25struct em_ipt_match {
26 const struct xt_match *match;
27 u32 hook;
28 u8 match_data[0] __aligned(8);
29};
30
31struct em_ipt_xt_match {
32 char *match_name;
33 int (*validate_match_data)(struct nlattr **tb, u8 mrev);
34};
35
36static 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
45static 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
63static 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
78static 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
86static 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
115static 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
157err:
158 kfree(im);
159 module_put(match->me);
160 return ret;
161}
162
163static 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
183static 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
210static 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
230static 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
240static int __init init_em_ipt(void)
241{
242 return tcf_em_register(&em_ipt_ops);
243}
244
245static void __exit exit_em_ipt(void)
246{
247 tcf_em_unregister(&em_ipt_ops);
248}
249
250MODULE_LICENSE("GPL");
251MODULE_AUTHOR("Eyal Birger <eyal.birger@gmail.com>");
252MODULE_DESCRIPTION("TC extended match for IPtables matches");
253
254module_init(init_em_ipt);
255module_exit(exit_em_ipt);
256
257MODULE_ALIAS_TCF_EMATCH(TCF_EM_IPT);