iptables-restore.c 11 KB

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