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