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