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