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