summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHenrik Nordstrom <henrik@henriknordstrom.net>2008-05-13 13:08:26 +0200
committerPatrick McHardy <kaber@trash.net>2008-05-13 13:08:26 +0200
commit96296cfb7e01298234c7fa9403619f50391620d1 (patch)
tree8ae6e4457a0d70a40b573b606488744a62a7a3bb
parent48c1bc6fa2f3aa755e89518054ef3f612d7b2c1f (diff)
iptables --list-rules command
Adds iptables --list-rules (-S) command, acting as a combination of iptables --list and iptables-save. The primary motivation behind this patch is to get iptables-save like output capabilities in iptables-restore, allowing "iptables-restore -n" to be used as a consistent API to iptables for all kind of operations, not only blind updates.. As a bonus iptables also gets the capability of printing the rules as-is. This completely replaces the earlier patch which added the --rules option. Henrik Nordstrom <henrik@henriknordstrom.net>
-rw-r--r--include/ip6tables.h1
-rw-r--r--include/iptables.h2
-rw-r--r--ip6tables-save.c193
-rw-r--r--ip6tables.8.in5
-rw-r--r--ip6tables.c280
-rw-r--r--iptables-save.c205
-rw-r--r--iptables.8.in5
-rw-r--r--iptables.c289
8 files changed, 551 insertions, 429 deletions
diff --git a/include/ip6tables.h b/include/ip6tables.h
index 0bed11f8..077fee92 100644
--- a/include/ip6tables.h
+++ b/include/ip6tables.h
@@ -22,5 +22,6 @@ extern int do_command6(int argc, char *argv[], char **table,
extern int for_each_chain(int (*fn)(const ip6t_chainlabel, int, ip6tc_handle_t *), int verbose, int builtinstoo, ip6tc_handle_t *handle);
extern int flush_entries(const ip6t_chainlabel chain, int verbose, ip6tc_handle_t *handle);
extern int delete_chain(const ip6t_chainlabel chain, int verbose, ip6tc_handle_t *handle);
+void print_rule(const struct ip6t_entry *e, ip6tc_handle_t *h, const char *chain, int counters);
#endif /*_IP6TABLES_USER_H*/
diff --git a/include/iptables.h b/include/iptables.h
index e5f8b400..ecc7168f 100644
--- a/include/iptables.h
+++ b/include/iptables.h
@@ -23,6 +23,8 @@ extern int flush_entries(const ipt_chainlabel chain, int verbose,
iptc_handle_t *handle);
extern int for_each_chain(int (*fn)(const ipt_chainlabel, int, iptc_handle_t *),
int verbose, int builtinstoo, iptc_handle_t *handle);
+extern void print_rule(const struct ipt_entry *e,
+ iptc_handle_t *handle, const char *chain, int counters);
/* kernel revision handling */
extern int kernel_version;
diff --git a/ip6tables-save.c b/ip6tables-save.c
index bd4ccbfe..7b03d7c2 100644
--- a/ip6tables-save.c
+++ b/ip6tables-save.c
@@ -33,199 +33,6 @@ static const struct option options[] = {
};
-/* This assumes that mask is contiguous, and byte-bounded. */
-static void
-print_iface(char letter, const char *iface, const unsigned char *mask,
- int invert)
-{
- unsigned int i;
-
- if (mask[0] == 0)
- return;
-
- printf("-%c %s", letter, invert ? "! " : "");
-
- for (i = 0; i < IFNAMSIZ; i++) {
- if (mask[i] != 0) {
- if (iface[i] != '\0')
- printf("%c", iface[i]);
- } else {
- /* we can access iface[i-1] here, because
- * a few lines above we make sure that mask[0] != 0 */
- if (iface[i-1] != '\0')
- printf("+");
- break;
- }
- }
-
- printf(" ");
-}
-
-/* These are hardcoded backups in ip6tables.c, so they are safe */
-struct pprot {
- char *name;
- u_int8_t num;
-};
-
-static const struct pprot chain_protos[] = {
- { "tcp", IPPROTO_TCP },
- { "udp", IPPROTO_UDP },
- { "icmpv6", IPPROTO_ICMPV6 },
- { "esp", IPPROTO_ESP },
- { "ah", IPPROTO_AH },
-};
-
-/* The ip6tables looks up the /etc/protocols. */
-static void print_proto(u_int16_t proto, int invert)
-{
- if (proto) {
- unsigned int i;
- const char *invertstr = invert ? "! " : "";
-
- struct protoent *pent = getprotobynumber(proto);
- if (pent) {
- printf("-p %s%s ",
- invertstr, pent->p_name);
- return;
- }
-
- for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
- if (chain_protos[i].num == proto) {
- printf("-p %s%s ",
- invertstr, chain_protos[i].name);
- return;
- }
-
- printf("-p %s%u ", invertstr, proto);
- }
-}
-
-static int print_match(const struct ip6t_entry_match *e,
- const struct ip6t_ip6 *ip)
-{
- struct xtables_match *match
- = find_match(e->u.user.name, TRY_LOAD, NULL);
-
- if (match) {
- printf("-m %s ", e->u.user.name);
-
- /* some matches don't provide a save function */
- if (match->save)
- match->save(ip, e);
- } else {
- if (e->u.match_size) {
- fprintf(stderr,
- "Can't find library for match `%s'\n",
- e->u.user.name);
- exit(1);
- }
- }
- return 0;
-}
-
-/* print a given ip including mask if neccessary */
-static void print_ip(char *prefix, const struct in6_addr *ip, const struct in6_addr *mask, int invert)
-{
- char buf[51];
- int l = ipv6_prefix_length(mask);
-
- if (l == 0 && !invert)
- return;
-
- printf("%s %s%s",
- prefix,
- invert ? "! " : "",
- inet_ntop(AF_INET6, ip, buf, sizeof buf));
-
- if (l == -1)
- printf("/%s ", inet_ntop(AF_INET6, mask, buf, sizeof buf));
- else
- printf("/%d ", l);
-}
-
-/* We want this to be readable, so only print out neccessary fields.
- * Because that's the kind of world I want to live in. */
-static void print_rule(const struct ip6t_entry *e,
- ip6tc_handle_t *h, const char *chain, int counters)
-{
- struct ip6t_entry_target *t;
- const char *target_name;
-
- /* print counters */
- if (counters)
- printf("[%llu:%llu] ", (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
-
- /* print chain name */
- printf("-A %s ", chain);
-
- /* Print IP part. */
- print_ip("-s", &(e->ipv6.src), &(e->ipv6.smsk),
- e->ipv6.invflags & IP6T_INV_SRCIP);
-
- print_ip("-d", &(e->ipv6.dst), &(e->ipv6.dmsk),
- e->ipv6.invflags & IP6T_INV_DSTIP);
-
- print_iface('i', e->ipv6.iniface, e->ipv6.iniface_mask,
- e->ipv6.invflags & IP6T_INV_VIA_IN);
-
- print_iface('o', e->ipv6.outiface, e->ipv6.outiface_mask,
- e->ipv6.invflags & IP6T_INV_VIA_OUT);
-
- print_proto(e->ipv6.proto, e->ipv6.invflags & IP6T_INV_PROTO);
-
-#if 0
- /* not definied in ipv6
- * FIXME: linux/netfilter_ipv6/ip6_tables: IP6T_INV_FRAG why definied? */
- if (e->ipv6.flags & IPT_F_FRAG)
- printf("%s-f ",
- e->ipv6.invflags & IP6T_INV_FRAG ? "! " : "");
-#endif
-
- if (e->ipv6.flags & IP6T_F_TOS)
- printf("%s-? %d ",
- e->ipv6.invflags & IP6T_INV_TOS ? "! " : "",
- e->ipv6.tos);
-
- /* Print matchinfo part */
- if (e->target_offset) {
- IP6T_MATCH_ITERATE(e, print_match, &e->ipv6);
- }
-
- /* Print target name */
- target_name = ip6tc_get_target(e, h);
- if (target_name && (*target_name != '\0'))
- printf("-j %s ", target_name);
-
- /* Print targinfo part */
- t = ip6t_get_target((struct ip6t_entry *)e);
- if (t->u.user.name[0]) {
- struct xtables_target *target
- = find_target(t->u.user.name, TRY_LOAD);
-
- if (!target) {
- fprintf(stderr, "Can't find library for target `%s'\n",
- t->u.user.name);
- exit(1);
- }
-
- if (target->save)
- target->save(&e->ipv6, t);
- else {
- /* If the target size is greater than ip6t_entry_target
- * there is something to be saved, we just don't know
- * how to print it */
- if (t->u.target_size !=
- sizeof(struct ip6t_entry_target)) {
- fprintf(stderr, "Target `%s' is missing "
- "save function\n",
- t->u.user.name);
- exit(1);
- }
- }
- }
- printf("\n");
-}
-
/* Debugging prototype. */
static int for_each_table(int (*func)(const char *tablename))
{
diff --git a/ip6tables.8.in b/ip6tables.8.in
index f1033635..45b14dca 100644
--- a/ip6tables.8.in
+++ b/ip6tables.8.in
@@ -197,6 +197,11 @@ arguments given. The exact rules are suppressed until you use
ip6tables -L -v
.fi
.TP
+.BR "-S, --list-rules " "[\fIchain\fP]"
+Print all rules in the selected chain. If no chain is selected, all
+chains are printed like iptables-save. Like every other iptables command,
+it applies to the specified table (filter is the default).
+.TP
.BR "-F, --flush " "[\fIchain\fP]"
Flush the selected chain (all the chains in the table if none is given).
This is equivalent to deleting all the rules one by one.
diff --git a/ip6tables.c b/ip6tables.c
index dc74bcbe..606de3c9 100644
--- a/ip6tables.c
+++ b/ip6tables.c
@@ -78,9 +78,10 @@
#define CMD_DELETE_CHAIN 0x0200U
#define CMD_SET_POLICY 0x0400U
#define CMD_RENAME_CHAIN 0x0800U
-#define NUMBER_OF_CMD 13
+#define CMD_LIST_RULES 0x1000U
+#define NUMBER_OF_CMD 14
static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
- 'N', 'X', 'P', 'E' };
+ 'N', 'X', 'P', 'E', 'S' };
#define OPTION_OFFSET 256
@@ -106,6 +107,7 @@ static struct option original_opts[] = {
{.name = "insert", .has_arg = 1, .val = 'I'},
{.name = "replace", .has_arg = 1, .val = 'R'},
{.name = "list", .has_arg = 2, .val = 'L'},
+ {.name = "list-rules", .has_arg = 2, .val = 'S'},
{.name = "flush", .has_arg = 2, .val = 'F'},
{.name = "zero", .has_arg = 2, .val = 'Z'},
{.name = "new-chain", .has_arg = 1, .val = 'N'},
@@ -154,7 +156,7 @@ static unsigned int global_option_offset = 0;
static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
/* Well, it's better than "Re: Linux vs FreeBSD" */
{
- /* -n -s -d -p -j -v -x -i -o --line -c */
+ /* -n -s -d -p -j -v -x -i -o --line -c */
/*INSERT*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x',' '},
/*DELETE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x','x'},
/*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x','x'},
@@ -166,7 +168,8 @@ static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
/*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
/*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x',' '},
-/*RENAME*/ {'x','x','x','x','x',' ','x','x','x','x','x'}
+/*RENAME*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*LIST_RULES*/{'x','x','x','x','x',' ','x','x','x','x','x'}
};
static int inverse_for_options[NUMBER_OF_OPT] =
@@ -300,6 +303,8 @@ exit_printhelp(struct ip6tables_rule_match *matches)
" --replace -R chain rulenum\n"
" Replace rule rulenum (1 = first) in chain\n"
" --list -L [chain] List the rules in a chain or all chains\n"
+" --list-rules\n"
+" -S [chain] Print the rules in a chain or all chains\n"
" --flush -F [chain] Delete all rules in chain or all chains\n"
" --zero -Z [chain] Zero counters in chain or all chains\n"
" --new -N chain Create a new user-defined chain\n"
@@ -1098,6 +1103,239 @@ list_entries(const ip6t_chainlabel chain, int verbose, int numeric,
return found;
}
+/* This assumes that mask is contiguous, and byte-bounded. */
+static void
+print_iface(char letter, const char *iface, const unsigned char *mask,
+ int invert)
+{
+ unsigned int i;
+
+ if (mask[0] == 0)
+ return;
+
+ printf("-%c %s", letter, invert ? "! " : "");
+
+ for (i = 0; i < IFNAMSIZ; i++) {
+ if (mask[i] != 0) {
+ if (iface[i] != '\0')
+ printf("%c", iface[i]);
+ } else {
+ /* we can access iface[i-1] here, because
+ * a few lines above we make sure that mask[0] != 0 */
+ if (iface[i-1] != '\0')
+ printf("+");
+ break;
+ }
+ }
+
+ printf(" ");
+}
+
+/* The ip6tables looks up the /etc/protocols. */
+static void print_proto(u_int16_t proto, int invert)
+{
+ if (proto) {
+ unsigned int i;
+ const char *invertstr = invert ? "! " : "";
+
+ struct protoent *pent = getprotobynumber(proto);
+ if (pent) {
+ printf("-p %s%s ",
+ invertstr, pent->p_name);
+ return;
+ }
+
+ for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
+ if (chain_protos[i].num == proto) {
+ printf("-p %s%s ",
+ invertstr, chain_protos[i].name);
+ return;
+ }
+
+ printf("-p %s%u ", invertstr, proto);
+ }
+}
+
+static int print_match_save(const struct ip6t_entry_match *e,
+ const struct ip6t_ip6 *ip)
+{
+ struct xtables_match *match
+ = find_match(e->u.user.name, TRY_LOAD, NULL);
+
+ if (match) {
+ printf("-m %s ", e->u.user.name);
+
+ /* some matches don't provide a save function */
+ if (match->save)
+ match->save(ip, e);
+ } else {
+ if (e->u.match_size) {
+ fprintf(stderr,
+ "Can't find library for match `%s'\n",
+ e->u.user.name);
+ exit(1);
+ }
+ }
+ return 0;
+}
+
+/* print a given ip including mask if neccessary */
+static void print_ip(char *prefix, const struct in6_addr *ip, const struct in6_addr *mask, int invert)
+{
+ char buf[51];
+ int l = ipv6_prefix_length(mask);
+
+ if (l == 0 && !invert)
+ return;
+
+ printf("%s %s%s",
+ prefix,
+ invert ? "! " : "",
+ inet_ntop(AF_INET6, ip, buf, sizeof buf));
+
+ if (l == -1)
+ printf("/%s ", inet_ntop(AF_INET6, mask, buf, sizeof buf));
+ else
+ printf("/%d ", l);
+}
+
+/* We want this to be readable, so only print out neccessary fields.
+ * Because that's the kind of world I want to live in. */
+void print_rule(const struct ip6t_entry *e,
+ ip6tc_handle_t *h, const char *chain, int counters)
+{
+ struct ip6t_entry_target *t;
+ const char *target_name;
+
+ /* print counters for iptables-save */
+ if (counters > 0)
+ printf("[%llu:%llu] ", (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
+
+ /* print chain name */
+ printf("-A %s ", chain);
+
+ /* Print IP part. */
+ print_ip("-s", &(e->ipv6.src), &(e->ipv6.smsk),
+ e->ipv6.invflags & IP6T_INV_SRCIP);
+
+ print_ip("-d", &(e->ipv6.dst), &(e->ipv6.dmsk),
+ e->ipv6.invflags & IP6T_INV_DSTIP);
+
+ print_iface('i', e->ipv6.iniface, e->ipv6.iniface_mask,
+ e->ipv6.invflags & IP6T_INV_VIA_IN);
+
+ print_iface('o', e->ipv6.outiface, e->ipv6.outiface_mask,
+ e->ipv6.invflags & IP6T_INV_VIA_OUT);
+
+ print_proto(e->ipv6.proto, e->ipv6.invflags & IP6T_INV_PROTO);
+
+#if 0
+ /* not definied in ipv6
+ * FIXME: linux/netfilter_ipv6/ip6_tables: IP6T_INV_FRAG why definied? */
+ if (e->ipv6.flags & IPT_F_FRAG)
+ printf("%s-f ",
+ e->ipv6.invflags & IP6T_INV_FRAG ? "! " : "");
+#endif
+
+ if (e->ipv6.flags & IP6T_F_TOS)
+ printf("%s-? %d ",
+ e->ipv6.invflags & IP6T_INV_TOS ? "! " : "",
+ e->ipv6.tos);
+
+ /* Print matchinfo part */
+ if (e->target_offset) {
+ IP6T_MATCH_ITERATE(e, print_match_save, &e->ipv6);
+ }
+
+ /* print counters for iptables -R */
+ if (counters < 0)
+ printf("-c %llu %llu ", (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
+
+ /* Print target name */
+ target_name = ip6tc_get_target(e, h);
+ if (target_name && (*target_name != '\0'))
+ printf("-j %s ", target_name);
+
+ /* Print targinfo part */
+ t = ip6t_get_target((struct ip6t_entry *)e);
+ if (t->u.user.name[0]) {
+ struct xtables_target *target
+ = find_target(t->u.user.name, TRY_LOAD);
+
+ if (!target) {
+ fprintf(stderr, "Can't find library for target `%s'\n",
+ t->u.user.name);
+ exit(1);
+ }
+
+ if (target->save)
+ target->save(&e->ipv6, t);
+ else {
+ /* If the target size is greater than ip6t_entry_target
+ * there is something to be saved, we just don't know
+ * how to print it */
+ if (t->u.target_size !=
+ sizeof(struct ip6t_entry_target)) {
+ fprintf(stderr, "Target `%s' is missing "
+ "save function\n",
+ t->u.user.name);
+ exit(1);
+ }
+ }
+ }
+ printf("\n");
+}
+
+static int
+list_rules(const ip6t_chainlabel chain, int counters,
+ ip6tc_handle_t *handle)
+{
+ const char *this = NULL;
+ int found = 0;
+
+ if (counters)
+ counters = -1; /* iptables -c format */
+
+ /* Dump out chain names first,
+ * thereby preventing dependency conflicts */
+ for (this = ip6tc_first_chain(handle);
+ this;
+ this = ip6tc_next_chain(handle)) {
+ if (chain && strcmp(this, chain) != 0)
+ continue;
+
+ if (ip6tc_builtin(this, *handle)) {
+ struct ip6t_counters count;
+ printf("-P %s %s", this, ip6tc_get_policy(this, &count, handle));
+ if (counters)
+ printf(" -c %llu %llu", (unsigned long long)count.pcnt, (unsigned long long)count.bcnt);
+ printf("\n");
+ } else {
+ printf("-N %s\n", this);
+ }
+ }
+
+ for (this = ip6tc_first_chain(handle);
+ this;
+ this = ip6tc_next_chain(handle)) {
+ const struct ip6t_entry *e;
+
+ if (chain && strcmp(this, chain) != 0)
+ continue;
+
+ /* Dump out rules */
+ e = ip6tc_first_rule(this, handle);
+ while(e) {
+ print_rule(e, handle, this, counters);
+ e = ip6tc_next_rule(e, handle);
+ }
+ found = 1;
+ }
+
+ errno = ENOENT;
+ return found;
+}
+
static struct ip6t_entry *
generate_entry(const struct ip6t_entry *fw,
struct ip6tables_rule_match *matches,
@@ -1201,7 +1439,7 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
opterr = 0;
while ((c = getopt_long(argc, argv,
- "-A:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:bvnt:m:xc:",
+ "-A:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:bvnt:m:xc:",
opts, NULL)) != -1) {
switch (c) {
/*
@@ -1256,6 +1494,15 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
chain = argv[optind++];
break;
+ case 'S':
+ add_command(&command, CMD_LIST_RULES, CMD_ZERO,
+ invert);
+ if (optarg) chain = optarg;
+ else if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ chain = argv[optind++];
+ break;
+
case 'F':
add_command(&command, CMD_FLUSH, CMD_NONE,
invert);
@@ -1266,7 +1513,7 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
break;
case 'Z':
- add_command(&command, CMD_ZERO, CMD_LIST,
+ add_command(&command, CMD_ZERO, CMD_LIST|CMD_LIST_RULES,
invert);
if (optarg) chain = optarg;
else if (optind < argc && argv[optind][0] != '-'
@@ -1515,7 +1762,6 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
break;
-
case 1: /* non option */
if (optarg[0] == '!' && optarg[1] == '\0') {
if (invert)
@@ -1767,20 +2013,13 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
options&OPT_VERBOSE,
handle);
break;
- case CMD_LIST:
- ret = list_entries(chain,
- options&OPT_VERBOSE,
- options&OPT_NUMERIC,
- options&OPT_EXPANDED,
- options&OPT_LINENUMBERS,
- handle);
- break;
case CMD_FLUSH:
ret = flush_entries(chain, options&OPT_VERBOSE, handle);
break;
case CMD_ZERO:
ret = zero_entries(chain, options&OPT_VERBOSE, handle);
break;
+ case CMD_LIST:
case CMD_LIST|CMD_ZERO:
ret = list_entries(chain,
options&OPT_VERBOSE,
@@ -1788,7 +2027,16 @@ int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
options&OPT_EXPANDED,
options&OPT_LINENUMBERS,
handle);
- if (ret)
+ if (ret && (command & CMD_ZERO))
+ ret = zero_entries(chain,
+ options&OPT_VERBOSE, handle);
+ break;
+ case CMD_LIST_RULES:
+ case CMD_LIST_RULES|CMD_ZERO:
+ ret = list_rules(chain,
+ options&OPT_VERBOSE,
+ handle);
+ if (ret && (command & CMD_ZERO))
ret = zero_entries(chain,
options&OPT_VERBOSE, handle);
break;
diff --git a/iptables-save.c b/iptables-save.c
index e8bdda2b..ecccac47 100644
--- a/iptables-save.c
+++ b/iptables-save.c
@@ -31,211 +31,6 @@ static const struct option options[] = {
{NULL},
};
-#define IP_PARTS_NATIVE(n) \
-(unsigned int)((n)>>24)&0xFF, \
-(unsigned int)((n)>>16)&0xFF, \
-(unsigned int)((n)>>8)&0xFF, \
-(unsigned int)((n)&0xFF)
-
-#define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n))
-
-/* This assumes that mask is contiguous, and byte-bounded. */
-static void
-print_iface(char letter, const char *iface, const unsigned char *mask,
- int invert)
-{
- unsigned int i;
-
- if (mask[0] == 0)
- return;
-
- printf("-%c %s", letter, invert ? "! " : "");
-
- for (i = 0; i < IFNAMSIZ; i++) {
- if (mask[i] != 0) {
- if (iface[i] != '\0')
- printf("%c", iface[i]);
- } else {
- /* we can access iface[i-1] here, because
- * a few lines above we make sure that mask[0] != 0 */
- if (iface[i-1] != '\0')
- printf("+");
- break;
- }
- }
-
- printf(" ");
-}
-
-/* These are hardcoded backups in iptables.c, so they are safe */
-struct pprot {
- char *name;
- u_int8_t num;
-};
-
-/* FIXME: why don't we use /etc/protocols ? */
-static const struct pprot chain_protos[] = {
- { "tcp", IPPROTO_TCP },
- { "udp", IPPROTO_UDP },
- { "icmp", IPPROTO_ICMP },
- { "esp", IPPROTO_ESP },
- { "ah", IPPROTO_AH },
- { "sctp", IPPROTO_SCTP },
-};
-
-static void print_proto(u_int16_t proto, int invert)
-{
- if (proto) {
- unsigned int i;
- const char *invertstr = invert ? "! " : "";
-
- struct protoent *pent = getprotobynumber(proto);
- if (pent) {
- printf("-p %s%s ", invertstr, pent->p_name);
- return;
- }
-
- for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
- if (chain_protos[i].num == proto) {
- printf("-p %s%s ",
- invertstr, chain_protos[i].name);
- return;
- }
-
- printf("-p %s%u ", invertstr, proto);
- }
-}
-
-static int print_match(const struct ipt_entry_match *e,
- const struct ipt_ip *ip)
-{
- struct xtables_match *match
- = find_match(e->u.user.name, TRY_LOAD, NULL);
-
- if (match) {
- printf("-m %s ", e->u.user.name);
-
- /* some matches don't provide a save function */
- if (match->save)
- match->save(ip, e);
- } else {
- if (e->u.match_size) {
- fprintf(stderr,
- "Can't find library for match `%s'\n",
- e->u.user.name);
- exit(1);
- }
- }
- return 0;
-}
-
-/* print a given ip including mask if neccessary */
-static void print_ip(char *prefix, u_int32_t ip, u_int32_t mask, int invert)
-{
- u_int32_t bits, hmask = ntohl(mask);
- int i;
-
- if (!mask && !ip && !invert)
- return;
-
- printf("%s %s%u.%u.%u.%u",
- prefix,
- invert ? "! " : "",
- IP_PARTS(ip));
-
- if (mask == 0xFFFFFFFFU) {
- printf("/32 ");
- return;
- }
-
- i = 32;
- bits = 0xFFFFFFFEU;
- while (--i >= 0 && hmask != bits)
- bits <<= 1;
- if (i >= 0)
- printf("/%u ", i);
- else
- printf("/%u.%u.%u.%u ", IP_PARTS(mask));
-}
-
-/* We want this to be readable, so only print out neccessary fields.
- * Because that's the kind of world I want to live in. */
-static void print_rule(const struct ipt_entry *e,
- iptc_handle_t *h, const char *chain, int counters)
-{
- struct ipt_entry_target *t;
- const char *target_name;
-
- /* print counters */
- if (counters)
- printf("[%llu:%llu] ", (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
-
- /* print chain name */
- printf("-A %s ", chain);
-
- /* Print IP part. */
- print_ip("-s", e->ip.src.s_addr,e->ip.smsk.s_addr,
- e->ip.invflags & IPT_INV_SRCIP);
-
- print_ip("-d", e->ip.dst.s_addr, e->ip.dmsk.s_addr,
- e->ip.invflags & IPT_INV_DSTIP);
-
- print_iface('i', e->ip.iniface, e->ip.iniface_mask,
- e->ip.invflags & IPT_INV_VIA_IN);
-
- print_iface('o', e->ip.outiface, e->ip.outiface_mask,
- e->ip.invflags & IPT_INV_VIA_OUT);
-
- print_proto(e->ip.proto, e->ip.invflags & IPT_INV_PROTO);
-
- if (e->ip.flags & IPT_F_FRAG)
- printf("%s-f ",
- e->ip.invflags & IPT_INV_FRAG ? "! " : "");
-
- /* Print matchinfo part */
- if (e->target_offset) {
- IPT_MATCH_ITERATE(e, print_match, &e->ip);
- }
-
- /* Print target name */
- target_name = iptc_get_target(e, h);
- if (target_name && (*target_name != '\0'))
-#ifdef IPT_F_GOTO
- printf("-%c %s ", e->ip.flags & IPT_F_GOTO ? 'g' : 'j', target_name);
-#else
- printf("-j %s ", target_name);
-#endif
-
- /* Print targinfo part */
- t = ipt_get_target((struct ipt_entry *)e);
- if (t->u.user.name[0]) {
- struct xtables_target *target
- = find_target(t->u.user.name, TRY_LOAD);
-
- if (!target) {
- fprintf(stderr, "Can't find library for target `%s'\n",
- t->u.user.name);
- exit(1);
- }
-
- if (target->save)
- target->save(&e->ip, t);
- else {
- /* If the target size is greater than ipt_entry_target
- * there is something to be saved, we just don't know
- * how to print it */
- if (t->u.target_size !=
- sizeof(struct ipt_entry_target)) {
- fprintf(stderr, "Target `%s' is missing "
- "save function\n",
- t->u.user.name);
- exit(1);
- }
- }
- }
- printf("\n");
-}
-
/* Debugging prototype. */
static int for_each_table(int (*func)(const char *tablename))
{
diff --git a/iptables.8.in b/iptables.8.in
index 8ebedb82..0b945cb7 100644
--- a/iptables.8.in
+++ b/iptables.8.in
@@ -205,6 +205,11 @@ arguments given. The exact rules are suppressed until you use
iptables -L -v
.fi
.TP
+.BR "-S, --list-rules " "[\fIchain\fP]"
+Print all rules in the selected chain. If no chain is selected, all
+chains are printed like iptables-save. Like every other iptables command,
+it applies to the specified table (filter is the default).
+.TP
.BR "-F, --flush " "[\fIchain\fP]"
Flush the selected chain (all the chains in the table if none is given).
This is equivalent to deleting all the rules one by one.
diff --git a/iptables.c b/iptables.c
index 8c876d22..98db70fc 100644
--- a/iptables.c
+++ b/iptables.c
@@ -75,9 +75,10 @@
#define CMD_DELETE_CHAIN 0x0200U
#define CMD_SET_POLICY 0x0400U
#define CMD_RENAME_CHAIN 0x0800U
-#define NUMBER_OF_CMD 13
+#define CMD_LIST_RULES 0x1000U
+#define NUMBER_OF_CMD 14
static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
- 'N', 'X', 'P', 'E' };
+ 'N', 'X', 'P', 'E', 'S' };
#define OPTION_OFFSET 256
@@ -104,6 +105,7 @@ static struct option original_opts[] = {
{.name = "insert", .has_arg = 1, .val = 'I'},
{.name = "replace", .has_arg = 1, .val = 'R'},
{.name = "list", .has_arg = 2, .val = 'L'},
+ {.name = "list-rules", .has_arg = 2, .val = 'S'},
{.name = "flush", .has_arg = 2, .val = 'F'},
{.name = "zero", .has_arg = 2, .val = 'Z'},
{.name = "new-chain", .has_arg = 1, .val = 'N'},
@@ -154,7 +156,7 @@ static unsigned int global_option_offset = 0;
static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
/* Well, it's better than "Re: Linux vs FreeBSD" */
{
- /* -n -s -d -p -j -v -x -i -o -f --line -c */
+ /* -n -s -d -p -j -v -x -i -o -f --line -c */
/*INSERT*/ {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x',' '},
/*DELETE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x','x'},
/*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x','x','x'},
@@ -166,7 +168,8 @@ static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
/*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'},
/*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'},
/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x','x',' '},
-/*RENAME*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'}
+/*RENAME*/ {'x','x','x','x','x',' ','x','x','x','x','x','x'},
+/*LIST_RULES*/{'x','x','x','x','x',' ','x','x','x','x','x','x'}
};
static int inverse_for_options[NUMBER_OF_OPT] =
@@ -303,6 +306,8 @@ exit_printhelp(struct iptables_rule_match *matches)
" --replace -R chain rulenum\n"
" Replace rule rulenum (1 = first) in chain\n"
" --list -L [chain] List the rules in a chain or all chains\n"
+" --list-rules\n"
+" -S [chain] Print the rules in a chain or all chains\n"
" --flush -F [chain] Delete all rules in chain or all chains\n"
" --zero -Z [chain] Zero counters in chain or all chains\n"
" --new -N chain Create a new user-defined chain\n"
@@ -1101,6 +1106,249 @@ list_entries(const ipt_chainlabel chain, int verbose, int numeric,
return found;
}
+static void print_proto(u_int16_t proto, int invert)
+{
+ if (proto) {
+ unsigned int i;
+ const char *invertstr = invert ? "! " : "";
+
+ struct protoent *pent = getprotobynumber(proto);
+ if (pent) {
+ printf("-p %s%s ", invertstr, pent->p_name);
+ return;
+ }
+
+ for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
+ if (chain_protos[i].num == proto) {
+ printf("-p %s%s ",
+ invertstr, chain_protos[i].name);
+ return;
+ }
+
+ printf("-p %s%u ", invertstr, proto);
+ }
+}
+
+#define IP_PARTS_NATIVE(n) \
+(unsigned int)((n)>>24)&0xFF, \
+(unsigned int)((n)>>16)&0xFF, \
+(unsigned int)((n)>>8)&0xFF, \
+(unsigned int)((n)&0xFF)
+
+#define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n))
+
+/* This assumes that mask is contiguous, and byte-bounded. */
+static void
+print_iface(char letter, const char *iface, const unsigned char *mask,
+ int invert)
+{
+ unsigned int i;
+
+ if (mask[0] == 0)
+ return;
+
+ printf("-%c %s", letter, invert ? "! " : "");
+
+ for (i = 0; i < IFNAMSIZ; i++) {
+ if (mask[i] != 0) {
+ if (iface[i] != '\0')
+ printf("%c", iface[i]);
+ } else {
+ /* we can access iface[i-1] here, because
+ * a few lines above we make sure that mask[0] != 0 */
+ if (iface[i-1] != '\0')
+ printf("+");
+ break;
+ }
+ }
+
+ printf(" ");
+}
+
+static int print_match_save(const struct ipt_entry_match *e,
+ const struct ipt_ip *ip)
+{
+ struct xtables_match *match
+ = find_match(e->u.user.name, TRY_LOAD, NULL);
+
+ if (match) {
+ printf("-m %s ", e->u.user.name);
+
+ /* some matches don't provide a save function */
+ if (match->save)
+ match->save(ip, e);
+ } else {
+ if (e->u.match_size) {
+ fprintf(stderr,
+ "Can't find library for match `%s'\n",
+ e->u.user.name);
+ exit(1);
+ }
+ }
+ return 0;
+}
+
+/* print a given ip including mask if neccessary */
+static void print_ip(char *prefix, u_int32_t ip, u_int32_t mask, int invert)
+{
+ u_int32_t bits, hmask = ntohl(mask);
+ int i;
+
+ if (!mask && !ip && !invert)
+ return;
+
+ printf("%s %s%u.%u.%u.%u",
+ prefix,
+ invert ? "! " : "",
+ IP_PARTS(ip));
+
+ if (mask == 0xFFFFFFFFU) {
+ printf("/32 ");
+ return;
+ }
+
+ i = 32;
+ bits = 0xFFFFFFFEU;
+ while (--i >= 0 && hmask != bits)
+ bits <<= 1;
+ if (i >= 0)
+ printf("/%u ", i);
+ else
+ printf("/%u.%u.%u.%u ", IP_PARTS(mask));
+}
+
+/* We want this to be readable, so only print out neccessary fields.
+ * Because that's the kind of world I want to live in. */
+void print_rule(const struct ipt_entry *e,
+ iptc_handle_t *h, const char *chain, int counters)
+{
+ struct ipt_entry_target *t;
+ const char *target_name;
+
+ /* print counters for iptables-save */
+ if (counters > 0)
+ printf("[%llu:%llu] ", (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
+
+ /* print chain name */
+ printf("-A %s ", chain);
+
+ /* Print IP part. */
+ print_ip("-s", e->ip.src.s_addr,e->ip.smsk.s_addr,
+ e->ip.invflags & IPT_INV_SRCIP);
+
+ print_ip("-d", e->ip.dst.s_addr, e->ip.dmsk.s_addr,
+ e->ip.invflags & IPT_INV_DSTIP);
+
+ print_iface('i', e->ip.iniface, e->ip.iniface_mask,
+ e->ip.invflags & IPT_INV_VIA_IN);
+
+ print_iface('o', e->ip.outiface, e->ip.outiface_mask,
+ e->ip.invflags & IPT_INV_VIA_OUT);
+
+ print_proto(e->ip.proto, e->ip.invflags & IPT_INV_PROTO);
+
+ if (e->ip.flags & IPT_F_FRAG)
+ printf("%s-f ",
+ e->ip.invflags & IPT_INV_FRAG ? "! " : "");
+
+ /* Print matchinfo part */
+ if (e->target_offset) {
+ IPT_MATCH_ITERATE(e, print_match_save, &e->ip);
+ }
+
+ /* print counters for iptables -R */
+ if (counters < 0)
+ printf("-c %llu %llu ", (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
+
+ /* Print target name */
+ target_name = iptc_get_target(e, h);
+ if (target_name && (*target_name != '\0'))
+#ifdef IPT_F_GOTO
+ printf("-%c %s ", e->ip.flags & IPT_F_GOTO ? 'g' : 'j', target_name);
+#else
+ printf("-j %s ", target_name);
+#endif
+
+ /* Print targinfo part */
+ t = ipt_get_target((struct ipt_entry *)e);
+ if (t->u.user.name[0]) {
+ struct xtables_target *target
+ = find_target(t->u.user.name, TRY_LOAD);
+
+ if (!target) {
+ fprintf(stderr, "Can't find library for target `%s'\n",
+ t->u.user.name);
+ exit(1);
+ }
+
+ if (target->save)
+ target->save(&e->ip, t);
+ else {
+ /* If the target size is greater than ipt_entry_target
+ * there is something to be saved, we just don't know
+ * how to print it */
+ if (t->u.target_size !=
+ sizeof(struct ipt_entry_target)) {
+ fprintf(stderr, "Target `%s' is missing "
+ "save function\n",
+ t->u.user.name);
+ exit(1);
+ }
+ }
+ }
+ printf("\n");
+}
+
+static int
+list_rules(const ipt_chainlabel chain, int counters,
+ iptc_handle_t *handle)
+{
+ const char *this = NULL;
+ int found = 0;
+
+ if (counters)
+ counters = -1; /* iptables -c format */
+
+ /* Dump out chain names first,
+ * thereby preventing dependency conflicts */
+ for (this = iptc_first_chain(handle);
+ this;
+ this = iptc_next_chain(handle)) {
+ if (chain && strcmp(this, chain) != 0)
+ continue;
+
+ if (iptc_builtin(this, *handle)) {
+ struct ipt_counters count;
+ printf("-P %s %s", this, iptc_get_policy(this, &count, handle));
+ if (counters)
+ printf(" -c %llu %llu", (unsigned long long)count.pcnt, (unsigned long long)count.bcnt);
+ printf("\n");
+ } else {
+ printf("-N %s\n", this);
+ }
+ }
+
+ for (this = iptc_first_chain(handle);
+ this;
+ this = iptc_next_chain(handle)) {
+ const struct ipt_entry *e;
+
+ if (chain && strcmp(this, chain) != 0)
+ continue;
+
+ /* Dump out rules */
+ e = iptc_first_rule(this, handle);
+ while(e) {
+ print_rule(e, handle, this, counters);
+ e = iptc_next_rule(e, handle);
+ }
+ found = 1;
+ }
+
+ errno = ENOENT;
+ return found;
+}
+
static struct ipt_entry *
generate_entry(const struct ipt_entry *fw,
struct iptables_rule_match *matches,
@@ -1218,7 +1466,7 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
opterr = 0;
while ((c = getopt_long(argc, argv,
- "-A:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:g:",
+ "-A:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:g:",
opts, NULL)) != -1) {
switch (c) {
/*
@@ -1273,6 +1521,15 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
chain = argv[optind++];
break;
+ case 'S':
+ add_command(&command, CMD_LIST_RULES, CMD_ZERO,
+ invert);
+ if (optarg) chain = optarg;
+ else if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ chain = argv[optind++];
+ break;
+
case 'F':
add_command(&command, CMD_FLUSH, CMD_NONE,
invert);
@@ -1283,7 +1540,7 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
break;
case 'Z':
- add_command(&command, CMD_ZERO, CMD_LIST,
+ add_command(&command, CMD_ZERO, CMD_LIST|CMD_LIST_RULES,
invert);
if (optarg) chain = optarg;
else if (optind < argc && argv[optind][0] != '-'
@@ -1807,20 +2064,13 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
options&OPT_VERBOSE,
handle);
break;
- case CMD_LIST:
- ret = list_entries(chain,
- options&OPT_VERBOSE,
- options&OPT_NUMERIC,
- options&OPT_EXPANDED,
- options&OPT_LINENUMBERS,
- handle);
- break;
case CMD_FLUSH:
ret = flush_entries(chain, options&OPT_VERBOSE, handle);
break;
case CMD_ZERO:
ret = zero_entries(chain, options&OPT_VERBOSE, handle);
break;
+ case CMD_LIST:
case CMD_LIST|CMD_ZERO:
ret = list_entries(chain,
options&OPT_VERBOSE,
@@ -1828,7 +2078,16 @@ int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
options&OPT_EXPANDED,
options&OPT_LINENUMBERS,
handle);
- if (ret)
+ if (ret && (command & CMD_ZERO))
+ ret = zero_entries(chain,
+ options&OPT_VERBOSE, handle);
+ break;
+ case CMD_LIST_RULES:
+ case CMD_LIST_RULES|CMD_ZERO:
+ ret = list_rules(chain,
+ options&OPT_VERBOSE,
+ handle);
+ if (ret && (command & CMD_ZERO))
ret = zero_entries(chain,
options&OPT_VERBOSE, handle);
break;