1 /* Code to restore the iptables state, from file by ip6tables-save.
2 * Author: Andras Kis-Szabo <kisza@sch.bme.hu>
3 *
4 * based on iptables-restore
5 * Authors:
6 * Harald Welte <laforge@gnumonks.org>
7 * Rusty Russell <rusty@linuxcare.com.au>
8 * This code is distributed under the terms of GNU GPL v2
9 */
10
11 #include <getopt.h>
12 #include <errno.h>
13 #include <stdbool.h>
14 #include <string.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include "ip6tables.h"
18 #include "xshared.h"
19 #include "xtables.h"
20 #include "libiptc/libip6tc.h"
21 #include "ip6tables-multi.h"
22
23 #ifdef DEBUG
24 #define DEBUGP(x, args...) fprintf(stderr, x, ## args)
25 #else
26 #define DEBUGP(x, args...)
27 #endif
28
29 static int counters = 0, verbose = 0, noflush = 0, wait = 0;
30
31 static struct timeval wait_interval = {
32 .tv_sec = 1,
33 };
34
35 /* Keeping track of external matches and targets. */
36 static const struct option options[] = {
37 {.name = "counters", .has_arg = 0, .val = 'c'},
38 {.name = "verbose", .has_arg = 0, .val = 'v'},
39 {.name = "test", .has_arg = 0, .val = 't'},
40 {.name = "help", .has_arg = 0, .val = 'h'},
41 {.name = "noflush", .has_arg = 0, .val = 'n'},
42 {.name = "modprobe", .has_arg = 1, .val = 'M'},
43 {.name = "table", .has_arg = 1, .val = 'T'},
44 {.name = "wait", .has_arg = 2, .val = 'w'},
45 {.name = "wait-interval", .has_arg = 2, .val = 'W'},
46 {NULL},
47 };
48
49 static void print_usage(const char *name, const char *version) __attribute__((noreturn));
50
print_usage(const char * name,const char * version)51 static void print_usage(const char *name, const char *version)
52 {
53 fprintf(stderr, "Usage: %s [-c] [-v] [-t] [-h] [-n] [-w secs] [-W usecs] [-T table] [-M command]\n"
54 " [ --counters ]\n"
55 " [ --verbose ]\n"
56 " [ --test ]\n"
57 " [ --help ]\n"
58 " [ --noflush ]\n"
59 " [ --wait=<seconds>\n"
60 " [ --wait-interval=<usecs>\n"
61 " [ --table=<TABLE> ]\n"
62 " [ --modprobe=<command> ]\n", name);
63
64 exit(1);
65 }
66
create_handle(const char * tablename)67 static struct xtc_handle *create_handle(const char *tablename)
68 {
69 struct xtc_handle *handle;
70
71 handle = ip6tc_init(tablename);
72
73 if (!handle) {
74 /* try to insmod the module if iptc_init failed */
75 xtables_load_ko(xtables_modprobe_program, false);
76 handle = ip6tc_init(tablename);
77 }
78
79 if (!handle) {
80 xtables_error(PARAMETER_PROBLEM, "%s: unable to initialize "
81 "table '%s'\n", ip6tables_globals.program_name,
82 tablename);
83 exit(1);
84 }
85 return handle;
86 }
87
parse_counters(char * string,struct xt_counters * ctr)88 static int parse_counters(char *string, struct xt_counters *ctr)
89 {
90 unsigned long long pcnt, bcnt;
91 int ret;
92
93 ret = sscanf(string, "[%llu:%llu]", &pcnt, &bcnt);
94 ctr->pcnt = pcnt;
95 ctr->bcnt = bcnt;
96 return ret == 2;
97 }
98
99 /* global new argv and argc */
100 static char *newargv[255];
101 static int newargc;
102
103 /* function adding one argument to newargv, updating newargc
104 * returns true if argument added, false otherwise */
add_argv(char * what)105 static int add_argv(char *what) {
106 DEBUGP("add_argv: %s\n", what);
107 if (what && newargc + 1 < ARRAY_SIZE(newargv)) {
108 newargv[newargc] = strdup(what);
109 newargv[++newargc] = NULL;
110 return 1;
111 } else {
112 xtables_error(PARAMETER_PROBLEM,
113 "Parser cannot handle more arguments\n");
114 return 0;
115 }
116 }
117
free_argv(void)118 static void free_argv(void) {
119 int i;
120
121 for (i = 0; i < newargc; i++)
122 free(newargv[i]);
123 }
124
add_param_to_argv(char * parsestart)125 static void add_param_to_argv(char *parsestart)
126 {
127 int quote_open = 0, escaped = 0, param_len = 0;
128 char param_buffer[1024], *curchar;
129
130 /* After fighting with strtok enough, here's now
131 * a 'real' parser. According to Rusty I'm now no
132 * longer a real hacker, but I can live with that */
133
134 for (curchar = parsestart; *curchar; curchar++) {
135 if (quote_open) {
136 if (escaped) {
137 param_buffer[param_len++] = *curchar;
138 escaped = 0;
139 continue;
140 } else if (*curchar == '\\') {
141 escaped = 1;
142 continue;
143 } else if (*curchar == '"') {
144 quote_open = 0;
145 *curchar = ' ';
146 } else {
147 param_buffer[param_len++] = *curchar;
148 continue;
149 }
150 } else {
151 if (*curchar == '"') {
152 quote_open = 1;
153 continue;
154 }
155 }
156
157 if (*curchar == ' '
158 || *curchar == '\t'
159 || * curchar == '\n') {
160 if (!param_len) {
161 /* two spaces? */
162 continue;
163 }
164
165 param_buffer[param_len] = '\0';
166
167 /* check if table name specified */
168 if (!strncmp(param_buffer, "-t", 2)
169 || !strncmp(param_buffer, "--table", 8)) {
170 xtables_error(PARAMETER_PROBLEM,
171 "The -t option (seen in line %u) cannot be "
172 "used in ip6tables-restore.\n", line);
173 exit(1);
174 }
175
176 add_argv(param_buffer);
177 param_len = 0;
178 } else {
179 /* regular character, copy to buffer */
180 param_buffer[param_len++] = *curchar;
181
182 if (param_len >= sizeof(param_buffer))
183 xtables_error(PARAMETER_PROBLEM,
184 "Parameter too long!");
185 }
186 }
187 }
188
ip6tables_restore_main(int argc,char * argv[])189 int ip6tables_restore_main(int argc, char *argv[])
190 {
191 struct xtc_handle *handle = NULL;
192 char buffer[10240];
193 int c, lock;
194 char curtable[XT_TABLE_MAXNAMELEN + 1];
195 FILE *in;
196 int in_table = 0, testing = 0;
197 const char *tablename = NULL;
198 const struct xtc_ops *ops = &ip6tc_ops;
199
200 line = 0;
201 lock = XT_LOCK_NOT_ACQUIRED;
202
203 ip6tables_globals.program_name = "ip6tables-restore";
204 c = xtables_init_all(&ip6tables_globals, NFPROTO_IPV6);
205 if (c < 0) {
206 fprintf(stderr, "%s/%s Failed to initialize xtables\n",
207 ip6tables_globals.program_name,
208 ip6tables_globals.program_version);
209 exit(1);
210 }
211 #if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
212 init_extensions();
213 init_extensions6();
214 #endif
215
216 while ((c = getopt_long(argc, argv, "bcvthnwWM:T:", options, NULL)) != -1) {
217 switch (c) {
218 case 'b':
219 fprintf(stderr, "-b/--binary option is not implemented\n");
220 break;
221 case 'c':
222 counters = 1;
223 break;
224 case 'v':
225 verbose = 1;
226 break;
227 case 't':
228 testing = 1;
229 break;
230 case 'h':
231 print_usage("ip6tables-restore",
232 IPTABLES_VERSION);
233 break;
234 case 'n':
235 noflush = 1;
236 break;
237 case 'w':
238 wait = parse_wait_time(argc, argv);
239 break;
240 case 'W':
241 parse_wait_interval(argc, argv, &wait_interval);
242 break;
243 case 'M':
244 xtables_modprobe_program = optarg;
245 break;
246 case 'T':
247 tablename = optarg;
248 break;
249 }
250 }
251
252 if (optind == argc - 1) {
253 in = fopen(argv[optind], "re");
254 if (!in) {
255 fprintf(stderr, "Can't open %s: %s\n", argv[optind],
256 strerror(errno));
257 exit(1);
258 }
259 }
260 else if (optind < argc) {
261 fprintf(stderr, "Unknown arguments found on commandline\n");
262 exit(1);
263 }
264 else in = stdin;
265
266 /* Grab standard input. */
267 while (fgets(buffer, sizeof(buffer), in)) {
268 int ret = 0;
269
270 line++;
271 if (buffer[0] == '\n')
272 continue;
273 else if (buffer[0] == '#') {
274 if (verbose) {
275 fputs(buffer, stdout);
276 fflush(stdout);
277 }
278 continue;
279 } else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) {
280 if (!testing) {
281 DEBUGP("Calling commit\n");
282 ret = ops->commit(handle);
283 ops->free(handle);
284 handle = NULL;
285 } else {
286 DEBUGP("Not calling commit, testing\n");
287 ret = 1;
288 }
289
290 /* Done with the current table, release the lock. */
291 if (lock >= 0) {
292 xtables_unlock(lock);
293 lock = XT_LOCK_NOT_ACQUIRED;
294 }
295
296 in_table = 0;
297 } else if ((buffer[0] == '*') && (!in_table)) {
298 /* Acquire a lock before we create a new table handle */
299 lock = xtables_lock_or_exit(wait, &wait_interval);
300
301 /* New table */
302 char *table;
303
304 table = strtok(buffer+1, " \t\n");
305 DEBUGP("line %u, table '%s'\n", line, table);
306 if (!table) {
307 xtables_error(PARAMETER_PROBLEM,
308 "%s: line %u table name invalid\n",
309 xt_params->program_name, line);
310 exit(1);
311 }
312 strncpy(curtable, table, XT_TABLE_MAXNAMELEN);
313 curtable[XT_TABLE_MAXNAMELEN] = '\0';
314
315 if (tablename != NULL && strcmp(tablename, table) != 0)
316 continue;
317 if (handle)
318 ops->free(handle);
319
320 handle = create_handle(table);
321 if (noflush == 0) {
322 DEBUGP("Cleaning all chains of table '%s'\n",
323 table);
324 for_each_chain6(flush_entries6, verbose, 1,
325 handle);
326
327 DEBUGP("Deleting all user-defined chains "
328 "of table '%s'\n", table);
329 for_each_chain6(delete_chain6, verbose, 0,
330 handle);
331 }
332
333 ret = 1;
334 in_table = 1;
335
336 } else if ((buffer[0] == ':') && (in_table)) {
337 /* New chain. */
338 char *policy, *chain;
339
340 chain = strtok(buffer+1, " \t\n");
341 DEBUGP("line %u, chain '%s'\n", line, chain);
342 if (!chain) {
343 xtables_error(PARAMETER_PROBLEM,
344 "%s: line %u chain name invalid\n",
345 xt_params->program_name, line);
346 exit(1);
347 }
348
349 if (strlen(chain) >= XT_EXTENSION_MAXNAMELEN)
350 xtables_error(PARAMETER_PROBLEM,
351 "Invalid chain name `%s' "
352 "(%u chars max)",
353 chain, XT_EXTENSION_MAXNAMELEN - 1);
354
355 if (ops->builtin(chain, handle) <= 0) {
356 if (noflush && ops->is_chain(chain, handle)) {
357 DEBUGP("Flushing existing user defined chain '%s'\n", chain);
358 if (!ops->flush_entries(chain, handle))
359 xtables_error(PARAMETER_PROBLEM,
360 "error flushing chain "
361 "'%s':%s\n", chain,
362 strerror(errno));
363 } else {
364 DEBUGP("Creating new chain '%s'\n", chain);
365 if (!ops->create_chain(chain, handle))
366 xtables_error(PARAMETER_PROBLEM,
367 "error creating chain "
368 "'%s':%s\n", chain,
369 strerror(errno));
370 }
371 }
372
373 policy = strtok(NULL, " \t\n");
374 DEBUGP("line %u, policy '%s'\n", line, policy);
375 if (!policy) {
376 xtables_error(PARAMETER_PROBLEM,
377 "%s: line %u policy invalid\n",
378 xt_params->program_name, line);
379 exit(1);
380 }
381
382 if (strcmp(policy, "-") != 0) {
383 struct xt_counters count;
384
385 if (counters) {
386 char *ctrs;
387 ctrs = strtok(NULL, " \t\n");
388
389 if (!ctrs || !parse_counters(ctrs, &count))
390 xtables_error(PARAMETER_PROBLEM,
391 "invalid policy counters "
392 "for chain '%s'\n", chain);
393
394 } else {
395 memset(&count, 0, sizeof(count));
396 }
397
398 DEBUGP("Setting policy of chain %s to %s\n",
399 chain, policy);
400
401 if (!ops->set_policy(chain, policy, &count,
402 handle))
403 xtables_error(OTHER_PROBLEM,
404 "Can't set policy `%s'"
405 " on `%s' line %u: %s\n",
406 policy, chain, line,
407 ops->strerror(errno));
408 }
409
410 ret = 1;
411
412 } else if (in_table) {
413 int a;
414 char *ptr = buffer;
415 char *pcnt = NULL;
416 char *bcnt = NULL;
417 char *parsestart;
418
419 /* reset the newargv */
420 newargc = 0;
421
422 if (buffer[0] == '[') {
423 /* we have counters in our input */
424 ptr = strchr(buffer, ']');
425 if (!ptr)
426 xtables_error(PARAMETER_PROBLEM,
427 "Bad line %u: need ]\n",
428 line);
429
430 pcnt = strtok(buffer+1, ":");
431 if (!pcnt)
432 xtables_error(PARAMETER_PROBLEM,
433 "Bad line %u: need :\n",
434 line);
435
436 bcnt = strtok(NULL, "]");
437 if (!bcnt)
438 xtables_error(PARAMETER_PROBLEM,
439 "Bad line %u: need ]\n",
440 line);
441
442 /* start command parsing after counter */
443 parsestart = ptr + 1;
444 } else {
445 /* start command parsing at start of line */
446 parsestart = buffer;
447 }
448
449 add_argv(argv[0]);
450 add_argv("-t");
451 add_argv(curtable);
452
453 if (counters && pcnt && bcnt) {
454 add_argv("--set-counters");
455 add_argv((char *) pcnt);
456 add_argv((char *) bcnt);
457 }
458
459 add_param_to_argv(parsestart);
460
461 DEBUGP("calling do_command6(%u, argv, &%s, handle):\n",
462 newargc, curtable);
463
464 for (a = 0; a < newargc; a++)
465 DEBUGP("argv[%u]: %s\n", a, newargv[a]);
466
467 ret = do_command6(newargc, newargv,
468 &newargv[2], &handle, true);
469
470 free_argv();
471 fflush(stdout);
472 }
473 if (tablename != NULL && strcmp(tablename, curtable) != 0)
474 continue;
475 if (!ret) {
476 fprintf(stderr, "%s: line %u failed\n",
477 xt_params->program_name, line);
478 exit(1);
479 }
480 }
481 if (in_table) {
482 fprintf(stderr, "%s: COMMIT expected at line %u\n",
483 xt_params->program_name, line + 1);
484 exit(1);
485 }
486
487 fclose(in);
488 return 0;
489 }
490