2 * net/sched/em_ipset.c ipset ematch
4 * Copyright (c) 2012 Florian Westphal <fw@strlen.de>
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 * version 2 as published by the Free Software Foundation.
11 #include <linux/gfp.h>
12 #include <linux/module.h>
13 #include <linux/types.h>
14 #include <linux/kernel.h>
15 #include <linux/string.h>
16 #include <linux/skbuff.h>
17 #include <linux/netfilter/xt_set.h>
18 #include <linux/ipv6.h>
20 #include <net/pkt_cls.h>
22 static int em_ipset_change(struct tcf_proto
*tp
, void *data
, int data_len
,
23 struct tcf_ematch
*em
)
25 struct xt_set_info
*set
= data
;
27 struct net
*net
= dev_net(qdisc_dev(tp
->q
));
29 if (data_len
!= sizeof(*set
))
32 index
= ip_set_nfnl_get_byindex(net
, set
->index
);
33 if (index
== IPSET_INVALID_ID
)
36 em
->datalen
= sizeof(*set
);
37 em
->data
= (unsigned long)kmemdup(data
, em
->datalen
, GFP_KERNEL
);
41 ip_set_nfnl_put(net
, index
);
45 static void em_ipset_destroy(struct tcf_proto
*p
, struct tcf_ematch
*em
)
47 const struct xt_set_info
*set
= (const void *) em
->data
;
49 ip_set_nfnl_put(dev_net(qdisc_dev(p
->q
)), set
->index
);
50 kfree((void *) em
->data
);
54 static int em_ipset_match(struct sk_buff
*skb
, struct tcf_ematch
*em
,
55 struct tcf_pkt_info
*info
)
57 struct ip_set_adt_opt opt
;
58 struct xt_action_param acpar
;
59 const struct xt_set_info
*set
= (const void *) em
->data
;
60 struct net_device
*dev
, *indev
= NULL
;
61 int ret
, network_offset
;
63 switch (skb
->protocol
) {
65 acpar
.family
= NFPROTO_IPV4
;
66 if (!pskb_network_may_pull(skb
, sizeof(struct iphdr
)))
68 acpar
.thoff
= ip_hdrlen(skb
);
70 case htons(ETH_P_IPV6
):
71 acpar
.family
= NFPROTO_IPV6
;
72 if (!pskb_network_may_pull(skb
, sizeof(struct ipv6hdr
)))
74 /* doesn't call ipv6_find_hdr() because ipset doesn't use thoff, yet */
75 acpar
.thoff
= sizeof(struct ipv6hdr
);
83 opt
.family
= acpar
.family
;
85 opt
.flags
= set
->flags
;
87 opt
.ext
.timeout
= ~0u;
89 network_offset
= skb_network_offset(skb
);
90 skb_pull(skb
, network_offset
);
96 if (dev
&& skb
->skb_iif
)
97 indev
= dev_get_by_index_rcu(dev_net(dev
), skb
->skb_iif
);
99 acpar
.in
= indev
? indev
: dev
;
102 ret
= ip_set_test(set
->index
, skb
, &acpar
, &opt
);
106 skb_push(skb
, network_offset
);
110 static struct tcf_ematch_ops em_ipset_ops
= {
111 .kind
= TCF_EM_IPSET
,
112 .change
= em_ipset_change
,
113 .destroy
= em_ipset_destroy
,
114 .match
= em_ipset_match
,
115 .owner
= THIS_MODULE
,
116 .link
= LIST_HEAD_INIT(em_ipset_ops
.link
)
119 static int __init
init_em_ipset(void)
121 return tcf_em_register(&em_ipset_ops
);
124 static void __exit
exit_em_ipset(void)
126 tcf_em_unregister(&em_ipset_ops
);
129 MODULE_LICENSE("GPL");
130 MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
131 MODULE_DESCRIPTION("TC extended match for IP sets");
133 module_init(init_em_ipset
);
134 module_exit(exit_em_ipset
);
136 MODULE_ALIAS_TCF_EMATCH(TCF_EM_IPSET
);