contrib/fwd: generic drop target belongs into the drop chain
[project/luci.git] / contrib / fwd / src / fwd_rules.c
index c3d4ebb..6708e55 100644 (file)
 #include "fwd_rules.h"
 #include "fwd_xtables.h"
 
-static void
-fwd_ipt_rule_append(struct fwd_ipt_rulebuf *r, const char *fmt, ...)
-{
-       int len = 0;
-       char buf[256]; buf[0] = 0;
-
-       va_list ap;
-       va_start(ap, fmt);
-       len = vsnprintf(buf, sizeof(buf), fmt, ap);
-       va_end(ap);     
-
-       if( len > 0 )
-       {
-               r->buf = realloc(r->buf, r->len + len + 1);
-               memcpy(&r->buf[r->len], buf, len);
-               r->buf[r->len + len] = 0;
-               r->len += len;
-       }
-}
-
-static struct fwd_ipt_rulebuf * fwd_ipt_init(const char *table)
-{
-       struct fwd_ipt_rulebuf *r;
-
-       if( (r = fwd_alloc_ptr(struct fwd_ipt_rulebuf)) != NULL )
-       {
-               fwd_ipt_rule_append(r, IPT " -t %s", table);
-               return r;
-       }
-
-       return NULL;
-}
-
-static void fwd_ipt_add_srcport(
-       struct fwd_ipt_rulebuf *r, struct fwd_portrange *p
-) {
-       if( p != NULL )
-       {
-               if( p->min < p->max )
-                       fwd_ipt_rule_append(r, " --sport %u:%u", p->min, p->max);
-               else
-                       fwd_ipt_rule_append(r, " --sport %u", p->min);
-       }
-}
-
-static void fwd_ipt_add_destport(
-       struct fwd_ipt_rulebuf *r, struct fwd_portrange *p
-) {
-       if( p != NULL )
-       {
-               if( p->min < p->max )
-                       fwd_ipt_rule_append(r, " --dport %u:%u", p->min, p->max);
-               else
-                       fwd_ipt_rule_append(r, " --dport %u", p->min);
-       }
-}
-
-static void fwd_ipt_add_proto(
-       struct fwd_ipt_rulebuf *r, struct fwd_proto *p
-) {
-       if( p != NULL )
-       {
-               switch( p->type )
-               {
-                       case FWD_PR_TCPUDP:
-                               fwd_ipt_rule_append(r, " -p tcp -p udp");
-                               break;
-
-                       case FWD_PR_TCP:
-                               fwd_ipt_rule_append(r, " -p tcp");
-                               break;
-
-                       case FWD_PR_UDP:
-                               fwd_ipt_rule_append(r, " -p udp");
-                               break;
-
-                       case FWD_PR_ICMP:
-                               fwd_ipt_rule_append(r, " -p icmp");
-                               break;
-
-                       case FWD_PR_ALL:
-                               fwd_ipt_rule_append(r, " -p all");
-                               break;
-
-                       case FWD_PR_CUSTOM:
-                               fwd_ipt_rule_append(r, " -p %u", p->proto);
-                               break;
-               }
-       }
-}
-
-static void fwd_ipt_add_srcaddr(
-       struct fwd_ipt_rulebuf *r, struct fwd_cidr *c
-) {
-       if( c != NULL )
-       {
-               if( c->prefix < 32 )
-                       fwd_ipt_rule_append(r, " -s %s/%u",
-                               inet_ntoa(c->addr), c->prefix);
-               else
-                       fwd_ipt_rule_append(r, " -s %s", inet_ntoa(c->addr));
-       }
-}
-
-static void fwd_ipt_add_destaddr(
-       struct fwd_ipt_rulebuf *r, struct fwd_cidr *c
-) {
-       if( c != NULL )
-       {
-               if( c->prefix < 32 )
-                       fwd_ipt_rule_append(r, " -d %s/%u",
-                               inet_ntoa(c->addr), c->prefix);
-               else
-                       fwd_ipt_rule_append(r, " -d %s", inet_ntoa(c->addr));
-       }
-}
-
-static void fwd_ipt_add_srcmac(
-       struct fwd_ipt_rulebuf *r, struct fwd_mac *m
-) {
-       if( m != NULL )
-       {
-               fwd_ipt_rule_append(r,
-                       " -m mac --mac-source %02x:%02x:%02x:%02x:%02x:%02x",
-                       m->mac[0], m->mac[1], m->mac[2],
-                       m->mac[3], m->mac[4], m->mac[5]);
-       }
-}
-
-static void fwd_ipt_add_icmptype(
-       struct fwd_ipt_rulebuf *r, struct fwd_icmptype *i
-) {
-       if( i != NULL )
-       {
-               if( i->name )
-                       fwd_ipt_rule_append(r, " --icmp-type %s", i->name);
-               else if( i->code > -1 )
-                       fwd_ipt_rule_append(r, " --icmp-type %u/%u", i->type, i->code);
-               else
-                       fwd_ipt_rule_append(r, " --icmp-type %u", i->type);
-       }
-}
-
-static void fwd_ipt_add_dnat_target(
-       struct fwd_ipt_rulebuf *r, struct fwd_cidr *c, struct fwd_portrange *p
-) {
-       if( c != NULL )
-       {
-               fwd_ipt_rule_append(r, " -j DNAT --to-destination %s",
-                       inet_ntoa(c->addr));
-
-               if( (p != NULL) && (p->min < p->max) )
-                       fwd_ipt_rule_append(r, ":%u-%u", p->min, p->max);
-               else if( p != NULL )
-                       fwd_ipt_rule_append(r, ":%u", p->min);
-       }
-}
-
-static void fwd_ipt_add_policy_target(
-       struct fwd_ipt_rulebuf *r, enum fwd_policy p
-) {
-       fwd_ipt_rule_append(r, " -j %s",
-               (p == FWD_P_ACCEPT)
-                       ? "handle_accept"
-                       : (p == FWD_P_REJECT)
-                               ? "handle_reject"
-                               : "handle_drop"
-       );
-}
-
-static void fwd_ipt_add_comment(
-       struct fwd_ipt_rulebuf *r, const char *t, struct fwd_zone *z,
-       struct fwd_network_list *n, struct fwd_network_list *n2
-) {
-       if( (n != NULL) && (n2 != NULL) )
-               fwd_ipt_add_format(r, " -m comment --comment '%s:%s src:%s dest:%s'",
-                       t, z->name, n->name, n2->name);
-       else if( (n == NULL) && (n2 != NULL) )
-               fwd_ipt_add_format(r, " -m comment --comment '%s:%s dest:%s'",
-                       t, z->name, n2->name);
-       else
-               fwd_ipt_add_format(r, " -m comment --comment '%s:%s src:%s'",
-                       t, z->name, n->name);
-}
-
-static void fwd_ipt_exec(struct fwd_ipt_rulebuf *r)
-{
-       if( r->len )
-               printf("%s\n", r->buf);
-
-       fwd_free_ptr(r->buf);
-       fwd_free_ptr(r);
-}
 
 /* -P <chain> <policy> */
 static void fwd_r_set_policy(
@@ -238,7 +45,7 @@ static void fwd_r_jump_chain(
        if( (r = fwd_xt_init_rule(h)) != NULL )
        {
                fwd_xt_get_target(r, chain2);
-               fwd_xt_exec_rule(r, chain1);
+               fwd_xt_append_rule(r, chain1);
        }
 }
 
@@ -252,9 +59,9 @@ static void fwd_r_drop_invalid(struct iptc_handle *h, const char *chain)
        {
                if( (m = fwd_xt_get_match(r, "state")) != NULL )
                {
-                       fwd_xt_parse_match(r, m, "--state", "INVALID", 0);
+                       fwd_xt_parse_match(r, m, "--state", "INVALID");
                        fwd_xt_get_target(r, "DROP");
-                       fwd_xt_exec_rule(r, chain);
+                       fwd_xt_append_rule(r, chain);
                }
        }
 }
