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  * $Id: iptables-restore.c 6706 2006-12-09 13:06:04Z /C=DE/ST=Berlin/L=Berlin/O=Netfilter Project/OU=Development/CN=yasuyuki/emailAddress=yasuyuki (at) netfilter.org $
      8  */
      9 
     10 #include <getopt.h>
     11 #include <sys/errno.h>
     12 #include <string.h>
     13 #include <stdio.h>
     14 #include <stdlib.h>
     15 #include "iptables.h"
     16 #include "libiptc/libiptc.h"
     17 
     18 #ifdef DEBUG
     19 #define DEBUGP(x, args...) fprintf(stderr, x, ## args)
     20 #else
     21 #define DEBUGP(x, args...)
     22 #endif
     23 
     24 static int binary = 0, counters = 0, verbose = 0, noflush = 0;
     25 
     26 /* Keeping track of external matches and targets.  */
     27 static struct option options[] = {
     28 	{ "binary", 0, 0, 'b' },
     29 	{ "counters", 0, 0, 'c' },
     30 	{ "verbose", 0, 0, 'v' },
     31 	{ "test", 0, 0, 't' },
     32 	{ "help", 0, 0, 'h' },
     33 	{ "noflush", 0, 0, 'n'},
     34 	{ "modprobe", 1, 0, 'M'},
     35 	{ 0 }
     36 };
     37 
     38 static void print_usage(const char *name, const char *version) __attribute__((noreturn));
     39 
     40 static void print_usage(const char *name, const char *version)
     41 {
     42 	fprintf(stderr, "Usage: %s [-b] [-c] [-v] [-t] [-h]\n"
     43 			"	   [ --binary ]\n"
     44 			"	   [ --counters ]\n"
     45 			"	   [ --verbose ]\n"
     46 			"	   [ --test ]\n"
     47 			"	   [ --help ]\n"
     48 			"	   [ --noflush ]\n"
     49 		        "          [ --modprobe=<command>]\n", name);
     50 
     51 	exit(1);
     52 }
     53 
     54 iptc_handle_t create_handle(const char *tablename, const char* modprobe )
     55 {
     56 	iptc_handle_t handle;
     57 
     58 	handle = iptc_init(tablename);
     59 
     60 	if (!handle) {
     61 		/* try to insmod the module if iptc_init failed */
     62 		iptables_insmod("ip_tables", modprobe);
     63 		handle = iptc_init(tablename);
     64 	}
     65 
     66 	if (!handle) {
     67 		exit_error(PARAMETER_PROBLEM, "%s: unable to initialize"
     68 			"table '%s'\n", program_name, tablename);
     69 		exit(1);
     70 	}
     71 	return handle;
     72 }
     73 
     74 static int parse_counters(char *string, struct ipt_counters *ctr)
     75 {
     76 	return (sscanf(string, "[%llu:%llu]", (unsigned long long *)&ctr->pcnt, (unsigned long long *)&ctr->bcnt) == 2);
     77 }
     78 
     79 /* global new argv and argc */
     80 static char *newargv[255];
     81 static int newargc;
     82 
     83 /* function adding one argument to newargv, updating newargc
     84  * returns true if argument added, false otherwise */
     85 static int add_argv(char *what) {
     86 	DEBUGP("add_argv: %s\n", what);
     87 	if (what && ((newargc + 1) < sizeof(newargv)/sizeof(char *))) {
     88 		newargv[newargc] = strdup(what);
     89 		newargc++;
     90 		return 1;
     91 	} else
     92 		return 0;
     93 }
     94 
     95 static void free_argv(void) {
     96 	int i;
     97 
     98 	for (i = 0; i < newargc; i++)
     99 		free(newargv[i]);
    100 }
    101 
    102 #ifdef IPTABLES_MULTI
    103 int
    104 iptables_restore_main(int argc, char *argv[])
    105 #else
    106 int
    107 main(int argc, char *argv[])
    108 #endif
    109 {
    110 	iptc_handle_t handle = NULL;
    111 	char buffer[10240];
    112 	int c;
    113 	char curtable[IPT_TABLE_MAXNAMELEN + 1];
    114 	FILE *in;
    115 	const char *modprobe = 0;
    116 	int in_table = 0, testing = 0;
    117 
    118 	program_name = "iptables-restore";
    119 	program_version = IPTABLES_VERSION;
    120 	line = 0;
    121 
    122 	lib_dir = getenv("IPTABLES_LIB_DIR");
    123 	if (!lib_dir)
    124 		lib_dir = IPT_LIB_DIR;
    125 
    126 #ifdef NO_SHARED_LIBS
    127 	init_extensions();
    128 #endif
    129 
    130 	while ((c = getopt_long(argc, argv, "bcvthnM:", options, NULL)) != -1) {
    131 		switch (c) {
    132 			case 'b':
    133 				binary = 1;
    134 				break;
    135 			case 'c':
    136 				counters = 1;
    137 				break;
    138 			case 'v':
    139 				verbose = 1;
    140 				break;
    141 			case 't':
    142 				testing = 1;
    143 				break;
    144 			case 'h':
    145 				print_usage("iptables-restore",
    146 					    IPTABLES_VERSION);
    147 				break;
    148 			case 'n':
    149 				noflush = 1;
    150 				break;
    151 			case 'M':
    152 				modprobe = optarg;
    153 				break;
    154 		}
    155 	}
    156 
    157 	if (optind == argc - 1) {
    158 		in = fopen(argv[optind], "r");
    159 		if (!in) {
    160 			fprintf(stderr, "Can't open %s: %s", argv[optind],
    161 				strerror(errno));
    162 			exit(1);
    163 		}
    164 	}
    165 	else if (optind < argc) {
    166 		fprintf(stderr, "Unknown arguments found on commandline");
    167 		exit(1);
    168 	}
    169 	else in = stdin;
    170 
    171 	/* Grab standard input. */
    172 	while (fgets(buffer, sizeof(buffer), in)) {
    173 		int ret = 0;
    174 
    175 		line++;
    176 		if (buffer[0] == '\n')
    177 			continue;
    178 		else if (buffer[0] == '#') {
    179 			if (verbose)
    180 				fputs(buffer, stdout);
    181 			continue;
    182 		} else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) {
    183 			if (!testing) {
    184 				DEBUGP("Calling commit\n");
    185 				ret = iptc_commit(&handle);
    186 			} else {
    187 				DEBUGP("Not calling commit, testing\n");
    188 				ret = 1;
    189 			}
    190 			in_table = 0;
    191 		} else if ((buffer[0] == '*') && (!in_table)) {
    192 			/* New table */
    193 			char *table;
    194 
    195 			table = strtok(buffer+1, " \t\n");
    196 			DEBUGP("line %u, table '%s'\n", line, table);
    197 			if (!table) {
    198 				exit_error(PARAMETER_PROBLEM,
    199 					"%s: line %u table name invalid\n",
    200 					program_name, line);
    201 				exit(1);
    202 			}
    203 			strncpy(curtable, table, IPT_TABLE_MAXNAMELEN);
    204 			curtable[IPT_TABLE_MAXNAMELEN] = '\0';
    205 
    206 			if (handle)
    207 				iptc_free(&handle);
    208 
    209 			handle = create_handle(table, modprobe);
    210 			if (noflush == 0) {
    211 				DEBUGP("Cleaning all chains of table '%s'\n",
    212 					table);
    213 				for_each_chain(flush_entries, verbose, 1,
    214 						&handle);
    215 
    216 				DEBUGP("Deleting all user-defined chains "
    217 				       "of table '%s'\n", table);
    218 				for_each_chain(delete_chain, verbose, 0,
    219 						&handle) ;
    220 			}
    221 
    222 			ret = 1;
    223 			in_table = 1;
    224 
    225 		} else if ((buffer[0] == ':') && (in_table)) {
    226 			/* New chain. */
    227 			char *policy, *chain;
    228 
    229 			chain = strtok(buffer+1, " \t\n");
    230 			DEBUGP("line %u, chain '%s'\n", line, chain);
    231 			if (!chain) {
    232 				exit_error(PARAMETER_PROBLEM,
    233 					   "%s: line %u chain name invalid\n",
    234 					   program_name, line);
    235 				exit(1);
    236 			}
    237 
    238 			if (iptc_builtin(chain, handle) <= 0) {
    239 				if (noflush && iptc_is_chain(chain, handle)) {
    240 					DEBUGP("Flushing existing user defined chain '%s'\n", chain);
    241 					if (!iptc_flush_entries(chain, &handle))
    242 						exit_error(PARAMETER_PROBLEM,
    243 							   "error flushing chain "
    244 							   "'%s':%s\n", chain,
    245 							   strerror(errno));
    246 				} else {
    247 					DEBUGP("Creating new chain '%s'\n", chain);
    248 					if (!iptc_create_chain(chain, &handle))
    249 						exit_error(PARAMETER_PROBLEM,
    250 							   "error creating chain "
    251 							   "'%s':%s\n", chain,
    252 							   strerror(errno));
    253 				}
    254 			}
    255 
    256 			policy = strtok(NULL, " \t\n");
    257 			DEBUGP("line %u, policy '%s'\n", line, policy);
    258 			if (!policy) {
    259 				exit_error(PARAMETER_PROBLEM,
    260 					   "%s: line %u policy invalid\n",
    261 					   program_name, line);
    262 				exit(1);
    263 			}
    264 
    265 			if (strcmp(policy, "-") != 0) {
    266 				struct ipt_counters count;
    267 
    268 				if (counters) {
    269 					char *ctrs;
    270 					ctrs = strtok(NULL, " \t\n");
    271 
    272 					if (!ctrs || !parse_counters(ctrs, &count))
    273 						exit_error(PARAMETER_PROBLEM,
    274 							   "invalid policy counters "
    275 							   "for chain '%s'\n", chain);
    276 
    277 				} else {
    278 					memset(&count, 0,
    279 					       sizeof(struct ipt_counters));
    280 				}
    281 
    282 				DEBUGP("Setting policy of chain %s to %s\n",
    283 					chain, policy);
    284 
    285 				if (!iptc_set_policy(chain, policy, &count,
    286 						     &handle))
    287 					exit_error(OTHER_PROBLEM,
    288 						"Can't set policy `%s'"
    289 						" on `%s' line %u: %s\n",
    290 						chain, policy, line,
    291 						iptc_strerror(errno));
    292 			}
    293 
    294 			ret = 1;
    295 
    296 		} else if (in_table) {
    297 			int a;
    298 			char *ptr = buffer;
    299 			char *pcnt = NULL;
    300 			char *bcnt = NULL;
    301 			char *parsestart;
    302 
    303 			/* the parser */
    304 			char *param_start, *curchar;
    305 			int quote_open;
    306 
    307 			/* reset the newargv */
    308 			newargc = 0;
    309 
    310 			if (buffer[0] == '[') {
    311 				/* we have counters in our input */
    312 				ptr = strchr(buffer, ']');
    313 				if (!ptr)
    314 					exit_error(PARAMETER_PROBLEM,
    315 						   "Bad line %u: need ]\n",
    316 						   line);
    317 
    318 				pcnt = strtok(buffer+1, ":");
    319 				if (!pcnt)
    320 					exit_error(PARAMETER_PROBLEM,
    321 						   "Bad line %u: need :\n",
    322 						   line);
    323 
    324 				bcnt = strtok(NULL, "]");
    325 				if (!bcnt)
    326 					exit_error(PARAMETER_PROBLEM,
    327 						   "Bad line %u: need ]\n",
    328 						   line);
    329 
    330 				/* start command parsing after counter */
    331 				parsestart = ptr + 1;
    332 			} else {
    333 				/* start command parsing at start of line */
    334 				parsestart = buffer;
    335 			}
    336 
    337 			add_argv(argv[0]);
    338 			add_argv("-t");
    339 			add_argv((char *) &curtable);
    340 
    341 			if (counters && pcnt && bcnt) {
    342 				add_argv("--set-counters");
    343 				add_argv((char *) pcnt);
    344 				add_argv((char *) bcnt);
    345 			}
    346 
    347 			/* After fighting with strtok enough, here's now
    348 			 * a 'real' parser. According to Rusty I'm now no
    349 			 * longer a real hacker, but I can live with that */
    350 
    351 			quote_open = 0;
    352 			param_start = parsestart;
    353 
    354 			for (curchar = parsestart; *curchar; curchar++) {
    355 				if (*curchar == '"') {
    356 					/* quote_open cannot be true if there
    357 					 * was no previous character.  Thus,
    358 					 * curchar-1 has to be within bounds */
    359 					if (quote_open &&
    360 					    *(curchar-1) != '\\') {
    361 						quote_open = 0;
    362 						*curchar = ' ';
    363 					} else {
    364 						quote_open = 1;
    365 						param_start++;
    366 					}
    367 				}
    368 				if (*curchar == ' '
    369 				    || *curchar == '\t'
    370 				    || * curchar == '\n') {
    371 					char param_buffer[1024];
    372 					int param_len = curchar-param_start;
    373 
    374 					if (quote_open)
    375 						continue;
    376 
    377 					if (!param_len) {
    378 						/* two spaces? */
    379 						param_start++;
    380 						continue;
    381 					}
    382 
    383 					/* end of one parameter */
    384 					strncpy(param_buffer, param_start,
    385 						param_len);
    386 					*(param_buffer+param_len) = '\0';
    387 
    388 					/* check if table name specified */
    389 					if (!strncmp(param_buffer, "-t", 3)
    390                                             || !strncmp(param_buffer, "--table", 8)) {
    391 						exit_error(PARAMETER_PROBLEM,
    392 						   "Line %u seems to have a "
    393 						   "-t table option.\n", line);
    394 						exit(1);
    395 					}
    396 
    397 					add_argv(param_buffer);
    398 					param_start += param_len + 1;
    399 				} else {
    400 					/* regular character, skip */
    401 				}
    402 			}
    403 
    404 			DEBUGP("calling do_command(%u, argv, &%s, handle):\n",
    405 				newargc, curtable);
    406 
    407 			for (a = 0; a < newargc; a++)
    408 				DEBUGP("argv[%u]: %s\n", a, newargv[a]);
    409 
    410 			ret = do_command(newargc, newargv,
    411 					 &newargv[2], &handle);
    412 
    413 			free_argv();
    414 		}
    415 		if (!ret) {
    416 			fprintf(stderr, "%s: line %u failed\n",
    417 					program_name, line);
    418 			exit(1);
    419 		}
    420 	}
    421 	if (in_table) {
    422 		fprintf(stderr, "%s: COMMIT expected at line %u\n",
    423 				program_name, line + 1);
    424 		exit(1);
    425 	}
    426 
    427 	return 0;
    428 }
    429