xref: /openbmc/linux/net/netfilter/xt_set.c (revision bd3129fc)
1d956798dSJozsef Kadlecsik /* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
2d956798dSJozsef Kadlecsik  *                         Patrick Schaaf <bof@bof.de>
3d956798dSJozsef Kadlecsik  *                         Martin Josefsson <gandalf@wlug.westbo.se>
4075e64c0SJozsef Kadlecsik  * Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
5d956798dSJozsef Kadlecsik  *
6d956798dSJozsef Kadlecsik  * This program is free software; you can redistribute it and/or modify
7d956798dSJozsef Kadlecsik  * it under the terms of the GNU General Public License version 2 as
8d956798dSJozsef Kadlecsik  * published by the Free Software Foundation.
9d956798dSJozsef Kadlecsik  */
10d956798dSJozsef Kadlecsik 
11d956798dSJozsef Kadlecsik /* Kernel module which implements the set match and SET target
12d956798dSJozsef Kadlecsik  * for netfilter/iptables. */
13d956798dSJozsef Kadlecsik 
14d956798dSJozsef Kadlecsik #include <linux/module.h>
15d956798dSJozsef Kadlecsik #include <linux/skbuff.h>
16d956798dSJozsef Kadlecsik 
17d956798dSJozsef Kadlecsik #include <linux/netfilter/x_tables.h>
18d956798dSJozsef Kadlecsik #include <linux/netfilter/xt_set.h>
19a73f89a6SJozsef Kadlecsik #include <linux/netfilter/ipset/ip_set_timeout.h>
20d956798dSJozsef Kadlecsik 
21d956798dSJozsef Kadlecsik MODULE_LICENSE("GPL");
22d956798dSJozsef Kadlecsik MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
23d956798dSJozsef Kadlecsik MODULE_DESCRIPTION("Xtables: IP set match and target module");
24d956798dSJozsef Kadlecsik MODULE_ALIAS("xt_SET");
25d956798dSJozsef Kadlecsik MODULE_ALIAS("ipt_set");
26d956798dSJozsef Kadlecsik MODULE_ALIAS("ip6t_set");
27d956798dSJozsef Kadlecsik MODULE_ALIAS("ipt_SET");
28d956798dSJozsef Kadlecsik MODULE_ALIAS("ip6t_SET");
29d956798dSJozsef Kadlecsik 
30d956798dSJozsef Kadlecsik static inline int
31d956798dSJozsef Kadlecsik match_set(ip_set_id_t index, const struct sk_buff *skb,
32b66554cfSJozsef Kadlecsik 	  const struct xt_action_param *par,
33075e64c0SJozsef Kadlecsik 	  struct ip_set_adt_opt *opt, int inv)
34d956798dSJozsef Kadlecsik {
35b66554cfSJozsef Kadlecsik 	if (ip_set_test(index, skb, par, opt))
36d956798dSJozsef Kadlecsik 		inv = !inv;
37d956798dSJozsef Kadlecsik 	return inv;
38d956798dSJozsef Kadlecsik }
39d956798dSJozsef Kadlecsik 
40ac8cc925SJozsef Kadlecsik #define ADT_OPT(n, f, d, fs, cfs, t)	\
41127f5591SJozsef Kadlecsik struct ip_set_adt_opt n = {		\
42127f5591SJozsef Kadlecsik 	.family	= f,			\
43127f5591SJozsef Kadlecsik 	.dim = d,			\
44127f5591SJozsef Kadlecsik 	.flags = fs,			\
45127f5591SJozsef Kadlecsik 	.cmdflags = cfs,		\
46075e64c0SJozsef Kadlecsik 	.ext.timeout = t,		\
47127f5591SJozsef Kadlecsik }
48ac8cc925SJozsef Kadlecsik 
49d956798dSJozsef Kadlecsik /* Revision 0 interface: backward compatible with netfilter/iptables */
50d956798dSJozsef Kadlecsik 
51d956798dSJozsef Kadlecsik static bool
52d956798dSJozsef Kadlecsik set_match_v0(const struct sk_buff *skb, struct xt_action_param *par)
53d956798dSJozsef Kadlecsik {
54d956798dSJozsef Kadlecsik 	const struct xt_set_info_match_v0 *info = par->matchinfo;
55ac8cc925SJozsef Kadlecsik 	ADT_OPT(opt, par->family, info->match_set.u.compat.dim,
56ac8cc925SJozsef Kadlecsik 		info->match_set.u.compat.flags, 0, UINT_MAX);
57d956798dSJozsef Kadlecsik 
58b66554cfSJozsef Kadlecsik 	return match_set(info->match_set.index, skb, par, &opt,
59d956798dSJozsef Kadlecsik 			 info->match_set.u.compat.flags & IPSET_INV_MATCH);
60d956798dSJozsef Kadlecsik }
61d956798dSJozsef Kadlecsik 
62d956798dSJozsef Kadlecsik static void
63d956798dSJozsef Kadlecsik compat_flags(struct xt_set_info_v0 *info)
64d956798dSJozsef Kadlecsik {
65d956798dSJozsef Kadlecsik 	u_int8_t i;
66d956798dSJozsef Kadlecsik 
67d956798dSJozsef Kadlecsik 	/* Fill out compatibility data according to enum ip_set_kopt */
68d956798dSJozsef Kadlecsik 	info->u.compat.dim = IPSET_DIM_ZERO;
69d956798dSJozsef Kadlecsik 	if (info->u.flags[0] & IPSET_MATCH_INV)
70d956798dSJozsef Kadlecsik 		info->u.compat.flags |= IPSET_INV_MATCH;
71d956798dSJozsef Kadlecsik 	for (i = 0; i < IPSET_DIM_MAX-1 && info->u.flags[i]; i++) {
72d956798dSJozsef Kadlecsik 		info->u.compat.dim++;
73d956798dSJozsef Kadlecsik 		if (info->u.flags[i] & IPSET_SRC)
74d956798dSJozsef Kadlecsik 			info->u.compat.flags |= (1<<info->u.compat.dim);
75d956798dSJozsef Kadlecsik 	}
76d956798dSJozsef Kadlecsik }
77d956798dSJozsef Kadlecsik 
78d956798dSJozsef Kadlecsik static int
79d956798dSJozsef Kadlecsik set_match_v0_checkentry(const struct xt_mtchk_param *par)
80d956798dSJozsef Kadlecsik {
81d956798dSJozsef Kadlecsik 	struct xt_set_info_match_v0 *info = par->matchinfo;
82d956798dSJozsef Kadlecsik 	ip_set_id_t index;
83d956798dSJozsef Kadlecsik 
84d956798dSJozsef Kadlecsik 	index = ip_set_nfnl_get_byindex(info->match_set.index);
85d956798dSJozsef Kadlecsik 
86d956798dSJozsef Kadlecsik 	if (index == IPSET_INVALID_ID) {
87d956798dSJozsef Kadlecsik 		pr_warning("Cannot find set indentified by id %u to match\n",
88d956798dSJozsef Kadlecsik 			   info->match_set.index);
89d956798dSJozsef Kadlecsik 		return -ENOENT;
90d956798dSJozsef Kadlecsik 	}
91d956798dSJozsef Kadlecsik 	if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) {
92d956798dSJozsef Kadlecsik 		pr_warning("Protocol error: set match dimension "
93d956798dSJozsef Kadlecsik 			   "is over the limit!\n");
94eafbd3fdSJozsef Kadlecsik 		ip_set_nfnl_put(info->match_set.index);
95d956798dSJozsef Kadlecsik 		return -ERANGE;
96d956798dSJozsef Kadlecsik 	}
97d956798dSJozsef Kadlecsik 
98d956798dSJozsef Kadlecsik 	/* Fill out compatibility data */
99d956798dSJozsef Kadlecsik 	compat_flags(&info->match_set);
100d956798dSJozsef Kadlecsik 
101d956798dSJozsef Kadlecsik 	return 0;
102d956798dSJozsef Kadlecsik }
103d956798dSJozsef Kadlecsik 
104d956798dSJozsef Kadlecsik static void
105d956798dSJozsef Kadlecsik set_match_v0_destroy(const struct xt_mtdtor_param *par)
106d956798dSJozsef Kadlecsik {
107d956798dSJozsef Kadlecsik 	struct xt_set_info_match_v0 *info = par->matchinfo;
108d956798dSJozsef Kadlecsik 
109d956798dSJozsef Kadlecsik 	ip_set_nfnl_put(info->match_set.index);
110d956798dSJozsef Kadlecsik }
111d956798dSJozsef Kadlecsik 
112bd3129fcSJozsef Kadlecsik /* Revision 1 match */
113bd3129fcSJozsef Kadlecsik 
114bd3129fcSJozsef Kadlecsik static bool
115bd3129fcSJozsef Kadlecsik set_match_v1(const struct sk_buff *skb, struct xt_action_param *par)
116bd3129fcSJozsef Kadlecsik {
117bd3129fcSJozsef Kadlecsik 	const struct xt_set_info_match_v1 *info = par->matchinfo;
118bd3129fcSJozsef Kadlecsik 	ADT_OPT(opt, par->family, info->match_set.dim,
119bd3129fcSJozsef Kadlecsik 		info->match_set.flags, 0, UINT_MAX);
120bd3129fcSJozsef Kadlecsik 
121bd3129fcSJozsef Kadlecsik 	if (opt.flags & IPSET_RETURN_NOMATCH)
122bd3129fcSJozsef Kadlecsik 		opt.cmdflags |= IPSET_FLAG_RETURN_NOMATCH;
123bd3129fcSJozsef Kadlecsik 
124bd3129fcSJozsef Kadlecsik 	return match_set(info->match_set.index, skb, par, &opt,
125bd3129fcSJozsef Kadlecsik 			 info->match_set.flags & IPSET_INV_MATCH);
126bd3129fcSJozsef Kadlecsik }
127bd3129fcSJozsef Kadlecsik 
128bd3129fcSJozsef Kadlecsik static int
129bd3129fcSJozsef Kadlecsik set_match_v1_checkentry(const struct xt_mtchk_param *par)
130bd3129fcSJozsef Kadlecsik {
131bd3129fcSJozsef Kadlecsik 	struct xt_set_info_match_v1 *info = par->matchinfo;
132bd3129fcSJozsef Kadlecsik 	ip_set_id_t index;
133bd3129fcSJozsef Kadlecsik 
134bd3129fcSJozsef Kadlecsik 	index = ip_set_nfnl_get_byindex(info->match_set.index);
135bd3129fcSJozsef Kadlecsik 
136bd3129fcSJozsef Kadlecsik 	if (index == IPSET_INVALID_ID) {
137bd3129fcSJozsef Kadlecsik 		pr_warning("Cannot find set indentified by id %u to match\n",
138bd3129fcSJozsef Kadlecsik 			   info->match_set.index);
139bd3129fcSJozsef Kadlecsik 		return -ENOENT;
140bd3129fcSJozsef Kadlecsik 	}
141bd3129fcSJozsef Kadlecsik 	if (info->match_set.dim > IPSET_DIM_MAX) {
142bd3129fcSJozsef Kadlecsik 		pr_warning("Protocol error: set match dimension "
143bd3129fcSJozsef Kadlecsik 			   "is over the limit!\n");
144bd3129fcSJozsef Kadlecsik 		ip_set_nfnl_put(info->match_set.index);
145bd3129fcSJozsef Kadlecsik 		return -ERANGE;
146bd3129fcSJozsef Kadlecsik 	}
147bd3129fcSJozsef Kadlecsik 
148bd3129fcSJozsef Kadlecsik 	return 0;
149bd3129fcSJozsef Kadlecsik }
150bd3129fcSJozsef Kadlecsik 
151bd3129fcSJozsef Kadlecsik static void
152bd3129fcSJozsef Kadlecsik set_match_v1_destroy(const struct xt_mtdtor_param *par)
153bd3129fcSJozsef Kadlecsik {
154bd3129fcSJozsef Kadlecsik 	struct xt_set_info_match_v1 *info = par->matchinfo;
155bd3129fcSJozsef Kadlecsik 
156bd3129fcSJozsef Kadlecsik 	ip_set_nfnl_put(info->match_set.index);
157bd3129fcSJozsef Kadlecsik }
158bd3129fcSJozsef Kadlecsik 
159bd3129fcSJozsef Kadlecsik /* Revision 3 match */
160bd3129fcSJozsef Kadlecsik 
161bd3129fcSJozsef Kadlecsik static bool
162bd3129fcSJozsef Kadlecsik match_counter(u64 counter, const struct ip_set_counter_match *info)
163bd3129fcSJozsef Kadlecsik {
164bd3129fcSJozsef Kadlecsik 	switch (info->op) {
165bd3129fcSJozsef Kadlecsik 	case IPSET_COUNTER_NONE:
166bd3129fcSJozsef Kadlecsik 		return true;
167bd3129fcSJozsef Kadlecsik 	case IPSET_COUNTER_EQ:
168bd3129fcSJozsef Kadlecsik 		return counter == info->value;
169bd3129fcSJozsef Kadlecsik 	case IPSET_COUNTER_NE:
170bd3129fcSJozsef Kadlecsik 		return counter != info->value;
171bd3129fcSJozsef Kadlecsik 	case IPSET_COUNTER_LT:
172bd3129fcSJozsef Kadlecsik 		return counter < info->value;
173bd3129fcSJozsef Kadlecsik 	case IPSET_COUNTER_GT:
174bd3129fcSJozsef Kadlecsik 		return counter > info->value;
175bd3129fcSJozsef Kadlecsik 	}
176bd3129fcSJozsef Kadlecsik 	return false;
177bd3129fcSJozsef Kadlecsik }
178bd3129fcSJozsef Kadlecsik 
179bd3129fcSJozsef Kadlecsik static bool
180bd3129fcSJozsef Kadlecsik set_match_v3(const struct sk_buff *skb, struct xt_action_param *par)
181bd3129fcSJozsef Kadlecsik {
182bd3129fcSJozsef Kadlecsik 	const struct xt_set_info_match_v3 *info = par->matchinfo;
183bd3129fcSJozsef Kadlecsik 	ADT_OPT(opt, par->family, info->match_set.dim,
184bd3129fcSJozsef Kadlecsik 		info->match_set.flags, info->flags, UINT_MAX);
185bd3129fcSJozsef Kadlecsik 	int ret;
186bd3129fcSJozsef Kadlecsik 
187bd3129fcSJozsef Kadlecsik 	if (info->packets.op != IPSET_COUNTER_NONE ||
188bd3129fcSJozsef Kadlecsik 	    info->bytes.op != IPSET_COUNTER_NONE)
189bd3129fcSJozsef Kadlecsik 		opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS;
190bd3129fcSJozsef Kadlecsik 
191bd3129fcSJozsef Kadlecsik 	ret = match_set(info->match_set.index, skb, par, &opt,
192bd3129fcSJozsef Kadlecsik 			info->match_set.flags & IPSET_INV_MATCH);
193bd3129fcSJozsef Kadlecsik 
194bd3129fcSJozsef Kadlecsik 	if (!(ret && opt.cmdflags & IPSET_FLAG_MATCH_COUNTERS))
195bd3129fcSJozsef Kadlecsik 		return ret;
196bd3129fcSJozsef Kadlecsik 
197bd3129fcSJozsef Kadlecsik 	if (!match_counter(opt.ext.packets, &info->packets))
198bd3129fcSJozsef Kadlecsik 		return 0;
199bd3129fcSJozsef Kadlecsik 	return match_counter(opt.ext.bytes, &info->bytes);
200bd3129fcSJozsef Kadlecsik }
201bd3129fcSJozsef Kadlecsik 
202bd3129fcSJozsef Kadlecsik #define set_match_v3_checkentry	set_match_v1_checkentry
203bd3129fcSJozsef Kadlecsik #define set_match_v3_destroy	set_match_v1_destroy
204bd3129fcSJozsef Kadlecsik 
205bd3129fcSJozsef Kadlecsik /* Revision 0 interface: backward compatible with netfilter/iptables */
206bd3129fcSJozsef Kadlecsik 
207d956798dSJozsef Kadlecsik static unsigned int
208d956798dSJozsef Kadlecsik set_target_v0(struct sk_buff *skb, const struct xt_action_param *par)
209d956798dSJozsef Kadlecsik {
210d956798dSJozsef Kadlecsik 	const struct xt_set_info_target_v0 *info = par->targinfo;
211ac8cc925SJozsef Kadlecsik 	ADT_OPT(add_opt, par->family, info->add_set.u.compat.dim,
212ac8cc925SJozsef Kadlecsik 		info->add_set.u.compat.flags, 0, UINT_MAX);
213ac8cc925SJozsef Kadlecsik 	ADT_OPT(del_opt, par->family, info->del_set.u.compat.dim,
214ac8cc925SJozsef Kadlecsik 		info->del_set.u.compat.flags, 0, UINT_MAX);
215d956798dSJozsef Kadlecsik 
216d956798dSJozsef Kadlecsik 	if (info->add_set.index != IPSET_INVALID_ID)
217b66554cfSJozsef Kadlecsik 		ip_set_add(info->add_set.index, skb, par, &add_opt);
218d956798dSJozsef Kadlecsik 	if (info->del_set.index != IPSET_INVALID_ID)
219b66554cfSJozsef Kadlecsik 		ip_set_del(info->del_set.index, skb, par, &del_opt);
220d956798dSJozsef Kadlecsik 
221d956798dSJozsef Kadlecsik 	return XT_CONTINUE;
222d956798dSJozsef Kadlecsik }
223d956798dSJozsef Kadlecsik 
224d956798dSJozsef Kadlecsik static int
225d956798dSJozsef Kadlecsik set_target_v0_checkentry(const struct xt_tgchk_param *par)
226d956798dSJozsef Kadlecsik {
227d956798dSJozsef Kadlecsik 	struct xt_set_info_target_v0 *info = par->targinfo;
228d956798dSJozsef Kadlecsik 	ip_set_id_t index;
229d956798dSJozsef Kadlecsik 
230d956798dSJozsef Kadlecsik 	if (info->add_set.index != IPSET_INVALID_ID) {
231d956798dSJozsef Kadlecsik 		index = ip_set_nfnl_get_byindex(info->add_set.index);
232d956798dSJozsef Kadlecsik 		if (index == IPSET_INVALID_ID) {
233d956798dSJozsef Kadlecsik 			pr_warning("Cannot find add_set index %u as target\n",
234d956798dSJozsef Kadlecsik 				   info->add_set.index);
235d956798dSJozsef Kadlecsik 			return -ENOENT;
236d956798dSJozsef Kadlecsik 		}
237d956798dSJozsef Kadlecsik 	}
238d956798dSJozsef Kadlecsik 
239d956798dSJozsef Kadlecsik 	if (info->del_set.index != IPSET_INVALID_ID) {
240d956798dSJozsef Kadlecsik 		index = ip_set_nfnl_get_byindex(info->del_set.index);
241d956798dSJozsef Kadlecsik 		if (index == IPSET_INVALID_ID) {
242d956798dSJozsef Kadlecsik 			pr_warning("Cannot find del_set index %u as target\n",
243d956798dSJozsef Kadlecsik 				   info->del_set.index);
244eafbd3fdSJozsef Kadlecsik 			if (info->add_set.index != IPSET_INVALID_ID)
245eafbd3fdSJozsef Kadlecsik 				ip_set_nfnl_put(info->add_set.index);
246d956798dSJozsef Kadlecsik 			return -ENOENT;
247d956798dSJozsef Kadlecsik 		}
248d956798dSJozsef Kadlecsik 	}
249d956798dSJozsef Kadlecsik 	if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0 ||
250d956798dSJozsef Kadlecsik 	    info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) {
251d956798dSJozsef Kadlecsik 		pr_warning("Protocol error: SET target dimension "
252d956798dSJozsef Kadlecsik 			   "is over the limit!\n");
253eafbd3fdSJozsef Kadlecsik 		if (info->add_set.index != IPSET_INVALID_ID)
254eafbd3fdSJozsef Kadlecsik 			ip_set_nfnl_put(info->add_set.index);
255eafbd3fdSJozsef Kadlecsik 		if (info->del_set.index != IPSET_INVALID_ID)
256eafbd3fdSJozsef Kadlecsik 			ip_set_nfnl_put(info->del_set.index);
257d956798dSJozsef Kadlecsik 		return -ERANGE;
258d956798dSJozsef Kadlecsik 	}
259d956798dSJozsef Kadlecsik 
260d956798dSJozsef Kadlecsik 	/* Fill out compatibility data */
261d956798dSJozsef Kadlecsik 	compat_flags(&info->add_set);
262d956798dSJozsef Kadlecsik 	compat_flags(&info->del_set);
263d956798dSJozsef Kadlecsik 
264d956798dSJozsef Kadlecsik 	return 0;
265d956798dSJozsef Kadlecsik }
266d956798dSJozsef Kadlecsik 
267d956798dSJozsef Kadlecsik static void
268d956798dSJozsef Kadlecsik set_target_v0_destroy(const struct xt_tgdtor_param *par)
269d956798dSJozsef Kadlecsik {
270d956798dSJozsef Kadlecsik 	const struct xt_set_info_target_v0 *info = par->targinfo;
271d956798dSJozsef Kadlecsik 
272d956798dSJozsef Kadlecsik 	if (info->add_set.index != IPSET_INVALID_ID)
273d956798dSJozsef Kadlecsik 		ip_set_nfnl_put(info->add_set.index);
274d956798dSJozsef Kadlecsik 	if (info->del_set.index != IPSET_INVALID_ID)
275d956798dSJozsef Kadlecsik 		ip_set_nfnl_put(info->del_set.index);
276d956798dSJozsef Kadlecsik }
277d956798dSJozsef Kadlecsik 
278bd3129fcSJozsef Kadlecsik /* Revision 1 target */
279d956798dSJozsef Kadlecsik 
280d956798dSJozsef Kadlecsik static unsigned int
281ac8cc925SJozsef Kadlecsik set_target_v1(struct sk_buff *skb, const struct xt_action_param *par)
282d956798dSJozsef Kadlecsik {
283ac8cc925SJozsef Kadlecsik 	const struct xt_set_info_target_v1 *info = par->targinfo;
284ac8cc925SJozsef Kadlecsik 	ADT_OPT(add_opt, par->family, info->add_set.dim,
285ac8cc925SJozsef Kadlecsik 		info->add_set.flags, 0, UINT_MAX);
286ac8cc925SJozsef Kadlecsik 	ADT_OPT(del_opt, par->family, info->del_set.dim,
287ac8cc925SJozsef Kadlecsik 		info->del_set.flags, 0, UINT_MAX);
288d956798dSJozsef Kadlecsik 
289d956798dSJozsef Kadlecsik 	if (info->add_set.index != IPSET_INVALID_ID)
290b66554cfSJozsef Kadlecsik 		ip_set_add(info->add_set.index, skb, par, &add_opt);
291d956798dSJozsef Kadlecsik 	if (info->del_set.index != IPSET_INVALID_ID)
292b66554cfSJozsef Kadlecsik 		ip_set_del(info->del_set.index, skb, par, &del_opt);
293d956798dSJozsef Kadlecsik 
294d956798dSJozsef Kadlecsik 	return XT_CONTINUE;
295d956798dSJozsef Kadlecsik }
296d956798dSJozsef Kadlecsik 
297d956798dSJozsef Kadlecsik static int
298ac8cc925SJozsef Kadlecsik set_target_v1_checkentry(const struct xt_tgchk_param *par)
299d956798dSJozsef Kadlecsik {
300ac8cc925SJozsef Kadlecsik 	const struct xt_set_info_target_v1 *info = par->targinfo;
301d956798dSJozsef Kadlecsik 	ip_set_id_t index;
302d956798dSJozsef Kadlecsik 
303d956798dSJozsef Kadlecsik 	if (info->add_set.index != IPSET_INVALID_ID) {
304d956798dSJozsef Kadlecsik 		index = ip_set_nfnl_get_byindex(info->add_set.index);
305d956798dSJozsef Kadlecsik 		if (index == IPSET_INVALID_ID) {
306d956798dSJozsef Kadlecsik 			pr_warning("Cannot find add_set index %u as target\n",
307d956798dSJozsef Kadlecsik 				   info->add_set.index);
308d956798dSJozsef Kadlecsik 			return -ENOENT;
309d956798dSJozsef Kadlecsik 		}
310d956798dSJozsef Kadlecsik 	}
311d956798dSJozsef Kadlecsik 
312d956798dSJozsef Kadlecsik 	if (info->del_set.index != IPSET_INVALID_ID) {
313d956798dSJozsef Kadlecsik 		index = ip_set_nfnl_get_byindex(info->del_set.index);
314d956798dSJozsef Kadlecsik 		if (index == IPSET_INVALID_ID) {
315d956798dSJozsef Kadlecsik 			pr_warning("Cannot find del_set index %u as target\n",
316d956798dSJozsef Kadlecsik 				   info->del_set.index);
317eafbd3fdSJozsef Kadlecsik 			if (info->add_set.index != IPSET_INVALID_ID)
318eafbd3fdSJozsef Kadlecsik 				ip_set_nfnl_put(info->add_set.index);
319d956798dSJozsef Kadlecsik 			return -ENOENT;
320d956798dSJozsef Kadlecsik 		}
321d956798dSJozsef Kadlecsik 	}
322d956798dSJozsef Kadlecsik 	if (info->add_set.dim > IPSET_DIM_MAX ||
323eafbd3fdSJozsef Kadlecsik 	    info->del_set.dim > IPSET_DIM_MAX) {
324d956798dSJozsef Kadlecsik 		pr_warning("Protocol error: SET target dimension "
325d956798dSJozsef Kadlecsik 			   "is over the limit!\n");
326eafbd3fdSJozsef Kadlecsik 		if (info->add_set.index != IPSET_INVALID_ID)
327eafbd3fdSJozsef Kadlecsik 			ip_set_nfnl_put(info->add_set.index);
328eafbd3fdSJozsef Kadlecsik 		if (info->del_set.index != IPSET_INVALID_ID)
329eafbd3fdSJozsef Kadlecsik 			ip_set_nfnl_put(info->del_set.index);
330d956798dSJozsef Kadlecsik 		return -ERANGE;
331d956798dSJozsef Kadlecsik 	}
332d956798dSJozsef Kadlecsik 
333d956798dSJozsef Kadlecsik 	return 0;
334d956798dSJozsef Kadlecsik }
335d956798dSJozsef Kadlecsik 
336d956798dSJozsef Kadlecsik static void
337ac8cc925SJozsef Kadlecsik set_target_v1_destroy(const struct xt_tgdtor_param *par)
338d956798dSJozsef Kadlecsik {
339ac8cc925SJozsef Kadlecsik 	const struct xt_set_info_target_v1 *info = par->targinfo;
340d956798dSJozsef Kadlecsik 
341d956798dSJozsef Kadlecsik 	if (info->add_set.index != IPSET_INVALID_ID)
342d956798dSJozsef Kadlecsik 		ip_set_nfnl_put(info->add_set.index);
343d956798dSJozsef Kadlecsik 	if (info->del_set.index != IPSET_INVALID_ID)
344d956798dSJozsef Kadlecsik 		ip_set_nfnl_put(info->del_set.index);
345d956798dSJozsef Kadlecsik }
346d956798dSJozsef Kadlecsik 
347ac8cc925SJozsef Kadlecsik /* Revision 2 target */
348ac8cc925SJozsef Kadlecsik 
349ac8cc925SJozsef Kadlecsik static unsigned int
350ac8cc925SJozsef Kadlecsik set_target_v2(struct sk_buff *skb, const struct xt_action_param *par)
351ac8cc925SJozsef Kadlecsik {
352ac8cc925SJozsef Kadlecsik 	const struct xt_set_info_target_v2 *info = par->targinfo;
353075e64c0SJozsef Kadlecsik 	ADT_OPT(add_opt, par->family, info->add_set.dim,
354ac8cc925SJozsef Kadlecsik 		info->add_set.flags, info->flags, info->timeout);
355ac8cc925SJozsef Kadlecsik 	ADT_OPT(del_opt, par->family, info->del_set.dim,
356ac8cc925SJozsef Kadlecsik 		info->del_set.flags, 0, UINT_MAX);
357ac8cc925SJozsef Kadlecsik 
358127f5591SJozsef Kadlecsik 	/* Normalize to fit into jiffies */
359075e64c0SJozsef Kadlecsik 	if (add_opt.ext.timeout != IPSET_NO_TIMEOUT &&
360075e64c0SJozsef Kadlecsik 	    add_opt.ext.timeout > UINT_MAX/MSEC_PER_SEC)
361075e64c0SJozsef Kadlecsik 		add_opt.ext.timeout = UINT_MAX/MSEC_PER_SEC;
362ac8cc925SJozsef Kadlecsik 	if (info->add_set.index != IPSET_INVALID_ID)
363b66554cfSJozsef Kadlecsik 		ip_set_add(info->add_set.index, skb, par, &add_opt);
364ac8cc925SJozsef Kadlecsik 	if (info->del_set.index != IPSET_INVALID_ID)
365b66554cfSJozsef Kadlecsik 		ip_set_del(info->del_set.index, skb, par, &del_opt);
366ac8cc925SJozsef Kadlecsik 
367ac8cc925SJozsef Kadlecsik 	return XT_CONTINUE;
368ac8cc925SJozsef Kadlecsik }
369ac8cc925SJozsef Kadlecsik 
370ac8cc925SJozsef Kadlecsik #define set_target_v2_checkentry	set_target_v1_checkentry
371ac8cc925SJozsef Kadlecsik #define set_target_v2_destroy		set_target_v1_destroy
372ac8cc925SJozsef Kadlecsik 
373d956798dSJozsef Kadlecsik static struct xt_match set_matches[] __read_mostly = {
374d956798dSJozsef Kadlecsik 	{
375d956798dSJozsef Kadlecsik 		.name		= "set",
376d956798dSJozsef Kadlecsik 		.family		= NFPROTO_IPV4,
377d956798dSJozsef Kadlecsik 		.revision	= 0,
378d956798dSJozsef Kadlecsik 		.match		= set_match_v0,
379d956798dSJozsef Kadlecsik 		.matchsize	= sizeof(struct xt_set_info_match_v0),
380d956798dSJozsef Kadlecsik 		.checkentry	= set_match_v0_checkentry,
381d956798dSJozsef Kadlecsik 		.destroy	= set_match_v0_destroy,
382d956798dSJozsef Kadlecsik 		.me		= THIS_MODULE
383d956798dSJozsef Kadlecsik 	},
384d956798dSJozsef Kadlecsik 	{
385d956798dSJozsef Kadlecsik 		.name		= "set",
386d956798dSJozsef Kadlecsik 		.family		= NFPROTO_IPV4,
387d956798dSJozsef Kadlecsik 		.revision	= 1,
388ac8cc925SJozsef Kadlecsik 		.match		= set_match_v1,
389ac8cc925SJozsef Kadlecsik 		.matchsize	= sizeof(struct xt_set_info_match_v1),
390ac8cc925SJozsef Kadlecsik 		.checkentry	= set_match_v1_checkentry,
391ac8cc925SJozsef Kadlecsik 		.destroy	= set_match_v1_destroy,
392d956798dSJozsef Kadlecsik 		.me		= THIS_MODULE
393d956798dSJozsef Kadlecsik 	},
394d956798dSJozsef Kadlecsik 	{
395d956798dSJozsef Kadlecsik 		.name		= "set",
396d956798dSJozsef Kadlecsik 		.family		= NFPROTO_IPV6,
397d956798dSJozsef Kadlecsik 		.revision	= 1,
398ac8cc925SJozsef Kadlecsik 		.match		= set_match_v1,
399ac8cc925SJozsef Kadlecsik 		.matchsize	= sizeof(struct xt_set_info_match_v1),
400ac8cc925SJozsef Kadlecsik 		.checkentry	= set_match_v1_checkentry,
401ac8cc925SJozsef Kadlecsik 		.destroy	= set_match_v1_destroy,
402d956798dSJozsef Kadlecsik 		.me		= THIS_MODULE
403d956798dSJozsef Kadlecsik 	},
4043e0304a5SJozsef Kadlecsik 	/* --return-nomatch flag support */
4053e0304a5SJozsef Kadlecsik 	{
4063e0304a5SJozsef Kadlecsik 		.name		= "set",
4073e0304a5SJozsef Kadlecsik 		.family		= NFPROTO_IPV4,
4083e0304a5SJozsef Kadlecsik 		.revision	= 2,
4093e0304a5SJozsef Kadlecsik 		.match		= set_match_v1,
4103e0304a5SJozsef Kadlecsik 		.matchsize	= sizeof(struct xt_set_info_match_v1),
4113e0304a5SJozsef Kadlecsik 		.checkentry	= set_match_v1_checkentry,
4123e0304a5SJozsef Kadlecsik 		.destroy	= set_match_v1_destroy,
4133e0304a5SJozsef Kadlecsik 		.me		= THIS_MODULE
4143e0304a5SJozsef Kadlecsik 	},
4153e0304a5SJozsef Kadlecsik 	{
4163e0304a5SJozsef Kadlecsik 		.name		= "set",
4173e0304a5SJozsef Kadlecsik 		.family		= NFPROTO_IPV6,
4183e0304a5SJozsef Kadlecsik 		.revision	= 2,
4193e0304a5SJozsef Kadlecsik 		.match		= set_match_v1,
4203e0304a5SJozsef Kadlecsik 		.matchsize	= sizeof(struct xt_set_info_match_v1),
4213e0304a5SJozsef Kadlecsik 		.checkentry	= set_match_v1_checkentry,
4223e0304a5SJozsef Kadlecsik 		.destroy	= set_match_v1_destroy,
4233e0304a5SJozsef Kadlecsik 		.me		= THIS_MODULE
4243e0304a5SJozsef Kadlecsik 	},
4256e01781dSJozsef Kadlecsik 	/* counters support: update, match */
4266e01781dSJozsef Kadlecsik 	{
4276e01781dSJozsef Kadlecsik 		.name		= "set",
4286e01781dSJozsef Kadlecsik 		.family		= NFPROTO_IPV4,
4296e01781dSJozsef Kadlecsik 		.revision	= 3,
4306e01781dSJozsef Kadlecsik 		.match		= set_match_v3,
4316e01781dSJozsef Kadlecsik 		.matchsize	= sizeof(struct xt_set_info_match_v3),
4326e01781dSJozsef Kadlecsik 		.checkentry	= set_match_v3_checkentry,
4336e01781dSJozsef Kadlecsik 		.destroy	= set_match_v3_destroy,
4346e01781dSJozsef Kadlecsik 		.me		= THIS_MODULE
4356e01781dSJozsef Kadlecsik 	},
4366e01781dSJozsef Kadlecsik 	{
4376e01781dSJozsef Kadlecsik 		.name		= "set",
4386e01781dSJozsef Kadlecsik 		.family		= NFPROTO_IPV6,
4396e01781dSJozsef Kadlecsik 		.revision	= 3,
4406e01781dSJozsef Kadlecsik 		.match		= set_match_v3,
4416e01781dSJozsef Kadlecsik 		.matchsize	= sizeof(struct xt_set_info_match_v3),
4426e01781dSJozsef Kadlecsik 		.checkentry	= set_match_v3_checkentry,
4436e01781dSJozsef Kadlecsik 		.destroy	= set_match_v3_destroy,
4446e01781dSJozsef Kadlecsik 		.me		= THIS_MODULE
4456e01781dSJozsef Kadlecsik 	},
446d956798dSJozsef Kadlecsik };
447d956798dSJozsef Kadlecsik 
448d956798dSJozsef Kadlecsik static struct xt_target set_targets[] __read_mostly = {
449d956798dSJozsef Kadlecsik 	{
450d956798dSJozsef Kadlecsik 		.name		= "SET",
451d956798dSJozsef Kadlecsik 		.revision	= 0,
452d956798dSJozsef Kadlecsik 		.family		= NFPROTO_IPV4,
453d956798dSJozsef Kadlecsik 		.target		= set_target_v0,
454d956798dSJozsef Kadlecsik 		.targetsize	= sizeof(struct xt_set_info_target_v0),
455d956798dSJozsef Kadlecsik 		.checkentry	= set_target_v0_checkentry,
456d956798dSJozsef Kadlecsik 		.destroy	= set_target_v0_destroy,
457d956798dSJozsef Kadlecsik 		.me		= THIS_MODULE
458d956798dSJozsef Kadlecsik 	},
459d956798dSJozsef Kadlecsik 	{
460d956798dSJozsef Kadlecsik 		.name		= "SET",
461d956798dSJozsef Kadlecsik 		.revision	= 1,
462d956798dSJozsef Kadlecsik 		.family		= NFPROTO_IPV4,
463ac8cc925SJozsef Kadlecsik 		.target		= set_target_v1,
464ac8cc925SJozsef Kadlecsik 		.targetsize	= sizeof(struct xt_set_info_target_v1),
465ac8cc925SJozsef Kadlecsik 		.checkentry	= set_target_v1_checkentry,
466ac8cc925SJozsef Kadlecsik 		.destroy	= set_target_v1_destroy,
467d956798dSJozsef Kadlecsik 		.me		= THIS_MODULE
468d956798dSJozsef Kadlecsik 	},
469d956798dSJozsef Kadlecsik 	{
470d956798dSJozsef Kadlecsik 		.name		= "SET",
471d956798dSJozsef Kadlecsik 		.revision	= 1,
472d956798dSJozsef Kadlecsik 		.family		= NFPROTO_IPV6,
473ac8cc925SJozsef Kadlecsik 		.target		= set_target_v1,
474ac8cc925SJozsef Kadlecsik 		.targetsize	= sizeof(struct xt_set_info_target_v1),
475ac8cc925SJozsef Kadlecsik 		.checkentry	= set_target_v1_checkentry,
476ac8cc925SJozsef Kadlecsik 		.destroy	= set_target_v1_destroy,
477ac8cc925SJozsef Kadlecsik 		.me		= THIS_MODULE
478ac8cc925SJozsef Kadlecsik 	},
4793e0304a5SJozsef Kadlecsik 	/* --timeout and --exist flags support */
480ac8cc925SJozsef Kadlecsik 	{
481ac8cc925SJozsef Kadlecsik 		.name		= "SET",
482ac8cc925SJozsef Kadlecsik 		.revision	= 2,
483ac8cc925SJozsef Kadlecsik 		.family		= NFPROTO_IPV4,
484ac8cc925SJozsef Kadlecsik 		.target		= set_target_v2,
485ac8cc925SJozsef Kadlecsik 		.targetsize	= sizeof(struct xt_set_info_target_v2),
486ac8cc925SJozsef Kadlecsik 		.checkentry	= set_target_v2_checkentry,
487ac8cc925SJozsef Kadlecsik 		.destroy	= set_target_v2_destroy,
488ac8cc925SJozsef Kadlecsik 		.me		= THIS_MODULE
489ac8cc925SJozsef Kadlecsik 	},
490ac8cc925SJozsef Kadlecsik 	{
491ac8cc925SJozsef Kadlecsik 		.name		= "SET",
492ac8cc925SJozsef Kadlecsik 		.revision	= 2,
493ac8cc925SJozsef Kadlecsik 		.family		= NFPROTO_IPV6,
494ac8cc925SJozsef Kadlecsik 		.target		= set_target_v2,
495ac8cc925SJozsef Kadlecsik 		.targetsize	= sizeof(struct xt_set_info_target_v2),
496ac8cc925SJozsef Kadlecsik 		.checkentry	= set_target_v2_checkentry,
497ac8cc925SJozsef Kadlecsik 		.destroy	= set_target_v2_destroy,
498d956798dSJozsef Kadlecsik 		.me		= THIS_MODULE
499d956798dSJozsef Kadlecsik 	},
500d956798dSJozsef Kadlecsik };
501d956798dSJozsef Kadlecsik 
502d956798dSJozsef Kadlecsik static int __init xt_set_init(void)
503d956798dSJozsef Kadlecsik {
504d956798dSJozsef Kadlecsik 	int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches));
505d956798dSJozsef Kadlecsik 
506d956798dSJozsef Kadlecsik 	if (!ret) {
507d956798dSJozsef Kadlecsik 		ret = xt_register_targets(set_targets,
508d956798dSJozsef Kadlecsik 					  ARRAY_SIZE(set_targets));
509d956798dSJozsef Kadlecsik 		if (ret)
510d956798dSJozsef Kadlecsik 			xt_unregister_matches(set_matches,
511d956798dSJozsef Kadlecsik 					      ARRAY_SIZE(set_matches));
512d956798dSJozsef Kadlecsik 	}
513d956798dSJozsef Kadlecsik 	return ret;
514d956798dSJozsef Kadlecsik }
515d956798dSJozsef Kadlecsik 
516d956798dSJozsef Kadlecsik static void __exit xt_set_fini(void)
517d956798dSJozsef Kadlecsik {
518d956798dSJozsef Kadlecsik 	xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches));
519d956798dSJozsef Kadlecsik 	xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets));
520d956798dSJozsef Kadlecsik }
521d956798dSJozsef Kadlecsik 
522d956798dSJozsef Kadlecsik module_init(xt_set_init);
523d956798dSJozsef Kadlecsik module_exit(xt_set_fini);
524