@@ -269,9 +76,9 @@ static void fwd_r_accept_related(struct iptc_handle *h, const char *chain)
        {
                if( (m = fwd_xt_get_match(r, "state")) != NULL )
                {
-                       fwd_xt_parse_match(r, m, "--state", "RELATED,ESTABLISHED", 0);
+                       fwd_xt_parse_match(r, m, "--state", "RELATED,ESTABLISHED");
                        fwd_xt_get_target(r, "ACCEPT");
-                       fwd_xt_exec_rule(r, chain);
+                       fwd_xt_append_rule(r, chain);
                }
        }
 }
@@ -288,14 +95,14 @@ static void fwd_r_accept_lo(struct iptc_handle *h)
        {
                fwd_xt_parse_in(r, &n, 0);
                fwd_xt_get_target(r, "ACCEPT");
-               fwd_xt_exec_rule(r, "INPUT");
+               fwd_xt_append_rule(r, "INPUT");
        }
 
        if( (r = fwd_xt_init_rule(h)) != NULL )
        {
                fwd_xt_parse_out(r, &n, 0);
                fwd_xt_get_target(r, "ACCEPT");
-               fwd_xt_exec_rule(r, "OUTPUT");
+               fwd_xt_append_rule(r, "OUTPUT");
        }
 }
 
@@ -320,22 +127,22 @@ static void fwd_r_add_synflood(struct iptc_handle *h, struct fwd_defaults *def)
                /* -m tcp --syn */
                if( (m = fwd_xt_get_match(r, "tcp")) != NULL )
                {
-                       fwd_xt_parse_match(r, m, "--syn", NULL, 0);
+                       fwd_xt_parse_match(r, m, "--syn");
                }
 
                /* -m limit --limit x/second --limit-burst y */
                if( (m = fwd_xt_get_match(r, "limit")) != NULL )
                {
                        sprintf(buf, "%i/second", def->syn_rate);
-                       fwd_xt_parse_match(r, m, "--limit", buf, 0);
+                       fwd_xt_parse_match(r, m, "--limit", buf);
 
                        sprintf(buf, "%i", def->syn_burst);
-                       fwd_xt_parse_match(r, m, "--limit-burst", buf, 0);
+                       fwd_xt_parse_match(r, m, "--limit-burst", buf);
                }
 
                /* -j RETURN; -A syn_flood */
                fwd_xt_get_target(r, "RETURN");
