summaryrefslogtreecommitdiffstats
path: root/iptables.c
diff options
context:
space:
mode:
Diffstat (limited to 'iptables.c')
-rw-r--r--iptables.c289
1 files changed, 274 insertions, 15 deletions
diff --git a/iptables.c b/iptables.c
index 8c876d2..98db70f 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;