xref: /OK3568_Linux_fs/kernel/net/sched/em_ipset.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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