-               fwd_xt_exec_rule(r, "syn_flood");
+               fwd_xt_append_rule(r, "syn_flood");
        }
 
        /* drop rule */
@@ -343,7 +150,7 @@ static void fwd_r_add_synflood(struct iptc_handle *h, struct fwd_defaults *def)
        {       
                /* -j DROP; -A syn_flood */
                fwd_xt_get_target(r, "DROP");
-               fwd_xt_exec_rule(r, "syn_flood");
+               fwd_xt_append_rule(r, "syn_flood");
        }
 
        /* jump to syn_flood rule */
@@ -356,12 +163,12 @@ static void fwd_r_add_synflood(struct iptc_handle *h, struct fwd_defaults *def)
                /* -m tcp --syn */
                if( (m = fwd_xt_get_match(r, "tcp")) != NULL )
                {
-                       fwd_xt_parse_match(r, m, "--syn", NULL, 0);
+                       fwd_xt_parse_match(r, m, "--syn");
                }
 
                /* -j syn_flood; -A INPUT */
                fwd_xt_get_target(r, "syn_flood");
-               fwd_xt_exec_rule(r, "INPUT");
+               fwd_xt_append_rule(r, "INPUT");
        }
 }
 
@@ -385,11 +192,11 @@ static void fwd_r_handle_reject(struct iptc_handle *h)
                /* -j REJECT --reject-with tcp-reset */
                if( (t = fwd_xt_get_target(r, "REJECT")) != NULL )
                {
-                       fwd_xt_parse_target(r, t, "--reject-with", "tcp-reset", 0);
+                       fwd_xt_parse_target(r, t, "--reject-with", "tcp-reset");
                }
 
                /* -A handle_reject */
-               fwd_xt_exec_rule(r, "handle_reject");
+               fwd_xt_append_rule(r, "handle_reject");
        }
 
        /* common reject rule */
@@ -399,11 +206,11 @@ static void fwd_r_handle_reject(struct iptc_handle *h)
                if( (t = fwd_xt_get_target(r, "REJECT")) != NULL )
                {
                        fwd_xt_parse_target(r, t, "--reject-with",
-                               "icmp-port-unreachable", 0);
+                               "icmp-port-unreachable");
                }
 
                /* -A handle_reject */
-               fwd_xt_exec_rule(r, "handle_reject");
+               fwd_xt_append_rule(r, "handle_reject");
        }
 }
 
@@ -418,9 +225,9 @@ static void fwd_r_handle_drop(struct iptc_handle *h)
        /* common drop rule */
        if( (r = fwd_xt_init_rule(h)) != NULL )
        {
-               /* -j DROP; -A handle_reject */
+               /* -j DROP; -A handle_drop */
                fwd_xt_get_target(r, "DROP");
-               fwd_xt_exec_rule(r, "handle_reject");
+               fwd_xt_append_rule(r, "handle_drop");
        }
 }
 
