1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * net/sched/em_ipset.c ipset ematch
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (c) 2012 Florian Westphal <fw@strlen.de>
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun #include <linux/gfp.h>
9*4882a593Smuzhiyun #include <linux/module.h>
10*4882a593Smuzhiyun #include <linux/types.h>
11*4882a593Smuzhiyun #include <linux/kernel.h>
12*4882a593Smuzhiyun #include <linux/string.h>
13*4882a593Smuzhiyun #include <linux/skbuff.h>
14*4882a593Smuzhiyun #include <linux/netfilter/xt_set.h>
15*4882a593Smuzhiyun #include <linux/ipv6.h>
16*4882a593Smuzhiyun #include <net/ip.h>
17*4882a593Smuzhiyun #include <net/pkt_cls.h>
18*4882a593Smuzhiyun
em_ipset_change(struct net * net,void * data,int data_len,struct tcf_ematch * em)19*4882a593Smuzhiyun static int em_ipset_change(struct net *net, void *data, int data_len,
20*4882a593Smuzhiyun struct tcf_ematch *em)
21*4882a593Smuzhiyun {
22*4882a593Smuzhiyun struct xt_set_info *set = data;
23*4882a593Smuzhiyun ip_set_id_t index;
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun if (data_len != sizeof(*set))
26*4882a593Smuzhiyun return -EINVAL;
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun index = ip_set_nfnl_get_byindex(net, set->index);
29*4882a593Smuzhiyun if (index == IPSET_INVALID_ID)
30*4882a593Smuzhiyun return -ENOENT;
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun em->datalen = sizeof(*set);
33*4882a593Smuzhiyun em->data = (unsigned long)kmemdup(data, em->datalen, GFP_KERNEL);
34*4882a593Smuzhiyun if (em->data)
35*4882a593Smuzhiyun return 0;
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun ip_set_nfnl_put(net, index);
38*4882a593Smuzhiyun return -ENOMEM;
39*4882a593Smuzhiyun }
40*4882a593Smuzhiyun
em_ipset_destroy(struct tcf_ematch * em)41*4882a593Smuzhiyun static void em_ipset_destroy(struct tcf_ematch *em)
42*4882a593Smuzhiyun {
43*4882a593Smuzhiyun const struct xt_set_info *set = (const void *) em->data;
44*4882a593Smuzhiyun if (set) {
45*4882a593Smuzhiyun ip_set_nfnl_put(em->net, set->index);
46*4882a593Smuzhiyun kfree((void *) em->data);
47*4882a593Smuzhiyun }
48*4882a593Smuzhiyun }
49*4882a593Smuzhiyun
em_ipset_match(struct sk_buff * skb,struct tcf_ematch * em,struct tcf_pkt_info * info)50*4882a593Smuzhiyun static int em_ipset_match(struct sk_buff *skb, struct tcf_ematch *em,
51*4882a593Smuzhiyun struct tcf_pkt_info *info)
52*4882a593Smuzhiyun {
53*4882a593Smuzhiyun struct ip_set_adt_opt opt;
54*4882a593Smuzhiyun struct xt_action_param acpar;
55*4882a593Smuzhiyun const struct xt_set_info *set = (const void *) em->data;
56*4882a593Smuzhiyun struct net_device *dev, *indev = NULL;
57*4882a593Smuzhiyun struct nf_hook_state state = {
58*4882a593Smuzhiyun .net = em->net,
59*4882a593Smuzhiyun };
60*4882a593Smuzhiyun int ret, network_offset;
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun switch (skb_protocol(skb, true)) {
63*4882a593Smuzhiyun case htons(ETH_P_IP):
64*4882a593Smuzhiyun state.pf = NFPROTO_IPV4;
65*4882a593Smuzhiyun if (!pskb_network_may_pull(skb, sizeof(struct iphdr)))
66*4882a593Smuzhiyun return 0;
67*4882a593Smuzhiyun acpar.thoff = ip_hdrlen(skb);
68*4882a593Smuzhiyun break;
69*4882a593Smuzhiyun case htons(ETH_P_IPV6):
70*4882a593Smuzhiyun state.pf = NFPROTO_IPV6;
71*4882a593Smuzhiyun if (!pskb_network_may_pull(skb, sizeof(struct ipv6hdr)))
72*4882a593Smuzhiyun return 0;
73*4882a593Smuzhiyun /* doesn't call ipv6_find_hdr() because ipset doesn't use thoff, yet */
74*4882a593Smuzhiyun acpar.thoff = sizeof(struct ipv6hdr);
75*4882a593Smuzhiyun break;
76*4882a593Smuzhiyun default:
77*4882a593Smuzhiyun return 0;
78*4882a593Smuzhiyun }
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun opt.family = state.pf;
81*4882a593Smuzhiyun opt.dim = set->dim;
82*4882a593Smuzhiyun opt.flags = set->flags;
83*4882a593Smuzhiyun opt.cmdflags = 0;
84*4882a593Smuzhiyun opt.ext.timeout = ~0u;
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun network_offset = skb_network_offset(skb);
87*4882a593Smuzhiyun skb_pull(skb, network_offset);
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun dev = skb->dev;
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun rcu_read_lock();
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun if (skb->skb_iif)
94*4882a593Smuzhiyun indev = dev_get_by_index_rcu(em->net, skb->skb_iif);
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun state.in = indev ? indev : dev;
97*4882a593Smuzhiyun state.out = dev;
98*4882a593Smuzhiyun acpar.state = &state;
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun ret = ip_set_test(set->index, skb, &acpar, &opt);
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun rcu_read_unlock();
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun skb_push(skb, network_offset);
105*4882a593Smuzhiyun return ret;
106*4882a593Smuzhiyun }
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun static struct tcf_ematch_ops em_ipset_ops = {
109*4882a593Smuzhiyun .kind = TCF_EM_IPSET,
110*4882a593Smuzhiyun .change = em_ipset_change,
111*4882a593Smuzhiyun .destroy = em_ipset_destroy,
112*4882a593Smuzhiyun .match = em_ipset_match,
113*4882a593Smuzhiyun .owner = THIS_MODULE,
114*4882a593Smuzhiyun .link = LIST_HEAD_INIT(em_ipset_ops.link)
115*4882a593Smuzhiyun };
116*4882a593Smuzhiyun
init_em_ipset(void)117*4882a593Smuzhiyun static int __init init_em_ipset(void)
118*4882a593Smuzhiyun {
119*4882a593Smuzhiyun return tcf_em_register(&em_ipset_ops);
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun
exit_em_ipset(void)122*4882a593Smuzhiyun static void __exit exit_em_ipset(void)
123*4882a593Smuzhiyun {
124*4882a593Smuzhiyun tcf_em_unregister(&em_ipset_ops);
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun MODULE_LICENSE("GPL");
128*4882a593Smuzhiyun MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
129*4882a593Smuzhiyun MODULE_DESCRIPTION("TC extended match for IP sets");
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun module_init(init_em_ipset);
132*4882a593Smuzhiyun module_exit(exit_em_ipset);
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun MODULE_ALIAS_TCF_EMATCH(TCF_EM_IPSET);
135