1 // SPDX-License-Identifier: GPL-2.0-only
3 * net/sched/em_ipset.c ipset ematch
5 * Copyright (c) 2012 Florian Westphal <fw@strlen.de>
9 #include <linux/module.h>
10 #include <linux/types.h>
11 #include <linux/kernel.h>
12 #include <linux/string.h>
13 #include <linux/skbuff.h>
14 #include <linux/netfilter/xt_set.h>
15 #include <linux/ipv6.h>
17 #include <net/pkt_cls.h>
19 static int em_ipset_change(struct net
*net
, void *data
, int data_len
,
20 struct tcf_ematch
*em
)
22 struct xt_set_info
*set
= data
;
25 if (data_len
!= sizeof(*set
))
28 index
= ip_set_nfnl_get_byindex(net
, set
->index
);
29 if (index
== IPSET_INVALID_ID
)
32 em
->datalen
= sizeof(*set
);
33 em
->data
= (unsigned long)kmemdup(data
, em
->datalen
, GFP_KERNEL
);
37 ip_set_nfnl_put(net
, index
);
41 static void em_ipset_destroy(struct tcf_ematch
*em
)
43 const struct xt_set_info
*set
= (const void *) em
->data
;
45 ip_set_nfnl_put(em
->net
, set
->index
);
46 kfree((void *) em
->data
);
50 static int em_ipset_match(struct sk_buff
*skb
, struct tcf_ematch
*em
,
51 struct tcf_pkt_info
*info
)
53 struct ip_set_adt_opt opt
;
54 struct xt_action_param acpar
;
55 const struct xt_set_info
*set
= (const void *) em
->data
;
56 struct net_device
*dev
, *indev
= NULL
;
57 struct nf_hook_state state
= {
60 int ret
, network_offset
;
62 switch (skb_protocol(skb
, true)) {
64 state
.pf
= NFPROTO_IPV4
;
65 if (!pskb_network_may_pull(skb
, sizeof(struct iphdr
)))
67 acpar
.thoff
= ip_hdrlen(skb
);
69 case htons(ETH_P_IPV6
):
70 state
.pf
= NFPROTO_IPV6
;
71 if (!pskb_network_may_pull(skb
, sizeof(struct ipv6hdr
)))
73 /* doesn't call ipv6_find_hdr() because ipset doesn't use thoff, yet */
74 acpar
.thoff
= sizeof(struct ipv6hdr
);
80 opt
.family
= state
.pf
;
82 opt
.flags
= set
->flags
;
84 opt
.ext
.timeout
= ~0u;
86 network_offset
= skb_network_offset(skb
);
87 skb_pull(skb
, network_offset
);
94 indev
= dev_get_by_index_rcu(em
->net
, skb
->skb_iif
);
96 state
.in
= indev
? indev
: dev
;
100 ret
= ip_set_test(set
->index
, skb
, &acpar
, &opt
);
104 skb_push(skb
, network_offset
);
108 static struct tcf_ematch_ops em_ipset_ops
= {
109 .kind
= TCF_EM_IPSET
,
110 .change
= em_ipset_change
,
111 .destroy
= em_ipset_destroy
,
112 .match
= em_ipset_match
,
113 .owner
= THIS_MODULE
,
114 .link
= LIST_HEAD_INIT(em_ipset_ops
.link
)
117 static int __init
init_em_ipset(void)
119 return tcf_em_register(&em_ipset_ops
);
122 static void __exit
exit_em_ipset(void)
124 tcf_em_unregister(&em_ipset_ops
);
127 MODULE_LICENSE("GPL");
128 MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
129 MODULE_DESCRIPTION("TC extended match for IP sets");
131 module_init(init_em_ipset
);
132 module_exit(exit_em_ipset
);
134 MODULE_ALIAS_TCF_EMATCH(TCF_EM_IPSET
);