iptables-1.2.9-2.3.1.src.rpm
[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: iptables-restore.c,v 1.26 2003/05/02 15:30:11 laforge Exp $
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 "libiptc/libiptc.h"
17
18 #ifdef DEBUG
19 #define DEBUGP(x, args...) fprintf(stderr, x, ## args)
20 #else
21 #define DEBUGP(x, args...) 
22 #endif
23
24 static int binary = 0, counters = 0, verbose = 0, noflush = 0;
25
26 /* Keeping track of external matches and targets.  */
27 static struct option options[] = {
28         { "binary", 0, 0, 'b' },
29         { "counters", 0, 0, 'c' },
30         { "verbose", 1, 0, 'v' },
31         { "help", 0, 0, 'h' },
32         { "noflush", 0, 0, 'n'},
33         { "modprobe", 1, 0, 'M'},
34         { 0 }
35 };
36
37 static void print_usage(const char *name, const char *version) __attribute__((noreturn));
38
39 static void print_usage(const char *name, const char *version)
40 {
41         fprintf(stderr, "Usage: %s [-b] [-c] [-v] [-h]\n"
42                         "          [ --binary ]\n"
43                         "          [ --counters ]\n"
44                         "          [ --verbose ]\n"
45                         "          [ --help ]\n"
46                         "          [ --noflush ]\n"
47                         "          [ --modprobe=<command>]\n", name);
48                 
49         exit(1);
50 }
51
52 iptc_handle_t create_handle(const char *tablename, const char* modprobe )
53 {
54         iptc_handle_t handle;
55
56         handle = iptc_init(tablename);
57
58         if (!handle) {
59                 /* try to insmod the module if iptc_init failed */
60                 iptables_insmod("ip_tables", modprobe);
61                 handle = iptc_init(tablename);
62         }
63
64         if (!handle) {
65                 exit_error(PARAMETER_PROBLEM, "%s: unable to initialize"
66                         "table '%s'\n", program_name, tablename);
67                 exit(1);
68         }
69         return handle;
70 }
71
72 int parse_counters(char *string, struct ipt_counters *ctr)
73 {
74         if (string != NULL)
75                 return (sscanf(string, "[%llu:%llu]", &ctr->pcnt, &ctr->bcnt)
76                         == 2);
77         else
78                 return (0 == 2);
79 }
80
81 /* global new argv and argc */
82 static char *newargv[255];
83 static int newargc;
84
85 /* function adding one argument to newargv, updating newargc 
86  * returns true if argument added, false otherwise */
87 static int add_argv(char *what) {
88         DEBUGP("add_argv: %s\n", what);
89         if (what && ((newargc + 1) < sizeof(newargv)/sizeof(char *))) {
90                 newargv[newargc] = strdup(what);
91                 newargc++;
92                 return 1;
93         } else 
94                 return 0;
95 }
96
97 static void free_argv(void) {
98         int i;
99
100         for (i = 0; i < newargc; i++)
101                 free(newargv[i]);
102 }
103
104 int main(int argc, char *argv[])
105 {
106         iptc_handle_t handle = NULL;
107         char buffer[10240];
108         int c;
109         char curtable[IPT_TABLE_MAXNAMELEN + 1];
110         FILE *in;
111         const char *modprobe = 0;
112         int in_table = 0;
113
114         program_name = "iptables-restore";
115         program_version = IPTABLES_VERSION;
116         line = 0;
117
118 #ifdef NO_SHARED_LIBS
119         init_extensions();
120 #endif
121
122         while ((c = getopt_long(argc, argv, "bcvhnM:", options, NULL)) != -1) {
123                 switch (c) {
124                         case 'b':
125                                 binary = 1;
126                                 break;
127                         case 'c':
128                                 counters = 1;
129                                 break;
130                         case 'v':
131                                 verbose = 1;
132                                 break;
133                         case 'h':
134                                 print_usage("iptables-restore",
135                                             IPTABLES_VERSION);
136                                 break;
137                         case 'n':
138                                 noflush = 1;
139                                 break;
140                         case 'M':
141                                 modprobe = optarg;
142                                 break;
143                 }
144         }
145         
146         if (optind == argc - 1) {
147                 in = fopen(argv[optind], "r");
148                 if (!in) {
149                         fprintf(stderr, "Can't open %s: %s", argv[optind],
150                                 strerror(errno));
151                         exit(1);
152                 }
153         }
154         else if (optind < argc) {
155                 fprintf(stderr, "Unknown arguments found on commandline");
156                 exit(1);
157         }
158         else in = stdin;
159         
160         /* Grab standard input. */
161         while (fgets(buffer, sizeof(buffer), in)) {
162                 int ret = 0;
163
164                 line++;
165                 if (buffer[0] == '\n') continue;
166                 else if (buffer[0] == '#') {
167                         if (verbose) fputs(buffer, stdout);
168                         continue;
169                 } else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) {
170                         DEBUGP("Calling commit\n");
171                         ret = iptc_commit(&handle);
172                         in_table = 0;
173                 } else if ((buffer[0] == '*') && (!in_table)) {
174                         /* New table */
175                         char *table;
176
177                         table = strtok(buffer+1, " \t\n");
178                         DEBUGP("line %u, table '%s'\n", line, table);
179                         if (!table) {
180                                 exit_error(PARAMETER_PROBLEM, 
181                                         "%s: line %u table name invalid\n",
182                                         program_name, line);
183                                 exit(1);
184                         }
185                         strncpy(curtable, table, IPT_TABLE_MAXNAMELEN);
186
187                         if (handle)
188                                 iptc_free(&handle);
189
190                         handle = create_handle(table, modprobe);
191                         if (noflush == 0) {
192                                 DEBUGP("Cleaning all chains of table '%s'\n",
193                                         table);
194                                 for_each_chain(flush_entries, verbose, 1, 
195                                                 &handle);
196         
197                                 DEBUGP("Deleting all user-defined chains "
198                                        "of table '%s'\n", table);
199                                 for_each_chain(delete_chain, verbose, 0, 
200                                                 &handle) ;
201                         }
202
203                         ret = 1;
204                         in_table = 1;
205
206                 } else if ((buffer[0] == ':') && (in_table)) {
207                         /* New chain. */
208                         char *policy, *chain;
209
210                         chain = strtok(buffer+1, " \t\n");
211                         DEBUGP("line %u, chain '%s'\n", line, chain);
212                         if (!chain) {
213                                 exit_error(PARAMETER_PROBLEM,
214                                            "%s: line %u chain name invalid\n",
215                                            program_name, line);
216                                 exit(1);
217                         }
218
219                         if (!iptc_builtin(chain, handle)) {
220                                 DEBUGP("Creating new chain '%s'\n", chain);
221                                 if (!iptc_create_chain(chain, &handle)) 
222                                         exit_error(PARAMETER_PROBLEM, 
223                                                    "error creating chain "
224                                                    "'%s':%s\n", chain, 
225                                                    strerror(errno));
226                         }
227
228                         policy = strtok(NULL, " \t\n");
229                         DEBUGP("line %u, policy '%s'\n", line, policy);
230                         if (!policy) {
231                                 exit_error(PARAMETER_PROBLEM,
232                                            "%s: line %u policy invalid\n",
233                                            program_name, line);
234                                 exit(1);
235                         }
236
237                         if (strcmp(policy, "-") != 0) {
238                                 struct ipt_counters count;
239
240                                 if (counters) {
241                                         char *ctrs;
242                                         ctrs = strtok(NULL, " \t\n");
243
244                                         parse_counters(ctrs, &count);
245
246                                 } else {
247                                         memset(&count, 0, 
248                                                sizeof(struct ipt_counters));
249                                 }
250
251                                 DEBUGP("Setting policy of chain %s to %s\n",
252                                         chain, policy);
253
254                                 if (!iptc_set_policy(chain, policy, &count,
255                                                      &handle))
256                                         exit_error(OTHER_PROBLEM,
257                                                 "Can't set policy `%s'"
258                                                 " on `%s' line %u: %s\n",
259                                                 chain, policy, line,
260                                                 iptc_strerror(errno));
261                         }
262
263                         ret = 1;
264
265                 } else if (in_table) {
266                         int a;
267                         char *ptr = buffer;
268                         char *pcnt = NULL;
269                         char *bcnt = NULL;
270                         char *parsestart;
271
272                         /* the parser */
273                         char *param_start, *curchar;
274                         int quote_open;
275
276                         /* reset the newargv */
277                         newargc = 0;
278
279                         if (buffer[0] == '[') {
280                                 /* we have counters in our input */
281                                 ptr = strchr(buffer, ']');
282                                 if (!ptr)
283                                         exit_error(PARAMETER_PROBLEM,
284                                                    "Bad line %u: need ]\n",
285                                                    line);
286
287                                 pcnt = strtok(buffer+1, ":");
288                                 if (!pcnt)
289                                         exit_error(PARAMETER_PROBLEM,
290                                                    "Bad line %u: need :\n",
291                                                    line);
292
293                                 bcnt = strtok(NULL, "]");
294                                 if (!bcnt)
295                                         exit_error(PARAMETER_PROBLEM,
296                                                    "Bad line %u: need ]\n",
297                                                    line);
298
299                                 /* start command parsing after counter */
300                                 parsestart = ptr + 1;
301                         } else {
302                                 /* start command parsing at start of line */
303                                 parsestart = buffer;
304                         }
305
306                         add_argv(argv[0]);
307                         add_argv("-t");
308                         add_argv((char *) &curtable);
309                         
310                         if (counters && pcnt && bcnt) {
311                                 add_argv("--set-counters");
312                                 add_argv((char *) pcnt);
313                                 add_argv((char *) bcnt);
314                         }
315
316                         /* After fighting with strtok enough, here's now
317                          * a 'real' parser. According to Rusty I'm now no
318                          * longer a real hacker, but I can live with that */
319
320                         quote_open = 0;
321                         param_start = parsestart;
322                         
323                         for (curchar = parsestart; *curchar; curchar++) {
324                                 if (*curchar == '"') {
325                                         if (quote_open) {
326                                                 quote_open = 0;
327                                                 *curchar = ' ';
328                                         } else {
329                                                 quote_open = 1;
330                                                 param_start++;
331                                         }
332                                 } 
333                                 if (*curchar == ' '
334                                     || *curchar == '\t'
335                                     || * curchar == '\n') {
336                                         char param_buffer[1024];
337                                         int param_len = curchar-param_start;
338
339                                         if (quote_open)
340                                                 continue;
341
342                                         if (!param_len) {
343                                                 /* two spaces? */
344                                                 param_start++;
345                                                 continue;
346                                         }
347                                         
348                                         /* end of one parameter */
349                                         strncpy(param_buffer, param_start,
350                                                 param_len);
351                                         *(param_buffer+param_len) = '\0';
352
353                                         /* check if table name specified */
354                                         if (!strncmp(param_buffer, "-t", 3)
355                                             || !strncmp(param_buffer, "--table", 8)) {
356                                                 exit_error(PARAMETER_PROBLEM, 
357                                                    "Line %u seems to have a "
358                                                    "-t table option.\n", line);
359                                                 exit(1);
360                                         }
361
362                                         add_argv(param_buffer);
363                                         param_start += param_len + 1;
364                                 } else {
365                                         /* regular character, skip */
366                                 }
367                         }
368
369                         DEBUGP("calling do_command(%u, argv, &%s, handle):\n",
370                                 newargc, curtable);
371
372                         for (a = 0; a < newargc; a++)
373                                 DEBUGP("argv[%u]: %s\n", a, newargv[a]);
374
375                         ret = do_command(newargc, newargv, 
376                                          &newargv[2], &handle);
377
378                         free_argv();
379                 }
380                 if (!ret) {
381                         fprintf(stderr, "%s: line %u failed\n",
382                                         program_name, line);
383                         exit(1);
384                 }
385         }
386
387         return 0;
388 }