@@ -437,10 +244,178 @@ static void fwd_r_handle_accept(struct iptc_handle *h)
        {
                /* -j ACCEPT; -A handle_accept */
                fwd_xt_get_target(r, "ACCEPT");
-               fwd_xt_exec_rule(r, "handle_accept");
+               fwd_xt_append_rule(r, "handle_accept");
+       }
+}
+
+/* add comment match */
+static void fwd_r_add_comment(
+       struct fwd_xt_rule *r, const char *t, struct fwd_zone *z,
+       struct fwd_network_list *n, struct fwd_network_list *n2
+) {
+       struct xtables_match *m;
+       char buf[256];
+
+       if( (m = fwd_xt_get_match(r, "comment")) != NULL )
+       {
+               if( (n != NULL) && (n2 != NULL) )
+                       snprintf(buf, sizeof(buf), "%s:%s src:%s dest:%s",
+                               t, z->name, n->name, n2->name);
+               else if( (n == NULL) && (n2 != NULL) )
+                       snprintf(buf, sizeof(buf), "%s:%s dest:%s", t, z->name, n2->name);
+               else
+                       snprintf(buf, sizeof(buf), "%s:%s src:%s", t, z->name, n->name);
+
+               fwd_xt_parse_match(r, m, "--comment", buf);
+       }
+}
+
+/* add --sport (if applicable) */
+static void fwd_r_add_sport(
+       struct fwd_xt_rule *r, struct fwd_portrange *p
+) {
+       int proto = r->entry->ip.proto;
+       char buf[12];
+       struct xtables_match *m;
+
+       /* have portrange and proto is tcp or udp ... */
+       if( (p != NULL) && ((proto == 6) || (proto == 17)) )
+       {
+               /* get match ... */
+               if( (m = fwd_xt_get_match(r, (proto == 6) ? "tcp" : "udp")) != NULL )
+               {
+                       snprintf(buf, sizeof(buf), "%u:%u", p->min, p->max);
+                       fwd_xt_parse_match(r, m, "--sport", buf);
+               }
+       }
+}
+
+/* add --dport (if applicable) */
+static void fwd_r_add_dport(
+       struct fwd_xt_rule *r, struct fwd_portrange *p
+) {
+       int proto = r->entry->ip.proto;
+       char buf[12];
+       struct xtables_match *m;
+
+       /* have portrange and proto is tcp or udp ... */
+       if( (p != NULL) && ((proto == 6) || (proto == 17)) )
+       {
+               /* get match ... */
+               if( (m = fwd_xt_get_match(r, (proto == 6) ? "tcp" : "udp")) != NULL )
+               {
+                       snprintf(buf, sizeof(buf), "%u:%u", p->min, p->max);
+                       fwd_xt_parse_match(r, m, "--dport", buf);
+               }
+       }
+}
+
+/* add --icmp-type (of applicable) */
+static void fwd_r_add_icmptype(
+       struct fwd_xt_rule *r, struct fwd_icmptype *i
+) {
+       int proto = r->entry->ip.proto;
+       struct xtables_match *m;
+       char buf[32];
+
+       /* have icmp-type and proto is icmp ... */
+       if( (i != NULL) && (proto == 1) )
+       {
+               /* get match ... */
+               if( (m = fwd_xt_get_match(r, "icmp")) != NULL )
+               {
+                       if( i->name[0] )
+                               snprintf(buf, sizeof(buf), "%s", i->name);
+                       else
+                               snprintf(buf, sizeof(buf), "%u/%u", i->type, i->code);
+
+                       fwd_xt_parse_match(r, m, "--icmp-type", buf);
+               }
+       }
+}
+
+/* add -m mac --mac-source ... */
+static void fwd_r_add_srcmac(
+       struct fwd_xt_rule *r, struct fwd_mac *mac
+) {
+       struct xtables_match *m;
+       char buf[18];
+
+       if( mac != NULL )
+       {
+               if( (m = fwd_xt_get_match(r, "mac")) != NULL )
+               {
+                       snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
+                               mac->mac[0], mac->mac[1], mac->mac[2],
+                               mac->mac[3], mac->mac[4], mac->mac[5]);
+
+                       fwd_xt_parse_match(r, m, "--mac-source", buf);
+               }
+       }
+}
+
+/* add policy target */
+static void fwd_r_add_policytarget(
+       struct fwd_xt_rule *r, enum fwd_policy pol
+) {
+       switch(pol)
+       {
+               case FWD_P_ACCEPT:
+                       fwd_xt_get_target(r, "handle_accept");
+                       break;
+
+               case FWD_P_REJECT:
+                       fwd_xt_get_target(r, "handle_reject");
+                       break;
+
+               case FWD_P_DROP:
+               case FWD_P_UNSPEC:
+                       fwd_xt_get_target(r, "handle_drop");
+                       break;
+       }
+}
+
+/* add dnat target */
+static void fwd_r_add_dnattarget(
+       struct fwd_xt_rule *r, struct fwd_cidr *c, struct fwd_portrange *p
+) {
+       struct xtables_target *t;
+       char buf[32];
+
+       if( c != NULL )
+       {
+               if( (t = fwd_xt_get_target(r, "DNAT")) != NULL )
+               {
+                       if( p != NULL )
+                               snprintf(buf, sizeof(buf), "%s:%u-%u",
+                                       inet_ntoa(c->addr), p->min, p->max);
+                       else
+                               snprintf(buf, sizeof(buf), "%s", inet_ntoa(c->addr));
+
+                       fwd_xt_parse_target(r, t, "--to-destination", buf);
+               }
        }
 }
 
