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 <sys/errno.h>
     13 #include <stdbool.h>
     14 #include <string.h>
     15 #include <stdio.h>
     16 #include <stdlib.h>
     17 #include "ip6tables.h"
     18 #include "xtables.h"
     19 #include "libiptc/libip6tc.h"
     20 #include "ip6tables-multi.h"
     21 
     22 #ifdef DEBUG
     23 #define DEBUGP(x, args...) fprintf(stderr, x, ## args)
     24 #else
     25 #define DEBUGP(x, args...)
     26 #endif
     27 
     28 static int binary = 0, counters = 0, verbose = 0, noflush = 0;
     29 
     30 /* Keeping track of external matches and targets.  */
     31 static const struct option options[] = {
     32 	{.name = "binary",   .has_arg = false, .val = 'b'},
     33 	{.name = "counters", .has_arg = false, .val = 'c'},
     34 	{.name = "verbose",  .has_arg = false, .val = 'v'},
     35 	{.name = "test",     .has_arg = false, .val = 't'},
     36 	{.name = "help",     .has_arg = false, .val = 'h'},
     37 	{.name = "noflush",  .has_arg = false, .val = 'n'},
     38 	{.name = "modprobe", .has_arg = true,  .val = 'M'},
     39 	{NULL},
     40 };
     41 
     42 static void print_usage(const char *name, const char *version) __attribute__((noreturn));
     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 			"          [ --modprobe=<command>]\n", name);
     54 
     55 	exit(1);
     56 }
     57 
     58 static struct ip6tc_handle *create_handle(const char *tablename)
     59 {
     60 	struct ip6tc_handle *handle;
     61 
     62 	handle = ip6tc_init(tablename);
     63 
     64 	if (!handle) {
     65 		/* try to insmod the module if iptc_init failed */
     66 		xtables_load_ko(xtables_modprobe_program, false);
     67 		handle = ip6tc_init(tablename);
     68 	}
     69 
     70 	if (!handle) {
     71 		xtables_error(PARAMETER_PROBLEM, "%s: unable to initialize "
     72 			"table '%s'\n", ip6tables_globals.program_name,
     73 			tablename);
     74 		exit(1);
     75 	}
     76 	return handle;
     77 }
     78 
     79 static int parse_counters(char *string, struct ip6t_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 		newargc++;
    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 #ifdef IPTABLES_MULTI
    117 int ip6tables_restore_main(int argc, char *argv[])
    118 #else
    119 int main(int argc, char *argv[])
    120 #endif
    121 {
    122 	struct ip6tc_handle *handle = NULL;
    123 	char buffer[10240];
    124 	int c;
    125 	char curtable[IP6T_TABLE_MAXNAMELEN + 1];
    126 	FILE *in;
    127 	int in_table = 0, testing = 0;
    128 
    129 	line = 0;
    130 
    131 	ip6tables_globals.program_name = "ip6tables-restore";
    132 	c = xtables_init_all(&ip6tables_globals, NFPROTO_IPV6);
    133 	if (c < 0) {
    134 		fprintf(stderr, "%s/%s Failed to initialize xtables\n",
    135 				ip6tables_globals.program_name,
    136 				ip6tables_globals.program_version);
    137 		exit(1);
    138 	}
    139 #if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
    140 	init_extensions();
    141 	init_extensions6();
    142 #endif
    143 
    144 	while ((c = getopt_long(argc, argv, "bcvthnM:", options, NULL)) != -1) {
    145 		switch (c) {
    146 			case 'b':
    147 				binary = 1;
    148 				break;
    149 			case 'c':
    150 				counters = 1;
    151 				break;
    152 			case 'v':
    153 				verbose = 1;
    154 				break;
    155 			case 't':
    156 				testing = 1;
    157 				break;
    158 			case 'h':
    159 				print_usage("ip6tables-restore",
    160 					    IPTABLES_VERSION);
    161 				break;
    162 			case 'n':
    163 				noflush = 1;
    164 				break;
    165 			case 'M':
    166 				xtables_modprobe_program = optarg;
    167 				break;
    168 		}
    169 	}
    170 
    171 	if (optind == argc - 1) {
    172 		in = fopen(argv[optind], "re");
    173 		if (!in) {
    174 			fprintf(stderr, "Can't open %s: %s\n", argv[optind],
    175 				strerror(errno));
    176 			exit(1);
    177 		}
    178 	}
    179 	else if (optind < argc) {
    180 		fprintf(stderr, "Unknown arguments found on commandline\n");
    181 		exit(1);
    182 	}
    183 	else in = stdin;
    184 
    185 	/* Grab standard input. */
    186 	while (fgets(buffer, sizeof(buffer), in)) {
    187 		int ret = 0;
    188 
    189 		line++;
    190 		if (buffer[0] == '\n')
    191 			continue;
    192 		else if (buffer[0] == '#') {
    193 			if (verbose)
    194 				fputs(buffer, stdout);
    195 			continue;
    196 		} else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) {
    197 			if (!testing) {
    198 				DEBUGP("Calling commit\n");
    199 				ret = ip6tc_commit(handle);
    200 				ip6tc_free(handle);
    201 				handle = NULL;
    202 			} else {
    203 				DEBUGP("Not calling commit, testing\n");
    204 				ret = 1;
    205 			}
    206 			in_table = 0;
    207 		} else if ((buffer[0] == '*') && (!in_table)) {
    208 			/* New table */
    209 			char *table;
    210 
    211 			table = strtok(buffer+1, " \t\n");
    212 			DEBUGP("line %u, table '%s'\n", line, table);
    213 			if (!table) {
    214 				xtables_error(PARAMETER_PROBLEM,
    215 					"%s: line %u table name invalid\n",
    216 					ip6tables_globals.program_name,
    217 					line);
    218 				exit(1);
    219 			}
    220 			strncpy(curtable, table, IP6T_TABLE_MAXNAMELEN);
    221 			curtable[IP6T_TABLE_MAXNAMELEN] = '\0';
    222 
    223 			if (handle)
    224 				ip6tc_free(handle);
    225 
    226 			handle = create_handle(table);
    227 			if (noflush == 0) {
    228 				DEBUGP("Cleaning all chains of table '%s'\n",
    229 					table);
    230 				for_each_chain6(flush_entries6, verbose, 1,
    231 						handle);
    232 
    233 				DEBUGP("Deleting all user-defined chains "
    234 				       "of table '%s'\n", table);
    235 				for_each_chain6(delete_chain6, verbose, 0,
    236 						handle);
    237 			}
    238 
    239 			ret = 1;
    240 			in_table = 1;
    241 
    242 		} else if ((buffer[0] == ':') && (in_table)) {
    243 			/* New chain. */
    244 			char *policy, *chain;
    245 
    246 			chain = strtok(buffer+1, " \t\n");
    247 			DEBUGP("line %u, chain '%s'\n", line, chain);
    248 			if (!chain) {
    249 				xtables_error(PARAMETER_PROBLEM,
    250 					   "%s: line %u chain name invalid\n",
    251 					   ip6tables_globals.program_name,
    252 					   line);
    253 				exit(1);
    254 			}
    255 
    256 			if (strlen(chain) >= XT_EXTENSION_MAXNAMELEN)
    257 				xtables_error(PARAMETER_PROBLEM,
    258 					   "Invalid chain name `%s' "
    259 					   "(%u chars max)",
    260 					   chain, XT_EXTENSION_MAXNAMELEN - 1);
    261 
    262 			if (ip6tc_builtin(chain, handle) <= 0) {
    263 				if (noflush && ip6tc_is_chain(chain, handle)) {
    264 					DEBUGP("Flushing existing user defined chain '%s'\n", chain);
    265 					if (!ip6tc_flush_entries(chain, handle))
    266 						xtables_error(PARAMETER_PROBLEM,
    267 							   "error flushing chain "
    268 							   "'%s':%s\n", chain,
    269 							   strerror(errno));
    270 				} else {
    271 					DEBUGP("Creating new chain '%s'\n", chain);
    272 					if (!ip6tc_create_chain(chain, handle))
    273 						xtables_error(PARAMETER_PROBLEM,
    274 							   "error creating chain "
    275 							   "'%s':%s\n", chain,
    276 							   strerror(errno));
    277 				}
    278 			}
    279 
    280 			policy = strtok(NULL, " \t\n");
    281 			DEBUGP("line %u, policy '%s'\n", line, policy);
    282 			if (!policy) {
    283 				xtables_error(PARAMETER_PROBLEM,
    284 					   "%s: line %u policy invalid\n",
    285 					   ip6tables_globals.program_name,
    286 					   line);
    287 				exit(1);
    288 			}
    289 
    290 			if (strcmp(policy, "-") != 0) {
    291 				struct ip6t_counters count;
    292 
    293 				if (counters) {
    294 					char *ctrs;
    295 					ctrs = strtok(NULL, " \t\n");
    296 
    297 					if (!ctrs || !parse_counters(ctrs, &count))
    298 						xtables_error(PARAMETER_PROBLEM,
    299 							  "invalid policy counters "
    300 							  "for chain '%s'\n", chain);
    301 
    302 				} else {
    303 					memset(&count, 0,
    304 					       sizeof(struct ip6t_counters));
    305 				}
    306 
    307 				DEBUGP("Setting policy of chain %s to %s\n",
    308 					chain, policy);
    309 
    310 				if (!ip6tc_set_policy(chain, policy, &count,
    311 						     handle))
    312 					xtables_error(OTHER_PROBLEM,
    313 						"Can't set policy `%s'"
    314 						" on `%s' line %u: %s\n",
    315 						policy, chain, line,
    316 						ip6tc_strerror(errno));
    317 			}
    318 
    319 			ret = 1;
    320 
    321 		} else if (in_table) {
    322 			int a;
    323 			char *ptr = buffer;
    324 			char *pcnt = NULL;
    325 			char *bcnt = NULL;
    326 			char *parsestart;
    327 
    328 			/* the parser */
    329 			char *curchar;
    330 			int quote_open, escaped;
    331 			size_t param_len;
    332 
    333 			/* reset the newargv */
    334 			newargc = 0;
    335 
    336 			if (buffer[0] == '[') {
    337 				/* we have counters in our input */
    338 				ptr = strchr(buffer, ']');
    339 				if (!ptr)
    340 					xtables_error(PARAMETER_PROBLEM,
    341 						   "Bad line %u: need ]\n",
    342 						   line);
    343 
    344 				pcnt = strtok(buffer+1, ":");
    345 				if (!pcnt)
    346 					xtables_error(PARAMETER_PROBLEM,
    347 						   "Bad line %u: need :\n",
    348 						   line);
    349 
    350 				bcnt = strtok(NULL, "]");
    351 				if (!bcnt)
    352 					xtables_error(PARAMETER_PROBLEM,
    353 						   "Bad line %u: need ]\n",
    354 						   line);
    355 
    356 				/* start command parsing after counter */
    357 				parsestart = ptr + 1;
    358 			} else {
    359 				/* start command parsing at start of line */
    360 				parsestart = buffer;
    361 			}
    362 
    363 			add_argv(argv[0]);
    364 			add_argv("-t");
    365 			add_argv(curtable);
    366 
    367 			if (counters && pcnt && bcnt) {
    368 				add_argv("--set-counters");
    369 				add_argv((char *) pcnt);
    370 				add_argv((char *) bcnt);
    371 			}
    372 
    373 			/* After fighting with strtok enough, here's now
    374 			 * a 'real' parser. According to Rusty I'm now no
    375 			 * longer a real hacker, but I can live with that */
    376 
    377 			quote_open = 0;
    378 			escaped = 0;
    379 			param_len = 0;
    380 
    381 			for (curchar = parsestart; *curchar; curchar++) {
    382 				char param_buffer[1024];
    383 
    384 				if (quote_open) {
    385 					if (escaped) {
    386 						param_buffer[param_len++] = *curchar;
    387 						escaped = 0;
    388 						continue;
    389 					} else if (*curchar == '\\') {
    390 						escaped = 1;
    391 						continue;
    392 					} else if (*curchar == '"') {
    393 						quote_open = 0;
    394 						*curchar = ' ';
    395 					} else {
    396 						param_buffer[param_len++] = *curchar;
    397 						continue;
    398 					}
    399 				} else {
    400 					if (*curchar == '"') {
    401 						quote_open = 1;
    402 						continue;
    403 					}
    404 				}
    405 
    406 				if (*curchar == ' '
    407 				    || *curchar == '\t'
    408 				    || * curchar == '\n') {
    409 					if (!param_len) {
    410 						/* two spaces? */
    411 						continue;
    412 					}
    413 
    414 					param_buffer[param_len] = '\0';
    415 
    416 					/* check if table name specified */
    417 					if (!strncmp(param_buffer, "-t", 2)
    418                                             || !strncmp(param_buffer, "--table", 8)) {
    419 						xtables_error(PARAMETER_PROBLEM,
    420 						   "Line %u seems to have a "
    421 						   "-t table option.\n", line);
    422 						exit(1);
    423 					}
    424 
    425 					add_argv(param_buffer);
    426 					param_len = 0;
    427 				} else {
    428 					/* regular character, copy to buffer */
    429 					param_buffer[param_len++] = *curchar;
    430 
    431 					if (param_len >= sizeof(param_buffer))
    432 						xtables_error(PARAMETER_PROBLEM,
    433 						   "Parameter too long!");
    434 				}
    435 			}
    436 
    437 			DEBUGP("calling do_command6(%u, argv, &%s, handle):\n",
    438 				newargc, curtable);
    439 
    440 			for (a = 0; a < newargc; a++)
    441 				DEBUGP("argv[%u]: %s\n", a, newargv[a]);
    442 
    443 			ret = do_command6(newargc, newargv,
    444 					 &newargv[2], &handle);
    445 
    446 			free_argv();
    447 			fflush(stdout);
    448 		}
    449 		if (!ret) {
    450 			fprintf(stderr, "%s: line %u failed\n",
    451 					ip6tables_globals.program_name,
    452 					line);
    453 			exit(1);
    454 		}
    455 	}
    456 	if (in_table) {
    457 		fprintf(stderr, "%s: COMMIT expected at line %u\n",
    458 				ip6tables_globals.program_name,
    459 				line + 1);
    460 		exit(1);
    461 	}
    462 
    463 	if (in != NULL)
    464 		fclose(in);
    465 	return 0;
    466 }
    467