blob: 34437e0745917ea5569423857d3b03f7754d0053 [file] [log] [blame]
Amin Azez8d3eccb2006-11-13 20:23:36 +00001/* Code to convert iptables-save format to xml format,
2 * (C) 2006 Ufo Mechanic <azez@ufomechanic.net>
3 * based on iptables-restor (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
4 * based on previous code from Rusty Russell <rusty@linuxcare.com.au>
5 *
6 * This code is distributed under the terms of GNU GPL v2
7 *
8 * $Id: iptables-xml.c,v 1.4 2006/11/09 12:02:17 azez Exp $
9 */
10
11#include <getopt.h>
12#include <sys/errno.h>
13#include <string.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <stdarg.h>
17#include "iptables.h"
18#include "libiptc/libiptc.h"
19
20#ifdef DEBUG
21#define DEBUGP(x, args...) fprintf(stderr, x, ## args)
22#else
23#define DEBUGP(x, args...)
24#endif
25
26/* no need to link with iptables.o */
27const char *program_name;
28const char *program_version;
29int line = 0;
30
31void
32exit_error(enum exittype status, char *msg, ...)
33{
34 va_list args;
35
36 va_start(args, msg);
37 fprintf(stderr, "%s v%s: ", program_name, program_version);
38 vfprintf(stderr, msg, args);
39 va_end(args);
40 fprintf(stderr, "\n");
41 /* On error paths, make sure that we don't leak memory */
42 exit(status);
43}
44
45static void print_usage(const char *name, const char *version)
46 __attribute__ ((noreturn));
47
48static int verbose = 0;
49/* Whether to combine actions of sequential rules with identical conditions */
50static int combine = 0;
51/* Keeping track of external matches and targets. */
52static struct option options[] = {
53 {"verbose", 0, 0, 'v'},
54 {"combine", 0, 0, 'c'},
55 {"help", 0, 0, 'h'},
56 {0}
57};
58
59static void
60print_usage(const char *name, const char *version)
61{
62 fprintf(stderr, "Usage: %s [-c] [-v] [-h]\n"
63 " [--combine ]\n"
64 " [ --verbose ]\n" " [ --help ]\n", name);
65
66 exit(1);
67}
68
69int
70parse_counters(char *string, struct ipt_counters *ctr)
71{
72 if (string != NULL)
73 return (sscanf
74 (string, "[%llu:%llu]",
75 (unsigned long long *) &ctr->pcnt,
76 (unsigned long long *) &ctr->bcnt) == 2);
77 else
78 return (0 == 2);
79}
80
81/* global new argv and argc */
82static char *newargv[255];
83static int newargc = 0;
84
85static char *oldargv[255];
86static int oldargc = 0;
87
88/* arg meta data, were they quoted, frinstance */
89static int newargvattr[255];
90
91#define IPT_CHAIN_MAXNAMELEN IPT_TABLE_MAXNAMELEN
92char closeActionTag[IPT_TABLE_MAXNAMELEN + 1];
93char closeRuleTag[IPT_TABLE_MAXNAMELEN + 1];
94char curTable[IPT_TABLE_MAXNAMELEN + 1];
95char curChain[IPT_CHAIN_MAXNAMELEN + 1];
96
97typedef struct chain
98{
99 char *chain;
100 char *policy;
101 struct ipt_counters count;
102 int created;
103} chain;
104
105#define maxChains 10240 /* max chains per table */
106static chain chains[maxChains];
107static int nextChain = 0;
108
109/* funCtion adding one argument to newargv, updating newargc
110 * returns true if argument added, false otherwise */
111static int
112add_argv(char *what, int quoted)
113{
114 DEBUGP("add_argv: %d %s\n", newargc, what);
115 if (what && ((newargc + 1) < sizeof(newargv) / sizeof(char *))) {
116 newargv[newargc] = strdup(what);
117 newargvattr[newargc] = quoted;
118 newargc++;
119 return 1;
120 } else
121 return 0;
122}
123
124static void
125free_argv(void)
126{
127 int i;
128
129 for (i = 0; i < newargc; i++) {
130 free(newargv[i]);
131 newargv[i] = NULL;
132 }
133 newargc = 0;
134
135 for (i = 0; i < oldargc; i++) {
136 free(oldargv[i]);
137 oldargv[i] = NULL;
138 }
139 oldargc = 0;
140}
141
142/* save parsed rule for comparison with next rule
143 to perform action agregation on duplicate conditions */
144static void
145save_argv(void)
146{
147 int i;
148
149 for (i = 0; i < oldargc; i++)
150 free(oldargv[i]);
151 oldargc = newargc;
152 newargc = 0;
153 for (i = 0; i < oldargc; i++) {
154 oldargv[i] = newargv[i];
155 newargv[i] = NULL;
156 }
157}
158
159/* like puts but with xml encoding */
160static void
161xmlEncode(char *text)
162{
163 while (text && *text) {
164 if ((unsigned char) (*text) >= 127)
165 printf("&#%d;", (unsigned char) (*text));
166 else if (*text == '&')
167 printf("&amp;");
168 else if (*text == '<')
169 printf("&lt;");
170 else if (*text == '>')
171 printf("&gt;");
172 else if (*text == '"')
173 printf("&quot;");
174 else
175 putchar(*text);
176 text++;
177 }
178}
179
180/* Output text as a comment, avoiding a double hyphen */
181static void
182xmlCommentEscape(char *comment)
183{
184 int h_count = 0;
185
186 while (comment && *comment) {
187 if (*comment == '-') {
188 h_count++;
189 if (h_count >= 2) {
190 h_count = 0;
191 putchar(' ');
192 }
193 putchar('*');
194 }
195 /* strip trailing newline */
196 if (*comment == '\n' && *(comment + 1) == 0);
197 else
198 putchar(*comment);
199 comment++;
200 }
201}
202
203static void
204xmlComment(char *comment)
205{
206 printf("<!-- ");
207 xmlCommentEscape(comment);
208 printf(" -->\n");
209}
210
211static void
212xmlAttrS(char *name, char *value)
213{
214 printf("%s=\"", name);
215 xmlEncode(value);
216 printf("\" ");
217}
218
219static void
220xmlAttrI(char *name, long long int num)
221{
222 printf("%s=\"%lld\" ", name, num);
223}
224
225static void
226closeChain()
227{
228 if (curChain[0] == 0)
229 return;
230
231 if (closeActionTag[0])
232 printf("%s\n", closeActionTag);
233 closeActionTag[0] = 0;
234 if (closeRuleTag[0])
235 printf("%s\n", closeRuleTag);
236 closeRuleTag[0] = 0;
237 if (curChain[0])
238 printf(" </chain>\n");
239 curChain[0] = 0;
240 //lastRule[0]=0;
241}
242
243static void
244openChain(char *chain, char *policy, struct ipt_counters *ctr, char close)
245{
246 closeChain();
247
248 strncpy(curChain, chain, IPT_CHAIN_MAXNAMELEN);
249 curChain[IPT_CHAIN_MAXNAMELEN] = '\0';
250
251 printf(" <chain ");
252 xmlAttrS("name", curChain);
253 if (strcmp(policy, "-") != 0)
254 xmlAttrS("policy", policy);
255 xmlAttrI("packet-count", (unsigned long long) ctr->pcnt);
256 xmlAttrI("byte-count", (unsigned long long) ctr->bcnt);
257 if (close) {
258 printf("%c", close);
259 curChain[0] = 0;
260 }
261 printf(">\n");
262}
263
264static int
265existsChain(char *chain)
266{
267 /* open a saved chain */
268 int c = 0;
269
270 if (0 == strcmp(curChain, chain))
271 return 1;
272 for (c = 0; c < nextChain; c++)
273 if (chains[c].chain && strcmp(chains[c].chain, chain) == 0)
274 return 1;
275 return 0;
276}
277
278static void
279needChain(char *chain)
280{
281 /* open a saved chain */
282 int c = 0;
283
284 if (0 == strcmp(curChain, chain))
285 return;
286
287 for (c = 0; c < nextChain; c++)
288 if (chains[c].chain && strcmp(chains[c].chain, chain) == 0) {
289 openChain(chains[c].chain, chains[c].policy,
290 &(chains[c].count), '\0');
291 /* And, mark it as done so we don't create
292 an empty chain at table-end time */
293 chains[c].created = 1;
294 }
295}
296
297static void
298saveChain(char *chain, char *policy, struct ipt_counters *ctr)
299{
300 if (nextChain >= maxChains) {
301 exit_error(PARAMETER_PROBLEM,
302 "%s: line %u chain name invalid\n",
303 program_name, line);
304 exit(1);
305 };
306 chains[nextChain].chain = strdup(chain);
307 chains[nextChain].policy = strdup(policy);
308 chains[nextChain].count = *ctr;
309 chains[nextChain].created = 0;
310 nextChain++;
311}
312
313static void
314finishChains()
315{
316 int c;
317
318 for (c = 0; c < nextChain; c++)
319 if (!chains[c].created) {
320 openChain(chains[c].chain, chains[c].policy,
321 &(chains[c].count), '/');
322 free(chains[c].chain);
323 free(chains[c].policy);
324 }
325 nextChain = 0;
326}
327
328static void
329closeTable()
330{
331 closeChain();
332 finishChains();
333 if (curTable[0])
334 printf(" </table>\n");
335 curTable[0] = 0;
336}
337
338static void
339openTable(char *table)
340{
341 closeTable();
342
343 strncpy(curTable, table, IPT_TABLE_MAXNAMELEN);
344 curTable[IPT_TABLE_MAXNAMELEN] = '\0';
345
346 printf(" <table ");
347 xmlAttrS("name", curTable);
348 printf(">\n");
349}
350
351// is char* -j --jump -g or --goto
352static int
353isTarget(char *arg)
354{
355 return ((arg)
356 && (strcmp((arg), "-j") == 0 || strcmp((arg), "--jump") == 0
357 || strcmp((arg), "-g") == 0
358 || strcmp((arg), "--goto") == 0));
359}
360
361// part=-1 means do conditions, part=1 means do rules, part=0 means do both
362static void
363do_rule_part(char *leveltag1, char *leveltag2, int part, int argc,
364 char *argv[], int argvattr[])
365{
366 int arg = 1; // ignore leading -A
367 char invert_next = 0;
368 char *thisChain = NULL;
369 char *spacer = ""; // space when needed to assemble arguments
370 char *level1 = NULL;
371 char *level2 = NULL;
372 char *leveli1 = " ";
373 char *leveli2 = " ";
374
375#define CLOSE_LEVEL(LEVEL) \
376 do { \
377 if (level ## LEVEL) printf("</%s>\n", \
378 (leveltag ## LEVEL)?(leveltag ## LEVEL):(level ## LEVEL)); \
379 level ## LEVEL=NULL;\
380 } while(0)
381
382#define OPEN_LEVEL(LEVEL,TAG) \
383 do {\
384 level ## LEVEL=TAG;\
385 if (leveltag ## LEVEL) {\
386 printf("%s<%s ", (leveli ## LEVEL), \
387 (leveltag ## LEVEL));\
388 xmlAttrS("type", (TAG)); \
389 } else printf("%s<%s ", (leveli ## LEVEL), (level ## LEVEL)); \
390 } while(0)
391
392 thisChain = argv[arg++];
393
394 if (part == 1) { /* skip */
395 /* use argvattr to tell which arguments were quoted
396 to avoid comparing quoted arguments, like comments, to -j, */
397 while (arg < argc && (argvattr[arg] || !isTarget(argv[arg])))
398 arg++;
399 }
400
401 /* Before we start, if the first arg is -[^-] and not -m or -j or -g
402 then start a dummy <match> tag for old style built-in matches.
403 We would do this in any case, but no need if it would be empty */
404 if (arg < argc && argv[arg][0] == '-' && !isTarget(argv[arg])
405 && strcmp(argv[arg], "-m") != 0) {
406 OPEN_LEVEL(1, "match");
407 printf(">\n");
408 }
409 while (arg < argc) {
410 // If ! is followed by -* then apply to that else output as data
411 // Stop, if we need to
412 if (part == -1 && !argvattr[arg] && (isTarget(argv[arg]))) {
413 break;
414 } else if (!argvattr[arg] && strcmp(argv[arg], "!") == 0) {
415 if ((arg + 1) < argc && argv[arg + 1][0] == '-')
416 invert_next = '!';
417 else
418 printf("%s%s", spacer, argv[arg]);
419 spacer = " ";
420 } else if (!argvattr[arg] && isTarget(argv[arg])
421 && existsChain(argv[arg + 1])
422 && (2 + arg >= argc)) {
423 if (!((1 + arg) < argc))
424 // no args to -j, -m or -g, ignore & finish loop
425 break;
426 CLOSE_LEVEL(2);
427 if (level1)
428 printf("%s", leveli1);
429 CLOSE_LEVEL(1);
430 spacer = "";
431 invert_next = 0;
432 if (strcmp(argv[arg], "-g") == 0
433 || strcmp(argv[arg], "--goto") == 0) {
434 /* goto user chain */
435 OPEN_LEVEL(1, "goto");
436 printf(">\n");
437 arg++;
438 OPEN_LEVEL(2, argv[arg]);
439 printf("/>\n");
440 level2 = NULL;
441 } else {
442 /* call user chain */
443 OPEN_LEVEL(1, "call");
444 printf(">\n");
445 arg++;
446 OPEN_LEVEL(2, argv[arg]);
447 printf("/>\n");
448 level2 = NULL;
449 }
450 } else if (!argvattr[arg]
451 && (isTarget(argv[arg])
452 || strcmp(argv[arg], "-m") == 0
453 || strcmp(argv[arg], "--module") == 0)) {
454 if (!((1 + arg) < argc))
455 // no args to -j, -m or -g, ignore & finish loop
456 break;
457 CLOSE_LEVEL(2);
458 if (level1)
459 printf("%s", leveli1);
460 CLOSE_LEVEL(1);
461 spacer = "";
462 invert_next = 0;
463 arg++;
464 OPEN_LEVEL(1, (argv[arg]));
465 // Optimize case, can we close this tag already?
466 if ((arg + 1) >= argc || (!argvattr[arg + 1]
467 && (isTarget(argv[arg + 1])
468 || strcmp(argv[arg + 1],
469 "-m") == 0
470 || strcmp(argv[arg + 1],
471 "--module") ==
472 0))) {
473 printf(" />\n");
474 level1 = NULL;
475 } else {
476 printf(">\n");
477 }
478 } else if (!argvattr[arg] && argv[arg][0] == '-') {
479 char *tag;
480 CLOSE_LEVEL(2);
481 // Skip past any -
482 tag = argv[arg];
483 while (*tag == '-' && *tag)
484 tag++;
485
486 spacer = "";
487 OPEN_LEVEL(2, tag);
488 if (invert_next)
489 printf(" invert=\"1\"");
490 invert_next = 0;
491
492 // Optimize case, can we close this tag already?
493 if (!((arg + 1) < argc)
494 || (argv[arg + 1][0] == '-' /* NOT QUOTED */ )) {
495 printf(" />\n");
496 level2 = NULL;
497 } else {
498 printf(">");
499 }
500 } else { // regular data
501 char *spaces = strchr(argv[arg], ' ');
502 printf("%s", spacer);
503 if (spaces || argvattr[arg])
504 printf("&quot;");
505 // if argv[arg] contains a space, enclose in quotes
506 xmlEncode(argv[arg]);
507 if (spaces || argvattr[arg])
508 printf("&quot;");
509 spacer = " ";
510 }
511 arg++;
512 }
513 CLOSE_LEVEL(2);
514 if (level1)
515 printf("%s", leveli1);
516 CLOSE_LEVEL(1);
517
518 return;
519}
520
521static int
522compareRules()
523{
524 /* compare arguments up to -j or -g for match.
525 NOTE: We don't want to combine actions if there were no criteria
526 in each rule, or rules didn't have an action
527 NOTE: Depends on arguments being in some kind of "normal" order which
528 is the case when processing the ACTUAL output of actual iptables-save
529 rather than a file merely in a compatable format */
530
531 int old = 0;
532 int new = 0;
533
534 int compare = 0;
535
536 while (new < newargc && old < oldargc) {
537 if (isTarget(oldargv[old]) && isTarget(newargv[new])) {
538 compare = 1;
539 break;
540 }
541 // break when old!=new
542 if (strcmp(oldargv[old], newargv[new]) != 0) {
543 compare = 0;
544 break;
545 }
546
547 old++;
548 new++;
549 }
550 // We won't match unless both rules had a target.
551 // This means we don't combine target-less rules, which is good
552
553 return compare == 1;
554}
555
556/* has a nice parsed rule starting with -A */
557static void
558do_rule(char *pcnt, char *bcnt, int argc, char *argv[], int argvattr[])
559{
560 /* are these conditions the same as the previous rule?
561 * If so, skip arg straight to -j or -g */
562 if (combine && argc > 2 && !isTarget(argv[2]) && compareRules()) {
563 xmlComment("Combine action from next rule");
564 } else {
565
566 if (closeActionTag[0]) {
567 printf("%s\n", closeActionTag);
568 closeActionTag[0] = 0;
569 }
570 if (closeRuleTag[0]) {
571 printf("%s\n", closeRuleTag);
572 closeRuleTag[0] = 0;
573 }
574
575 printf(" <rule ");
576 //xmlAttrS("table",curTable); // not needed in full mode
577 //xmlAttrS("chain",argv[1]); // not needed in full mode
578 if (pcnt)
579 xmlAttrS("packet-count", pcnt);
580 if (bcnt)
581 xmlAttrS("byte-count", bcnt);
582 printf(">\n");
583
584 strncpy(closeRuleTag, " </rule>\n", IPT_TABLE_MAXNAMELEN);
585 closeRuleTag[IPT_TABLE_MAXNAMELEN] = '\0';
586
587 /* no point in writing out condition if there isn't one */
588 if (argc >= 3 && !isTarget(argv[2])) {
589 printf(" <conditions>\n");
590 do_rule_part(NULL, NULL, -1, argc, argv, argvattr);
591 printf(" </conditions>\n");
592 }
593 }
594 /* Write out the action */
595 //do_rule_part("action","arg",1,argc,argv,argvattr);
596 if (!closeActionTag[0]) {
597 printf(" <actions>\n");
598 strncpy(closeActionTag, " </actions>\n",
599 IPT_TABLE_MAXNAMELEN);
600 closeActionTag[IPT_TABLE_MAXNAMELEN] = '\0';
601 }
602 do_rule_part(NULL, NULL, 1, argc, argv, argvattr);
603}
604
605
606#ifdef IPTABLES_MULTI
607int
608iptables_restore_main(int argc, char *argv[])
609#else
610int
611main(int argc, char *argv[])
612#endif
613{
614 char buffer[10240];
615 int c;
616 FILE *in;
617
618 program_name = "iptables-xml";
619 program_version = IPTABLES_VERSION;
620 line = 0;
621
622 while ((c = getopt_long(argc, argv, "cvh", options, NULL)) != -1) {
623 switch (c) {
624 case 'c':
625 combine = 1;
626 break;
627 case 'v':
628 printf("xptables-xml\n");
629 verbose = 1;
630 break;
631 case 'h':
632 print_usage("iptables-xml", IPTABLES_VERSION);
633 break;
634 }
635 }
636
637 if (optind == argc - 1) {
638 in = fopen(argv[optind], "r");
639 if (!in) {
640 fprintf(stderr, "Can't open %s: %s", argv[optind],
641 strerror(errno));
642 exit(1);
643 }
644 } else if (optind < argc) {
645 fprintf(stderr, "Unknown arguments found on commandline");
646 exit(1);
647 } else
648 in = stdin;
649
650 printf("<iptables-rules version=\"1.0\">\n");
651
652 /* Grab standard input. */
653 while (fgets(buffer, sizeof(buffer), in)) {
654 int ret = 0;
655
656 line++;
657
658 if (buffer[0] == '\n')
659 continue;
660 else if (buffer[0] == '#') {
661 xmlComment(buffer);
662 continue;
663 }
664
665 if (verbose) {
666 printf("<!-- line %d ", line);
667 xmlCommentEscape(buffer);
668 printf(" -->\n");
669 }
670
671 if ((strcmp(buffer, "COMMIT\n") == 0) && (curTable[0])) {
672 DEBUGP("Calling commit\n");
673 closeTable();
674 ret = 1;
675 } else if ((buffer[0] == '*')) {
676 /* New table */
677 char *table;
678
679 table = strtok(buffer + 1, " \t\n");
680 DEBUGP("line %u, table '%s'\n", line, table);
681 if (!table) {
682 exit_error(PARAMETER_PROBLEM,
683 "%s: line %u table name invalid\n",
684 program_name, line);
685 exit(1);
686 }
687 openTable(table);
688
689 ret = 1;
690 } else if ((buffer[0] == ':') && (curTable[0])) {
691 /* New chain. */
692 char *policy, *chain;
693 struct ipt_counters count;
694 char *ctrs;
695
696 chain = strtok(buffer + 1, " \t\n");
697 DEBUGP("line %u, chain '%s'\n", line, chain);
698 if (!chain) {
699 exit_error(PARAMETER_PROBLEM,
700 "%s: line %u chain name invalid\n",
701 program_name, line);
702 exit(1);
703 }
704
705 DEBUGP("Creating new chain '%s'\n", chain);
706
707 policy = strtok(NULL, " \t\n");
708 DEBUGP("line %u, policy '%s'\n", line, policy);
709 if (!policy) {
710 exit_error(PARAMETER_PROBLEM,
711 "%s: line %u policy invalid\n",
712 program_name, line);
713 exit(1);
714 }
715
716 ctrs = strtok(NULL, " \t\n");
717 parse_counters(ctrs, &count);
718 saveChain(chain, policy, &count);
719
720 ret = 1;
721 } else if (curTable[0]) {
722 int a;
723 char *ptr = buffer;
724 char *pcnt = NULL;
725 char *bcnt = NULL;
726 char *parsestart;
727 char *chain = NULL;
728
729 /* the parser */
730 char *param_start, *curchar;
731 int quote_open, quoted;
732
733 /* reset the newargv */
734 newargc = 0;
735
736 if (buffer[0] == '[') {
737 /* we have counters in our input */
738 ptr = strchr(buffer, ']');
739 if (!ptr)
740 exit_error(PARAMETER_PROBLEM,
741 "Bad line %u: need ]\n",
742 line);
743
744 pcnt = strtok(buffer + 1, ":");
745 if (!pcnt)
746 exit_error(PARAMETER_PROBLEM,
747 "Bad line %u: need :\n",
748 line);
749
750 bcnt = strtok(NULL, "]");
751 if (!bcnt)
752 exit_error(PARAMETER_PROBLEM,
753 "Bad line %u: need ]\n",
754 line);
755
756 /* start command parsing after counter */
757 parsestart = ptr + 1;
758 } else {
759 /* start command parsing at start of line */
760 parsestart = buffer;
761 }
762
763
764 /* This is a 'real' parser crafted in artist mode
765 * not hacker mode. If the author can live with that
766 * then so can everyone else */
767
768 quote_open = 0;
769 /* We need to know which args were quoted so we
770 can preserve quote */
771 quoted = 0;
772 param_start = parsestart;
773
774 for (curchar = parsestart; *curchar; curchar++) {
775 if (*curchar == '"') {
776 /* quote_open cannot be true if there
777 * was no previous character. Thus,
778 * curchar-1 has to be within bounds */
779 if (quote_open &&
780 *(curchar - 1) != '\\') {
781 quote_open = 0;
782 *curchar = ' ';
783 } else {
784 quote_open = 1;
785 quoted = 1;
786 param_start++;
787 }
788 }
789 if (*curchar == ' '
790 || *curchar == '\t' || *curchar == '\n') {
791 char param_buffer[1024];
792 int param_len = curchar - param_start;
793
794 if (quote_open)
795 continue;
796
797 if (!param_len) {
798 /* two spaces? */
799 param_start++;
800 continue;
801 }
802
803 /* end of one parameter */
804 strncpy(param_buffer, param_start,
805 param_len);
806 *(param_buffer + param_len) = '\0';
807
808 /* check if table name specified */
809 if (!strncmp(param_buffer, "-t", 3)
810 || !strncmp(param_buffer,
811 "--table", 8)) {
812 exit_error(PARAMETER_PROBLEM,
813 "Line %u seems to have a "
814 "-t table option.\n",
815 line);
816 exit(1);
817 }
818
819 add_argv(param_buffer, quoted);
820 if (newargc >= 2
821 && 0 ==
822 strcmp(newargv[newargc - 2], "-A"))
823 chain = newargv[newargc - 1];
824 quoted = 0;
825 param_start += param_len + 1;
826 } else {
827 /* regular character, skip */
828 }
829 }
830
831 DEBUGP("calling do_command(%u, argv, &%s, handle):\n",
832 newargc, curTable);
833
834 for (a = 0; a < newargc; a++)
835 DEBUGP("argv[%u]: %s\n", a, newargv[a]);
836
837 needChain(chain);// Should we explicitly look for -A
838 do_rule(pcnt, bcnt, newargc, newargv, newargvattr);
839
840 save_argv();
841 ret = 1;
842 }
843 if (!ret) {
844 fprintf(stderr, "%s: line %u failed\n",
845 program_name, line);
846 exit(1);
847 }
848 }
849 if (curTable[0]) {
850 fprintf(stderr, "%s: COMMIT expected at line %u\n",
851 program_name, line + 1);
852 exit(1);
853 }
854
855 printf("</iptables-rules>\n");
856 free_argv();
857
858 return 0;
859}