• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Code to restore the iptables state, from file by iptables-save.
2  * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
3  * based on previous code from Rusty Russell <rusty@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@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 
print_usage(const char * name,const char * version)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 
create_handle(const char * tablename,const char * modprobe)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 
parse_counters(char * string,struct ipt_counters * ctr)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 */
add_argv(char * what)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 
free_argv(void)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
iptables_restore_main(int argc,char * argv[])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