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