+/* parse comment string and look for match */
+static int fwd_r_cmp(const char *what, const char *cmt, const char *cmp)
+{
+       char *match;
+
+       if( (match = strstr(cmt, what)) == NULL )
+               return 0;
+
+       match += strlen(what);
+
+       if( strncmp(match, cmp, strlen(cmp)) != 0 )
+               return 0;
+
+       if( (match[strlen(cmp)] != ' ') && (match[strlen(cmp)] != '\0') )
+               return 0;
+
+       return 1;
+}
+
 
 static void fwd_ipt_defaults_create(struct fwd_data *d)
 {
@@ -494,9 +469,10 @@ static void fwd_ipt_defaults_create(struct fwd_data *d)
        fwd_r_jump_chain(h_filter, "FORWARD", "forwardings");
        fwd_r_new_chain(h_nat, "zonemasq");
        fwd_r_new_chain(h_nat, "redirects");
+       fwd_r_new_chain(h_nat, "loopback");
        fwd_r_jump_chain(h_nat, "POSTROUTING", "zonemasq");
        fwd_r_jump_chain(h_nat, "PREROUTING", "redirects");
-       fwd_r_jump_chain(h_nat, "POSTROUTING", "redirects");
+       fwd_r_jump_chain(h_nat, "POSTROUTING", "loopback");
 
        /* standard drop, accept, reject chain */
        fwd_r_handle_drop(h_filter);
@@ -589,12 +565,22 @@ void fwd_ipt_addif(struct fwd_handle *h, const char *net)
 {
        struct fwd_data *e;
        struct fwd_zone *z;
-       struct fwd_ipt_rulebuf *b;
        struct fwd_rule *c;
        struct fwd_redirect *r;
        struct fwd_forwarding *f;
        struct fwd_addr_list *a, *a2;
        struct fwd_network_list *n, *n2;
+       struct fwd_proto p;
+
+       struct fwd_xt_rule *x;
+       struct xtables_match *m;
+       struct xtables_target *t;
+
+       struct iptc_handle *h_filter, *h_nat;
+
+       if( !(h_filter = iptc_init("filter")) || !(h_nat = iptc_init("nat")) )
+               fwd_fatal("Unable to obtain libiptc handle");
+
 
        if( !(z = fwd_lookup_zone(h, net)) )
                return;
@@ -612,10 +598,13 @@ void fwd_ipt_addif(struct fwd_handle *h, const char *net)
        {
                printf("\n# Net %s (%s) - masq\n", n->name, n->ifname);
 
-               b = fwd_ipt_init("nat");
-               fwd_ipt_add_format(b, " -A zonemasq -o %s -j MASQUERADE", n->ifname);
-               fwd_ipt_add_comment(b, "masq", z, NULL, n);
-               fwd_ipt_exec(b);
+               if( (x = fwd_xt_init_rule(h_nat)) != NULL )
+               {
+                       fwd_xt_parse_out(x, n, 0);                                      /* -o ... */
+                       fwd_xt_get_target(x, "MASQUERADE");                     /* -j MASQUERADE */
+                       fwd_r_add_comment(x, "masq", z, NULL, n);       /* -m comment ... */
+                       fwd_xt_append_rule(x, "zonemasq");                      /* -A zonemasq */
+               }
        }
 
        /* Build MSS fix rule */
@@ -623,12 +612,26 @@ void fwd_ipt_addif(struct fwd_handle *h, const char *net)
        {
                printf("\n# Net %s (%s) - mtu_fix\n", n->name, n->ifname);
 
-               b = fwd_ipt_init("filter");
-               fwd_ipt_add_format(b,
-                       " -A mssfix -o %s -p tcp --tcp-flags SYN,RST SYN"
-                       " -j TCPMSS --clamp-mss-to-pmtu", n->ifname);
-               fwd_ipt_add_comment(b, "mssfix", z, NULL, n);
-               fwd_ipt_exec(b);
+               if( (x = fwd_xt_init_rule(h_filter)) != NULL )
+               {
+                       p.type = FWD_PR_TCP;
+                       fwd_xt_parse_out(x, n, 0);                                      /* -o ... */
+                       fwd_xt_parse_proto(x, &p, 0);                           /* -p tcp */
+
+                       /* -m tcp --tcp-flags SYN,RST SYN */
+                       if( (m = fwd_xt_get_match(x, "tcp")) != NULL )
+                               fwd_xt_parse_match(x, m, "--tcp-flags", "SYN,RST", "SYN");
+
+                       /* -j TCPMSS --clamp-mss-to-pmtu */
+                       if( (t = fwd_xt_get_target(x, "TCPMSS")) != NULL )
+                               fwd_xt_parse_target(x, t, "--clamp-mss-to-pmtu");
+
+                       /* -m comment ... */
+                       fwd_r_add_comment(x, "mssfix", z, NULL, n);
+
+                       /* -A mssfix */
+                       fwd_xt_append_rule(x, "mssfix");
+               }
        }
 
        /* Build intra-zone forwarding rules */
@@ -641,12 +644,14 @@ void fwd_ipt_addif(struct fwd_handle *h, const char *net)
                                n->name, n->ifname, z->name, n->name, n->ifname,
                                z->name, n2->name, n2->ifname);
 
-                       b = fwd_ipt_init("filter");
-                       fwd_ipt_add_format(b, " -A zones -i %s -o %s",
-                               n->ifname, n2->ifname);
-                       fwd_ipt_add_policy_target(b, z->forward);
-                       fwd_ipt_add_comment(b, "zone", z, n, n2);
-                       fwd_ipt_exec(b);
+                       if( (x = fwd_xt_init_rule(h_filter)) != NULL )
+                       {
+                               fwd_xt_parse_in(x, n, 0);                               /* -i ... */
+                               fwd_xt_parse_out(x, n2, 0);                             /* -o ... */
+                               fwd_r_add_policytarget(x, z->forward);  /* -j handle_... */
+                               fwd_r_add_comment(x, "zone", z, n, n2); /* -m comment ... */
+                               fwd_xt_append_rule(x, "zones");                 /* -A zones */
+                       }
                }
        }
 
@@ -661,12 +666,14 @@ void fwd_ipt_addif(struct fwd_handle *h, const char *net)
                                f->dest->name, n2->name, n2->ifname);
 
                        /* Build forwarding rule */
