xref: /openbmc/linux/net/netfilter/xt_set.c (revision 7fe2f639)
1 /* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
2  *                         Patrick Schaaf <bof@bof.de>
3  *                         Martin Josefsson <gandalf@wlug.westbo.se>
4  * Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
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 <kadlec@blackhole.kfki.hu>");
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 		ip_set_nfnl_put(info->match_set.index);
85 		return -ERANGE;
86 	}
87 
88 	/* Fill out compatibility data */
89 	compat_flags(&info->match_set);
90 
91 	return 0;
92 }
93 
94 static void
95 set_match_v0_destroy(const struct xt_mtdtor_param *par)
96 {
97 	struct xt_set_info_match_v0 *info = par->matchinfo;
98 
99 	ip_set_nfnl_put(info->match_set.index);
100 }
101 
102 static unsigned int
103 set_target_v0(struct sk_buff *skb, const struct xt_action_param *par)
104 {
105 	const struct xt_set_info_target_v0 *info = par->targinfo;
106 
107 	if (info->add_set.index != IPSET_INVALID_ID)
108 		ip_set_add(info->add_set.index, skb, par->family,
109 			   info->add_set.u.compat.dim,
110 			   info->add_set.u.compat.flags);
111 	if (info->del_set.index != IPSET_INVALID_ID)
112 		ip_set_del(info->del_set.index, skb, par->family,
113 			   info->del_set.u.compat.dim,
114 			   info->del_set.u.compat.flags);
115 
116 	return XT_CONTINUE;
117 }
118 
119 static int
120 set_target_v0_checkentry(const struct xt_tgchk_param *par)
121 {
122 	struct xt_set_info_target_v0 *info = par->targinfo;
123 	ip_set_id_t index;
124 
125 	if (info->add_set.index != IPSET_INVALID_ID) {
126 		index = ip_set_nfnl_get_byindex(info->add_set.index);
127 		if (index == IPSET_INVALID_ID) {
128 			pr_warning("Cannot find add_set index %u as target\n",
129 				   info->add_set.index);
130 			return -ENOENT;
131 		}
132 	}
133 
134 	if (info->del_set.index != IPSET_INVALID_ID) {
135 		index = ip_set_nfnl_get_byindex(info->del_set.index);
136 		if (index == IPSET_INVALID_ID) {
137 			pr_warning("Cannot find del_set index %u as target\n",
138 				   info->del_set.index);
139 			if (info->add_set.index != IPSET_INVALID_ID)
140 				ip_set_nfnl_put(info->add_set.index);
141 			return -ENOENT;
142 		}
143 	}
144 	if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0 ||
145 	    info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) {
146 		pr_warning("Protocol error: SET target dimension "
147 			   "is over the limit!\n");
148 		if (info->add_set.index != IPSET_INVALID_ID)
149 			ip_set_nfnl_put(info->add_set.index);
150 		if (info->del_set.index != IPSET_INVALID_ID)
151 			ip_set_nfnl_put(info->del_set.index);
152 		return -ERANGE;
153 	}
154 
155 	/* Fill out compatibility data */
156 	compat_flags(&info->add_set);
157 	compat_flags(&info->del_set);
158 
159 	return 0;
160 }
161 
162 static void
163 set_target_v0_destroy(const struct xt_tgdtor_param *par)
164 {
165 	const struct xt_set_info_target_v0 *info = par->targinfo;
166 
167 	if (info->add_set.index != IPSET_INVALID_ID)
168 		ip_set_nfnl_put(info->add_set.index);
169 	if (info->del_set.index != IPSET_INVALID_ID)
170 		ip_set_nfnl_put(info->del_set.index);
171 }
172 
173 /* Revision 1: current interface to netfilter/iptables */
174 
175 static bool
176 set_match(const struct sk_buff *skb, struct xt_action_param *par)
177 {
178 	const struct xt_set_info_match *info = par->matchinfo;
179 
180 	return match_set(info->match_set.index, skb, par->family,
181 			 info->match_set.dim,
182 			 info->match_set.flags,
183 			 info->match_set.flags & IPSET_INV_MATCH);
184 }
185 
186 static int
187 set_match_checkentry(const struct xt_mtchk_param *par)
188 {
189 	struct xt_set_info_match *info = par->matchinfo;
190 	ip_set_id_t index;
191 
192 	index = ip_set_nfnl_get_byindex(info->match_set.index);
193 
194 	if (index == IPSET_INVALID_ID) {
195 		pr_warning("Cannot find set indentified by id %u to match\n",
196 			   info->match_set.index);
197 		return -ENOENT;
198 	}
199 	if (info->match_set.dim > IPSET_DIM_MAX) {
200 		pr_warning("Protocol error: set match dimension "
201 			   "is over the limit!\n");
202 		ip_set_nfnl_put(info->match_set.index);
203 		return -ERANGE;
204 	}
205 
206 	return 0;
207 }
208 
209 static void
210 set_match_destroy(const struct xt_mtdtor_param *par)
211 {
212 	struct xt_set_info_match *info = par->matchinfo;
213 
214 	ip_set_nfnl_put(info->match_set.index);
215 }
216 
217 static unsigned int
218 set_target(struct sk_buff *skb, const struct xt_action_param *par)
219 {
220 	const struct xt_set_info_target *info = par->targinfo;
221 
222 	if (info->add_set.index != IPSET_INVALID_ID)
223 		ip_set_add(info->add_set.index,
224 			   skb, par->family,
225 			   info->add_set.dim,
226 			   info->add_set.flags);
227 	if (info->del_set.index != IPSET_INVALID_ID)
228 		ip_set_del(info->del_set.index,
229 			   skb, par->family,
230 			   info->del_set.dim,
231 			   info->del_set.flags);
232 
233 	return XT_CONTINUE;
234 }
235 
236 static int
237 set_target_checkentry(const struct xt_tgchk_param *par)
238 {
239 	const struct xt_set_info_target *info = par->targinfo;
240 	ip_set_id_t index;
241 
242 	if (info->add_set.index != IPSET_INVALID_ID) {
243 		index = ip_set_nfnl_get_byindex(info->add_set.index);
244 		if (index == IPSET_INVALID_ID) {
245 			pr_warning("Cannot find add_set index %u as target\n",
246 				   info->add_set.index);
247 			return -ENOENT;
248 		}
249 	}
250 
251 	if (info->del_set.index != IPSET_INVALID_ID) {
252 		index = ip_set_nfnl_get_byindex(info->del_set.index);
253 		if (index == IPSET_INVALID_ID) {
254 			pr_warning("Cannot find del_set index %u as target\n",
255 				   info->del_set.index);
256 			if (info->add_set.index != IPSET_INVALID_ID)
257 				ip_set_nfnl_put(info->add_set.index);
258 			return -ENOENT;
259 		}
260 	}
261 	if (info->add_set.dim > IPSET_DIM_MAX ||
262 	    info->del_set.dim > IPSET_DIM_MAX) {
263 		pr_warning("Protocol error: SET target dimension "
264 			   "is over the limit!\n");
265 		if (info->add_set.index != IPSET_INVALID_ID)
266 			ip_set_nfnl_put(info->add_set.index);
267 		if (info->del_set.index != IPSET_INVALID_ID)
268 			ip_set_nfnl_put(info->del_set.index);
269 		return -ERANGE;
270 	}
271 
272 	return 0;
273 }
274 
275 static void
276 set_target_destroy(const struct xt_tgdtor_param *par)
277 {
278 	const struct xt_set_info_target *info = par->targinfo;
279 
280 	if (info->add_set.index != IPSET_INVALID_ID)
281 		ip_set_nfnl_put(info->add_set.index);
282 	if (info->del_set.index != IPSET_INVALID_ID)
283 		ip_set_nfnl_put(info->del_set.index);
284 }
285 
286 static struct xt_match set_matches[] __read_mostly = {
287 	{
288 		.name		= "set",
289 		.family		= NFPROTO_IPV4,
290 		.revision	= 0,
291 		.match		= set_match_v0,
292 		.matchsize	= sizeof(struct xt_set_info_match_v0),
293 		.checkentry	= set_match_v0_checkentry,
294 		.destroy	= set_match_v0_destroy,
295 		.me		= THIS_MODULE
296 	},
297 	{
298 		.name		= "set",
299 		.family		= NFPROTO_IPV4,
300 		.revision	= 1,
301 		.match		= set_match,
302 		.matchsize	= sizeof(struct xt_set_info_match),
303 		.checkentry	= set_match_checkentry,
304 		.destroy	= set_match_destroy,
305 		.me		= THIS_MODULE
306 	},
307 	{
308 		.name		= "set",
309 		.family		= NFPROTO_IPV6,
310 		.revision	= 1,
311 		.match		= set_match,
312 		.matchsize	= sizeof(struct xt_set_info_match),
313 		.checkentry	= set_match_checkentry,
314 		.destroy	= set_match_destroy,
315 		.me		= THIS_MODULE
316 	},
317 };
318 
319 static struct xt_target set_targets[] __read_mostly = {
320 	{
321 		.name		= "SET",
322 		.revision	= 0,
323 		.family		= NFPROTO_IPV4,
324 		.target		= set_target_v0,
325 		.targetsize	= sizeof(struct xt_set_info_target_v0),
326 		.checkentry	= set_target_v0_checkentry,
327 		.destroy	= set_target_v0_destroy,
328 		.me		= THIS_MODULE
329 	},
330 	{
331 		.name		= "SET",
332 		.revision	= 1,
333 		.family		= NFPROTO_IPV4,
334 		.target		= set_target,
335 		.targetsize	= sizeof(struct xt_set_info_target),
336 		.checkentry	= set_target_checkentry,
337 		.destroy	= set_target_destroy,
338 		.me		= THIS_MODULE
339 	},
340 	{
341 		.name		= "SET",
342 		.revision	= 1,
343 		.family		= NFPROTO_IPV6,
344 		.target		= set_target,
345 		.targetsize	= sizeof(struct xt_set_info_target),
346 		.checkentry	= set_target_checkentry,
347 		.destroy	= set_target_destroy,
348 		.me		= THIS_MODULE
349 	},
350 };
351 
352 static int __init xt_set_init(void)
353 {
354 	int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches));
355 
356 	if (!ret) {
357 		ret = xt_register_targets(set_targets,
358 					  ARRAY_SIZE(set_targets));
359 		if (ret)
360 			xt_unregister_matches(set_matches,
361 					      ARRAY_SIZE(set_matches));
362 	}
363 	return ret;
364 }
365 
366 static void __exit xt_set_fini(void)
367 {
368 	xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches));
369 	xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets));
370 }
371 
372 module_init(xt_set_init);
373 module_exit(xt_set_fini);
374