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