libxt_SET.c revision e39f367d905670e39e6f08d2b73c715a6d0b4bfb
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
26static void
27set_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
37static 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
43static void
44set_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
51static void
52set_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
62static void
63parse_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
91static int
92set_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
111static void
112print_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
130static void
131set_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
140static void
141set_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
153static void
154set_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
169static void
170parse_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
196static int
197set_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
218static void
219print_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
235static void
236set_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
245static void
246set_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
258static void
259set_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
269static 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
277static void
278set_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
294static void
295set_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
305static int
306set_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
338static void
339set_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
352static void
353set_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
365static 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
413void _init(void)
414{
415	xtables_register_targets(set_tg_reg, ARRAY_SIZE(set_tg_reg));
416}
417