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 <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 {.name = "table", .has_arg = true, .val = 'T'},
40 {NULL},
41 };
42
43 static void print_usage(const char *name, const char *version) __attribute__((noreturn));
44
print_usage(const char * name,const char * version)45 static void print_usage(const char *name, const char *version)
46 {
47 fprintf(stderr, "Usage: %s [-b] [-c] [-v] [-t] [-h]\n"
48 " [ --binary ]\n"
49 " [ --counters ]\n"
50 " [ --verbose ]\n"
51 " [ --test ]\n"
52 " [ --help ]\n"
53 " [ --noflush ]\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 = ip6tc_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 = ip6tc_init(tablename);
69 }
70
71 if (!handle) {
72 xtables_error(PARAMETER_PROBLEM, "%s: unable to initialize "
73 "table '%s'\n", ip6tables_globals.program_name,
74 tablename);
75 exit(1);
76 }
77 return handle;
78 }
79
parse_counters(char * string,struct xt_counters * ctr)80 static int parse_counters(char *string, struct xt_counters *ctr)
81 {
82 unsigned long long pcnt, bcnt;
83 int ret;
84
85 ret = sscanf(string, "[%llu:%llu]", &pcnt, &bcnt);
86 ctr->pcnt = pcnt;
87 ctr->bcnt = bcnt;
88 return ret == 2;
89 }
90
91 /* global new argv and argc */
92 static char *newargv[255];
93 static int newargc;
94
95 /* function adding one argument to newargv, updating newargc
96 * returns true if argument added, false otherwise */
add_argv(char * what)97 static int add_argv(char *what) {
98 DEBUGP("add_argv: %s\n", what);
99 if (what && newargc + 1 < ARRAY_SIZE(newargv)) {
100 newargv[newargc] = strdup(what);
101 newargv[++newargc] = NULL;
102 return 1;
103 } else {
104 xtables_error(PARAMETER_PROBLEM,
105 "Parser cannot handle more arguments\n");
106 return 0;
107 }
108 }
109
free_argv(void)110 static void free_argv(void) {
111 int i;
112
113 for (i = 0; i < newargc; i++)
114 free(newargv[i]);
115 }
116
add_param_to_argv(char * parsestart)117 static void add_param_to_argv(char *parsestart)
118 {
119 int quote_open = 0, escaped = 0, param_len = 0;
120 char param_buffer[1024], *curchar;
121
122 /* After fighting with strtok enough, here's now
123 * a 'real' parser. According to Rusty I'm now no
124 * longer a real hacker, but I can live with that */
125
126 for (curchar = parsestart; *curchar; curchar++) {
127 if (quote_open) {
128 if (escaped) {
129 param_buffer[param_len++] = *curchar;
130 escaped = 0;
131 continue;
132 } else if (*curchar == '\\') {
133 escaped = 1;
134 continue;
135 } else if (*curchar == '"') {
136 quote_open = 0;
137 *curchar = ' ';
138 } else {
139 param_buffer[param_len++] = *curchar;
140 continue;
141 }
142 } else {
143 if (*curchar == '"') {
144 quote_open = 1;
145 continue;
146 }
147 }
148
149 if (*curchar == ' '
150 || *curchar == '\t'
151 || * curchar == '\n') {
152 if (!param_len) {
153 /* two spaces? */
154 continue;
155 }
156
157 param_buffer[param_len] = '\0';
158
159 /* check if table name specified */
160 if (!strncmp(param_buffer, "-t", 2)
161 || !strncmp(param_buffer, "--table", 8)) {
162 xtables_error(PARAMETER_PROBLEM,
163 "The -t option (seen in line %u) cannot be "
164 "used in ip6tables-restore.\n", line);
165 exit(1);
166 }
167
168 add_argv(param_buffer);
169 param_len = 0;
170 } else {
171 /* regular character, copy to buffer */
172 param_buffer[param_len++] = *curchar;
173
174 if (param_len >= sizeof(param_buffer))
175 xtables_error(PARAMETER_PROBLEM,
176 "Parameter too long!");
177 }
178 }
179 }
180
ip6tables_restore_main(int argc,char * argv[])181 int ip6tables_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 = &ip6tc_ops;
191
192 line = 0;
193
194 ip6tables_globals.program_name = "ip6tables-restore";
195 c = xtables_init_all(&ip6tables_globals, NFPROTO_IPV6);
196 if (c < 0) {
197 fprintf(stderr, "%s/%s Failed to initialize xtables\n",
198 ip6tables_globals.program_name,
199 ip6tables_globals.program_version);
200 exit(1);
201 }
202 #if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
203 init_extensions();
204 init_extensions6();
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("ip6tables-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 != NULL && 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_chain6(flush_entries6, verbose, 1,
298 handle);
299
300 DEBUGP("Deleting all user-defined chains "
301 "of table '%s'\n", table);
302 for_each_chain6(delete_chain6, 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_command6(%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_command6(newargc, newargv,
441 &newargv[2], &handle, true);
442
443 free_argv();
444 fflush(stdout);
445 }
446 if (tablename != NULL && 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