xref: /linux-6.15/net/netfilter/xt_set.c (revision ef264cf0)
1 /* Copyright (C) 2000-2002 Joakim Axelsson <[email protected]>
2  *                         Patrick Schaaf <[email protected]>
3  *                         Martin Josefsson <[email protected]>
4  * Copyright (C) 2003-2013 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 	  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 struct ip_set_adt_opt n = {		\
42 	.family	= f,			\
43 	.dim = d,			\
44 	.flags = fs,			\
45 	.cmdflags = cfs,		\
46 	.ext.timeout = t,		\
47 }
48 
49 /* Revision 0 interface: backward compatible with netfilter/iptables */
50 
51 static bool
52 set_match_v0(const struct sk_buff *skb, struct xt_action_param *par)
53 {
54 	const struct xt_set_info_match_v0 *info = par->matchinfo;
55 	ADT_OPT(opt, par->family, info->match_set.u.compat.dim,
56 		info->match_set.u.compat.flags, 0, UINT_MAX);
57 
58 	return match_set(info->match_set.index, skb, par, &opt,
59 			 info->match_set.u.compat.flags & IPSET_INV_MATCH);
60 }
61 
62 static void
63 compat_flags(struct xt_set_info_v0 *info)
64 {
65 	u_int8_t i;
66 
67 	/* Fill out compatibility data according to enum ip_set_kopt */
68 	info->u.compat.dim = IPSET_DIM_ZERO;
69 	if (info->u.flags[0] & IPSET_MATCH_INV)
70 		info->u.compat.flags |= IPSET_INV_MATCH;
71 	for (i = 0; i < IPSET_DIM_MAX-1 && info->u.flags[i]; i++) {
72 		info->u.compat.dim++;
73 		if (info->u.flags[i] & IPSET_SRC)
74 			info->u.compat.flags |= (1<<info->u.compat.dim);
75 	}
76 }
77 
78 static int
79 set_match_v0_checkentry(const struct xt_mtchk_param *par)
80 {
81 	struct xt_set_info_match_v0 *info = par->matchinfo;
82 	ip_set_id_t index;
83 
84 	index = ip_set_nfnl_get_byindex(par->net, info->match_set.index);
85 
86 	if (index == IPSET_INVALID_ID) {
87 		pr_warning("Cannot find set identified by id %u to match\n",
88 			   info->match_set.index);
89 		return -ENOENT;
90 	}
91 	if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) {
92 		pr_warning("Protocol error: set match dimension "
93 			   "is over the limit!\n");
94 		ip_set_nfnl_put(par->net, info->match_set.index);
95 		return -ERANGE;
96 	}
97 
98 	/* Fill out compatibility data */
99 	compat_flags(&info->match_set);
100 
101 	return 0;
102 }
103 
104 static void
105 set_match_v0_destroy(const struct xt_mtdtor_param *par)
106 {
107 	struct xt_set_info_match_v0 *info = par->matchinfo;
108 
109 	ip_set_nfnl_put(par->net, info->match_set.index);
110 }
111 
112 /* Revision 1 match */
113 
114 static bool
115 set_match_v1(const struct sk_buff *skb, struct xt_action_param *par)
116 {
117 	const struct xt_set_info_match_v1 *info = par->matchinfo;
118 	ADT_OPT(opt, par->family, info->match_set.dim,
119 		info->match_set.flags, 0, UINT_MAX);
120 
121 	if (opt.flags & IPSET_RETURN_NOMATCH)
122 		opt.cmdflags |= IPSET_FLAG_RETURN_NOMATCH;
123 
124 	return match_set(info->match_set.index, skb, par, &opt,
125 			 info->match_set.flags & IPSET_INV_MATCH);
126 }
127 
128 static int
129 set_match_v1_checkentry(const struct xt_mtchk_param *par)
130 {
131 	struct xt_set_info_match_v1 *info = par->matchinfo;
132 	ip_set_id_t index;
133 
134 	index = ip_set_nfnl_get_byindex(par->net, info->match_set.index);
135 
136 	if (index == IPSET_INVALID_ID) {
137 		pr_warning("Cannot find set identified by id %u to match\n",
138 			   info->match_set.index);
139 		return -ENOENT;
140 	}
141 	if (info->match_set.dim > IPSET_DIM_MAX) {
142 		pr_warning("Protocol error: set match dimension "
143 			   "is over the limit!\n");
144 		ip_set_nfnl_put(par->net, info->match_set.index);
145 		return -ERANGE;
146 	}
147 
148 	return 0;
149 }
150 
151 static void
152 set_match_v1_destroy(const struct xt_mtdtor_param *par)
153 {
154 	struct xt_set_info_match_v1 *info = par->matchinfo;
155 
156 	ip_set_nfnl_put(par->net, info->match_set.index);
157 }
158 
159 /* Revision 3 match */
160 
161 static bool
162 match_counter(u64 counter, const struct ip_set_counter_match *info)
163 {
164 	switch (info->op) {
165 	case IPSET_COUNTER_NONE:
166 		return true;
167 	case IPSET_COUNTER_EQ:
168 		return counter == info->value;
169 	case IPSET_COUNTER_NE:
170 		return counter != info->value;
171 	case IPSET_COUNTER_LT:
172 		return counter < info->value;
173 	case IPSET_COUNTER_GT:
174 		return counter > info->value;
175 	}
176 	return false;
177 }
178 
179 static bool
180 set_match_v3(const struct sk_buff *skb, struct xt_action_param *par)
181 {
182 	const struct xt_set_info_match_v3 *info = par->matchinfo;
183 	ADT_OPT(opt, par->family, info->match_set.dim,
184 		info->match_set.flags, info->flags, UINT_MAX);
185 	int ret;
186 
187 	if (info->packets.op != IPSET_COUNTER_NONE ||
188 	    info->bytes.op != IPSET_COUNTER_NONE)
189 		opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS;
190 
191 	ret = match_set(info->match_set.index, skb, par, &opt,
192 			info->match_set.flags & IPSET_INV_MATCH);
193 
194 	if (!(ret && opt.cmdflags & IPSET_FLAG_MATCH_COUNTERS))
195 		return ret;
196 
197 	if (!match_counter(opt.ext.packets, &info->packets))
198 		return 0;
199 	return match_counter(opt.ext.bytes, &info->bytes);
200 }
201 
202 #define set_match_v3_checkentry	set_match_v1_checkentry
203 #define set_match_v3_destroy	set_match_v1_destroy
204 
205 /* Revision 0 interface: backward compatible with netfilter/iptables */
206 
207 static unsigned int
208 set_target_v0(struct sk_buff *skb, const struct xt_action_param *par)
209 {
210 	const struct xt_set_info_target_v0 *info = par->targinfo;
211 	ADT_OPT(add_opt, par->family, info->add_set.u.compat.dim,
212 		info->add_set.u.compat.flags, 0, UINT_MAX);
213 	ADT_OPT(del_opt, par->family, info->del_set.u.compat.dim,
214 		info->del_set.u.compat.flags, 0, UINT_MAX);
215 
216 	if (info->add_set.index != IPSET_INVALID_ID)
217 		ip_set_add(info->add_set.index, skb, par, &add_opt);
218 	if (info->del_set.index != IPSET_INVALID_ID)
219 		ip_set_del(info->del_set.index, skb, par, &del_opt);
220 
221 	return XT_CONTINUE;
222 }
223 
224 static int
225 set_target_v0_checkentry(const struct xt_tgchk_param *par)
226 {
227 	struct xt_set_info_target_v0 *info = par->targinfo;
228 	ip_set_id_t index;
229 
230 	if (info->add_set.index != IPSET_INVALID_ID) {
231 		index = ip_set_nfnl_get_byindex(par->net, info->add_set.index);
232 		if (index == IPSET_INVALID_ID) {
233 			pr_warning("Cannot find add_set index %u as target\n",
234 				   info->add_set.index);
235 			return -ENOENT;
236 		}
237 	}
238 
239 	if (info->del_set.index != IPSET_INVALID_ID) {
240 		index = ip_set_nfnl_get_byindex(par->net, info->del_set.index);
241 		if (index == IPSET_INVALID_ID) {
242 			pr_warning("Cannot find del_set index %u as target\n",
243 				   info->del_set.index);
244 			if (info->add_set.index != IPSET_INVALID_ID)
245 				ip_set_nfnl_put(par->net, info->add_set.index);
246 			return -ENOENT;
247 		}
248 	}
249 	if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0 ||
250 	    info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) {
251 		pr_warning("Protocol error: SET target dimension "
252 			   "is over the limit!\n");
253 		if (info->add_set.index != IPSET_INVALID_ID)
254 			ip_set_nfnl_put(par->net, info->add_set.index);
255 		if (info->del_set.index != IPSET_INVALID_ID)
256 			ip_set_nfnl_put(par->net, info->del_set.index);
257 		return -ERANGE;
258 	}
259 
260 	/* Fill out compatibility data */
261 	compat_flags(&info->add_set);
262 	compat_flags(&info->del_set);
263 
264 	return 0;
265 }
266 
267 static void
268 set_target_v0_destroy(const struct xt_tgdtor_param *par)
269 {
270 	const struct xt_set_info_target_v0 *info = par->targinfo;
271 
272 	if (info->add_set.index != IPSET_INVALID_ID)
273 		ip_set_nfnl_put(par->net, info->add_set.index);
274 	if (info->del_set.index != IPSET_INVALID_ID)
275 		ip_set_nfnl_put(par->net, info->del_set.index);
276 }
277 
278 /* Revision 1 target */
279 
280 static unsigned int
281 set_target_v1(struct sk_buff *skb, const struct xt_action_param *par)
282 {
283 	const struct xt_set_info_target_v1 *info = par->targinfo;
284 	ADT_OPT(add_opt, par->family, info->add_set.dim,
285 		info->add_set.flags, 0, UINT_MAX);
286 	ADT_OPT(del_opt, par->family, info->del_set.dim,
287 		info->del_set.flags, 0, UINT_MAX);
288 
289 	if (info->add_set.index != IPSET_INVALID_ID)
290 		ip_set_add(info->add_set.index, skb, par, &add_opt);
291 	if (info->del_set.index != IPSET_INVALID_ID)
292 		ip_set_del(info->del_set.index, skb, par, &del_opt);
293 
294 	return XT_CONTINUE;
295 }
296 
297 static int
298 set_target_v1_checkentry(const struct xt_tgchk_param *par)
299 {
300 	const struct xt_set_info_target_v1 *info = par->targinfo;
301 	ip_set_id_t index;
302 
303 	if (info->add_set.index != IPSET_INVALID_ID) {
304 		index = ip_set_nfnl_get_byindex(par->net, info->add_set.index);
305 		if (index == IPSET_INVALID_ID) {
306 			pr_warning("Cannot find add_set index %u as target\n",
307 				   info->add_set.index);
308 			return -ENOENT;
309 		}
310 	}
311 
312 	if (info->del_set.index != IPSET_INVALID_ID) {
313 		index = ip_set_nfnl_get_byindex(par->net, info->del_set.index);
314 		if (index == IPSET_INVALID_ID) {
315 			pr_warning("Cannot find del_set index %u as target\n",
316 				   info->del_set.index);
317 			if (info->add_set.index != IPSET_INVALID_ID)
318 				ip_set_nfnl_put(par->net, info->add_set.index);
319 			return -ENOENT;
320 		}
321 	}
322 	if (info->add_set.dim > IPSET_DIM_MAX ||
323 	    info->del_set.dim > IPSET_DIM_MAX) {
324 		pr_warning("Protocol error: SET target dimension "
325 			   "is over the limit!\n");
326 		if (info->add_set.index != IPSET_INVALID_ID)
327 			ip_set_nfnl_put(par->net, info->add_set.index);
328 		if (info->del_set.index != IPSET_INVALID_ID)
329 			ip_set_nfnl_put(par->net, info->del_set.index);
330 		return -ERANGE;
331 	}
332 
333 	return 0;
334 }
335 
336 static void
337 set_target_v1_destroy(const struct xt_tgdtor_param *par)
338 {
339 	const struct xt_set_info_target_v1 *info = par->targinfo;
340 
341 	if (info->add_set.index != IPSET_INVALID_ID)
342 		ip_set_nfnl_put(par->net, info->add_set.index);
343 	if (info->del_set.index != IPSET_INVALID_ID)
344 		ip_set_nfnl_put(par->net, info->del_set.index);
345 }
346 
347 /* Revision 2 target */
348 
349 static unsigned int
350 set_target_v2(struct sk_buff *skb, const struct xt_action_param *par)
351 {
352 	const struct xt_set_info_target_v2 *info = par->targinfo;
353 	ADT_OPT(add_opt, par->family, info->add_set.dim,
354 		info->add_set.flags, info->flags, info->timeout);
355 	ADT_OPT(del_opt, par->family, info->del_set.dim,
356 		info->del_set.flags, 0, UINT_MAX);
357 
358 	/* Normalize to fit into jiffies */
359 	if (add_opt.ext.timeout != IPSET_NO_TIMEOUT &&
360 	    add_opt.ext.timeout > UINT_MAX/MSEC_PER_SEC)
361 		add_opt.ext.timeout = UINT_MAX/MSEC_PER_SEC;
362 	if (info->add_set.index != IPSET_INVALID_ID)
363 		ip_set_add(info->add_set.index, skb, par, &add_opt);
364 	if (info->del_set.index != IPSET_INVALID_ID)
365 		ip_set_del(info->del_set.index, skb, par, &del_opt);
366 
367 	return XT_CONTINUE;
368 }
369 
370 #define set_target_v2_checkentry	set_target_v1_checkentry
371 #define set_target_v2_destroy		set_target_v1_destroy
372 
373 static struct xt_match set_matches[] __read_mostly = {
374 	{
375 		.name		= "set",
376 		.family		= NFPROTO_IPV4,
377 		.revision	= 0,
378 		.match		= set_match_v0,
379 		.matchsize	= sizeof(struct xt_set_info_match_v0),
380 		.checkentry	= set_match_v0_checkentry,
381 		.destroy	= set_match_v0_destroy,
382 		.me		= THIS_MODULE
383 	},
384 	{
385 		.name		= "set",
386 		.family		= NFPROTO_IPV4,
387 		.revision	= 1,
388 		.match		= set_match_v1,
389 		.matchsize	= sizeof(struct xt_set_info_match_v1),
390 		.checkentry	= set_match_v1_checkentry,
391 		.destroy	= set_match_v1_destroy,
392 		.me		= THIS_MODULE
393 	},
394 	{
395 		.name		= "set",
396 		.family		= NFPROTO_IPV6,
397 		.revision	= 1,
398 		.match		= set_match_v1,
399 		.matchsize	= sizeof(struct xt_set_info_match_v1),
400 		.checkentry	= set_match_v1_checkentry,
401 		.destroy	= set_match_v1_destroy,
402 		.me		= THIS_MODULE
403 	},
404 	/* --return-nomatch flag support */
405 	{
406 		.name		= "set",
407 		.family		= NFPROTO_IPV4,
408 		.revision	= 2,
409 		.match		= set_match_v1,
410 		.matchsize	= sizeof(struct xt_set_info_match_v1),
411 		.checkentry	= set_match_v1_checkentry,
412 		.destroy	= set_match_v1_destroy,
413 		.me		= THIS_MODULE
414 	},
415 	{
416 		.name		= "set",
417 		.family		= NFPROTO_IPV6,
418 		.revision	= 2,
419 		.match		= set_match_v1,
420 		.matchsize	= sizeof(struct xt_set_info_match_v1),
421 		.checkentry	= set_match_v1_checkentry,
422 		.destroy	= set_match_v1_destroy,
423 		.me		= THIS_MODULE
424 	},
425 	/* counters support: update, match */
426 	{
427 		.name		= "set",
428 		.family		= NFPROTO_IPV4,
429 		.revision	= 3,
430 		.match		= set_match_v3,
431 		.matchsize	= sizeof(struct xt_set_info_match_v3),
432 		.checkentry	= set_match_v3_checkentry,
433 		.destroy	= set_match_v3_destroy,
434 		.me		= THIS_MODULE
435 	},
436 	{
437 		.name		= "set",
438 		.family		= NFPROTO_IPV6,
439 		.revision	= 3,
440 		.match		= set_match_v3,
441 		.matchsize	= sizeof(struct xt_set_info_match_v3),
442 		.checkentry	= set_match_v3_checkentry,
443 		.destroy	= set_match_v3_destroy,
444 		.me		= THIS_MODULE
445 	},
446 };
447 
448 static struct xt_target set_targets[] __read_mostly = {
449 	{
450 		.name		= "SET",
451 		.revision	= 0,
452 		.family		= NFPROTO_IPV4,
453 		.target		= set_target_v0,
454 		.targetsize	= sizeof(struct xt_set_info_target_v0),
455 		.checkentry	= set_target_v0_checkentry,
456 		.destroy	= set_target_v0_destroy,
457 		.me		= THIS_MODULE
458 	},
459 	{
460 		.name		= "SET",
461 		.revision	= 1,
462 		.family		= NFPROTO_IPV4,
463 		.target		= set_target_v1,
464 		.targetsize	= sizeof(struct xt_set_info_target_v1),
465 		.checkentry	= set_target_v1_checkentry,
466 		.destroy	= set_target_v1_destroy,
467 		.me		= THIS_MODULE
468 	},
469 	{
470 		.name		= "SET",
471 		.revision	= 1,
472 		.family		= NFPROTO_IPV6,
473 		.target		= set_target_v1,
474 		.targetsize	= sizeof(struct xt_set_info_target_v1),
475 		.checkentry	= set_target_v1_checkentry,
476 		.destroy	= set_target_v1_destroy,
477 		.me		= THIS_MODULE
478 	},
479 	/* --timeout and --exist flags support */
480 	{
481 		.name		= "SET",
482 		.revision	= 2,
483 		.family		= NFPROTO_IPV4,
484 		.target		= set_target_v2,
485 		.targetsize	= sizeof(struct xt_set_info_target_v2),
486 		.checkentry	= set_target_v2_checkentry,
487 		.destroy	= set_target_v2_destroy,
488 		.me		= THIS_MODULE
489 	},
490 	{
491 		.name		= "SET",
492 		.revision	= 2,
493 		.family		= NFPROTO_IPV6,
494 		.target		= set_target_v2,
495 		.targetsize	= sizeof(struct xt_set_info_target_v2),
496 		.checkentry	= set_target_v2_checkentry,
497 		.destroy	= set_target_v2_destroy,
498 		.me		= THIS_MODULE
499 	},
500 };
501 
502 static int __init xt_set_init(void)
503 {
504 	int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches));
505 
506 	if (!ret) {
507 		ret = xt_register_targets(set_targets,
508 					  ARRAY_SIZE(set_targets));
509 		if (ret)
510 			xt_unregister_matches(set_matches,
511 					      ARRAY_SIZE(set_matches));
512 	}
513 	return ret;
514 }
515 
516 static void __exit xt_set_fini(void)
517 {
518 	xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches));
519 	xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets));
520 }
521 
522 module_init(xt_set_init);
523 module_exit(xt_set_fini);
524