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
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
print_usage(const char * name,const char * version)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
create_handle(const char * tablename)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
parse_counters(char * string,struct xt_counters * ctr)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 */
add_argv(char * what)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
free_argv(void)109 static void free_argv(void) {
110 int i;
111
112 for (i = 0; i < newargc; i++)
113 free(newargv[i]);
114 }
115
add_param_to_argv(char * parsestart)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
iptables_restore_main(int argc,char * argv[])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