-                       b = fwd_ipt_init("filter");
-                       fwd_ipt_add_format(b, " -A forwardings -i %s -o %s",
-                               n->ifname, n2->ifname);
-                       fwd_ipt_add_policy_target(b, FWD_P_ACCEPT);
-                       fwd_ipt_add_comment(b, "forward", z, n, n2);
-                       fwd_ipt_exec(b);
+                       if( (x = fwd_xt_init_rule(h_filter)) != NULL )
+                       {
+                               fwd_xt_parse_in(x, n, 0);                                       /* -i ... */
+                               fwd_xt_parse_out(x, n2, 0);                                     /* -o ... */
+                               fwd_r_add_policytarget(x, FWD_P_ACCEPT);        /* -j handle_... */
+                               fwd_r_add_comment(x, "forward", z, n, n2);      /* -m comment ... */
+                               fwd_xt_append_rule(x, "forwardings");                   /* -A forwardings */
+                       }
                }
        }
 
@@ -677,43 +684,49 @@ void fwd_ipt_addif(struct fwd_handle *h, const char *net)
                        n->name, n->ifname, z->name, n->name, n->ifname);
 
                /* DNAT */
-               b = fwd_ipt_init("nat");
-               fwd_ipt_add_format(b, " -A redirects -i %s -d %s",
-                       n->ifname, inet_ntoa(a->ipaddr.v4));
-               fwd_ipt_add_proto(b, r->proto);
-               fwd_ipt_add_srcaddr(b, r->src_ip);
-               fwd_ipt_add_srcport(b, r->src_port);
-               fwd_ipt_add_destport(b, r->src_dport);
-               fwd_ipt_add_srcmac(b, r->src_mac);
-               fwd_ipt_add_dnat_target(b, r->dest_ip, r->dest_port);
-               fwd_ipt_add_comment(b, "redir", z, n, NULL);
-               fwd_ipt_exec(b);
+               if( (x = fwd_xt_init_rule(h_nat)) != NULL )
+               {
+                       fwd_xt_parse_in(x, n, 0);                                       /* -i ... */
+                       fwd_xt_parse_src(x, r->src_ip, 0);                      /* -s ... */
+                       fwd_xt_parse_dest(x, &a->ipaddr, 0);            /* -d ... */
+                       fwd_xt_parse_proto(x, r->proto, 0);                     /* -p ... */
+                       fwd_r_add_sport(x, r->src_port);                        /* --sport ... */
+                       fwd_r_add_dport(x, r->src_dport);                       /* --dport ... */
+                       fwd_r_add_srcmac(x, r->src_mac);                        /* -m mac --mac-source ... */
+                       fwd_r_add_dnattarget(x, r->dest_ip, r->dest_port);      /* -j DNAT ... */
+                       fwd_r_add_comment(x, "redir", z, n, NULL);      /* -m comment ... */
+                       fwd_xt_append_rule(x, "redirects");                     /* -A redirects */
+               }
 
                /* Forward */
-               b = fwd_ipt_init("filter");
-               fwd_ipt_add_format(b, " -A redirects -i %s", n->ifname);
-               fwd_ipt_add_proto(b, r->proto);
-               fwd_ipt_add_srcmac(b, r->src_mac);
-               fwd_ipt_add_srcaddr(b, r->src_ip);
-               fwd_ipt_add_srcport(b, r->src_port);
-               fwd_ipt_add_destaddr(b, r->dest_ip);
-               fwd_ipt_add_destport(b, r->dest_port);
-               fwd_ipt_add_policy_target(b, FWD_P_ACCEPT);
-               fwd_ipt_add_comment(b, "redir", z, n, NULL);
-               fwd_ipt_exec(b);
+               if( (x = fwd_xt_init_rule(h_filter)) != NULL )
+               {
+                       fwd_xt_parse_in(x, n, 0);                                       /* -i ... */
+                       fwd_xt_parse_src(x, r->src_ip, 0);                      /* -s ... */
+                       fwd_xt_parse_dest(x, r->dest_ip, 0);            /* -d ... */
+                       fwd_xt_parse_proto(x, r->proto, 0);                     /* -p ... */
+                       fwd_r_add_srcmac(x, r->src_mac);                        /* -m mac --mac-source ... */
+                       fwd_r_add_sport(x, r->src_port);                        /* --sport ... */
+                       fwd_r_add_dport(x, r->dest_port);                       /* --dport ... */
+                       fwd_r_add_policytarget(x, FWD_P_ACCEPT);        /* -j handle_accept */
+                       fwd_r_add_comment(x, "redir", z, n, NULL);      /* -m comment ... */
+                       fwd_xt_append_rule(x, "redirects");                     /* -A redirects */
+               }
 
                /* Add loopback rule if neither src_ip nor src_mac are defined */
                if( !r->src_ip && !r->src_mac )
                {
-                       b = fwd_ipt_init("nat");
-                       fwd_ipt_add_format(b, " -A redirects -i ! %s -d %s",
-                               n->ifname, inet_ntoa(r->dest_ip->addr));
-                       fwd_ipt_add_proto(b, r->proto);
-                       fwd_ipt_add_srcport(b, r->src_port);
-                       fwd_ipt_add_destport(b, r->src_dport);
-                       fwd_ipt_add_format(b, " -j MASQUERADE");
-                       fwd_ipt_add_comment(b, "redir", z, n, NULL);
-                       fwd_ipt_exec(b);
+                       if( (x = fwd_xt_init_rule(h_nat)) != NULL )
+                       {
+                               fwd_xt_parse_in(x, n, 1);                                       /* -i ! ... */
+                               fwd_xt_parse_dest(x, r->dest_ip, 0);            /* -d ... */
+                               fwd_xt_parse_proto(x, r->proto, 0);                     /* -p ... */
+                               fwd_r_add_sport(x, r->src_port);                        /* --sport ... */
+                               fwd_r_add_dport(x, r->src_dport);                       /* --dport ... */
+                               fwd_xt_get_target(x, "MASQUERADE");                     /* -j MASQUERADE */
+                               fwd_r_add_comment(x, "redir", z, n, NULL);      /* -m comment ... */
+                               fwd_xt_append_rule(x, "loopback");                      /* -A loopback */
+                       }
                }
        }
 
