12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2ccc007e4SEyal Birger /*
3ccc007e4SEyal Birger * net/sched/em_ipt.c IPtables matches Ematch
4ccc007e4SEyal Birger *
5ccc007e4SEyal Birger * (c) 2018 Eyal Birger <eyal.birger@gmail.com>
6ccc007e4SEyal Birger */
7ccc007e4SEyal Birger
8ccc007e4SEyal Birger #include <linux/gfp.h>
9ccc007e4SEyal Birger #include <linux/module.h>
10ccc007e4SEyal Birger #include <linux/types.h>
11ccc007e4SEyal Birger #include <linux/kernel.h>
12ccc007e4SEyal Birger #include <linux/string.h>
13ccc007e4SEyal Birger #include <linux/skbuff.h>
14ccc007e4SEyal Birger #include <linux/tc_ematch/tc_em_ipt.h>
15ccc007e4SEyal Birger #include <linux/netfilter.h>
16ccc007e4SEyal Birger #include <linux/netfilter/x_tables.h>
17ccc007e4SEyal Birger #include <linux/netfilter_ipv4/ip_tables.h>
18ccc007e4SEyal Birger #include <linux/netfilter_ipv6/ip6_tables.h>
19ccc007e4SEyal Birger #include <net/pkt_cls.h>
20ccc007e4SEyal Birger
21ccc007e4SEyal Birger struct em_ipt_match {
22ccc007e4SEyal Birger const struct xt_match *match;
23ccc007e4SEyal Birger u32 hook;
24ba3d24d4SNikolay Aleksandrov u8 nfproto;
25b90feaffSGustavo A. R. Silva u8 match_data[] __aligned(8);
26ccc007e4SEyal Birger };
27ccc007e4SEyal Birger
28ccc007e4SEyal Birger struct em_ipt_xt_match {
29ccc007e4SEyal Birger char *match_name;
30ccc007e4SEyal Birger int (*validate_match_data)(struct nlattr **tb, u8 mrev);
31ccc007e4SEyal Birger };
32ccc007e4SEyal Birger
33ccc007e4SEyal Birger static const struct nla_policy em_ipt_policy[TCA_EM_IPT_MAX + 1] = {
34ccc007e4SEyal Birger [TCA_EM_IPT_MATCH_NAME] = { .type = NLA_STRING,
35ccc007e4SEyal Birger .len = XT_EXTENSION_MAXNAMELEN },
36ccc007e4SEyal Birger [TCA_EM_IPT_MATCH_REVISION] = { .type = NLA_U8 },
37ccc007e4SEyal Birger [TCA_EM_IPT_HOOK] = { .type = NLA_U32 },
38ccc007e4SEyal Birger [TCA_EM_IPT_NFPROTO] = { .type = NLA_U8 },
39ccc007e4SEyal Birger [TCA_EM_IPT_MATCH_DATA] = { .type = NLA_UNSPEC },
40ccc007e4SEyal Birger };
41ccc007e4SEyal Birger
check_match(struct net * net,struct em_ipt_match * im,int mdata_len)42ccc007e4SEyal Birger static int check_match(struct net *net, struct em_ipt_match *im, int mdata_len)
43ccc007e4SEyal Birger {
44ccc007e4SEyal Birger struct xt_mtchk_param mtpar = {};
45ccc007e4SEyal Birger union {
46ccc007e4SEyal Birger struct ipt_entry e4;
47ccc007e4SEyal Birger struct ip6t_entry e6;
48ccc007e4SEyal Birger } e = {};
49ccc007e4SEyal Birger
50ccc007e4SEyal Birger mtpar.net = net;
51ccc007e4SEyal Birger mtpar.table = "filter";
52ccc007e4SEyal Birger mtpar.hook_mask = 1 << im->hook;
53ccc007e4SEyal Birger mtpar.family = im->match->family;
54ccc007e4SEyal Birger mtpar.match = im->match;
55ccc007e4SEyal Birger mtpar.entryinfo = &e;
56ccc007e4SEyal Birger mtpar.matchinfo = (void *)im->match_data;
57ccc007e4SEyal Birger return xt_check_match(&mtpar, mdata_len, 0, 0);
58ccc007e4SEyal Birger }
59ccc007e4SEyal Birger
policy_validate_match_data(struct nlattr ** tb,u8 mrev)60ccc007e4SEyal Birger static int policy_validate_match_data(struct nlattr **tb, u8 mrev)
61ccc007e4SEyal Birger {
62ccc007e4SEyal Birger if (mrev != 0) {
63ccc007e4SEyal Birger pr_err("only policy match revision 0 supported");
64ccc007e4SEyal Birger return -EINVAL;
65ccc007e4SEyal Birger }
66ccc007e4SEyal Birger
67ccc007e4SEyal Birger if (nla_get_u32(tb[TCA_EM_IPT_HOOK]) != NF_INET_PRE_ROUTING) {
68ccc007e4SEyal Birger pr_err("policy can only be matched on NF_INET_PRE_ROUTING");
69ccc007e4SEyal Birger return -EINVAL;
70ccc007e4SEyal Birger }
71ccc007e4SEyal Birger
72ccc007e4SEyal Birger return 0;
73ccc007e4SEyal Birger }
74ccc007e4SEyal Birger
addrtype_validate_match_data(struct nlattr ** tb,u8 mrev)750c4231c7SNikolay Aleksandrov static int addrtype_validate_match_data(struct nlattr **tb, u8 mrev)
760c4231c7SNikolay Aleksandrov {
770c4231c7SNikolay Aleksandrov if (mrev != 1) {
780c4231c7SNikolay Aleksandrov pr_err("only addrtype match revision 1 supported");
790c4231c7SNikolay Aleksandrov return -EINVAL;
800c4231c7SNikolay Aleksandrov }
810c4231c7SNikolay Aleksandrov
820c4231c7SNikolay Aleksandrov return 0;
830c4231c7SNikolay Aleksandrov }
840c4231c7SNikolay Aleksandrov
85ccc007e4SEyal Birger static const struct em_ipt_xt_match em_ipt_xt_matches[] = {
86ccc007e4SEyal Birger {
87ccc007e4SEyal Birger .match_name = "policy",
88ccc007e4SEyal Birger .validate_match_data = policy_validate_match_data
89ccc007e4SEyal Birger },
900c4231c7SNikolay Aleksandrov {
910c4231c7SNikolay Aleksandrov .match_name = "addrtype",
920c4231c7SNikolay Aleksandrov .validate_match_data = addrtype_validate_match_data
930c4231c7SNikolay Aleksandrov },
94ccc007e4SEyal Birger {}
95ccc007e4SEyal Birger };
96ccc007e4SEyal Birger
get_xt_match(struct nlattr ** tb)97ccc007e4SEyal Birger static struct xt_match *get_xt_match(struct nlattr **tb)
98ccc007e4SEyal Birger {
99ccc007e4SEyal Birger const struct em_ipt_xt_match *m;
100ccc007e4SEyal Birger struct nlattr *mname_attr;
101ccc007e4SEyal Birger u8 nfproto, mrev = 0;
102ccc007e4SEyal Birger int ret;
103ccc007e4SEyal Birger
104ccc007e4SEyal Birger mname_attr = tb[TCA_EM_IPT_MATCH_NAME];
105ccc007e4SEyal Birger for (m = em_ipt_xt_matches; m->match_name; m++) {
106ccc007e4SEyal Birger if (!nla_strcmp(mname_attr, m->match_name))
107ccc007e4SEyal Birger break;
108ccc007e4SEyal Birger }
109ccc007e4SEyal Birger
110ccc007e4SEyal Birger if (!m->match_name) {
111ccc007e4SEyal Birger pr_err("Unsupported xt match");
112ccc007e4SEyal Birger return ERR_PTR(-EINVAL);
113ccc007e4SEyal Birger }
114ccc007e4SEyal Birger
115ccc007e4SEyal Birger if (tb[TCA_EM_IPT_MATCH_REVISION])
116ccc007e4SEyal Birger mrev = nla_get_u8(tb[TCA_EM_IPT_MATCH_REVISION]);
117ccc007e4SEyal Birger
118ccc007e4SEyal Birger ret = m->validate_match_data(tb, mrev);
119ccc007e4SEyal Birger if (ret < 0)
120ccc007e4SEyal Birger return ERR_PTR(ret);
121ccc007e4SEyal Birger
122ccc007e4SEyal Birger nfproto = nla_get_u8(tb[TCA_EM_IPT_NFPROTO]);
123ccc007e4SEyal Birger return xt_request_find_match(nfproto, m->match_name, mrev);
124ccc007e4SEyal Birger }
125ccc007e4SEyal Birger
em_ipt_change(struct net * net,void * data,int data_len,struct tcf_ematch * em)126ccc007e4SEyal Birger static int em_ipt_change(struct net *net, void *data, int data_len,
127ccc007e4SEyal Birger struct tcf_ematch *em)
128ccc007e4SEyal Birger {
129ccc007e4SEyal Birger struct nlattr *tb[TCA_EM_IPT_MAX + 1];
130ccc007e4SEyal Birger struct em_ipt_match *im = NULL;
131ccc007e4SEyal Birger struct xt_match *match;
132ccc007e4SEyal Birger int mdata_len, ret;
133ba3d24d4SNikolay Aleksandrov u8 nfproto;
134ccc007e4SEyal Birger
1358cb08174SJohannes Berg ret = nla_parse_deprecated(tb, TCA_EM_IPT_MAX, data, data_len,
1368cb08174SJohannes Berg em_ipt_policy, NULL);
137ccc007e4SEyal Birger if (ret < 0)
138ccc007e4SEyal Birger return ret;
139ccc007e4SEyal Birger
140ccc007e4SEyal Birger if (!tb[TCA_EM_IPT_HOOK] || !tb[TCA_EM_IPT_MATCH_NAME] ||
141ccc007e4SEyal Birger !tb[TCA_EM_IPT_MATCH_DATA] || !tb[TCA_EM_IPT_NFPROTO])
142ccc007e4SEyal Birger return -EINVAL;
143ccc007e4SEyal Birger
144ba3d24d4SNikolay Aleksandrov nfproto = nla_get_u8(tb[TCA_EM_IPT_NFPROTO]);
145ba3d24d4SNikolay Aleksandrov switch (nfproto) {
146ba3d24d4SNikolay Aleksandrov case NFPROTO_IPV4:
147ba3d24d4SNikolay Aleksandrov case NFPROTO_IPV6:
148ba3d24d4SNikolay Aleksandrov break;
149ba3d24d4SNikolay Aleksandrov default:
150ba3d24d4SNikolay Aleksandrov return -EINVAL;
151ba3d24d4SNikolay Aleksandrov }
152ba3d24d4SNikolay Aleksandrov
153ccc007e4SEyal Birger match = get_xt_match(tb);
154ccc007e4SEyal Birger if (IS_ERR(match)) {
155ccc007e4SEyal Birger pr_err("unable to load match\n");
156ccc007e4SEyal Birger return PTR_ERR(match);
157ccc007e4SEyal Birger }
158ccc007e4SEyal Birger
159ccc007e4SEyal Birger mdata_len = XT_ALIGN(nla_len(tb[TCA_EM_IPT_MATCH_DATA]));
160ccc007e4SEyal Birger im = kzalloc(sizeof(*im) + mdata_len, GFP_KERNEL);
161ccc007e4SEyal Birger if (!im) {
162ccc007e4SEyal Birger ret = -ENOMEM;
163ccc007e4SEyal Birger goto err;
164ccc007e4SEyal Birger }
165ccc007e4SEyal Birger
166ccc007e4SEyal Birger im->match = match;
167ccc007e4SEyal Birger im->hook = nla_get_u32(tb[TCA_EM_IPT_HOOK]);
168ba3d24d4SNikolay Aleksandrov im->nfproto = nfproto;
169ccc007e4SEyal Birger nla_memcpy(im->match_data, tb[TCA_EM_IPT_MATCH_DATA], mdata_len);
170ccc007e4SEyal Birger
171ccc007e4SEyal Birger ret = check_match(net, im, mdata_len);
172ccc007e4SEyal Birger if (ret)
173ccc007e4SEyal Birger goto err;
174ccc007e4SEyal Birger
175ccc007e4SEyal Birger em->datalen = sizeof(*im) + mdata_len;
176ccc007e4SEyal Birger em->data = (unsigned long)im;
177ccc007e4SEyal Birger return 0;
178ccc007e4SEyal Birger
179ccc007e4SEyal Birger err:
180ccc007e4SEyal Birger kfree(im);
181ccc007e4SEyal Birger module_put(match->me);
182ccc007e4SEyal Birger return ret;
183ccc007e4SEyal Birger }
184ccc007e4SEyal Birger
em_ipt_destroy(struct tcf_ematch * em)185ccc007e4SEyal Birger static void em_ipt_destroy(struct tcf_ematch *em)
186ccc007e4SEyal Birger {
187ccc007e4SEyal Birger struct em_ipt_match *im = (void *)em->data;
188ccc007e4SEyal Birger
189ccc007e4SEyal Birger if (!im)
190ccc007e4SEyal Birger return;
191ccc007e4SEyal Birger
192ccc007e4SEyal Birger if (im->match->destroy) {
193ccc007e4SEyal Birger struct xt_mtdtor_param par = {
194ccc007e4SEyal Birger .net = em->net,
195ccc007e4SEyal Birger .match = im->match,
196ccc007e4SEyal Birger .matchinfo = im->match_data,
197ccc007e4SEyal Birger .family = im->match->family
198ccc007e4SEyal Birger };
199ccc007e4SEyal Birger im->match->destroy(&par);
200ccc007e4SEyal Birger }
201ccc007e4SEyal Birger module_put(im->match->me);
2023c9143d9SXu Wang kfree(im);
203ccc007e4SEyal Birger }
204ccc007e4SEyal Birger
em_ipt_match(struct sk_buff * skb,struct tcf_ematch * em,struct tcf_pkt_info * info)205ccc007e4SEyal Birger static int em_ipt_match(struct sk_buff *skb, struct tcf_ematch *em,
206ccc007e4SEyal Birger struct tcf_pkt_info *info)
207ccc007e4SEyal Birger {
208ccc007e4SEyal Birger const struct em_ipt_match *im = (const void *)em->data;
209ccc007e4SEyal Birger struct xt_action_param acpar = {};
210ccc007e4SEyal Birger struct net_device *indev = NULL;
211f4c1c40cSNikolay Aleksandrov u8 nfproto = im->match->family;
212ccc007e4SEyal Birger struct nf_hook_state state;
213ccc007e4SEyal Birger int ret;
214ccc007e4SEyal Birger
215*d7bf2ebeSToke Høiland-Jørgensen switch (skb_protocol(skb, true)) {
2169e10edd7SNikolay Aleksandrov case htons(ETH_P_IP):
2179e10edd7SNikolay Aleksandrov if (!pskb_network_may_pull(skb, sizeof(struct iphdr)))
2189e10edd7SNikolay Aleksandrov return 0;
219f4c1c40cSNikolay Aleksandrov if (nfproto == NFPROTO_UNSPEC)
220f4c1c40cSNikolay Aleksandrov nfproto = NFPROTO_IPV4;
2219e10edd7SNikolay Aleksandrov break;
2229e10edd7SNikolay Aleksandrov case htons(ETH_P_IPV6):
2239e10edd7SNikolay Aleksandrov if (!pskb_network_may_pull(skb, sizeof(struct ipv6hdr)))
2249e10edd7SNikolay Aleksandrov return 0;
225f4c1c40cSNikolay Aleksandrov if (nfproto == NFPROTO_UNSPEC)
226f4c1c40cSNikolay Aleksandrov nfproto = NFPROTO_IPV6;
2279e10edd7SNikolay Aleksandrov break;
2289e10edd7SNikolay Aleksandrov default:
2299e10edd7SNikolay Aleksandrov return 0;
2309e10edd7SNikolay Aleksandrov }
2319e10edd7SNikolay Aleksandrov
232ccc007e4SEyal Birger rcu_read_lock();
233ccc007e4SEyal Birger
234ccc007e4SEyal Birger if (skb->skb_iif)
235ccc007e4SEyal Birger indev = dev_get_by_index_rcu(em->net, skb->skb_iif);
236ccc007e4SEyal Birger
237f4c1c40cSNikolay Aleksandrov nf_hook_state_init(&state, im->hook, nfproto,
238ccc007e4SEyal Birger indev ?: skb->dev, skb->dev, NULL, em->net, NULL);
239ccc007e4SEyal Birger
240ccc007e4SEyal Birger acpar.match = im->match;
241ccc007e4SEyal Birger acpar.matchinfo = im->match_data;
242ccc007e4SEyal Birger acpar.state = &state;
243ccc007e4SEyal Birger
244ccc007e4SEyal Birger ret = im->match->match(skb, &acpar);
245ccc007e4SEyal Birger
246ccc007e4SEyal Birger rcu_read_unlock();
247ccc007e4SEyal Birger return ret;
248ccc007e4SEyal Birger }
249ccc007e4SEyal Birger
em_ipt_dump(struct sk_buff * skb,struct tcf_ematch * em)250ccc007e4SEyal Birger static int em_ipt_dump(struct sk_buff *skb, struct tcf_ematch *em)
251ccc007e4SEyal Birger {
252ccc007e4SEyal Birger struct em_ipt_match *im = (void *)em->data;
253ccc007e4SEyal Birger
254ccc007e4SEyal Birger if (nla_put_string(skb, TCA_EM_IPT_MATCH_NAME, im->match->name) < 0)
255ccc007e4SEyal Birger return -EMSGSIZE;
256ccc007e4SEyal Birger if (nla_put_u32(skb, TCA_EM_IPT_HOOK, im->hook) < 0)
257ccc007e4SEyal Birger return -EMSGSIZE;
258ccc007e4SEyal Birger if (nla_put_u8(skb, TCA_EM_IPT_MATCH_REVISION, im->match->revision) < 0)
259ccc007e4SEyal Birger return -EMSGSIZE;
260ba3d24d4SNikolay Aleksandrov if (nla_put_u8(skb, TCA_EM_IPT_NFPROTO, im->nfproto) < 0)
261ccc007e4SEyal Birger return -EMSGSIZE;
262ccc007e4SEyal Birger if (nla_put(skb, TCA_EM_IPT_MATCH_DATA,
263ccc007e4SEyal Birger im->match->usersize ?: im->match->matchsize,
264ccc007e4SEyal Birger im->match_data) < 0)
265ccc007e4SEyal Birger return -EMSGSIZE;
266ccc007e4SEyal Birger
267ccc007e4SEyal Birger return 0;
268ccc007e4SEyal Birger }
269ccc007e4SEyal Birger
270ccc007e4SEyal Birger static struct tcf_ematch_ops em_ipt_ops = {
271ccc007e4SEyal Birger .kind = TCF_EM_IPT,
272ccc007e4SEyal Birger .change = em_ipt_change,
273ccc007e4SEyal Birger .destroy = em_ipt_destroy,
274ccc007e4SEyal Birger .match = em_ipt_match,
275ccc007e4SEyal Birger .dump = em_ipt_dump,
276ccc007e4SEyal Birger .owner = THIS_MODULE,
277ccc007e4SEyal Birger .link = LIST_HEAD_INIT(em_ipt_ops.link)
278ccc007e4SEyal Birger };
279ccc007e4SEyal Birger
init_em_ipt(void)280ccc007e4SEyal Birger static int __init init_em_ipt(void)
281ccc007e4SEyal Birger {
282ccc007e4SEyal Birger return tcf_em_register(&em_ipt_ops);
283ccc007e4SEyal Birger }
284ccc007e4SEyal Birger
exit_em_ipt(void)285ccc007e4SEyal Birger static void __exit exit_em_ipt(void)
286ccc007e4SEyal Birger {
287ccc007e4SEyal Birger tcf_em_unregister(&em_ipt_ops);
288ccc007e4SEyal Birger }
289ccc007e4SEyal Birger
290ccc007e4SEyal Birger MODULE_LICENSE("GPL");
291ccc007e4SEyal Birger MODULE_AUTHOR("Eyal Birger <eyal.birger@gmail.com>");
292ccc007e4SEyal Birger MODULE_DESCRIPTION("TC extended match for IPtables matches");
293ccc007e4SEyal Birger
294ccc007e4SEyal Birger module_init(init_em_ipt);
295ccc007e4SEyal Birger module_exit(exit_em_ipt);
296ccc007e4SEyal Birger
297ccc007e4SEyal Birger MODULE_ALIAS_TCF_EMATCH(TCF_EM_IPT);
298