1 /* Code to restore the iptables state, from file by iptables-save. 2 * (C) 2000-2002 by Harald Welte <laforge (at) gnumonks.org> 3 * based on previous code from Rusty Russell <rusty (at) linuxcare.com.au> 4 * 5 * This code is distributed under the terms of GNU GPL v2 6 */ 7 8 #include <getopt.h> 9 #include <sys/errno.h> 10 #include <stdbool.h> 11 #include <string.h> 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include "iptables.h" 15 #include "xtables.h" 16 #include "libiptc/libiptc.h" 17 #include "iptables-multi.h" 18 19 #ifdef DEBUG 20 #define DEBUGP(x, args...) fprintf(stderr, x, ## args) 21 #else 22 #define DEBUGP(x, args...) 23 #endif 24 25 static int binary = 0, counters = 0, verbose = 0, noflush = 0; 26 27 /* Keeping track of external matches and targets. */ 28 static const struct option options[] = { 29 {.name = "binary", .has_arg = false, .val = 'b'}, 30 {.name = "counters", .has_arg = false, .val = 'c'}, 31 {.name = "verbose", .has_arg = false, .val = 'v'}, 32 {.name = "test", .has_arg = false, .val = 't'}, 33 {.name = "help", .has_arg = false, .val = 'h'}, 34 {.name = "noflush", .has_arg = false, .val = 'n'}, 35 {.name = "modprobe", .has_arg = true, .val = 'M'}, 36 {.name = "table", .has_arg = true, .val = 'T'}, 37 {NULL}, 38 }; 39 40 static void print_usage(const char *name, const char *version) __attribute__((noreturn)); 41 42 #define prog_name iptables_globals.program_name 43 44 static void print_usage(const char *name, const char *version) 45 { 46 fprintf(stderr, "Usage: %s [-b] [-c] [-v] [-t] [-h]\n" 47 " [ --binary ]\n" 48 " [ --counters ]\n" 49 " [ --verbose ]\n" 50 " [ --test ]\n" 51 " [ --help ]\n" 52 " [ --noflush ]\n" 53 " [ --table=<TABLE> ]\n" 54 " [ --modprobe=<command>]\n", name); 55 56 exit(1); 57 } 58 59 static struct xtc_handle *create_handle(const char *tablename) 60 { 61 struct xtc_handle *handle; 62 63 handle = iptc_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 = iptc_init(tablename); 69 } 70 71 if (!handle) { 72 xtables_error(PARAMETER_PROBLEM, "%s: unable to initialize " 73 "table '%s'\n", prog_name, tablename); 74 exit(1); 75 } 76 return handle; 77 } 78 79 static int parse_counters(char *string, struct xt_counters *ctr) 80 { 81 unsigned long long pcnt, bcnt; 82 int ret; 83 84 ret = sscanf(string, "[%llu:%llu]", &pcnt, &bcnt); 85 ctr->pcnt = pcnt; 86 ctr->bcnt = bcnt; 87 return ret == 2; 88 } 89 90 /* global new argv and argc */ 91 static char *newargv[255]; 92 static int newargc; 93 94 /* function adding one argument to newargv, updating newargc 95 * returns true if argument added, false otherwise */ 96 static int add_argv(char *what) { 97 DEBUGP("add_argv: %s\n", what); 98 if (what && newargc + 1 < ARRAY_SIZE(newargv)) { 99 newargv[newargc] = strdup(what); 100 newargv[++newargc] = NULL; 101 return 1; 102 } else { 103 xtables_error(PARAMETER_PROBLEM, 104 "Parser cannot handle more arguments\n"); 105 return 0; 106 } 107 } 108 109 static void free_argv(void) { 110 int i; 111 112 for (i = 0; i < newargc; i++) 113 free(newargv[i]); 114 } 115 116 static void add_param_to_argv(char *parsestart) 117 { 118 int quote_open = 0, escaped = 0, param_len = 0; 119 char param_buffer[1024], *curchar; 120 121 /* After fighting with strtok enough, here's now 122 * a 'real' parser. According to Rusty I'm now no 123 * longer a real hacker, but I can live with that */ 124 125 for (curchar = parsestart; *curchar; curchar++) { 126 if (quote_open) { 127 if (escaped) { 128 param_buffer[param_len++] = *curchar; 129 escaped = 0; 130 continue; 131 } else if (*curchar == '\\') { 132 escaped = 1; 133 continue; 134 } else if (*curchar == '"') { 135 quote_open = 0; 136 *curchar = ' '; 137 } else { 138 param_buffer[param_len++] = *curchar; 139 continue; 140 } 141 } else { 142 if (*curchar == '"') { 143 quote_open = 1; 144 continue; 145 } 146 } 147 148 if (*curchar == ' ' 149 || *curchar == '\t' 150 || * curchar == '\n') { 151 if (!param_len) { 152 /* two spaces? */ 153 continue; 154 } 155 156 param_buffer[param_len] = '\0'; 157 158 /* check if table name specified */ 159 if (!strncmp(param_buffer, "-t", 2) 160 || !strncmp(param_buffer, "--table", 8)) { 161 xtables_error(PARAMETER_PROBLEM, 162 "The -t option (seen in line %u) cannot be " 163 "used in iptables-restore.\n", line); 164 exit(1); 165 } 166 167 add_argv(param_buffer); 168 param_len = 0; 169 } else { 170 /* regular character, copy to buffer */ 171 param_buffer[param_len++] = *curchar; 172 173 if (param_len >= sizeof(param_buffer)) 174 xtables_error(PARAMETER_PROBLEM, 175 "Parameter too long!"); 176 } 177 } 178 } 179 180 int 181 iptables_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 = &iptc_ops; 191 192 line = 0; 193 194 iptables_globals.program_name = "iptables-restore"; 195 c = xtables_init_all(&iptables_globals, NFPROTO_IPV4); 196 if (c < 0) { 197 fprintf(stderr, "%s/%s Failed to initialize xtables\n", 198 iptables_globals.program_name, 199 iptables_globals.program_version); 200 exit(1); 201 } 202 #if defined(ALL_INCLUSIVE) || defined(NO_SHARED_LIBS) 203 init_extensions(); 204 init_extensions4(); 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("iptables-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 && (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_chain4(flush_entries4, verbose, 1, 298 handle); 299 300 DEBUGP("Deleting all user-defined chains " 301 "of table '%s'\n", table); 302 for_each_chain4(delete_chain4, 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_command4(%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_command4(newargc, newargv, 441 &newargv[2], &handle, true); 442 443 free_argv(); 444 fflush(stdout); 445 } 446 if (tablename && (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