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 <libgen.h>
11 #include <stdbool.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 "xtables-multi.h"
19 #include "nft.h"
20 #include "nft-bridge.h"
21 #include "nft-cache.h"
22 #include <libnftnl/chain.h>
23
24 static int counters, verbose;
25
26 /* Keeping track of external matches and targets. */
27 static const struct option options[] = {
28 {.name = "counters", .has_arg = false, .val = 'c'},
29 {.name = "verbose", .has_arg = false, .val = 'v'},
30 {.name = "version", .has_arg = 0, .val = 'V'},
31 {.name = "test", .has_arg = false, .val = 't'},
32 {.name = "help", .has_arg = false, .val = 'h'},
33 {.name = "noflush", .has_arg = false, .val = 'n'},
34 {.name = "modprobe", .has_arg = true, .val = 'M'},
35 {.name = "table", .has_arg = true, .val = 'T'},
36 {.name = "ipv4", .has_arg = false, .val = '4'},
37 {.name = "ipv6", .has_arg = false, .val = '6'},
38 {.name = "wait", .has_arg = 2, .val = 'w'},
39 {.name = "wait-interval", .has_arg = 2, .val = 'W'},
40 {NULL},
41 };
42
43 #define prog_name xtables_globals.program_name
44 #define prog_vers xtables_globals.program_version
45
print_usage(const char * name,const char * version)46 static void print_usage(const char *name, const char *version)
47 {
48 fprintf(stderr, "Usage: %s [-c] [-v] [-V] [-t] [-h] [-n] [-T table] [-M command] [-4] [-6] [file]\n"
49 " [ --counters ]\n"
50 " [ --verbose ]\n"
51 " [ --version]\n"
52 " [ --test ]\n"
53 " [ --help ]\n"
54 " [ --noflush ]\n"
55 " [ --table=<TABLE> ]\n"
56 " [ --modprobe=<command> ]\n"
57 " [ --ipv4 ]\n"
58 " [ --ipv6 ]\n", name);
59 }
60
61 static const struct nft_xt_restore_cb restore_cb = {
62 .commit = nft_commit,
63 .abort = nft_abort,
64 .table_flush = nft_cmd_table_flush,
65 .do_command = do_commandx,
66 .chain_set = nft_cmd_chain_set,
67 .chain_restore = nft_cmd_chain_restore,
68 };
69
70 struct nft_xt_restore_state {
71 const struct builtin_table *curtable;
72 struct argv_store av_store;
73 bool in_table;
74 };
75
xtables_restore_parse_line(struct nft_handle * h,const struct nft_xt_restore_parse * p,struct nft_xt_restore_state * state,char * buffer)76 static void xtables_restore_parse_line(struct nft_handle *h,
77 const struct nft_xt_restore_parse *p,
78 struct nft_xt_restore_state *state,
79 char *buffer)
80 {
81 const struct nft_xt_restore_cb *cb = p->cb;
82 int ret = 0;
83
84 if (buffer[0] == '\n')
85 return;
86 else if (buffer[0] == '#') {
87 if (verbose) {
88 fputs(buffer, stdout);
89 fflush(stdout);
90 }
91 return;
92 } else if (state->in_table &&
93 (strncmp(buffer, "COMMIT", 6) == 0) &&
94 (buffer[6] == '\0' || buffer[6] == '\n')) {
95 if (!p->testing) {
96 /* Commit per table, although we support
97 * global commit at once, stick by now to
98 * the existing behaviour.
99 */
100 DEBUGP("Calling commit\n");
101 if (cb->commit)
102 ret = cb->commit(h);
103 } else {
104 DEBUGP("Not calling commit, testing\n");
105 if (cb->abort)
106 ret = cb->abort(h);
107 }
108 state->in_table = false;
109
110 } else if ((buffer[0] == '*') && (!state->in_table || !p->commit)) {
111 /* New table */
112 char *table;
113
114 table = strtok(buffer+1, " \t\n");
115 DEBUGP("line %u, table '%s'\n", line, table);
116 if (!table)
117 xtables_error(PARAMETER_PROBLEM,
118 "%s: line %u table name invalid\n",
119 xt_params->program_name, line);
120
121 state->curtable = nft_table_builtin_find(h, table);
122 if (!state->curtable)
123 xtables_error(PARAMETER_PROBLEM,
124 "%s: line %u table name '%s' invalid\n",
125 xt_params->program_name, line, table);
126
127 if (p->tablename && (strcmp(p->tablename, table) != 0))
128 return;
129
130 /* implicit commit if no explicit COMMIT supported */
131 if (!p->commit)
132 cb->commit(h);
133
134 if (h->noflush == 0) {
135 DEBUGP("Cleaning all chains of table '%s'\n", table);
136 if (cb->table_flush)
137 cb->table_flush(h, table, verbose);
138 }
139
140 ret = 1;
141 state->in_table = true;
142
143 if (cb->table_new)
144 cb->table_new(h, table);
145
146 } else if ((buffer[0] == ':') && state->in_table) {
147 /* New chain. */
148 char *policy, *chain = NULL;
149 struct xt_counters count = {};
150
151 chain = strtok(buffer+1, " \t\n");
152 DEBUGP("line %u, chain '%s'\n", line, chain);
153 if (!chain)
154 xtables_error(PARAMETER_PROBLEM,
155 "%s: line %u chain name invalid\n",
156 xt_params->program_name, line);
157
158 if (strlen(chain) >= XT_EXTENSION_MAXNAMELEN)
159 xtables_error(PARAMETER_PROBLEM,
160 "Invalid chain name `%s' (%u chars max)",
161 chain, XT_EXTENSION_MAXNAMELEN - 1);
162
163 policy = strtok(NULL, " \t\n");
164 DEBUGP("line %u, policy '%s'\n", line, policy);
165 if (!policy)
166 xtables_error(PARAMETER_PROBLEM,
167 "%s: line %u policy invalid\n",
168 xt_params->program_name, line);
169
170 if (nft_chain_builtin_find(state->curtable, chain)) {
171 if (counters) {
172 char *ctrs;
173 ctrs = strtok(NULL, " \t\n");
174
175 if (!ctrs || !parse_counters(ctrs, &count))
176 xtables_error(PARAMETER_PROBLEM,
177 "invalid policy counters for chain '%s'\n",
178 chain);
179
180 }
181 if (cb->chain_set &&
182 cb->chain_set(h, state->curtable->name,
183 chain, policy, &count) < 0) {
184 xtables_error(OTHER_PROBLEM,
185 "Can't set policy `%s' on `%s' line %u: %s\n",
186 policy, chain, line,
187 strerror(errno));
188 }
189 DEBUGP("Setting policy of chain %s to %s\n",
190 chain, policy);
191 } else if (cb->chain_restore(h, chain, state->curtable->name) < 0 &&
192 errno != EEXIST) {
193 xtables_error(PARAMETER_PROBLEM,
194 "cannot create chain '%s' (%s)\n",
195 chain, strerror(errno));
196 } else if (h->family == NFPROTO_BRIDGE &&
197 !ebt_cmd_user_chain_policy(h, state->curtable->name,
198 chain, policy)) {
199 xtables_error(OTHER_PROBLEM,
200 "Can't set policy `%s' on `%s' line %u: %s\n",
201 policy, chain, line,
202 strerror(errno));
203 }
204 ret = 1;
205 } else if (state->in_table) {
206 char *pcnt = NULL;
207 char *bcnt = NULL;
208 char *parsestart = buffer;
209
210 add_argv(&state->av_store, xt_params->program_name, 0);
211 add_argv(&state->av_store, "-t", 0);
212 add_argv(&state->av_store, state->curtable->name, 0);
213
214 tokenize_rule_counters(&parsestart, &pcnt, &bcnt, line);
215 if (counters && pcnt && bcnt) {
216 add_argv(&state->av_store, "--set-counters", 0);
217 add_argv(&state->av_store, pcnt, 0);
218 add_argv(&state->av_store, bcnt, 0);
219 }
220
221 add_param_to_argv(&state->av_store, parsestart, line);
222
223 DEBUGP("calling do_command4(%u, argv, &%s, handle):\n",
224 state->av_store.argc, state->curtable->name);
225 debug_print_argv(&state->av_store);
226
227 ret = cb->do_command(h, state->av_store.argc,
228 state->av_store.argv,
229 &state->av_store.argv[2], true);
230 if (ret < 0) {
231 if (cb->abort)
232 ret = cb->abort(h);
233 else
234 ret = 0;
235
236 if (ret < 0) {
237 fprintf(stderr,
238 "failed to abort commit operation\n");
239 }
240 exit(1);
241 }
242
243 free_argv(&state->av_store);
244 fflush(stdout);
245 }
246 if (p->tablename && state->curtable &&
247 (strcmp(p->tablename, state->curtable->name) != 0))
248 return;
249 if (!ret) {
250 fprintf(stderr, "%s: line %u failed\n",
251 xt_params->program_name, line);
252 exit(1);
253 }
254 }
255
xtables_restore_parse(struct nft_handle * h,const struct nft_xt_restore_parse * p)256 void xtables_restore_parse(struct nft_handle *h,
257 const struct nft_xt_restore_parse *p)
258 {
259 struct nft_xt_restore_state state = {};
260 char buffer[10240] = {};
261
262 if (!verbose && !h->noflush)
263 nft_cache_level_set(h, NFT_CL_FAKE, NULL);
264
265 line = 0;
266 while (fgets(buffer, sizeof(buffer), p->in)) {
267 h->error.lineno = ++line;
268 DEBUGP("%s: input line %d: '%s'\n", __func__, line, buffer);
269 xtables_restore_parse_line(h, p, &state, buffer);
270 }
271 if (state.in_table && p->commit) {
272 fprintf(stderr, "%s: COMMIT expected at line %u\n",
273 xt_params->program_name, line + 1);
274 exit(1);
275 } else if (state.in_table && p->cb->commit && !p->cb->commit(h)) {
276 xtables_error(OTHER_PROBLEM, "%s: final implicit COMMIT failed",
277 xt_params->program_name);
278 }
279 }
280
281 static int
xtables_restore_main(int family,const char * progname,int argc,char * argv[])282 xtables_restore_main(int family, const char *progname, int argc, char *argv[])
283 {
284 const struct builtin_table *tables;
285 struct nft_xt_restore_parse p = {
286 .commit = true,
287 .cb = &restore_cb,
288 };
289 bool noflush = false;
290 struct nft_handle h;
291 int c;
292
293 line = 0;
294
295 xtables_globals.program_name = progname;
296 c = xtables_init_all(&xtables_globals, family);
297 if (c < 0) {
298 fprintf(stderr, "%s/%s Failed to initialize xtables\n",
299 xtables_globals.program_name,
300 xtables_globals.program_version);
301 exit(1);
302 }
303
304 while ((c = getopt_long(argc, argv, "bcvVthnM:T:wW", options, NULL)) != -1) {
305 switch (c) {
306 case 'b':
307 fprintf(stderr, "-b/--binary option is not implemented\n");
308 break;
309 case 'c':
310 counters = 1;
311 break;
312 case 'v':
313 verbose = 1;
314 break;
315 case 'V':
316 printf("%s v%s (nf_tables)\n", prog_name, prog_vers);
317 exit(0);
318 case 't':
319 p.testing = 1;
320 break;
321 case 'h':
322 print_usage(prog_name, PACKAGE_VERSION);
323 exit(0);
324 case 'n':
325 noflush = true;
326 break;
327 case 'M':
328 xtables_modprobe_program = optarg;
329 break;
330 case 'T':
331 p.tablename = optarg;
332 break;
333 case 'w': /* fallthrough. Ignored by xt-restore */
334 case 'W':
335 if (!optarg && xs_has_arg(argc, argv))
336 optind++;
337 break;
338 default:
339 fprintf(stderr,
340 "Try `%s -h' for more information.\n",
341 prog_name);
342 exit(1);
343 }
344 }
345
346 if (optind == argc - 1) {
347 p.in = fopen(argv[optind], "re");
348 if (!p.in) {
349 fprintf(stderr, "Can't open %s: %s\n", argv[optind],
350 strerror(errno));
351 exit(1);
352 }
353 } else if (optind < argc) {
354 fprintf(stderr, "Unknown arguments found on commandline\n");
355 exit(1);
356 } else {
357 p.in = stdin;
358 }
359
360 switch (family) {
361 case NFPROTO_IPV4:
362 case NFPROTO_IPV6: /* fallthough, same table */
363 tables = xtables_ipv4;
364 #if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS)
365 init_extensions();
366 init_extensions4();
367 #endif
368 break;
369 case NFPROTO_ARP:
370 tables = xtables_arp;
371 break;
372 case NFPROTO_BRIDGE:
373 tables = xtables_bridge;
374 break;
375 default:
376 fprintf(stderr, "Unknown family %d\n", family);
377 return 1;
378 }
379
380 if (nft_init(&h, family, tables) < 0) {
381 fprintf(stderr, "%s/%s Failed to initialize nft: %s\n",
382 xtables_globals.program_name,
383 xtables_globals.program_version,
384 strerror(errno));
385 exit(EXIT_FAILURE);
386 }
387 h.noflush = noflush;
388 h.restore = true;
389
390 xtables_restore_parse(&h, &p);
391
392 nft_fini(&h);
393 xtables_fini();
394 fclose(p.in);
395 return 0;
396 }
397
xtables_ip4_restore_main(int argc,char * argv[])398 int xtables_ip4_restore_main(int argc, char *argv[])
399 {
400 return xtables_restore_main(NFPROTO_IPV4, basename(*argv),
401 argc, argv);
402 }
403
xtables_ip6_restore_main(int argc,char * argv[])404 int xtables_ip6_restore_main(int argc, char *argv[])
405 {
406 return xtables_restore_main(NFPROTO_IPV6, basename(*argv),
407 argc, argv);
408 }
409
410 static const struct nft_xt_restore_cb ebt_restore_cb = {
411 .commit = nft_bridge_commit,
412 .table_flush = nft_cmd_table_flush,
413 .do_command = do_commandeb,
414 .chain_set = nft_cmd_chain_set,
415 .chain_restore = nft_cmd_chain_restore,
416 };
417
418 static const struct option ebt_restore_options[] = {
419 {.name = "noflush", .has_arg = 0, .val = 'n'},
420 { 0 }
421 };
422
xtables_eb_restore_main(int argc,char * argv[])423 int xtables_eb_restore_main(int argc, char *argv[])
424 {
425 struct nft_xt_restore_parse p = {
426 .in = stdin,
427 .cb = &ebt_restore_cb,
428 };
429 bool noflush = false;
430 struct nft_handle h;
431 int c;
432
433 while ((c = getopt_long(argc, argv, "n",
434 ebt_restore_options, NULL)) != -1) {
435 switch(c) {
436 case 'n':
437 noflush = 1;
438 break;
439 default:
440 fprintf(stderr,
441 "Usage: ebtables-restore [ --noflush ]\n");
442 exit(1);
443 break;
444 }
445 }
446
447 nft_init_eb(&h, "ebtables-restore");
448 h.noflush = noflush;
449 xtables_restore_parse(&h, &p);
450 nft_fini_eb(&h);
451
452 return 0;
453 }
454
455 static const struct nft_xt_restore_cb arp_restore_cb = {
456 .commit = nft_commit,
457 .table_flush = nft_cmd_table_flush,
458 .do_command = do_commandarp,
459 .chain_set = nft_cmd_chain_set,
460 .chain_restore = nft_cmd_chain_restore,
461 };
462
xtables_arp_restore_main(int argc,char * argv[])463 int xtables_arp_restore_main(int argc, char *argv[])
464 {
465 struct nft_xt_restore_parse p = {
466 .in = stdin,
467 .cb = &arp_restore_cb,
468 };
469 struct nft_handle h;
470
471 nft_init_arp(&h, "arptables-restore");
472 xtables_restore_parse(&h, &p);
473 nft_fini(&h);
474 xtables_fini();
475
476 return 0;
477 }
478