xref: /linux-6.15/net/netfilter/xt_set.c (revision 565d76cb)
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 #include <linux/version.h>
17 
18 #include <linux/netfilter/x_tables.h>
19 #include <linux/netfilter/xt_set.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 	  u8 pf, u8 dim, u8 flags, int inv)
33 {
34 	if (ip_set_test(index, skb, pf, dim, flags))
35 		inv = !inv;
36 	return inv;
37 }
38 
39 /* Revision 0 interface: backward compatible with netfilter/iptables */
40 
41 static bool
42 set_match_v0(const struct sk_buff *skb, struct xt_action_param *par)
43 {
44 	const struct xt_set_info_match_v0 *info = par->matchinfo;
45 
46 	return match_set(info->match_set.index, skb, par->family,
47 			 info->match_set.u.compat.dim,
48 			 info->match_set.u.compat.flags,
49 			 info->match_set.u.compat.flags & IPSET_INV_MATCH);
50 }
51 
52 static void
53 compat_flags(struct xt_set_info_v0 *info)
54 {
55 	u_int8_t i;
56 
57 	/* Fill out compatibility data according to enum ip_set_kopt */
58 	info->u.compat.dim = IPSET_DIM_ZERO;
59 	if (info->u.flags[0] & IPSET_MATCH_INV)
60 		info->u.compat.flags |= IPSET_INV_MATCH;
61 	for (i = 0; i < IPSET_DIM_MAX-1 && info->u.flags[i]; i++) {
62 		info->u.compat.dim++;
63 		if (info->u.flags[i] & IPSET_SRC)
64 			info->u.compat.flags |= (1<<info->u.compat.dim);
65 	}
66 }
67 
68 static int
69 set_match_v0_checkentry(const struct xt_mtchk_param *par)
70 {
71 	struct xt_set_info_match_v0 *info = par->matchinfo;
72 	ip_set_id_t index;
73 
74 	index = ip_set_nfnl_get_byindex(info->match_set.index);
75 
76 	if (index == IPSET_INVALID_ID) {
77 		pr_warning("Cannot find set indentified by id %u to match\n",
78 			   info->match_set.index);
79 		return -ENOENT;
80 	}
81 	if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) {
82 		pr_warning("Protocol error: set match dimension "
83 			   "is over the limit!\n");
84 		return -ERANGE;
85 	}
86 
87 	/* Fill out compatibility data */
88 	compat_flags(&info->match_set);
89 
90 	return 0;
91 }
92 
93 static void
94 set_match_v0_destroy(const struct xt_mtdtor_param *par)
95 {
96 	struct xt_set_info_match_v0 *info = par->matchinfo;
97 
98 	ip_set_nfnl_put(info->match_set.index);
99 }
100 
101 static unsigned int
102 set_target_v0(struct sk_buff *skb, const struct xt_action_param *par)
103 {
104 	const struct xt_set_info_target_v0 *info = par->targinfo;
105 
106 	if (info->add_set.index != IPSET_INVALID_ID)
107 		ip_set_add(info->add_set.index, skb, par->family,
108 			   info->add_set.u.compat.dim,
109 			   info->add_set.u.compat.flags);
110 	if (info->del_set.index != IPSET_INVALID_ID)
111 		ip_set_del(info->del_set.index, skb, par->family,
112 			   info->del_set.u.compat.dim,
113 			   info->del_set.u.compat.flags);
114 
115 	return XT_CONTINUE;
116 }
117 
118 static int
119 set_target_v0_checkentry(const struct xt_tgchk_param *par)
120 {
121 	struct xt_set_info_target_v0 *info = par->targinfo;
122 	ip_set_id_t index;
123 
124 	if (info->add_set.index != IPSET_INVALID_ID) {
125 		index = ip_set_nfnl_get_byindex(info->add_set.index);
126 		if (index == IPSET_INVALID_ID) {
127 			pr_warning("Cannot find add_set index %u as target\n",
128 				   info->add_set.index);
129 			return -ENOENT;
130 		}
131 	}
132 
133 	if (info->del_set.index != IPSET_INVALID_ID) {
134 		index = ip_set_nfnl_get_byindex(info->del_set.index);
135 		if (index == IPSET_INVALID_ID) {
136 			pr_warning("Cannot find del_set index %u as target\n",
137 				   info->del_set.index);
138 			return -ENOENT;
139 		}
140 	}
141 	if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0 ||
142 	    info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) {
143 		pr_warning("Protocol error: SET target dimension "
144 			   "is over the limit!\n");
145 		return -ERANGE;
146 	}
147 
148 	/* Fill out compatibility data */
149 	compat_flags(&info->add_set);
150 	compat_flags(&info->del_set);
151 
152 	return 0;
153 }
154 
155 static void
156 set_target_v0_destroy(const struct xt_tgdtor_param *par)
157 {
158 	const struct xt_set_info_target_v0 *info = par->targinfo;
159 
160 	if (info->add_set.index != IPSET_INVALID_ID)
161 		ip_set_nfnl_put(info->add_set.index);
162 	if (info->del_set.index != IPSET_INVALID_ID)
163 		ip_set_nfnl_put(info->del_set.index);
164 }
165 
166 /* Revision 1: current interface to netfilter/iptables */
167 
168 static bool
169 set_match(const struct sk_buff *skb, struct xt_action_param *par)
170 {
171 	const struct xt_set_info_match *info = par->matchinfo;
172 
173 	return match_set(info->match_set.index, skb, par->family,
174 			 info->match_set.dim,
175 			 info->match_set.flags,
176 			 info->match_set.flags & IPSET_INV_MATCH);
177 }
178 
179 static int
180 set_match_checkentry(const struct xt_mtchk_param *par)
181 {
182 	struct xt_set_info_match *info = par->matchinfo;
183 	ip_set_id_t index;
184 
185 	index = ip_set_nfnl_get_byindex(info->match_set.index);
186 
187 	if (index == IPSET_INVALID_ID) {
188 		pr_warning("Cannot find set indentified by id %u to match\n",
189 			   info->match_set.index);
190 		return -ENOENT;
191 	}
192 	if (info->match_set.dim > IPSET_DIM_MAX) {
193 		pr_warning("Protocol error: set match dimension "
194 			   "is over the limit!\n");
195 		return -ERANGE;
196 	}
197 
198 	return 0;
199 }
200 
201 static void
202 set_match_destroy(const struct xt_mtdtor_param *par)
203 {
204 	struct xt_set_info_match *info = par->matchinfo;
205 
206 	ip_set_nfnl_put(info->match_set.index);
207 }
208 
209 static unsigned int
210 set_target(struct sk_buff *skb, const struct xt_action_param *par)
211 {
212 	const struct xt_set_info_target *info = par->targinfo;
213 
214 	if (info->add_set.index != IPSET_INVALID_ID)
215 		ip_set_add(info->add_set.index,
216 			   skb, par->family,
217 			   info->add_set.dim,
218 			   info->add_set.flags);
219 	if (info->del_set.index != IPSET_INVALID_ID)
220 		ip_set_del(info->del_set.index,
221 			   skb, par->family,
222 			   info->add_set.dim,
223 			   info->del_set.flags);
224 
225 	return XT_CONTINUE;
226 }
227 
228 static int
229 set_target_checkentry(const struct xt_tgchk_param *par)
230 {
231 	const struct xt_set_info_target *info = par->targinfo;
232 	ip_set_id_t index;
233 
234 	if (info->add_set.index != IPSET_INVALID_ID) {
235 		index = ip_set_nfnl_get_byindex(info->add_set.index);
236 		if (index == IPSET_INVALID_ID) {
237 			pr_warning("Cannot find add_set index %u as target\n",
238 				   info->add_set.index);
239 			return -ENOENT;
240 		}
241 	}
242 
243 	if (info->del_set.index != IPSET_INVALID_ID) {
244 		index = ip_set_nfnl_get_byindex(info->del_set.index);
245 		if (index == IPSET_INVALID_ID) {
246 			pr_warning("Cannot find del_set index %u as target\n",
247 				   info->del_set.index);
248 			return -ENOENT;
249 		}
250 	}
251 	if (info->add_set.dim > IPSET_DIM_MAX ||
252 	    info->del_set.flags > IPSET_DIM_MAX) {
253 		pr_warning("Protocol error: SET target dimension "
254 			   "is over the limit!\n");
255 		return -ERANGE;
256 	}
257 
258 	return 0;
259 }
260 
261 static void
262 set_target_destroy(const struct xt_tgdtor_param *par)
263 {
264 	const struct xt_set_info_target *info = par->targinfo;
265 
266 	if (info->add_set.index != IPSET_INVALID_ID)
267 		ip_set_nfnl_put(info->add_set.index);
268 	if (info->del_set.index != IPSET_INVALID_ID)
269 		ip_set_nfnl_put(info->del_set.index);
270 }
271 
272 static struct xt_match set_matches[] __read_mostly = {
273 	{
274 		.name		= "set",
275 		.family		= NFPROTO_IPV4,
276 		.revision	= 0,
277 		.match		= set_match_v0,
278 		.matchsize	= sizeof(struct xt_set_info_match_v0),
279 		.checkentry	= set_match_v0_checkentry,
280 		.destroy	= set_match_v0_destroy,
281 		.me		= THIS_MODULE
282 	},
283 	{
284 		.name		= "set",
285 		.family		= NFPROTO_IPV4,
286 		.revision	= 1,
287 		.match		= set_match,
288 		.matchsize	= sizeof(struct xt_set_info_match),
289 		.checkentry	= set_match_checkentry,
290 		.destroy	= set_match_destroy,
291 		.me		= THIS_MODULE
292 	},
293 	{
294 		.name		= "set",
295 		.family		= NFPROTO_IPV6,
296 		.revision	= 1,
297 		.match		= set_match,
298 		.matchsize	= sizeof(struct xt_set_info_match),
299 		.checkentry	= set_match_checkentry,
300 		.destroy	= set_match_destroy,
301 		.me		= THIS_MODULE
302 	},
303 };
304 
305 static struct xt_target set_targets[] __read_mostly = {
306 	{
307 		.name		= "SET",
308 		.revision	= 0,
309 		.family		= NFPROTO_IPV4,
310 		.target		= set_target_v0,
311 		.targetsize	= sizeof(struct xt_set_info_target_v0),
312 		.checkentry	= set_target_v0_checkentry,
313 		.destroy	= set_target_v0_destroy,
314 		.me		= THIS_MODULE
315 	},
316 	{
317 		.name		= "SET",
318 		.revision	= 1,
319 		.family		= NFPROTO_IPV4,
320 		.target		= set_target,
321 		.targetsize	= sizeof(struct xt_set_info_target),
322 		.checkentry	= set_target_checkentry,
323 		.destroy	= set_target_destroy,
324 		.me		= THIS_MODULE
325 	},
326 	{
327 		.name		= "SET",
328 		.revision	= 1,
329 		.family		= NFPROTO_IPV6,
330 		.target		= set_target,
331 		.targetsize	= sizeof(struct xt_set_info_target),
332 		.checkentry	= set_target_checkentry,
333 		.destroy	= set_target_destroy,
334 		.me		= THIS_MODULE
335 	},
336 };
337 
338 static int __init xt_set_init(void)
339 {
340 	int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches));
341 
342 	if (!ret) {
343 		ret = xt_register_targets(set_targets,
344 					  ARRAY_SIZE(set_targets));
345 		if (ret)
346 			xt_unregister_matches(set_matches,
347 					      ARRAY_SIZE(set_matches));
348 	}
349 	return ret;
350 }
351 
352 static void __exit xt_set_fini(void)
353 {
354 	xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches));
355 	xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets));
356 }
357 
358 module_init(xt_set_init);
359 module_exit(xt_set_fini);
360