Home | History | Annotate | Download | only in iptables
      1 /* Code to restore the iptables state, from file by ip6tables-save.
      2  * Author:  Andras Kis-Szabo <kisza (at) sch.bme.hu>
      3  *
      4  * based on iptables-restore
      5  * Authors:
      6  *      Harald Welte <laforge (at) gnumonks.org>
      7  *      Rusty Russell <rusty (at) linuxcare.com.au>
      8  * This code is distributed under the terms of GNU GPL v2
      9  */
     10 
     11 #include <getopt.h>
     12 #include <errno.h>
     13 #include <stdbool.h>
     14 #include <string.h>
     15 #include <stdio.h>
     16 #include <stdlib.h>
     17 #include "ip6tables.h"
     18 #include "xshared.h"
     19 #include "xtables.h"
     20 #include "libiptc/libip6tc.h"
     21 #include "ip6tables-multi.h"
     22 
     23 #ifdef DEBUG
     24 #define DEBUGP(x, args...) fprintf(stderr, x, ## args)
     25 #else
     26 #define DEBUGP(x, args...)
     27 #endif
     28 
     29 static int counters = 0, verbose = 0, noflush = 0, wait = 0;
     30 
     31 static struct timeval wait_interval = {
     32 	.tv_sec	= 1,
     33 };
     34 
     35 /* Keeping track of external matches and targets.  */
     36 static const struct option options[] = {
     37 	{.name = "counters",      .has_arg = 0, .val = 'c'},
     38 	{.name = "verbose",       .has_arg = 0, .val = 'v'},
     39 	{.name = "test",          .has_arg = 0, .val = 't'},
     40 	{.name = "help",          .has_arg = 0, .val = 'h'},
     41 	{.name = "noflush",       .has_arg = 0, .val = 'n'},
     42 	{.name = "modprobe",      .has_arg = 1, .val = 'M'},
     43 	{.name = "table",         .has_arg = 1, .val = 'T'},
     44 	{.name = "wait",          .has_arg = 2, .val = 'w'},
     45 	{.name = "wait-interval", .has_arg = 2, .val = 'W'},
     46 	{NULL},
     47 };
     48 
     49 static void print_usage(const char *name, const char *version) __attribute__((noreturn));
     50 
     51 static void print_usage(const char *name, const char *version)
     52 {
     53 	fprintf(stderr, "Usage: %s [-c] [-v] [-t] [-h] [-n] [-w secs] [-W usecs] [-T table] [-M command]\n"
     54 			"	   [ --counters ]\n"
     55 			"	   [ --verbose ]\n"
     56 			"	   [ --test ]\n"
     57 			"	   [ --help ]\n"
     58 			"	   [ --noflush ]\n"
     59 			"	   [ --wait=<seconds>\n"
     60 			"	   [ --wait-interval=<usecs>\n"
     61 			"	   [ --table=<TABLE> ]\n"
     62 			"	   [ --modprobe=<command> ]\n", name);
     63 
     64 	exit(1);
     65 }
     66 
     67 static struct xtc_handle *create_handle(const char *tablename)
     68 {
     69 	struct xtc_handle *handle;
     70 
     71 	handle = ip6tc_init(tablename);
     72 
     73 	if (!handle) {
     74 		/* try to insmod the module if iptc_init failed */
     75 		xtables_load_ko(xtables_modprobe_program, false);
     76 		handle = ip6tc_init(tablename);
     77 	}
     78 
     79 	if (!handle) {
     80 		xtables_error(PARAMETER_PROBLEM, "%s: unable to initialize "
     81 			"table '%s'\n", ip6tables_globals.program_name,
     82 			tablename);
     83 		exit(1);
     84 	}
     85 	return handle;
     86 }
     87 
     88 static int parse_counters(char *string, struct xt_counters *ctr)
     89 {
     90 	unsigned long long pcnt, bcnt;
     91 	int ret;
     92 
     93 	ret = sscanf(string, "[%llu:%llu]", &pcnt, &bcnt);
     94 	ctr->pcnt = pcnt;
     95 	ctr->bcnt = bcnt;
     96 	return ret == 2;
     97 }
     98 
     99 /* global new argv and argc */
    100 static char *newargv[255];
    101 static int newargc;
    102 
    103 /* function adding one argument to newargv, updating newargc
    104  * returns true if argument added, false otherwise */
    105 static int add_argv(char *what) {
    106 	DEBUGP("add_argv: %s\n", what);
    107 	if (what && newargc + 1 < ARRAY_SIZE(newargv)) {
    108 		newargv[newargc] = strdup(what);
    109 		newargv[++newargc] = NULL;
    110 		return 1;
    111 	} else {
    112 		xtables_error(PARAMETER_PROBLEM,
    113 			"Parser cannot handle more arguments\n");
    114 		return 0;
    115 	}
    116 }
    117 
    118 static void free_argv(void) {
    119 	int i;
    120 
    121 	for (i = 0; i < newargc; i++)
    122 		free(newargv[i]);
    123 }
    124 
    125 static void add_param_to_argv(char *parsestart)
    126 {
    127 	int quote_open = 0, escaped = 0, param_len = 0;
    128 	char param_buffer[1024], *curchar;
    129 
    130 	/* After fighting with strtok enough, here's now
    131 	 * a 'real' parser. According to Rusty I'm now no
    132 	 * longer a real hacker, but I can live with that */
    133 
    134 	for (curchar = parsestart; *curchar; curchar++) {
    135 		if (quote_open) {
    136 			if (escaped) {
    137 				param_buffer[param_len++] = *curchar;
    138 				escaped = 0;
    139 				continue;
    140 			} else if (*curchar == '\\') {
    141 				escaped = 1;
    142 				continue;
    143 			} else if (*curchar == '"') {
    144 				quote_open = 0;
    145 				*curchar = ' ';
    146 			} else {
    147 				param_buffer[param_len++] = *curchar;
    148 				continue;
    149 			}
    150 		} else {
    151 			if (*curchar == '"') {
    152 				quote_open = 1;
    153 				continue;
    154 			}
    155 		}
    156 
    157 		if (*curchar == ' '
    158 		    || *curchar == '\t'
    159 		    || * curchar == '\n') {
    160 			if (!param_len) {
    161 				/* two spaces? */
    162 				continue;
    163 			}
    164 
    165 			param_buffer[param_len] = '\0';
    166 
    167 			/* check if table name specified */
    168 			if (!strncmp(param_buffer, "-t", 2)
    169                             || !strncmp(param_buffer, "--table", 8)) {
    170 				xtables_error(PARAMETER_PROBLEM,
    171 				"The -t option (seen in line %u) cannot be "
    172 				"used in ip6tables-restore.\n", line);
    173 				exit(1);
    174 			}
    175 
    176 			add_argv(param_buffer);
    177 			param_len = 0;
    178 		} else {
    179 			/* regular character, copy to buffer */
    180 			param_buffer[param_len++] = *curchar;
    181 
    182 			if (param_len >= sizeof(param_buffer))
    183 				xtables_error(PARAMETER_PROBLEM,
    184 				   "Parameter too long!");
    185 		}
    186 	}
    187 }
    188 
    189 int ip6tables_restore_main(int argc, char *argv[])
    190 {
    191 	struct xtc_handle *handle = NULL;
    192 	char buffer[10240];
    193 	int c, lock;
    194 	char curtable[XT_TABLE_MAXNAMELEN + 1];
    195 	FILE *in;
    196 	int in_table = 0, testing = 0;
    197 	const char *tablename = NULL;
    198 	const struct xtc_ops *ops = &ip6tc_ops;
    199 
    200 	line = 0;
    201 	lock = XT_LOCK_NOT_ACQUIRED;
    202 
    203 	ip6tables_globals.program_name = "ip6tables-restore";
    204 	c = xtables_init_all(&ip6tables_globals, NFPROTO_IPV6);
    205 	if (c < 0) {
    206 		fprintf(stderr, "%s/%s Failed to initialize xtables\n",
    207 				ip6tables_globals.program_name,
    208 				ip6tables_globals.program_version);
    209 		exit(1);
    210 	}
    211 #if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
    212 	init_extensions();
    213 	init_extensions6();
    214 #endif
    215 
    216 	while ((c = getopt_long(argc, argv, "bcvthnwWM:T:", options, NULL)) != -1) {
    217 		switch (c) {
    218 			case 'b':
    219 				fprintf(stderr, "-b/--binary option is not implemented\n");
    220 				break;
    221 			case 'c':
    222 				counters = 1;
    223 				break;
    224 			case 'v':
    225 				verbose = 1;
    226 				break;
    227 			case 't':
    228 				testing = 1;
    229 				break;
    230 			case 'h':
    231 				print_usage("ip6tables-restore",
    232 					    IPTABLES_VERSION);
    233 				break;
    234 			case 'n':
    235 				noflush = 1;
    236 				break;
    237 			case 'w':
    238 				wait = parse_wait_time(argc, argv);
    239 				break;
    240 			case 'W':
    241 				parse_wait_interval(argc, argv, &wait_interval);
    242 				break;
    243 			case 'M':
    244 				xtables_modprobe_program = optarg;
    245 				break;
    246 			case 'T':
    247 				tablename = optarg;
    248 				break;
    249 		}
    250 	}
    251 
    252 	if (optind == argc - 1) {
    253 		in = fopen(argv[optind], "re");
    254 		if (!in) {
    255 			fprintf(stderr, "Can't open %s: %s\n", argv[optind],
    256 				strerror(errno));
    257 			exit(1);
    258 		}
    259 	}
    260 	else if (optind < argc) {
    261 		fprintf(stderr, "Unknown arguments found on commandline\n");
    262 		exit(1);
    263 	}
    264 	else in = stdin;
    265 
    266 	/* Grab standard input. */
    267 	while (fgets(buffer, sizeof(buffer), in)) {
    268 		int ret = 0;
    269 
    270 		line++;
    271 		if (buffer[0] == '\n')
    272 			continue;
    273 		else if (buffer[0] == '#') {
    274 			if (verbose) {
    275 				fputs(buffer, stdout);
    276 				fflush(stdout);
    277 			}
    278 			continue;
    279 		} else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) {
    280 			if (!testing) {
    281 				DEBUGP("Calling commit\n");
    282 				ret = ops->commit(handle);
    283 				ops->free(handle);
    284 				handle = NULL;
    285 			} else {
    286 				DEBUGP("Not calling commit, testing\n");
    287 				ret = 1;
    288 			}
    289 
    290 			/* Done with the current table, release the lock. */
    291 			if (lock >= 0) {
    292 				xtables_unlock(lock);
    293 				lock = XT_LOCK_NOT_ACQUIRED;
    294 			}
    295 
    296 			in_table = 0;
    297 		} else if ((buffer[0] == '*') && (!in_table)) {
    298 			/* Acquire a lock before we create a new table handle */
    299 			lock = xtables_lock(wait, &wait_interval);
    300 			if (lock == XT_LOCK_BUSY) {
    301 				fprintf(stderr, "Another app is currently holding the xtables lock. "
    302 					"Perhaps you want to use the -w option?\n");
    303 				exit(RESOURCE_PROBLEM);
    304 			}
    305 
    306 			/* New table */
    307 			char *table;
    308 
    309 			table = strtok(buffer+1, " \t\n");
    310 			DEBUGP("line %u, table '%s'\n", line, table);
    311 			if (!table) {
    312 				xtables_error(PARAMETER_PROBLEM,
    313 					"%s: line %u table name invalid\n",
    314 					xt_params->program_name, line);
    315 				exit(1);
    316 			}
    317 			strncpy(curtable, table, XT_TABLE_MAXNAMELEN);
    318 			curtable[XT_TABLE_MAXNAMELEN] = '\0';
    319 
    320 			if (tablename != NULL && strcmp(tablename, table) != 0)
    321 				continue;
    322 			if (handle)
    323 				ops->free(handle);
    324 
    325 			handle = create_handle(table);
    326 			if (noflush == 0) {
    327 				DEBUGP("Cleaning all chains of table '%s'\n",
    328 					table);
    329 				for_each_chain6(flush_entries6, verbose, 1,
    330 						handle);
    331 
    332 				DEBUGP("Deleting all user-defined chains "
    333 				       "of table '%s'\n", table);
    334 				for_each_chain6(delete_chain6, verbose, 0,
    335 						handle);
    336 			}
    337 
    338 			ret = 1;
    339 			in_table = 1;
    340 
    341 		} else if ((buffer[0] == ':') && (in_table)) {
    342 			/* New chain. */
    343 			char *policy, *chain;
    344 
    345 			chain = strtok(buffer+1, " \t\n");
    346 			DEBUGP("line %u, chain '%s'\n", line, chain);
    347 			if (!chain) {
    348 				xtables_error(PARAMETER_PROBLEM,
    349 					   "%s: line %u chain name invalid\n",
    350 					   xt_params->program_name, line);
    351 				exit(1);
    352 			}
    353 
    354 			if (strlen(chain) >= XT_EXTENSION_MAXNAMELEN)
    355 				xtables_error(PARAMETER_PROBLEM,
    356 					   "Invalid chain name `%s' "
    357 					   "(%u chars max)",
    358 					   chain, XT_EXTENSION_MAXNAMELEN - 1);
    359 
    360 			if (ops->builtin(chain, handle) <= 0) {
    361 				if (noflush && ops->is_chain(chain, handle)) {
    362 					DEBUGP("Flushing existing user defined chain '%s'\n", chain);
    363 					if (!ops->flush_entries(chain, handle))
    364 						xtables_error(PARAMETER_PROBLEM,
    365 							   "error flushing chain "
    366 							   "'%s':%s\n", chain,
    367 							   strerror(errno));
    368 				} else {
    369 					DEBUGP("Creating new chain '%s'\n", chain);
    370 					if (!ops->create_chain(chain, handle))
    371 						xtables_error(PARAMETER_PROBLEM,
    372 							   "error creating chain "
    373 							   "'%s':%s\n", chain,
    374 							   strerror(errno));
    375 				}
    376 			}
    377 
    378 			policy = strtok(NULL, " \t\n");
    379 			DEBUGP("line %u, policy '%s'\n", line, policy);
    380 			if (!policy) {
    381 				xtables_error(PARAMETER_PROBLEM,
    382 					   "%s: line %u policy invalid\n",
    383 					   xt_params->program_name, line);
    384 				exit(1);
    385 			}
    386 
    387 			if (strcmp(policy, "-") != 0) {
    388 				struct xt_counters count;
    389 
    390 				if (counters) {
    391 					char *ctrs;
    392 					ctrs = strtok(NULL, " \t\n");
    393 
    394 					if (!ctrs || !parse_counters(ctrs, &count))
    395 						xtables_error(PARAMETER_PROBLEM,
    396 							  "invalid policy counters "
    397 							  "for chain '%s'\n", chain);
    398 
    399 				} else {
    400 					memset(&count, 0, sizeof(count));
    401 				}
    402 
    403 				DEBUGP("Setting policy of chain %s to %s\n",
    404 					chain, policy);
    405 
    406 				if (!ops->set_policy(chain, policy, &count,
    407 						     handle))
    408 					xtables_error(OTHER_PROBLEM,
    409 						"Can't set policy `%s'"
    410 						" on `%s' line %u: %s\n",
    411 						policy, chain, line,
    412 						ops->strerror(errno));
    413 			}
    414 
    415 			ret = 1;
    416 
    417 		} else if (in_table) {
    418 			int a;
    419 			char *ptr = buffer;
    420 			char *pcnt = NULL;
    421 			char *bcnt = NULL;
    422 			char *parsestart;
    423 
    424 			/* reset the newargv */
    425 			newargc = 0;
    426 
    427 			if (buffer[0] == '[') {
    428 				/* we have counters in our input */
    429 				ptr = strchr(buffer, ']');
    430 				if (!ptr)
    431 					xtables_error(PARAMETER_PROBLEM,
    432 						   "Bad line %u: need ]\n",
    433 						   line);
    434 
    435 				pcnt = strtok(buffer+1, ":");
    436 				if (!pcnt)
    437 					xtables_error(PARAMETER_PROBLEM,
    438 						   "Bad line %u: need :\n",
    439 						   line);
    440 
    441 				bcnt = strtok(NULL, "]");
    442 				if (!bcnt)
    443 					xtables_error(PARAMETER_PROBLEM,
    444 						   "Bad line %u: need ]\n",
    445 						   line);
    446 
    447 				/* start command parsing after counter */
    448 				parsestart = ptr + 1;
    449 			} else {
    450 				/* start command parsing at start of line */
    451 				parsestart = buffer;
    452 			}
    453 
    454 			add_argv(argv[0]);
    455 			add_argv("-t");
    456 			add_argv(curtable);
    457 
    458 			if (counters && pcnt && bcnt) {
    459 				add_argv("--set-counters");
    460 				add_argv((char *) pcnt);
    461 				add_argv((char *) bcnt);
    462 			}
    463 
    464 			add_param_to_argv(parsestart);
    465 
    466 			DEBUGP("calling do_command6(%u, argv, &%s, handle):\n",
    467 				newargc, curtable);
    468 
    469 			for (a = 0; a < newargc; a++)
    470 				DEBUGP("argv[%u]: %s\n", a, newargv[a]);
    471 
    472 			ret = do_command6(newargc, newargv,
    473 					 &newargv[2], &handle, true);
    474 
    475 			free_argv();
    476 			fflush(stdout);
    477 		}
    478 		if (tablename != NULL && strcmp(tablename, curtable) != 0)
    479 			continue;
    480 		if (!ret) {
    481 			fprintf(stderr, "%s: line %u failed\n",
    482 					xt_params->program_name, line);
    483 			exit(1);
    484 		}
    485 	}
    486 	if (in_table) {
    487 		fprintf(stderr, "%s: COMMIT expected at line %u\n",
    488 				xt_params->program_name, line + 1);
    489 		exit(1);
    490 	}
    491 
    492 	fclose(in);
    493 	return 0;
    494 }
    495