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