xref: /linux-6.15/net/netfilter/xt_set.c (revision a671de08)
1 /* Copyright (C) 2000-2002 Joakim Axelsson <[email protected]>
2  *                         Patrick Schaaf <[email protected]>
3  *                         Martin Josefsson <[email protected]>
4  * Copyright (C) 2003-2011 Jozsef Kadlecsik <[email protected]>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  */
10 
11 /* Kernel module which implements the set match and SET target
12  * for netfilter/iptables. */
13 
14 #include <linux/module.h>
15 #include <linux/skbuff.h>
16 
17 #include <linux/netfilter/x_tables.h>
18 #include <linux/netfilter/xt_set.h>
19 #include <linux/netfilter/ipset/ip_set_timeout.h>
20 
21 MODULE_LICENSE("GPL");
22 MODULE_AUTHOR("Jozsef Kadlecsik <[email protected]>");
23 MODULE_DESCRIPTION("Xtables: IP set match and target module");
24 MODULE_ALIAS("xt_SET");
25 MODULE_ALIAS("ipt_set");
26 MODULE_ALIAS("ip6t_set");
27 MODULE_ALIAS("ipt_SET");
28 MODULE_ALIAS("ip6t_SET");
29 
30 static inline int
31 match_set(ip_set_id_t index, const struct sk_buff *skb,
32 	  const struct xt_action_param *par,
33 	  const struct ip_set_adt_opt *opt, int inv)
34 {
35 	if (ip_set_test(index, skb, par, opt))
36 		inv = !inv;
37 	return inv;
38 }
39 
40 #define ADT_OPT(n, f, d, fs, cfs, t)	\
41 const struct ip_set_adt_opt n = {	\
42 	.family	= f,			\
43 	.dim = d,			\
44 	.flags = fs,			\
45 	.cmdflags = cfs,		\
46 	.timeout = t,			\
47 }
48 #define ADT_MOPT(n, f, d, fs, cfs, t)	\
49 struct ip_set_adt_opt n = {		\
50 	.family	= f,			\
51 	.dim = d,			\
52 	.flags = fs,			\
53 	.cmdflags = cfs,		\
54 	.timeout = t,			\
55 }
56 
57 /* Revision 0 interface: backward compatible with netfilter/iptables */
58 
59 static bool
60 set_match_v0(const struct sk_buff *skb, struct xt_action_param *par)
61 {
62 	const struct xt_set_info_match_v0 *info = par->matchinfo;
63 	ADT_OPT(opt, par->family, info->match_set.u.compat.dim,
64 		info->match_set.u.compat.flags, 0, UINT_MAX);
65 
66 	return match_set(info->match_set.index, skb, par, &opt,
67 			 info->match_set.u.compat.flags & IPSET_INV_MATCH);
68 }
69 
70 static void
71 compat_flags(struct xt_set_info_v0 *info)
72 {
73 	u_int8_t i;
74 
75 	/* Fill out compatibility data according to enum ip_set_kopt */
76 	info->u.compat.dim = IPSET_DIM_ZERO;
77 	if (info->u.flags[0] & IPSET_MATCH_INV)
78 		info->u.compat.flags |= IPSET_INV_MATCH;
79 	for (i = 0; i < IPSET_DIM_MAX-1 && info->u.flags[i]; i++) {
80 		info->u.compat.dim++;
81 		if (info->u.flags[i] & IPSET_SRC)
82 			info->u.compat.flags |= (1<<info->u.compat.dim);
83 	}
84 }
85 
86 static int
87 set_match_v0_checkentry(const struct xt_mtchk_param *par)
88 {
89 	struct xt_set_info_match_v0 *info = par->matchinfo;
90 	ip_set_id_t index;
91 
92 	index = ip_set_nfnl_get_byindex(info->match_set.index);
93 
94 	if (index == IPSET_INVALID_ID) {
95 		pr_warning("Cannot find set indentified by id %u to match\n",
96 			   info->match_set.index);
97 		return -ENOENT;
98 	}
99 	if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) {
100 		pr_warning("Protocol error: set match dimension "
101 			   "is over the limit!\n");
102 		ip_set_nfnl_put(info->match_set.index);
103 		return -ERANGE;
104 	}
105 
106 	/* Fill out compatibility data */
107 	compat_flags(&info->match_set);
108 
109 	return 0;
110 }
111 
112 static void
113 set_match_v0_destroy(const struct xt_mtdtor_param *par)
114 {
115 	struct xt_set_info_match_v0 *info = par->matchinfo;
116 
117 	ip_set_nfnl_put(info->match_set.index);
118 }
119 
120 static unsigned int
121 set_target_v0(struct sk_buff *skb, const struct xt_action_param *par)
122 {
123 	const struct xt_set_info_target_v0 *info = par->targinfo;
124 	ADT_OPT(add_opt, par->family, info->add_set.u.compat.dim,
125 		info->add_set.u.compat.flags, 0, UINT_MAX);
126 	ADT_OPT(del_opt, par->family, info->del_set.u.compat.dim,
127 		info->del_set.u.compat.flags, 0, UINT_MAX);
128 
129 	if (info->add_set.index != IPSET_INVALID_ID)
130 		ip_set_add(info->add_set.index, skb, par, &add_opt);
131 	if (info->del_set.index != IPSET_INVALID_ID)
132 		ip_set_del(info->del_set.index, skb, par, &del_opt);
133 
134 	return XT_CONTINUE;
135 }
136 
137 static int
138 set_target_v0_checkentry(const struct xt_tgchk_param *par)
139 {
140 	struct xt_set_info_target_v0 *info = par->targinfo;
141 	ip_set_id_t index;
142 
143 	if (info->add_set.index != IPSET_INVALID_ID) {
144 		index = ip_set_nfnl_get_byindex(info->add_set.index);
145 		if (index == IPSET_INVALID_ID) {
146 			pr_warning("Cannot find add_set index %u as target\n",
147 				   info->add_set.index);
148 			return -ENOENT;
149 		}
150 	}
151 
152 	if (info->del_set.index != IPSET_INVALID_ID) {
153 		index = ip_set_nfnl_get_byindex(info->del_set.index);
154 		if (index == IPSET_INVALID_ID) {
155 			pr_warning("Cannot find del_set index %u as target\n",
156 				   info->del_set.index);
157 			if (info->add_set.index != IPSET_INVALID_ID)
158 				ip_set_nfnl_put(info->add_set.index);
159 			return -ENOENT;
160 		}
161 	}
162 	if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0 ||
163 	    info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) {
164 		pr_warning("Protocol error: SET target dimension "
165 			   "is over the limit!\n");
166 		if (info->add_set.index != IPSET_INVALID_ID)
167 			ip_set_nfnl_put(info->add_set.index);
168 		if (info->del_set.index != IPSET_INVALID_ID)
169 			ip_set_nfnl_put(info->del_set.index);
170 		return -ERANGE;
171 	}
172 
173 	/* Fill out compatibility data */
174 	compat_flags(&info->add_set);
175 	compat_flags(&info->del_set);
176 
177 	return 0;
178 }
179 
180 static void
181 set_target_v0_destroy(const struct xt_tgdtor_param *par)
182 {
183 	const struct xt_set_info_target_v0 *info = par->targinfo;
184 
185 	if (info->add_set.index != IPSET_INVALID_ID)
186 		ip_set_nfnl_put(info->add_set.index);
187 	if (info->del_set.index != IPSET_INVALID_ID)
188 		ip_set_nfnl_put(info->del_set.index);
189 }
190 
191 /* Revision 1 match and target */
192 
193 static bool
194 set_match_v1(const struct sk_buff *skb, struct xt_action_param *par)
195 {
196 	const struct xt_set_info_match_v1 *info = par->matchinfo;
197 	ADT_OPT(opt, par->family, info->match_set.dim,
198 		info->match_set.flags, 0, UINT_MAX);
199 
200 	return match_set(info->match_set.index, skb, par, &opt,
201 			 info->match_set.flags & IPSET_INV_MATCH);
202 }
203 
204 static int
205 set_match_v1_checkentry(const struct xt_mtchk_param *par)
206 {
207 	struct xt_set_info_match_v1 *info = par->matchinfo;
208 	ip_set_id_t index;
209 
210 	index = ip_set_nfnl_get_byindex(info->match_set.index);
211 
212 	if (index == IPSET_INVALID_ID) {
213 		pr_warning("Cannot find set indentified by id %u to match\n",
214 			   info->match_set.index);
215 		return -ENOENT;
216 	}
217 	if (info->match_set.dim > IPSET_DIM_MAX) {
218 		pr_warning("Protocol error: set match dimension "
219 			   "is over the limit!\n");
220 		ip_set_nfnl_put(info->match_set.index);
221 		return -ERANGE;
222 	}
223 
224 	return 0;
225 }
226 
227 static void
228 set_match_v1_destroy(const struct xt_mtdtor_param *par)
229 {
230 	struct xt_set_info_match_v1 *info = par->matchinfo;
231 
232 	ip_set_nfnl_put(info->match_set.index);
233 }
234 
235 static unsigned int
236 set_target_v1(struct sk_buff *skb, const struct xt_action_param *par)
237 {
238 	const struct xt_set_info_target_v1 *info = par->targinfo;
239 	ADT_OPT(add_opt, par->family, info->add_set.dim,
240 		info->add_set.flags, 0, UINT_MAX);
241 	ADT_OPT(del_opt, par->family, info->del_set.dim,
242 		info->del_set.flags, 0, UINT_MAX);
243 
244 	if (info->add_set.index != IPSET_INVALID_ID)
245 		ip_set_add(info->add_set.index, skb, par, &add_opt);
246 	if (info->del_set.index != IPSET_INVALID_ID)
247 		ip_set_del(info->del_set.index, skb, par, &del_opt);
248 
249 	return XT_CONTINUE;
250 }
251 
252 static int
253 set_target_v1_checkentry(const struct xt_tgchk_param *par)
254 {
255 	const struct xt_set_info_target_v1 *info = par->targinfo;
256 	ip_set_id_t index;
257 
258 	if (info->add_set.index != IPSET_INVALID_ID) {
259 		index = ip_set_nfnl_get_byindex(info->add_set.index);
260 		if (index == IPSET_INVALID_ID) {
261 			pr_warning("Cannot find add_set index %u as target\n",
262 				   info->add_set.index);
263 			return -ENOENT;
264 		}
265 	}
266 
267 	if (info->del_set.index != IPSET_INVALID_ID) {
268 		index = ip_set_nfnl_get_byindex(info->del_set.index);
269 		if (index == IPSET_INVALID_ID) {
270 			pr_warning("Cannot find del_set index %u as target\n",
271 				   info->del_set.index);
272 			if (info->add_set.index != IPSET_INVALID_ID)
273 				ip_set_nfnl_put(info->add_set.index);
274 			return -ENOENT;
275 		}
276 	}
277 	if (info->add_set.dim > IPSET_DIM_MAX ||
278 	    info->del_set.dim > IPSET_DIM_MAX) {
279 		pr_warning("Protocol error: SET target dimension "
280 			   "is over the limit!\n");
281 		if (info->add_set.index != IPSET_INVALID_ID)
282 			ip_set_nfnl_put(info->add_set.index);
283 		if (info->del_set.index != IPSET_INVALID_ID)
284 			ip_set_nfnl_put(info->del_set.index);
285 		return -ERANGE;
286 	}
287 
288 	return 0;
289 }
290 
291 static void
292 set_target_v1_destroy(const struct xt_tgdtor_param *par)
293 {
294 	const struct xt_set_info_target_v1 *info = par->targinfo;
295 
296 	if (info->add_set.index != IPSET_INVALID_ID)
297 		ip_set_nfnl_put(info->add_set.index);
298 	if (info->del_set.index != IPSET_INVALID_ID)
299 		ip_set_nfnl_put(info->del_set.index);
300 }
301 
302 /* Revision 2 target */
303 
304 static unsigned int
305 set_target_v2(struct sk_buff *skb, const struct xt_action_param *par)
306 {
307 	const struct xt_set_info_target_v2 *info = par->targinfo;
308 	ADT_MOPT(add_opt, par->family, info->add_set.dim,
309 		 info->add_set.flags, info->flags, info->timeout);
310 	ADT_OPT(del_opt, par->family, info->del_set.dim,
311 		info->del_set.flags, 0, UINT_MAX);
312 
313 	/* Normalize to fit into jiffies */
314 	if (add_opt.timeout != IPSET_NO_TIMEOUT &&
315 	    add_opt.timeout > UINT_MAX/MSEC_PER_SEC)
316 		add_opt.timeout = UINT_MAX/MSEC_PER_SEC;
317 	if (info->add_set.index != IPSET_INVALID_ID)
318 		ip_set_add(info->add_set.index, skb, par, &add_opt);
319 	if (info->del_set.index != IPSET_INVALID_ID)
320 		ip_set_del(info->del_set.index, skb, par, &del_opt);
321 
322 	return XT_CONTINUE;
323 }
324 
325 #define set_target_v2_checkentry	set_target_v1_checkentry
326 #define set_target_v2_destroy		set_target_v1_destroy
327 
328 static struct xt_match set_matches[] __read_mostly = {
329 	{
330 		.name		= "set",
331 		.family		= NFPROTO_IPV4,
332 		.revision	= 0,
333 		.match		= set_match_v0,
334 		.matchsize	= sizeof(struct xt_set_info_match_v0),
335 		.checkentry	= set_match_v0_checkentry,
336 		.destroy	= set_match_v0_destroy,
337 		.me		= THIS_MODULE
338 	},
339 	{
340 		.name		= "set",
341 		.family		= NFPROTO_IPV4,
342 		.revision	= 1,
343 		.match		= set_match_v1,
344 		.matchsize	= sizeof(struct xt_set_info_match_v1),
345 		.checkentry	= set_match_v1_checkentry,
346 		.destroy	= set_match_v1_destroy,
347 		.me		= THIS_MODULE
348 	},
349 	{
350 		.name		= "set",
351 		.family		= NFPROTO_IPV6,
352 		.revision	= 1,
353 		.match		= set_match_v1,
354 		.matchsize	= sizeof(struct xt_set_info_match_v1),
355 		.checkentry	= set_match_v1_checkentry,
356 		.destroy	= set_match_v1_destroy,
357 		.me		= THIS_MODULE
358 	},
359 	/* --return-nomatch flag support */
360 	{
361 		.name		= "set",
362 		.family		= NFPROTO_IPV4,
363 		.revision	= 2,
364 		.match		= set_match_v1,
365 		.matchsize	= sizeof(struct xt_set_info_match_v1),
366 		.checkentry	= set_match_v1_checkentry,
367 		.destroy	= set_match_v1_destroy,
368 		.me		= THIS_MODULE
369 	},
370 	{
371 		.name		= "set",
372 		.family		= NFPROTO_IPV6,
373 		.revision	= 2,
374 		.match		= set_match_v1,
375 		.matchsize	= sizeof(struct xt_set_info_match_v1),
376 		.checkentry	= set_match_v1_checkentry,
377 		.destroy	= set_match_v1_destroy,
378 		.me		= THIS_MODULE
379 	},
380 };
381 
382 static struct xt_target set_targets[] __read_mostly = {
383 	{
384 		.name		= "SET",
385 		.revision	= 0,
386 		.family		= NFPROTO_IPV4,
387 		.target		= set_target_v0,
388 		.targetsize	= sizeof(struct xt_set_info_target_v0),
389 		.checkentry	= set_target_v0_checkentry,
390 		.destroy	= set_target_v0_destroy,
391 		.me		= THIS_MODULE
392 	},
393 	{
394 		.name		= "SET",
395 		.revision	= 1,
396 		.family		= NFPROTO_IPV4,
397 		.target		= set_target_v1,
398 		.targetsize	= sizeof(struct xt_set_info_target_v1),
399 		.checkentry	= set_target_v1_checkentry,
400 		.destroy	= set_target_v1_destroy,
401 		.me		= THIS_MODULE
402 	},
403 	{
404 		.name		= "SET",
405 		.revision	= 1,
406 		.family		= NFPROTO_IPV6,
407 		.target		= set_target_v1,
408 		.targetsize	= sizeof(struct xt_set_info_target_v1),
409 		.checkentry	= set_target_v1_checkentry,
410 		.destroy	= set_target_v1_destroy,
411 		.me		= THIS_MODULE
412 	},
413 	/* --timeout and --exist flags support */
414 	{
415 		.name		= "SET",
416 		.revision	= 2,
417 		.family		= NFPROTO_IPV4,
418 		.target		= set_target_v2,
419 		.targetsize	= sizeof(struct xt_set_info_target_v2),
420 		.checkentry	= set_target_v2_checkentry,
421 		.destroy	= set_target_v2_destroy,
422 		.me		= THIS_MODULE
423 	},
424 	{
425 		.name		= "SET",
426 		.revision	= 2,
427 		.family		= NFPROTO_IPV6,
428 		.target		= set_target_v2,
429 		.targetsize	= sizeof(struct xt_set_info_target_v2),
430 		.checkentry	= set_target_v2_checkentry,
431 		.destroy	= set_target_v2_destroy,
432 		.me		= THIS_MODULE
433 	},
434 };
435 
436 static int __init xt_set_init(void)
437 {
438 	int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches));
439 
440 	if (!ret) {
441 		ret = xt_register_targets(set_targets,
442 					  ARRAY_SIZE(set_targets));
443 		if (ret)
444 			xt_unregister_matches(set_matches,
445 					      ARRAY_SIZE(set_matches));
446 	}
447 	return ret;
448 }
449 
450 static void __exit xt_set_fini(void)
451 {
452 	xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches));
453 	xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets));
454 }
455 
456 module_init(xt_set_init);
457 module_exit(xt_set_fini);
458