manpages: do not include v4-only modules in ip6tables manpage
[jleu-iptables.git] / iptables-xml.c
blobe5d194197823366e053cec3ceaed8309fa2d0074
1 /* 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>
6 * This code is distributed under the terms of GNU GPL v2
8 * $Id: iptables-xml.c,v 1.4 2006/11/09 12:02:17 azez Exp $
9 */
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 #include "iptables-multi.h"
20 #include <xtables.h>
22 #ifdef DEBUG
23 #define DEBUGP(x, args...) fprintf(stderr, x, ## args)
24 #else
25 #define DEBUGP(x, args...)
26 #endif
28 #ifndef IPTABLES_MULTI
29 int line = 0;
30 #endif
32 struct xtables_globals iptables_xml_globals = {
33 .option_offset = 0,
34 .program_version = IPTABLES_VERSION,
35 .program_name = "iptables-xml",
37 #define prog_name iptables_xml_globals.program_name
38 #define prog_vers iptables_xml_globals.program_version
40 static void print_usage(const char *name, const char *version)
41 __attribute__ ((noreturn));
43 static int verbose = 0;
44 /* Whether to combine actions of sequential rules with identical conditions */
45 static int combine = 0;
46 /* Keeping track of external matches and targets. */
47 static struct option options[] = {
48 {"verbose", 0, NULL, 'v'},
49 {"combine", 0, NULL, 'c'},
50 {"help", 0, NULL, 'h'},
51 { .name = NULL }
54 static void
55 print_usage(const char *name, const char *version)
57 fprintf(stderr, "Usage: %s [-c] [-v] [-h]\n"
58 " [--combine ]\n"
59 " [ --verbose ]\n" " [ --help ]\n", name);
61 exit(1);
64 static int
65 parse_counters(char *string, struct ipt_counters *ctr)
67 u_int64_t *pcnt, *bcnt;
69 if (string != NULL) {
70 pcnt = &ctr->pcnt;
71 bcnt = &ctr->bcnt;
72 return (sscanf
73 (string, "[%llu:%llu]",
74 (unsigned long long *)pcnt,
75 (unsigned long long *)bcnt) == 2);
76 } else
77 return (0 == 2);
80 /* global new argv and argc */
81 static char *newargv[255];
82 static unsigned int newargc = 0;
84 static char *oldargv[255];
85 static unsigned int oldargc = 0;
87 /* arg meta data, were they quoted, frinstance */
88 static int newargvattr[255];
90 #define IPT_CHAIN_MAXNAMELEN IPT_TABLE_MAXNAMELEN
91 static char closeActionTag[IPT_TABLE_MAXNAMELEN + 1];
92 static char closeRuleTag[IPT_TABLE_MAXNAMELEN + 1];
93 static char curTable[IPT_TABLE_MAXNAMELEN + 1];
94 static char curChain[IPT_CHAIN_MAXNAMELEN + 1];
96 struct chain {
97 char *chain;
98 char *policy;
99 struct ipt_counters count;
100 int created;
103 #define maxChains 10240 /* max chains per table */
104 static struct chain chains[maxChains];
105 static int nextChain = 0;
107 /* funCtion adding one argument to newargv, updating newargc
108 * returns true if argument added, false otherwise */
109 static int
110 add_argv(char *what, int quoted)
112 DEBUGP("add_argv: %d %s\n", newargc, what);
113 if (what && newargc + 1 < ARRAY_SIZE(newargv)) {
114 newargv[newargc] = strdup(what);
115 newargvattr[newargc] = quoted;
116 newargc++;
117 return 1;
118 } else
119 return 0;
122 static void
123 free_argv(void)
125 unsigned int i;
127 for (i = 0; i < newargc; i++) {
128 free(newargv[i]);
129 newargv[i] = NULL;
131 newargc = 0;
133 for (i = 0; i < oldargc; i++) {
134 free(oldargv[i]);
135 oldargv[i] = NULL;
137 oldargc = 0;
140 /* save parsed rule for comparison with next rule
141 to perform action agregation on duplicate conditions */
142 static void
143 save_argv(void)
145 unsigned int i;
147 for (i = 0; i < oldargc; i++)
148 free(oldargv[i]);
149 oldargc = newargc;
150 newargc = 0;
151 for (i = 0; i < oldargc; i++) {
152 oldargv[i] = newargv[i];
153 newargv[i] = NULL;
157 /* like puts but with xml encoding */
158 static void
159 xmlEncode(char *text)
161 while (text && *text) {
162 if ((unsigned char) (*text) >= 127)
163 printf("&#%d;", (unsigned char) (*text));
164 else if (*text == '&')
165 printf("&amp;");
166 else if (*text == '<')
167 printf("&lt;");
168 else if (*text == '>')
169 printf("&gt;");
170 else if (*text == '"')
171 printf("&quot;");
172 else
173 putchar(*text);
174 text++;
178 /* Output text as a comment, avoiding a double hyphen */
179 static void
180 xmlCommentEscape(char *comment)
182 int h_count = 0;
184 while (comment && *comment) {
185 if (*comment == '-') {
186 h_count++;
187 if (h_count >= 2) {
188 h_count = 0;
189 putchar(' ');
191 putchar('*');
193 /* strip trailing newline */
194 if (*comment == '\n' && *(comment + 1) == 0);
195 else
196 putchar(*comment);
197 comment++;
201 static void
202 xmlComment(char *comment)
204 printf("<!-- ");
205 xmlCommentEscape(comment);
206 printf(" -->\n");
209 static void
210 xmlAttrS(char *name, char *value)
212 printf("%s=\"", name);
213 xmlEncode(value);
214 printf("\" ");
217 static void
218 xmlAttrI(char *name, long long int num)
220 printf("%s=\"%lld\" ", name, num);
223 static void
224 closeChain(void)
226 if (curChain[0] == 0)
227 return;
229 if (closeActionTag[0])
230 printf("%s\n", closeActionTag);
231 closeActionTag[0] = 0;
232 if (closeRuleTag[0])
233 printf("%s\n", closeRuleTag);
234 closeRuleTag[0] = 0;
235 if (curChain[0])
236 printf(" </chain>\n");
237 curChain[0] = 0;
238 //lastRule[0]=0;
241 static void
242 openChain(char *chain, char *policy, struct ipt_counters *ctr, char close)
244 closeChain();
246 strncpy(curChain, chain, IPT_CHAIN_MAXNAMELEN);
247 curChain[IPT_CHAIN_MAXNAMELEN] = '\0';
249 printf(" <chain ");
250 xmlAttrS("name", curChain);
251 if (strcmp(policy, "-") != 0)
252 xmlAttrS("policy", policy);
253 xmlAttrI("packet-count", (unsigned long long) ctr->pcnt);
254 xmlAttrI("byte-count", (unsigned long long) ctr->bcnt);
255 if (close) {
256 printf("%c", close);
257 curChain[0] = 0;
259 printf(">\n");
262 static int
263 existsChain(char *chain)
265 /* open a saved chain */
266 int c = 0;
268 if (0 == strcmp(curChain, chain))
269 return 1;
270 for (c = 0; c < nextChain; c++)
271 if (chains[c].chain && strcmp(chains[c].chain, chain) == 0)
272 return 1;
273 return 0;
276 static void
277 needChain(char *chain)
279 /* open a saved chain */
280 int c = 0;
282 if (0 == strcmp(curChain, chain))
283 return;
285 for (c = 0; c < nextChain; c++)
286 if (chains[c].chain && strcmp(chains[c].chain, chain) == 0) {
287 openChain(chains[c].chain, chains[c].policy,
288 &(chains[c].count), '\0');
289 /* And, mark it as done so we don't create
290 an empty chain at table-end time */
291 chains[c].created = 1;
295 static void
296 saveChain(char *chain, char *policy, struct ipt_counters *ctr)
298 if (nextChain >= maxChains) {
299 xtables_error(PARAMETER_PROBLEM,
300 "%s: line %u chain name invalid\n",
301 prog_name, line);
302 exit(1);
304 chains[nextChain].chain = strdup(chain);
305 chains[nextChain].policy = strdup(policy);
306 chains[nextChain].count = *ctr;
307 chains[nextChain].created = 0;
308 nextChain++;
311 static void
312 finishChains(void)
314 int c;
316 for (c = 0; c < nextChain; c++)
317 if (!chains[c].created) {
318 openChain(chains[c].chain, chains[c].policy,
319 &(chains[c].count), '/');
320 free(chains[c].chain);
321 free(chains[c].policy);
323 nextChain = 0;
326 static void
327 closeTable(void)
329 closeChain();
330 finishChains();
331 if (curTable[0])
332 printf(" </table>\n");
333 curTable[0] = 0;
336 static void
337 openTable(char *table)
339 closeTable();
341 strncpy(curTable, table, IPT_TABLE_MAXNAMELEN);
342 curTable[IPT_TABLE_MAXNAMELEN] = '\0';
344 printf(" <table ");
345 xmlAttrS("name", curTable);
346 printf(">\n");
349 // is char* -j --jump -g or --goto
350 static int
351 isTarget(char *arg)
353 return ((arg)
354 && (strcmp((arg), "-j") == 0 || strcmp((arg), "--jump") == 0
355 || strcmp((arg), "-g") == 0
356 || strcmp((arg), "--goto") == 0));
359 // is it a terminating target like -j ACCEPT, etc
360 // (or I guess -j SNAT in nat table, but we don't check for that yet
361 static int
362 isTerminatingTarget(char *arg)
364 return ((arg)
365 && (strcmp((arg), "ACCEPT") == 0
366 || strcmp((arg), "DROP") == 0
367 || strcmp((arg), "QUEUE") == 0
368 || strcmp((arg), "RETURN") == 0));
371 // part=-1 means do conditions, part=1 means do rules, part=0 means do both
372 static void
373 do_rule_part(char *leveltag1, char *leveltag2, int part, int argc,
374 char *argv[], int argvattr[])
376 int arg = 1; // ignore leading -A
377 char invert_next = 0;
378 char *thisChain = NULL;
379 char *spacer = ""; // space when needed to assemble arguments
380 char *level1 = NULL;
381 char *level2 = NULL;
382 char *leveli1 = " ";
383 char *leveli2 = " ";
385 #define CLOSE_LEVEL(LEVEL) \
386 do { \
387 if (level ## LEVEL) printf("</%s>\n", \
388 (leveltag ## LEVEL)?(leveltag ## LEVEL):(level ## LEVEL)); \
389 level ## LEVEL=NULL;\
390 } while(0)
392 #define OPEN_LEVEL(LEVEL,TAG) \
393 do {\
394 level ## LEVEL=TAG;\
395 if (leveltag ## LEVEL) {\
396 printf("%s<%s ", (leveli ## LEVEL), \
397 (leveltag ## LEVEL));\
398 xmlAttrS("type", (TAG)); \
399 } else printf("%s<%s ", (leveli ## LEVEL), (level ## LEVEL)); \
400 } while(0)
402 thisChain = argv[arg++];
404 if (part == 1) { /* skip */
405 /* use argvattr to tell which arguments were quoted
406 to avoid comparing quoted arguments, like comments, to -j, */
407 while (arg < argc && (argvattr[arg] || !isTarget(argv[arg])))
408 arg++;
411 /* Before we start, if the first arg is -[^-] and not -m or -j or -g
412 then start a dummy <match> tag for old style built-in matches.
413 We would do this in any case, but no need if it would be empty */
414 if (arg < argc && argv[arg][0] == '-' && !isTarget(argv[arg])
415 && strcmp(argv[arg], "-m") != 0) {
416 OPEN_LEVEL(1, "match");
417 printf(">\n");
419 while (arg < argc) {
420 // If ! is followed by -* then apply to that else output as data
421 // Stop, if we need to
422 if (part == -1 && !argvattr[arg] && (isTarget(argv[arg]))) {
423 break;
424 } else if (!argvattr[arg] && strcmp(argv[arg], "!") == 0) {
425 if ((arg + 1) < argc && argv[arg + 1][0] == '-')
426 invert_next = '!';
427 else
428 printf("%s%s", spacer, argv[arg]);
429 spacer = " ";
430 } else if (!argvattr[arg] && isTarget(argv[arg])
431 && existsChain(argv[arg + 1])
432 && (2 + arg >= argc)) {
433 if (!((1 + arg) < argc))
434 // no args to -j, -m or -g, ignore & finish loop
435 break;
436 CLOSE_LEVEL(2);
437 if (level1)
438 printf("%s", leveli1);
439 CLOSE_LEVEL(1);
440 spacer = "";
441 invert_next = 0;
442 if (strcmp(argv[arg], "-g") == 0
443 || strcmp(argv[arg], "--goto") == 0) {
444 /* goto user chain */
445 OPEN_LEVEL(1, "goto");
446 printf(">\n");
447 arg++;
448 OPEN_LEVEL(2, argv[arg]);
449 printf("/>\n");
450 level2 = NULL;
451 } else {
452 /* call user chain */
453 OPEN_LEVEL(1, "call");
454 printf(">\n");
455 arg++;
456 OPEN_LEVEL(2, argv[arg]);
457 printf("/>\n");
458 level2 = NULL;
460 } else if (!argvattr[arg]
461 && (isTarget(argv[arg])
462 || strcmp(argv[arg], "-m") == 0
463 || strcmp(argv[arg], "--module") == 0)) {
464 if (!((1 + arg) < argc))
465 // no args to -j, -m or -g, ignore & finish loop
466 break;
467 CLOSE_LEVEL(2);
468 if (level1)
469 printf("%s", leveli1);
470 CLOSE_LEVEL(1);
471 spacer = "";
472 invert_next = 0;
473 arg++;
474 OPEN_LEVEL(1, (argv[arg]));
475 // Optimize case, can we close this tag already?
476 if ((arg + 1) >= argc || (!argvattr[arg + 1]
477 && (isTarget(argv[arg + 1])
478 || strcmp(argv[arg + 1],
479 "-m") == 0
480 || strcmp(argv[arg + 1],
481 "--module") ==
482 0))) {
483 printf(" />\n");
484 level1 = NULL;
485 } else {
486 printf(">\n");
488 } else if (!argvattr[arg] && argv[arg][0] == '-') {
489 char *tag;
490 CLOSE_LEVEL(2);
491 // Skip past any -
492 tag = argv[arg];
493 while (*tag == '-' && *tag)
494 tag++;
496 spacer = "";
497 OPEN_LEVEL(2, tag);
498 if (invert_next)
499 printf(" invert=\"1\"");
500 invert_next = 0;
502 // Optimize case, can we close this tag already?
503 if (!((arg + 1) < argc)
504 || (argv[arg + 1][0] == '-' /* NOT QUOTED */ )) {
505 printf(" />\n");
506 level2 = NULL;
507 } else {
508 printf(">");
510 } else { // regular data
511 char *spaces = strchr(argv[arg], ' ');
512 printf("%s", spacer);
513 if (spaces || argvattr[arg])
514 printf("&quot;");
515 // if argv[arg] contains a space, enclose in quotes
516 xmlEncode(argv[arg]);
517 if (spaces || argvattr[arg])
518 printf("&quot;");
519 spacer = " ";
521 arg++;
523 CLOSE_LEVEL(2);
524 if (level1)
525 printf("%s", leveli1);
526 CLOSE_LEVEL(1);
529 static int
530 compareRules(void)
532 /* compare arguments up to -j or -g for match.
533 NOTE: We don't want to combine actions if there were no criteria
534 in each rule, or rules didn't have an action
535 NOTE: Depends on arguments being in some kind of "normal" order which
536 is the case when processing the ACTUAL output of actual iptables-save
537 rather than a file merely in a compatable format */
539 unsigned int old = 0;
540 unsigned int new = 0;
542 int compare = 0;
544 while (new < newargc && old < oldargc) {
545 if (isTarget(oldargv[old]) && isTarget(newargv[new])) {
546 /* if oldarg was a terminating action then it makes no sense
547 * to combine further actions into the same xml */
548 if (((strcmp((oldargv[old]), "-j") == 0
549 || strcmp((oldargv[old]), "--jump") == 0)
550 && old+1 < oldargc
551 && isTerminatingTarget(oldargv[old+1]) )
552 || strcmp((oldargv[old]), "-g") == 0
553 || strcmp((oldargv[old]), "--goto") == 0 ) {
554 /* Previous rule had terminating action */
555 compare = 0;
556 } else {
557 compare = 1;
559 break;
561 // break when old!=new
562 if (strcmp(oldargv[old], newargv[new]) != 0) {
563 compare = 0;
564 break;
567 old++;
568 new++;
570 // We won't match unless both rules had a target.
571 // This means we don't combine target-less rules, which is good
573 return compare == 1;
576 /* has a nice parsed rule starting with -A */
577 static void
578 do_rule(char *pcnt, char *bcnt, int argc, char *argv[], int argvattr[])
580 /* are these conditions the same as the previous rule?
581 * If so, skip arg straight to -j or -g */
582 if (combine && argc > 2 && !isTarget(argv[2]) && compareRules()) {
583 xmlComment("Combine action from next rule");
584 } else {
586 if (closeActionTag[0]) {
587 printf("%s\n", closeActionTag);
588 closeActionTag[0] = 0;
590 if (closeRuleTag[0]) {
591 printf("%s\n", closeRuleTag);
592 closeRuleTag[0] = 0;
595 printf(" <rule ");
596 //xmlAttrS("table",curTable); // not needed in full mode
597 //xmlAttrS("chain",argv[1]); // not needed in full mode
598 if (pcnt)
599 xmlAttrS("packet-count", pcnt);
600 if (bcnt)
601 xmlAttrS("byte-count", bcnt);
602 printf(">\n");
604 strncpy(closeRuleTag, " </rule>\n", IPT_TABLE_MAXNAMELEN);
605 closeRuleTag[IPT_TABLE_MAXNAMELEN] = '\0';
607 /* no point in writing out condition if there isn't one */
608 if (argc >= 3 && !isTarget(argv[2])) {
609 printf(" <conditions>\n");
610 do_rule_part(NULL, NULL, -1, argc, argv, argvattr);
611 printf(" </conditions>\n");
614 /* Write out the action */
615 //do_rule_part("action","arg",1,argc,argv,argvattr);
616 if (!closeActionTag[0]) {
617 printf(" <actions>\n");
618 strncpy(closeActionTag, " </actions>\n",
619 IPT_TABLE_MAXNAMELEN);
620 closeActionTag[IPT_TABLE_MAXNAMELEN] = '\0';
622 do_rule_part(NULL, NULL, 1, argc, argv, argvattr);
625 #ifdef IPTABLES_MULTI
627 iptables_xml_main(int argc, char *argv[])
628 #else
630 main(int argc, char *argv[])
631 #endif
633 char buffer[10240];
634 int c;
635 FILE *in;
637 line = 0;
639 xtables_set_params(&iptables_xml_globals);
640 while ((c = getopt_long(argc, argv, "cvh", options, NULL)) != -1) {
641 switch (c) {
642 case 'c':
643 combine = 1;
644 break;
645 case 'v':
646 printf("xptables-xml\n");
647 verbose = 1;
648 break;
649 case 'h':
650 print_usage("iptables-xml", IPTABLES_VERSION);
651 break;
655 if (optind == argc - 1) {
656 in = fopen(argv[optind], "r");
657 if (!in) {
658 fprintf(stderr, "Can't open %s: %s", argv[optind],
659 strerror(errno));
660 exit(1);
662 } else if (optind < argc) {
663 fprintf(stderr, "Unknown arguments found on commandline");
664 exit(1);
665 } else
666 in = stdin;
668 printf("<iptables-rules version=\"1.0\">\n");
670 /* Grab standard input. */
671 while (fgets(buffer, sizeof(buffer), in)) {
672 int ret = 0;
674 line++;
676 if (buffer[0] == '\n')
677 continue;
678 else if (buffer[0] == '#') {
679 xmlComment(buffer);
680 continue;
683 if (verbose) {
684 printf("<!-- line %d ", line);
685 xmlCommentEscape(buffer);
686 printf(" -->\n");
689 if ((strcmp(buffer, "COMMIT\n") == 0) && (curTable[0])) {
690 DEBUGP("Calling commit\n");
691 closeTable();
692 ret = 1;
693 } else if ((buffer[0] == '*')) {
694 /* New table */
695 char *table;
697 table = strtok(buffer + 1, " \t\n");
698 DEBUGP("line %u, table '%s'\n", line, table);
699 if (!table) {
700 xtables_error(PARAMETER_PROBLEM,
701 "%s: line %u table name invalid\n",
702 prog_name, line);
703 exit(1);
705 openTable(table);
707 ret = 1;
708 } else if ((buffer[0] == ':') && (curTable[0])) {
709 /* New chain. */
710 char *policy, *chain;
711 struct ipt_counters count;
712 char *ctrs;
714 chain = strtok(buffer + 1, " \t\n");
715 DEBUGP("line %u, chain '%s'\n", line, chain);
716 if (!chain) {
717 xtables_error(PARAMETER_PROBLEM,
718 "%s: line %u chain name invalid\n",
719 prog_name, line);
720 exit(1);
723 DEBUGP("Creating new chain '%s'\n", chain);
725 policy = strtok(NULL, " \t\n");
726 DEBUGP("line %u, policy '%s'\n", line, policy);
727 if (!policy) {
728 xtables_error(PARAMETER_PROBLEM,
729 "%s: line %u policy invalid\n",
730 prog_name, line);
731 exit(1);
734 ctrs = strtok(NULL, " \t\n");
735 parse_counters(ctrs, &count);
736 saveChain(chain, policy, &count);
738 ret = 1;
739 } else if (curTable[0]) {
740 unsigned int a;
741 char *ptr = buffer;
742 char *pcnt = NULL;
743 char *bcnt = NULL;
744 char *parsestart;
745 char *chain = NULL;
747 /* the parser */
748 char *param_start, *curchar;
749 int quote_open, quoted;
751 /* reset the newargv */
752 newargc = 0;
754 if (buffer[0] == '[') {
755 /* we have counters in our input */
756 ptr = strchr(buffer, ']');
757 if (!ptr)
758 xtables_error(PARAMETER_PROBLEM,
759 "Bad line %u: need ]\n",
760 line);
762 pcnt = strtok(buffer + 1, ":");
763 if (!pcnt)
764 xtables_error(PARAMETER_PROBLEM,
765 "Bad line %u: need :\n",
766 line);
768 bcnt = strtok(NULL, "]");
769 if (!bcnt)
770 xtables_error(PARAMETER_PROBLEM,
771 "Bad line %u: need ]\n",
772 line);
774 /* start command parsing after counter */
775 parsestart = ptr + 1;
776 } else {
777 /* start command parsing at start of line */
778 parsestart = buffer;
782 /* This is a 'real' parser crafted in artist mode
783 * not hacker mode. If the author can live with that
784 * then so can everyone else */
786 quote_open = 0;
787 /* We need to know which args were quoted so we
788 can preserve quote */
789 quoted = 0;
790 param_start = parsestart;
792 for (curchar = parsestart; *curchar; curchar++) {
793 if (*curchar == '"') {
794 /* quote_open cannot be true if there
795 * was no previous character. Thus,
796 * curchar-1 has to be within bounds */
797 if (quote_open &&
798 *(curchar - 1) != '\\') {
799 quote_open = 0;
800 *curchar = ' ';
801 } else {
802 quote_open = 1;
803 quoted = 1;
804 param_start++;
807 if (*curchar == ' '
808 || *curchar == '\t' || *curchar == '\n') {
809 char param_buffer[1024];
810 int param_len = curchar - param_start;
812 if (quote_open)
813 continue;
815 if (!param_len) {
816 /* two spaces? */
817 param_start++;
818 continue;
821 /* end of one parameter */
822 strncpy(param_buffer, param_start,
823 param_len);
824 *(param_buffer + param_len) = '\0';
826 /* check if table name specified */
827 if (!strncmp(param_buffer, "-t", 3)
828 || !strncmp(param_buffer,
829 "--table", 8)) {
830 xtables_error(PARAMETER_PROBLEM,
831 "Line %u seems to have a "
832 "-t table option.\n",
833 line);
834 exit(1);
837 add_argv(param_buffer, quoted);
838 if (newargc >= 2
839 && 0 ==
840 strcmp(newargv[newargc - 2], "-A"))
841 chain = newargv[newargc - 1];
842 quoted = 0;
843 param_start += param_len + 1;
844 } else {
845 /* regular character, skip */
849 DEBUGP("calling do_command(%u, argv, &%s, handle):\n",
850 newargc, curTable);
852 for (a = 0; a < newargc; a++)
853 DEBUGP("argv[%u]: %s\n", a, newargv[a]);
855 needChain(chain);// Should we explicitly look for -A
856 do_rule(pcnt, bcnt, newargc, newargv, newargvattr);
858 save_argv();
859 ret = 1;
861 if (!ret) {
862 fprintf(stderr, "%s: line %u failed\n",
863 prog_name, line);
864 exit(1);
867 if (curTable[0]) {
868 fprintf(stderr, "%s: COMMIT expected at line %u\n",
869 prog_name, line + 1);
870 exit(1);
873 printf("</iptables-rules>\n");
874 free_argv();
876 return 0;