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