@@ -730,19 +743,21 @@ void fwd_ipt_addif(struct fwd_handle *h, const char *net)
                                        n->name, n->ifname, z->name, n->name, n->ifname,
                                        f->dest->name, n2->name, n2->ifname);
 
-                               b = fwd_ipt_init("filter");
-                               fwd_ipt_add_format(b, " -A rules -i %s -o %s",
-                                       n->ifname, n2->ifname);
-                               fwd_ipt_add_proto(b, c->proto);
-                               fwd_ipt_add_icmptype(b, c->icmp_type);
-                               fwd_ipt_add_srcmac(b, c->src_mac);
-                               fwd_ipt_add_srcaddr(b, c->src_ip);
-                               fwd_ipt_add_srcport(b, c->src_port);
-                               fwd_ipt_add_destaddr(b, c->dest_ip);
-                               fwd_ipt_add_destport(b, c->dest_port);
-                               fwd_ipt_add_policy_target(b, c->target);
-                               fwd_ipt_add_comment(b, "rule", z, n, n2);
-                               fwd_ipt_exec(b);
+                               if( (x = fwd_xt_init_rule(h_filter)) != NULL )
+                               {
+                                       fwd_xt_parse_in(x, n, 0);                               /* -i ... */
+                                       fwd_xt_parse_out(x, n2, 0);                             /* -o ... */
+                                       fwd_xt_parse_src(x, c->src_ip, 0);              /* -s ... */
+                                       fwd_xt_parse_dest(x, c->dest_ip, 0);    /* -d ... */
+                                       fwd_xt_parse_proto(x, c->proto, 0);             /* -p ... */
+                                       fwd_r_add_icmptype(x, c->icmp_type);    /* --icmp-type ... */
+                                       fwd_r_add_srcmac(x, c->src_mac);                /* --mac-source ... */
+                                       fwd_r_add_sport(x, c->src_port);                /* --sport ... */
+                                       fwd_r_add_dport(x, c->dest_port);               /* --dport ... */
+                                       fwd_r_add_policytarget(x, c->target);   /* -j handle_... */
+                                       fwd_r_add_comment(x, "rule", z, n, n2); /* -m comment ... */
+                                       fwd_xt_append_rule(x, "rules");                 /* -A rules */
+                               }
                        }
                }
 
@@ -752,19 +767,154 @@ void fwd_ipt_addif(struct fwd_handle *h, const char *net)
                        printf("\n# Net %s (%s) - rule Z:%s N:%s I:%s\n",
                                n->name, n->ifname, z->name, n->name, n->ifname);
 
-                       b = fwd_ipt_init("filter");
-                       fwd_ipt_add_format(b, " -A rules -i %s", n->ifname);
-                       fwd_ipt_add_proto(b, c->proto);
-                       fwd_ipt_add_icmptype(b, c->icmp_type);
-                       fwd_ipt_add_srcmac(b, c->src_mac);
-                       fwd_ipt_add_srcaddr(b, c->src_ip);
-                       fwd_ipt_add_srcport(b, c->src_port);
-                       fwd_ipt_add_destaddr(b, c->dest_ip);
-                       fwd_ipt_add_destport(b, c->dest_port);
-                       fwd_ipt_add_policy_target(b, c->target);
-                       fwd_ipt_add_comment(b, "rule", z, n, n2);
-                       fwd_ipt_exec(b);
+                       if( (x = fwd_xt_init_rule(h_filter)) != NULL )
+                       {
+                               fwd_xt_parse_in(x, n, 0);                                       /* -i ... */
+                               fwd_xt_parse_src(x, c->src_ip, 0);                      /* -s ... */
+                               fwd_xt_parse_dest(x, c->dest_ip, 0);            /* -d ... */
+                               fwd_xt_parse_proto(x, c->proto, 0);                     /* -p ... */
+                               fwd_r_add_icmptype(x, c->icmp_type);            /* --icmp-type ... */
+                               fwd_r_add_srcmac(x, c->src_mac);                        /* --mac-source ... */
+                               fwd_r_add_sport(x, c->src_port);                        /* --sport ... */
+                               fwd_r_add_dport(x, c->dest_port);                       /* --dport ... */
+                               fwd_r_add_policytarget(x, c->target);           /* -j handle_... */
+                               fwd_r_add_comment(x, "rule", z, n, NULL);       /* -m comment ... */
+                               fwd_xt_append_rule(x, "rules");                         /* -A rules */
+                       }
                }
        }
