• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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-2010 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 /* Shared library add-on to iptables to add IP set mangling target. */
12 #include <stdbool.h>
13 #include <stdio.h>
14 #include <netdb.h>
15 #include <string.h>
16 #include <stdlib.h>
17 #include <getopt.h>
18 #include <ctype.h>
19 
20 #include <xtables.h>
21 #include <linux/netfilter/xt_set.h>
22 #include "libxt_set.h"
23 
24 /* Revision 0 */
25 
26 static void
set_target_help_v0(void)27 set_target_help_v0(void)
28 {
29 	printf("SET target options:\n"
30 	       " --add-set name flags\n"
31 	       " --del-set name flags\n"
32 	       "		add/del src/dst IP/port from/to named sets,\n"
33 	       "		where flags are the comma separated list of\n"
34 	       "		'src' and 'dst' specifications.\n");
35 }
36 
37 static const struct option set_target_opts_v0[] = {
38 	{.name = "add-set", .has_arg = true, .val = '1'},
39 	{.name = "del-set", .has_arg = true, .val = '2'},
40 	XT_GETOPT_TABLEEND,
41 };
42 
43 static void
set_target_check_v0(unsigned int flags)44 set_target_check_v0(unsigned int flags)
45 {
46 	if (!flags)
47 		xtables_error(PARAMETER_PROBLEM,
48 			   "You must specify either `--add-set' or `--del-set'");
49 }
50 
51 static void
set_target_init_v0(struct xt_entry_target * target)52 set_target_init_v0(struct xt_entry_target *target)
53 {
54 	struct xt_set_info_target_v0 *info =
55 		(struct xt_set_info_target_v0 *) target->data;
56 
57 	info->add_set.index =
58 	info->del_set.index = IPSET_INVALID_ID;
59 
60 }
61 
62 static void
parse_target_v0(char ** argv,int invert,unsigned int * flags,struct xt_set_info_v0 * info,const char * what)63 parse_target_v0(char **argv, int invert, unsigned int *flags,
64 		struct xt_set_info_v0 *info, const char *what)
65 {
66 	if (info->u.flags[0])
67 		xtables_error(PARAMETER_PROBLEM,
68 			      "--%s can be specified only once", what);
69 
70 	if (xtables_check_inverse(optarg, &invert, NULL, 0, argv))
71 		xtables_error(PARAMETER_PROBLEM,
72 			      "Unexpected `!' after --%s", what);
73 
74 	if (!argv[optind]
75 	    || argv[optind][0] == '-' || argv[optind][0] == '!')
76 		xtables_error(PARAMETER_PROBLEM,
77 			      "--%s requires two args.", what);
78 
79 	if (strlen(optarg) > IPSET_MAXNAMELEN - 1)
80 		xtables_error(PARAMETER_PROBLEM,
81 			      "setname `%s' too long, max %d characters.",
82 			      optarg, IPSET_MAXNAMELEN - 1);
83 
84 	get_set_byname(optarg, (struct xt_set_info *)info);
85 	parse_dirs_v0(argv[optind], info);
86 	optind++;
87 
88 	*flags = 1;
89 }
90 
91 static int
set_target_parse_v0(int c,char ** argv,int invert,unsigned int * flags,const void * entry,struct xt_entry_target ** target)92 set_target_parse_v0(int c, char **argv, int invert, unsigned int *flags,
93 		    const void *entry, struct xt_entry_target **target)
94 {
95 	struct xt_set_info_target_v0 *myinfo =
96 		(struct xt_set_info_target_v0 *) (*target)->data;
97 
98 	switch (c) {
99 	case '1':		/* --add-set <set> <flags> */
100 		parse_target_v0(argv, invert, flags,
101 				&myinfo->add_set, "add-set");
102 		break;
103 	case '2':		/* --del-set <set>[:<flags>] <flags> */
104 		parse_target_v0(argv, invert, flags,
105 				&myinfo->del_set, "del-set");
106 		break;
107 	}
108 	return 1;
109 }
110 
111 static void
print_target_v0(const char * prefix,const struct xt_set_info_v0 * info)112 print_target_v0(const char *prefix, const struct xt_set_info_v0 *info)
113 {
114 	int i;
115 	char setname[IPSET_MAXNAMELEN];
116 
117 	if (info->index == IPSET_INVALID_ID)
118 		return;
119 	get_set_byid(setname, info->index);
120 	printf(" %s %s", prefix, setname);
121 	for (i = 0; i < IPSET_DIM_MAX; i++) {
122 		if (!info->u.flags[i])
123 			break;
124 		printf("%s%s",
125 		       i == 0 ? " " : ",",
126 		       info->u.flags[i] & IPSET_SRC ? "src" : "dst");
127 	}
128 }
129 
130 static void
set_target_print_v0(const void * ip,const struct xt_entry_target * target,int numeric)131 set_target_print_v0(const void *ip, const struct xt_entry_target *target,
132                     int numeric)
133 {
134 	const struct xt_set_info_target_v0 *info = (const void *)target->data;
135 
136 	print_target_v0("add-set", &info->add_set);
137 	print_target_v0("del-set", &info->del_set);
138 }
139 
140 static void
set_target_save_v0(const void * ip,const struct xt_entry_target * target)141 set_target_save_v0(const void *ip, const struct xt_entry_target *target)
142 {
143 	const struct xt_set_info_target_v0 *info = (const void *)target->data;
144 
145 	print_target_v0("--add-set", &info->add_set);
146 	print_target_v0("--del-set", &info->del_set);
147 }
148 
149 /* Revision 1 */
150 
151 #define set_target_help_v1	set_target_help_v0
152 
153 static void
set_target_init_v1(struct xt_entry_target * target)154 set_target_init_v1(struct xt_entry_target *target)
155 {
156 	struct xt_set_info_target_v1 *info =
157 		(struct xt_set_info_target_v1 *) target->data;
158 
159 	info->add_set.index =
160 	info->del_set.index = IPSET_INVALID_ID;
161 
162 }
163 
164 #define SET_TARGET_ADD		0x1
165 #define SET_TARGET_DEL		0x2
166 #define SET_TARGET_EXIST	0x4
167 #define SET_TARGET_TIMEOUT	0x8
168 
169 static void
parse_target(char ** argv,int invert,struct xt_set_info * info,const char * what)170 parse_target(char **argv, int invert, struct xt_set_info *info,
171 	     const char *what)
172 {
173 	if (info->dim)
174 		xtables_error(PARAMETER_PROBLEM,
175 			      "--%s can be specified only once", what);
176 
177 	if (xtables_check_inverse(optarg, &invert, NULL, 0, argv))
178 		xtables_error(PARAMETER_PROBLEM,
179 			      "Unexpected `!' after --%s", what);
180 
181 	if (!argv[optind]
182 	    || argv[optind][0] == '-' || argv[optind][0] == '!')
183 		xtables_error(PARAMETER_PROBLEM,
184 			      "--%s requires two args.", what);
185 
186 	if (strlen(optarg) > IPSET_MAXNAMELEN - 1)
187 		xtables_error(PARAMETER_PROBLEM,
188 			      "setname `%s' too long, max %d characters.",
189 			      optarg, IPSET_MAXNAMELEN - 1);
190 
191 	get_set_byname(optarg, info);
192 	parse_dirs(argv[optind], info);
193 	optind++;
194 }
195 
196 static int
set_target_parse_v1(int c,char ** argv,int invert,unsigned int * flags,const void * entry,struct xt_entry_target ** target)197 set_target_parse_v1(int c, char **argv, int invert, unsigned int *flags,
198 		    const void *entry, struct xt_entry_target **target)
199 {
200 	struct xt_set_info_target_v1 *myinfo =
201 		(struct xt_set_info_target_v1 *) (*target)->data;
202 
203 	switch (c) {
204 	case '1':		/* --add-set <set> <flags> */
205 		parse_target(argv, invert, &myinfo->add_set, "add-set");
206 		*flags |= SET_TARGET_ADD;
207 		break;
208 	case '2':		/* --del-set <set>[:<flags>] <flags> */
209 		parse_target(argv, invert, &myinfo->del_set, "del-set");
210 		*flags |= SET_TARGET_DEL;
211 		break;
212 	}
213 	return 1;
214 }
215 
216 #define set_target_check_v1	set_target_check_v0
217 
218 static void
print_target(const char * prefix,const struct xt_set_info * info)219 print_target(const char *prefix, const struct xt_set_info *info)
220 {
221 	int i;
222 	char setname[IPSET_MAXNAMELEN];
223 
224 	if (info->index == IPSET_INVALID_ID)
225 		return;
226 	get_set_byid(setname, info->index);
227 	printf(" %s %s", prefix, setname);
228 	for (i = 1; i <= info->dim; i++) {
229 		printf("%s%s",
230 		       i == 1 ? " " : ",",
231 		       info->flags & (1 << i) ? "src" : "dst");
232 	}
233 }
234 
235 static void
set_target_print_v1(const void * ip,const struct xt_entry_target * target,int numeric)236 set_target_print_v1(const void *ip, const struct xt_entry_target *target,
237 		    int numeric)
238 {
239 	const struct xt_set_info_target_v1 *info = (const void *)target->data;
240 
241 	print_target("add-set", &info->add_set);
242 	print_target("del-set", &info->del_set);
243 }
244 
245 static void
set_target_save_v1(const void * ip,const struct xt_entry_target * target)246 set_target_save_v1(const void *ip, const struct xt_entry_target *target)
247 {
248 	const struct xt_set_info_target_v1 *info = (const void *)target->data;
249 
250 	print_target("--add-set", &info->add_set);
251 	print_target("--del-set", &info->del_set);
252 }
253 
254 #define set_target_opts_v1	set_target_opts_v0
255 
256 /* Revision 2 */
257 
258 static void
set_target_help_v2(void)259 set_target_help_v2(void)
260 {
261 	printf("SET target options:\n"
262 	       " --add-set name flags [--exist] [--timeout n]\n"
263 	       " --del-set name flags\n"
264 	       "		add/del src/dst IP/port from/to named sets,\n"
265 	       "		where flags are the comma separated list of\n"
266 	       "		'src' and 'dst' specifications.\n");
267 }
268 
269 static const struct option set_target_opts_v2[] = {
270 	{.name = "add-set", .has_arg = true,  .val = '1'},
271 	{.name = "del-set", .has_arg = true,  .val = '2'},
272 	{.name = "exist",   .has_arg = false, .val = '3'},
273 	{.name = "timeout", .has_arg = true,  .val = '4'},
274 	XT_GETOPT_TABLEEND,
275 };
276 
277 static void
set_target_check_v2(unsigned int flags)278 set_target_check_v2(unsigned int flags)
279 {
280 	if (!(flags & (SET_TARGET_ADD|SET_TARGET_DEL)))
281 		xtables_error(PARAMETER_PROBLEM,
282 			   "You must specify either `--add-set' or `--del-set'");
283 	if (!(flags & SET_TARGET_ADD)) {
284 		if (flags & SET_TARGET_EXIST)
285 			xtables_error(PARAMETER_PROBLEM,
286 				"Flag `--exist' can be used with `--add-set' only");
287 		if (flags & SET_TARGET_TIMEOUT)
288 			xtables_error(PARAMETER_PROBLEM,
289 				"Option `--timeout' can be used with `--add-set' only");
290 	}
291 }
292 
293 
294 static void
set_target_init_v2(struct xt_entry_target * target)295 set_target_init_v2(struct xt_entry_target *target)
296 {
297 	struct xt_set_info_target_v2 *info =
298 		(struct xt_set_info_target_v2 *) target->data;
299 
300 	info->add_set.index =
301 	info->del_set.index = IPSET_INVALID_ID;
302 	info->timeout = UINT32_MAX;
303 }
304 
305 static int
set_target_parse_v2(int c,char ** argv,int invert,unsigned int * flags,const void * entry,struct xt_entry_target ** target)306 set_target_parse_v2(int c, char **argv, int invert, unsigned int *flags,
307 		    const void *entry, struct xt_entry_target **target)
308 {
309 	struct xt_set_info_target_v2 *myinfo =
310 		(struct xt_set_info_target_v2 *) (*target)->data;
311 	unsigned int timeout;
312 
313 	switch (c) {
314 	case '1':		/* --add-set <set> <flags> */
315 		parse_target(argv, invert, &myinfo->add_set, "add-set");
316 		*flags |= SET_TARGET_ADD;
317 		break;
318 	case '2':		/* --del-set <set>[:<flags>] <flags> */
319 		parse_target(argv, invert, &myinfo->del_set, "del-set");
320 		*flags |= SET_TARGET_DEL;
321 		break;
322 	case '3':
323 		myinfo->flags |= IPSET_FLAG_EXIST;
324 		*flags |= SET_TARGET_EXIST;
325 		break;
326 	case '4':
327 		if (!xtables_strtoui(optarg, NULL, &timeout, 0, UINT32_MAX - 1))
328 			xtables_error(PARAMETER_PROBLEM,
329 				      "Invalid value for option --timeout "
330 				      "or out of range 0-%u", UINT32_MAX - 1);
331 		myinfo->timeout = timeout;
332 		*flags |= SET_TARGET_TIMEOUT;
333 		break;
334 	}
335 	return 1;
336 }
337 
338 static void
set_target_print_v2(const void * ip,const struct xt_entry_target * target,int numeric)339 set_target_print_v2(const void *ip, const struct xt_entry_target *target,
340 		    int numeric)
341 {
342 	const struct xt_set_info_target_v2 *info = (const void *)target->data;
343 
344 	print_target("add-set", &info->add_set);
345 	if (info->flags & IPSET_FLAG_EXIST)
346 		printf(" exist");
347 	if (info->timeout != UINT32_MAX)
348 		printf(" timeout %u", info->timeout);
349 	print_target("del-set", &info->del_set);
350 }
351 
352 static void
set_target_save_v2(const void * ip,const struct xt_entry_target * target)353 set_target_save_v2(const void *ip, const struct xt_entry_target *target)
354 {
355 	const struct xt_set_info_target_v2 *info = (const void *)target->data;
356 
357 	print_target("--add-set", &info->add_set);
358 	if (info->flags & IPSET_FLAG_EXIST)
359 		printf(" --exist");
360 	if (info->timeout != UINT32_MAX)
361 		printf(" --timeout %u", info->timeout);
362 	print_target("--del-set", &info->del_set);
363 }
364 
365 static struct xtables_target set_tg_reg[] = {
366 	{
367 		.name		= "SET",
368 		.revision	= 0,
369 		.version	= XTABLES_VERSION,
370 		.family		= NFPROTO_IPV4,
371 		.size		= XT_ALIGN(sizeof(struct xt_set_info_target_v0)),
372 		.userspacesize	= XT_ALIGN(sizeof(struct xt_set_info_target_v0)),
373 		.help		= set_target_help_v0,
374 		.init		= set_target_init_v0,
375 		.parse		= set_target_parse_v0,
376 		.final_check	= set_target_check_v0,
377 		.print		= set_target_print_v0,
378 		.save		= set_target_save_v0,
379 		.extra_opts	= set_target_opts_v0,
380 	},
381 	{
382 		.name		= "SET",
383 		.revision	= 1,
384 		.version	= XTABLES_VERSION,
385 		.family		= NFPROTO_UNSPEC,
386 		.size		= XT_ALIGN(sizeof(struct xt_set_info_target_v1)),
387 		.userspacesize	= XT_ALIGN(sizeof(struct xt_set_info_target_v1)),
388 		.help		= set_target_help_v1,
389 		.init		= set_target_init_v1,
390 		.parse		= set_target_parse_v1,
391 		.final_check	= set_target_check_v1,
392 		.print		= set_target_print_v1,
393 		.save		= set_target_save_v1,
394 		.extra_opts	= set_target_opts_v1,
395 	},
396 	{
397 		.name		= "SET",
398 		.revision	= 2,
399 		.version	= XTABLES_VERSION,
400 		.family		= NFPROTO_UNSPEC,
401 		.size		= XT_ALIGN(sizeof(struct xt_set_info_target_v2)),
402 		.userspacesize	= XT_ALIGN(sizeof(struct xt_set_info_target_v2)),
403 		.help		= set_target_help_v2,
404 		.init		= set_target_init_v2,
405 		.parse		= set_target_parse_v2,
406 		.final_check	= set_target_check_v2,
407 		.print		= set_target_print_v2,
408 		.save		= set_target_save_v2,
409 		.extra_opts	= set_target_opts_v2,
410 	},
411 };
412 
_init(void)413 void _init(void)
414 {
415 	xtables_register_targets(set_tg_reg, ARRAY_SIZE(set_tg_reg));
416 }
417