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