+
+       if( !iptc_commit(h_nat) )
+               fwd_fatal("Cannot commit nat table: %s", iptc_strerror(errno));
+
+       if( !iptc_commit(h_filter) )
+               fwd_fatal("Cannot commit filter table: %s", iptc_strerror(errno));
+
+       iptc_free(h_nat);
+       iptc_free(h_filter);
+}
+
+
+static void fwd_ipt_delif_table(struct iptc_handle *h, const char *net)
+{
+       struct xt_entry_match *m;
+       struct ipt_entry *e;
+       const char *chain, *comment;
+       size_t off = 0, num = 0;
+
+       /* iterate chains */
+       for( chain = iptc_first_chain(h); chain;
+            chain = iptc_next_chain(h)
+       ) {
+               /* iterate rules */
+               for( e = iptc_first_rule(chain, h), num = 0; e;
+                    e = iptc_next_rule(e, h), num++
+               ) {
+                       repeat_rule:
+
+                       /* skip entries w/o matches */
+                       if( ! e->target_offset )
+                               continue;
+
+                       /* iterate matches */
+                       for( off = sizeof(struct ipt_entry);
+                            off < e->target_offset;
+                            off += m->u.match_size
+                       ) {
+                               m = (void *)e + off;
+
+                               /* yay */
+                               if( ! strcmp(m->u.user.name, "comment") )
+                               {
+                                       /* better use struct_xt_comment_info but well... */
+                                       comment = (void *)m + sizeof(struct xt_entry_match);
+
+                                       if( fwd_r_cmp("src:", comment, net) )
+                                       {
+                                               e = iptc_next_rule(e, h);
+                                               iptc_delete_num_entry(chain, num, h);
+
+                                               if( e != NULL )
+                                                       goto repeat_rule;
+                                               else
+                                                       break;
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+void fwd_ipt_delif(struct fwd_handle *h, const char *net)
+{
+       struct iptc_handle *h_filter, *h_nat;
+
+       if( !(h_filter = iptc_init("filter")) || !(h_nat = iptc_init("nat")) )
+               fwd_fatal("Unable to obtain libiptc handle");
+
+
+       printf("\n\n#\n# delif(%s)\n#\n", net);
+
+       /* delete network related rules */
+       fwd_ipt_delif_table(h_nat, net);
+       fwd_ipt_delif_table(h_filter, net);
+
+
+       if( !iptc_commit(h_nat) )
+               fwd_fatal("Cannot commit nat table: %s", iptc_strerror(errno));
+
+       if( !iptc_commit(h_filter) )
+               fwd_fatal("Cannot commit filter table: %s", iptc_strerror(errno));
+
+       iptc_free(h_nat);
+       iptc_free(h_filter);
+}
+
+
+static void fwd_ipt_clear_ruleset_table(struct iptc_handle *h)
+{
+       const char *chain;
+
+       /* pass 1: flush all chains */
+       for( chain = iptc_first_chain(h); chain;
+            chain = iptc_next_chain(h)
+       ) {
+               iptc_flush_entries(chain, h);
+       }
+
+       /* pass 2: remove user defined chains */
+       for( chain = iptc_first_chain(h); chain;
+            chain = iptc_next_chain(h)
+       ) {
+               if( ! iptc_builtin(chain, h) )
+                       iptc_delete_chain(chain, h);
+       }
+}
+
+void fwd_ipt_clear_ruleset(struct fwd_handle *h)
+{
+       struct iptc_handle *h_filter, *h_nat;
+
+       if( !(h_filter = iptc_init("filter")) || !(h_nat = iptc_init("nat")) )
+               fwd_fatal("Unable to obtain libiptc handle");
+
+       /* flush tables */
+       fwd_ipt_clear_ruleset_table(h_nat);
+       fwd_ipt_clear_ruleset_table(h_filter);
+
+       /* revert policies */
+       fwd_r_set_policy(h_filter, "INPUT", "ACCEPT");
+       fwd_r_set_policy(h_filter, "OUTPUT", "ACCEPT");
+       fwd_r_set_policy(h_filter, "FORWARD", "ACCEPT");        
+
+
+       if( !iptc_commit(h_nat) )
+               fwd_fatal("Cannot commit nat table: %s", iptc_strerror(errno));
+
+       if( !iptc_commit(h_filter) )
+               fwd_fatal("Cannot commit filter table: %s", iptc_strerror(errno));
+
+       iptc_free(h_nat);
+       iptc_free(h_filter);
 }