Home | History | Annotate | Download | only in extensions
      1 /* Copyright (C) 2000-2002 Joakim Axelsson <gozem (at) linux.nu>
      2  *                         Patrick Schaaf <bof (at) bof.de>
      3  *                         Martin Josefsson <gandalf (at) wlug.westbo.se>
      4  * Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec (at) 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
     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
     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
     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
     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
     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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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 
    413 void _init(void)
    414 {
    415 	xtables_register_targets(set_tg_reg, ARRAY_SIZE(set_tg_reg));
    416 }
    417