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 #include "config.h"
8 #include <getopt.h>
9 #include <errno.h>
10 #include <stdbool.h>
11 #include <string.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include "iptables.h"
15 #include "ip6tables.h"
16 #include "xshared.h"
17 #include "xtables.h"
18 #include "libiptc/libiptc.h"
19 #include "libiptc/libip6tc.h"
20 #include "iptables-multi.h"
21 #include "ip6tables-multi.h"
22
23 static int counters, verbose, noflush, wait;
24
25 static struct timeval wait_interval = {
26 .tv_sec = 1,
27 };
28
29 /* Keeping track of external matches and targets. */
30 static const struct option options[] = {
31 {.name = "counters", .has_arg = 0, .val = 'c'},
32 {.name = "verbose", .has_arg = 0, .val = 'v'},
33 {.name = "version", .has_arg = 0, .val = 'V'},
34 {.name = "test", .has_arg = 0, .val = 't'},
35 {.name = "help", .has_arg = 0, .val = 'h'},
36 {.name = "noflush", .has_arg = 0, .val = 'n'},
37 {.name = "modprobe", .has_arg = 1, .val = 'M'},
38 {.name = "table", .has_arg = 1, .val = 'T'},
39 {.name = "wait", .has_arg = 2, .val = 'w'},
40 {.name = "wait-interval", .has_arg = 2, .val = 'W'},
41 {NULL},
42 };
43
print_usage(const char * name,const char * version)44 static void print_usage(const char *name, const char *version)
45 {
46 fprintf(stderr, "Usage: %s [-c] [-v] [-V] [-t] [-h] [-n] [-w secs] [-W usecs] [-T table] [-M command] [file]\n"
47 " [ --counters ]\n"
48 " [ --verbose ]\n"
49 " [ --version]\n"
50 " [ --test ]\n"
51 " [ --help ]\n"
52 " [ --noflush ]\n"
53 " [ --wait=<seconds>\n"
54 " [ --wait-interval=<usecs>\n"
55 " [ --table=<TABLE> ]\n"
56 " [ --modprobe=<command> ]\n", name);
57 }
58
59 struct iptables_restore_cb {
60 const struct xtc_ops *ops;
61
62 int (*for_each_chain)(int (*fn)(const xt_chainlabel,
63 int, struct xtc_handle *),
64 int verbose, int builtinstoo,
65 struct xtc_handle *handle);
66 int (*flush_entries)(const xt_chainlabel, int, struct xtc_handle *);
67 int (*delete_chain)(const xt_chainlabel, int, struct xtc_handle *);
68 int (*do_command)(int argc, char *argv[], char **table,
69 struct xtc_handle **handle, bool restore);
70 };
71
72 static struct xtc_handle *
create_handle(const struct iptables_restore_cb * cb,const char * tablename)73 create_handle(const struct iptables_restore_cb *cb, const char *tablename)
74 {
75 struct xtc_handle *handle;
76
77 handle = cb->ops->init(tablename);
78
79 if (!handle) {
80 /* try to insmod the module if iptc_init failed */
81 xtables_load_ko(xtables_modprobe_program, false);
82 handle = cb->ops->init(tablename);
83 }
84
85 if (!handle)
86 xtables_error(PARAMETER_PROBLEM, "%s: unable to initialize "
87 "table '%s'\n", xt_params->program_name, tablename);
88
89 return handle;
90 }
91
92 static int
ip46tables_restore_main(const struct iptables_restore_cb * cb,int argc,char * argv[])93 ip46tables_restore_main(const struct iptables_restore_cb *cb,
94 int argc, char *argv[])
95 {
96 struct xtc_handle *handle = NULL;
97 struct argv_store av_store = {};
98 char buffer[10240];
99 int c, lock;
100 char curtable[XT_TABLE_MAXNAMELEN + 1] = {};
101 FILE *in;
102 int in_table = 0, testing = 0;
103 const char *tablename = NULL;
104
105 line = 0;
106 lock = XT_LOCK_NOT_ACQUIRED;
107
108 while ((c = getopt_long(argc, argv, "bcvVthnwWM:T:", options, NULL)) != -1) {
109 switch (c) {
110 case 'b':
111 fprintf(stderr, "-b/--binary option is not implemented\n");
112 break;
113 case 'c':
114 counters = 1;
115 break;
116 case 'v':
117 verbose = 1;
118 break;
119 case 'V':
120 printf("%s v%s (legacy)\n",
121 xt_params->program_name,
122 xt_params->program_version);
123 exit(0);
124 case 't':
125 testing = 1;
126 break;
127 case 'h':
128 print_usage(xt_params->program_name,
129 PACKAGE_VERSION);
130 exit(0);
131 case 'n':
132 noflush = 1;
133 break;
134 case 'w':
135 wait = parse_wait_time(argc, argv);
136 break;
137 case 'W':
138 parse_wait_interval(argc, argv, &wait_interval);
139 break;
140 case 'M':
141 xtables_modprobe_program = optarg;
142 break;
143 case 'T':
144 tablename = optarg;
145 break;
146 default:
147 fprintf(stderr,
148 "Try `%s -h' for more information.\n",
149 xt_params->program_name);
150 exit(1);
151 }
152 }
153
154 if (optind == argc - 1) {
155 in = fopen(argv[optind], "re");
156 if (!in) {
157 fprintf(stderr, "Can't open %s: %s\n", argv[optind],
158 strerror(errno));
159 exit(1);
160 }
161 }
162 else if (optind < argc) {
163 fprintf(stderr, "Unknown arguments found on commandline\n");
164 exit(1);
165 }
166 else in = stdin;
167
168 if (!wait_interval.tv_sec && !wait) {
169 fprintf(stderr, "Option --wait-interval requires option --wait\n");
170 exit(1);
171 }
172
173 /* Grab standard input. */
174 while (fgets(buffer, sizeof(buffer), in)) {
175 int ret = 0;
176
177 line++;
178 if (buffer[0] == '\n')
179 continue;
180 else if (buffer[0] == '#') {
181 if (verbose) {
182 fputs(buffer, stdout);
183 fflush(stdout);
184 }
185 continue;
186 } else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) {
187 if (!testing) {
188 DEBUGP("Calling commit\n");
189 ret = cb->ops->commit(handle);
190 cb->ops->free(handle);
191 handle = NULL;
192 } else {
193 DEBUGP("Not calling commit, testing\n");
194 ret = 1;
195 }
196
197 /* Done with the current table, release the lock. */
198 if (lock >= 0) {
199 xtables_unlock(lock);
200 lock = XT_LOCK_NOT_ACQUIRED;
201 }
202
203 in_table = 0;
204 } else if ((buffer[0] == '*') && (!in_table)) {
205 /* Acquire a lock before we create a new table handle */
206 lock = xtables_lock_or_exit(wait, &wait_interval);
207
208 /* New table */
209 char *table;
210
211 table = strtok(buffer+1, " \t\n");
212 DEBUGP("line %u, table '%s'\n", line, table);
213 if (!table)
214 xtables_error(PARAMETER_PROBLEM,
215 "%s: line %u table name invalid\n",
216 xt_params->program_name, line);
217
218 strncpy(curtable, table, XT_TABLE_MAXNAMELEN);
219 curtable[XT_TABLE_MAXNAMELEN] = '\0';
220
221 if (tablename && strcmp(tablename, table) != 0) {
222 if (lock >= 0) {
223 xtables_unlock(lock);
224 lock = XT_LOCK_NOT_ACQUIRED;
225 }
226 continue;
227 }
228 if (handle)
229 cb->ops->free(handle);
230
231 handle = create_handle(cb, table);
232 if (noflush == 0) {
233 DEBUGP("Cleaning all chains of table '%s'\n",
234 table);
235 cb->for_each_chain(cb->flush_entries, verbose, 1,
236 handle);
237
238 DEBUGP("Deleting all user-defined chains "
239 "of table '%s'\n", table);
240 cb->for_each_chain(cb->delete_chain, verbose, 0,
241 handle);
242 }
243
244 ret = 1;
245 in_table = 1;
246
247 } else if ((buffer[0] == ':') && (in_table)) {
248 /* New chain. */
249 char *policy, *chain;
250
251 chain = strtok(buffer+1, " \t\n");
252 DEBUGP("line %u, chain '%s'\n", line, chain);
253 if (!chain)
254 xtables_error(PARAMETER_PROBLEM,
255 "%s: line %u chain name invalid\n",
256 xt_params->program_name, line);
257
258 if (strlen(chain) >= XT_EXTENSION_MAXNAMELEN)
259 xtables_error(PARAMETER_PROBLEM,
260 "Invalid chain name `%s' "
261 "(%u chars max)",
262 chain, XT_EXTENSION_MAXNAMELEN - 1);
263
264 if (cb->ops->builtin(chain, handle) <= 0) {
265 if (noflush && cb->ops->is_chain(chain, handle)) {
266 DEBUGP("Flushing existing user defined chain '%s'\n", chain);
267 if (!cb->ops->flush_entries(chain, handle))
268 xtables_error(PARAMETER_PROBLEM,
269 "error flushing chain "
270 "'%s':%s\n", chain,
271 strerror(errno));
272 } else {
273 DEBUGP("Creating new chain '%s'\n", chain);
274 if (!cb->ops->create_chain(chain, handle))
275 xtables_error(PARAMETER_PROBLEM,
276 "error creating chain "
277 "'%s':%s\n", chain,
278 strerror(errno));
279 }
280 }
281
282 policy = strtok(NULL, " \t\n");
283 DEBUGP("line %u, policy '%s'\n", line, policy);
284 if (!policy)
285 xtables_error(PARAMETER_PROBLEM,
286 "%s: line %u policy invalid\n",
287 xt_params->program_name, line);
288
289 if (strcmp(policy, "-") != 0) {
290 struct xt_counters count = {};
291
292 if (counters) {
293 char *ctrs;
294 ctrs = strtok(NULL, " \t\n");
295
296 if (!ctrs || !parse_counters(ctrs, &count))
297 xtables_error(PARAMETER_PROBLEM,
298 "invalid policy counters "
299 "for chain '%s'\n", chain);
300 }
301
302 DEBUGP("Setting policy of chain %s to %s\n",
303 chain, policy);
304
305 if (!cb->ops->set_policy(chain, policy, &count,
306 handle))
307 xtables_error(OTHER_PROBLEM,
308 "Can't set policy `%s'"
309 " on `%s' line %u: %s\n",
310 policy, chain, line,
311 cb->ops->strerror(errno));
312 }
313
314 ret = 1;
315
316 } else if (in_table) {
317 char *pcnt = NULL;
318 char *bcnt = NULL;
319 char *parsestart = buffer;
320
321 add_argv(&av_store, argv[0], 0);
322 add_argv(&av_store, "-t", 0);
323 add_argv(&av_store, curtable, 0);
324
325 tokenize_rule_counters(&parsestart, &pcnt, &bcnt, line);
326 if (counters && pcnt && bcnt) {
327 add_argv(&av_store, "--set-counters", 0);
328 add_argv(&av_store, pcnt, 0);
329 add_argv(&av_store, bcnt, 0);
330 }
331
332 add_param_to_argv(&av_store, parsestart, line);
333
334 DEBUGP("calling do_command(%u, argv, &%s, handle):\n",
335 av_store.argc, curtable);
336 debug_print_argv(&av_store);
337
338 ret = cb->do_command(av_store.argc, av_store.argv,
339 &av_store.argv[2], &handle, true);
340
341 free_argv(&av_store);
342 fflush(stdout);
343 }
344 if (tablename && strcmp(tablename, curtable) != 0)
345 continue;
346 if (!ret) {
347 fprintf(stderr, "%s: line %u failed\n",
348 xt_params->program_name, line);
349 exit(1);
350 }
351 }
352 if (in_table) {
353 fprintf(stderr, "%s: COMMIT expected at line %u\n",
354 xt_params->program_name, line + 1);
355 exit(1);
356 }
357
358 fclose(in);
359 return 0;
360 }
361
362
363 #if defined ENABLE_IPV4
364 static const struct iptables_restore_cb ipt_restore_cb = {
365 .ops = &iptc_ops,
366 .for_each_chain = for_each_chain4,
367 .flush_entries = flush_entries4,
368 .delete_chain = delete_chain4,
369 .do_command = do_command4,
370 };
371
372 int
iptables_restore_main(int argc,char * argv[])373 iptables_restore_main(int argc, char *argv[])
374 {
375 int c, ret;
376
377 iptables_globals.program_name = "iptables-restore";
378 c = xtables_init_all(&iptables_globals, NFPROTO_IPV4);
379 if (c < 0) {
380 fprintf(stderr, "%s/%s Failed to initialize xtables\n",
381 iptables_globals.program_name,
382 iptables_globals.program_version);
383 exit(1);
384 }
385 #if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
386 init_extensions();
387 init_extensions4();
388 #endif
389
390 ret = ip46tables_restore_main(&ipt_restore_cb, argc, argv);
391
392 xtables_fini();
393 return ret;
394 }
395 #endif
396
397 #if defined ENABLE_IPV6
398 static const struct iptables_restore_cb ip6t_restore_cb = {
399 .ops = &ip6tc_ops,
400 .for_each_chain = for_each_chain6,
401 .flush_entries = flush_entries6,
402 .delete_chain = delete_chain6,
403 .do_command = do_command6,
404 };
405
406 int
ip6tables_restore_main(int argc,char * argv[])407 ip6tables_restore_main(int argc, char *argv[])
408 {
409 int c, ret;
410
411 ip6tables_globals.program_name = "ip6tables-restore";
412 c = xtables_init_all(&ip6tables_globals, NFPROTO_IPV6);
413 if (c < 0) {
414 fprintf(stderr, "%s/%s Failed to initialize xtables\n",
415 ip6tables_globals.program_name,
416 ip6tables_globals.program_version);
417 exit(1);
418 }
419 #if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
420 init_extensions();
421 init_extensions6();
422 #endif
423
424 ret = ip46tables_restore_main(&ip6t_restore_cb, argc, argv);
425
426 xtables_fini();
427 return ret;
428 }
429 #endif
430