iptables: add support for version 1.6.0
[project/firewall3.git] / iptables.c
index 0f41fb04fccebabc010ad378420b331ac901b203..95fc0d45835d565217d8caad63dc4486a92c426b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * firewall3 - 3rd OpenWrt UCI firewall implementation
  *
- *   Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
+ *   Copyright (C) 2013 Jo-Philipp Wich <jo@mein.io>
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -29,14 +29,30 @@ static struct xtables_globals xtg = {
        .option_offset = 0,
        .program_version = "4",
        .orig_opts = base_opts,
+#if XTABLES_VERSION_CODE > 10
+       .compat_rev = xtables_compatible_revision,
+#endif
 };
 
 static struct xtables_globals xtg6 = {
        .option_offset = 0,
        .program_version = "6",
        .orig_opts = base_opts,
+#if XTABLES_VERSION_CODE > 10
+       .compat_rev = xtables_compatible_revision,
+#endif
 };
 
+static struct {
+       bool retain;
+       int mcount, tcount;
+       struct xtables_match **matches;
+       struct xtables_target **targets;
+       void (*register_match)(struct xtables_match *);
+       void (*register_target)(struct xtables_target *);
+} xext;
+
+
 /* Required by certain extensions like SNAT and DNAT */
 int kernel_version = 0;
 
@@ -66,6 +82,7 @@ static void fw3_init_extensions(void)
 struct fw3_ipt_handle *
 fw3_ipt_open(enum fw3_family family, enum fw3_table table)
 {
+       int i;
        struct fw3_ipt_handle *h;
 
        h = fw3_alloc(sizeof(*h));
@@ -102,6 +119,14 @@ fw3_ipt_open(enum fw3_family family, enum fw3_table table)
        fw3_xt_reset();
        fw3_init_extensions();
 
+       if (xext.register_match)
+               for (i = 0; i < xext.mcount; i++)
+                       xext.register_match(xext.matches[i]);
+
+       if (xext.register_target)
+               for (i = 0; i < xext.tcount; i++)
+                       xext.register_target(xext.targets[i]);
+
        return h;
 }
 
@@ -233,6 +258,96 @@ fw3_ipt_delete_chain(struct fw3_ipt_handle *h, const char *chain)
                iptc_delete_chain(chain, h->handle);
 }
 
+static int
+get_rule_id(const void *base, unsigned int start, unsigned int end)
+{
+       uint32_t id;
+       unsigned int i;
+       const struct xt_entry_match *em;
+
+       for (i = start; i < end; i += em->u.match_size)
+       {
+               em = base + i;
+
+               if (strcmp(em->u.user.name, "id"))
+                       continue;
+
+               memcpy(&id, em->data, sizeof(id));
+
+               if ((id & FW3_ID_MASK) != FW3_ID_MAGIC)
+                       continue;
+
+               return (id & ~FW3_ID_MASK);
+       }
+
+       return -1;
+}
+
+void
+fw3_ipt_delete_id_rules(struct fw3_ipt_handle *h, const char *chain)
+{
+       unsigned int num;
+       const struct ipt_entry *e;
+       bool found;
+       int id;
+
+#ifndef DISABLE_IPV6
+       if (h->family == FW3_FAMILY_V6)
+       {
+               if (!ip6tc_is_chain(chain, h->handle))
+                       return;
+
+               do {
+                       found = false;
+
+                       const struct ip6t_entry *e6;
+                       for (num = 0, e6 = ip6tc_first_rule(chain, h->handle);
+                                e6 != NULL;
+                                num++, e6 = ip6tc_next_rule(e6, h->handle))
+                       {
+                               id = get_rule_id(e6, sizeof(*e6), e6->target_offset);
+
+                               if (id >= 0)
+                               {
+                                       if (fw3_pr_debug)
+                                               debug(h, "-D %s %u\n", chain, num + 1);
+
+                                       ip6tc_delete_num_entry(chain, num, h->handle);
+                                       found = true;
+                                       break;
+                               }
+                       }
+               } while (found);
+       }
+       else
+#endif
+       {
+               if (!iptc_is_chain(chain, h->handle))
+                       return;
+
+               do {
+                       found = false;
+
+                       for (num = 0, e = iptc_first_rule(chain, h->handle);
+                                e != NULL;
+                                num++, e = iptc_next_rule(e, h->handle))
+                       {
+                               id = get_rule_id(e, sizeof(*e), e->target_offset);
+
+                               if (id >= 0)
+                               {
+                                       if (fw3_pr_debug)
+                                               debug(h, "-D %s %u\n", chain, num + 1);
+
+                                       iptc_delete_num_entry(chain, num, h->handle);
+                                       found = true;
+                                       break;
+                               }
+                       }
+               } while (found);
+       }
+}
+
 void
 fw3_ipt_create_chain(struct fw3_ipt_handle *h, const char *fmt, ...)
 {
@@ -290,6 +405,69 @@ fw3_ipt_flush(struct fw3_ipt_handle *h)
        }
 }
 
+static bool
+chain_is_empty(struct fw3_ipt_handle *h, const char *chain)
+{
+#ifndef DISABLE_IPV6
+       if (h->family == FW3_FAMILY_V6)
+               return (!ip6tc_builtin(chain, h->handle) &&
+                       !ip6tc_first_rule(chain, h->handle));
+#endif
+
+       return (!iptc_builtin(chain, h->handle) &&
+               !iptc_first_rule(chain, h->handle));
+}
+
+void
+fw3_ipt_gc(struct fw3_ipt_handle *h)
+{
+       const char *chain;
+       bool found;
+
+#ifndef DISABLE_IPV6
+       if (h->family == FW3_FAMILY_V6)
+       {
+               do {
+                       found = false;
+
+                       for (chain = ip6tc_first_chain(h->handle);
+                                chain != NULL;
+                                chain = ip6tc_next_chain(h->handle))
+                       {
+                               if (!chain_is_empty(h, chain))
+                                       continue;
+
+                               fw3_ipt_delete_chain(h, chain);
+                               found = true;
+                               break;
+                       }
+               } while(found);
+       }
+       else
+#endif
+       {
+               do {
+                       found = false;
+
+                       for (chain = iptc_first_chain(h->handle);
+                                chain != NULL;
+                                chain = iptc_next_chain(h->handle))
+                       {
+                               warn("C=%s\n", chain);
+
+                               if (!chain_is_empty(h, chain))
+                                       continue;
+
+                               warn("D=%s\n", chain);
+
+                               fw3_ipt_delete_chain(h, chain);
+                               found = true;
+                               break;
+                       }
+               } while (found);
+       }
+}
+
 void
 fw3_ipt_commit(struct fw3_ipt_handle *h)
 {
@@ -314,17 +492,6 @@ fw3_ipt_commit(struct fw3_ipt_handle *h)
 void
 fw3_ipt_close(struct fw3_ipt_handle *h)
 {
-       if (h->libv)
-       {
-               while (h->libc > 0)
-               {
-                       h->libc--;
-                       dlclose(h->libv[h->libc]);
-               }
-
-               free(h->libv);
-       }
-
        free(h);
 }
 
@@ -336,6 +503,7 @@ fw3_ipt_rule_new(struct fw3_ipt_handle *h)
        r = fw3_alloc(sizeof(*r));
 
        r->h = h;
+       r->id = 0;
        r->argv = fw3_alloc(sizeof(char *));
        r->argv[r->argc++] = "fw3";
 
@@ -371,9 +539,11 @@ static bool
 load_extension(struct fw3_ipt_handle *h, const char *name)
 {
        char path[256];
-       void *lib, **tmp;
+       void *lib;
        const char *pfx = (h->family == FW3_FAMILY_V6) ? "libip6t" : "libipt";
 
+       xext.retain = true;
+
        snprintf(path, sizeof(path), "/usr/lib/iptables/libxt_%s.so", name);
        if (!(lib = dlopen(path, RTLD_NOW)))
        {
@@ -381,18 +551,9 @@ load_extension(struct fw3_ipt_handle *h, const char *name)
                lib = dlopen(path, RTLD_NOW);
        }
 
-       if (!lib)
-               return false;
-
-       tmp = realloc(h->libv, sizeof(lib) * (h->libc + 1));
-
-       if (!tmp)
-               return false;
+       xext.retain = false;
 
-       h->libv = tmp;
-       h->libv[h->libc++] = lib;
-
-       return true;
+       return !!lib;
 }
 
 static struct xtables_match *
@@ -876,7 +1037,7 @@ fw3_ipt_rule_time(struct fw3_ipt_rule *r, struct fw3_time *time)
        {
                for (i = 1, p = buf; i < 32; i++)
                {
-                       if (hasbit(time->monthdays, i))
+                       if (fw3_hasbit(time->monthdays, i))
                        {
                                if (p > buf)
                                        *p++ = ',';
@@ -885,14 +1046,14 @@ fw3_ipt_rule_time(struct fw3_ipt_rule *r, struct fw3_time *time)
                        }
                }
 
-               fw3_ipt_rule_addarg(r, hasbit(time->monthdays, 0), "--monthdays", buf);
+               fw3_ipt_rule_addarg(r, fw3_hasbit(time->monthdays, 0), "--monthdays", buf);
        }
 
        if (time->weekdays & 0xFE)
        {
                for (i = 1, p = buf; i < 8; i++)
                {
-                       if (hasbit(time->weekdays, i))
+                       if (fw3_hasbit(time->weekdays, i))
                        {
                                if (p > buf)
                                        *p++ = ',';
@@ -901,7 +1062,7 @@ fw3_ipt_rule_time(struct fw3_ipt_rule *r, struct fw3_time *time)
                        }
                }
 
-               fw3_ipt_rule_addarg(r, hasbit(time->weekdays, 0), "--weekdays", buf);
+               fw3_ipt_rule_addarg(r, fw3_hasbit(time->weekdays, 0), "--weekdays", buf);
        }
 }
 
@@ -972,7 +1133,7 @@ rule_print6(struct ip6t_entry *e)
 
        if (e->ipv6.flags & IP6T_F_PROTO)
        {
-               if (e->ipv6.flags & XT_INV_PROTO)
+               if (e->ipv6.invflags & XT_INV_PROTO)
                        printf(" !");
 
                pname = get_protoname(container_of(e, struct fw3_ipt_rule, e6));
@@ -985,7 +1146,7 @@ rule_print6(struct ip6t_entry *e)
 
        if (e->ipv6.iniface[0])
        {
-               if (e->ipv6.flags & IP6T_INV_VIA_IN)
+               if (e->ipv6.invflags & IP6T_INV_VIA_IN)
                        printf(" !");
 
                printf(" -i %s", e->ipv6.iniface);
@@ -993,7 +1154,7 @@ rule_print6(struct ip6t_entry *e)
 
        if (e->ipv6.outiface[0])
        {
-               if (e->ipv6.flags & IP6T_INV_VIA_OUT)
+               if (e->ipv6.invflags & IP6T_INV_VIA_OUT)
                        printf(" !");
 
                printf(" -o %s", e->ipv6.outiface);
@@ -1001,7 +1162,7 @@ rule_print6(struct ip6t_entry *e)
 
        if (memcmp(&e->ipv6.src, &in6addr_any, sizeof(struct in6_addr)))
        {
-               if (e->ipv6.flags & IP6T_INV_SRCIP)
+               if (e->ipv6.invflags & IP6T_INV_SRCIP)
                        printf(" !");
 
                printf(" -s %s/%s",
@@ -1011,7 +1172,7 @@ rule_print6(struct ip6t_entry *e)
 
        if (memcmp(&e->ipv6.dst, &in6addr_any, sizeof(struct in6_addr)))
        {
-               if (e->ipv6.flags & IP6T_INV_DSTIP)
+               if (e->ipv6.invflags & IP6T_INV_DSTIP)
                        printf(" !");
 
                printf(" -d %s/%s",
@@ -1030,7 +1191,7 @@ rule_print4(struct ipt_entry *e)
 
        if (e->ip.proto)
        {
-               if (e->ip.flags & XT_INV_PROTO)
+               if (e->ip.invflags & XT_INV_PROTO)
                        printf(" !");
 
                pname = get_protoname(container_of(e, struct fw3_ipt_rule, e));
@@ -1043,7 +1204,7 @@ rule_print4(struct ipt_entry *e)
 
        if (e->ip.iniface[0])
        {
-               if (e->ip.flags & IPT_INV_VIA_IN)
+               if (e->ip.invflags & IPT_INV_VIA_IN)
                        printf(" !");
 
                printf(" -i %s", e->ip.iniface);
@@ -1051,7 +1212,7 @@ rule_print4(struct ipt_entry *e)
 
        if (e->ip.outiface[0])
        {
-               if (e->ip.flags & IPT_INV_VIA_OUT)
+               if (e->ip.invflags & IPT_INV_VIA_OUT)
                        printf(" !");
 
                printf(" -o %s", e->ip.outiface);
@@ -1059,7 +1220,7 @@ rule_print4(struct ipt_entry *e)
 
        if (memcmp(&e->ip.src, &in_zero, sizeof(struct in_addr)))
        {
-               if (e->ip.flags & IPT_INV_SRCIP)
+               if (e->ip.invflags & IPT_INV_SRCIP)
                        printf(" !");
 
                printf(" -s %s/%s",
@@ -1069,7 +1230,7 @@ rule_print4(struct ipt_entry *e)
 
        if (memcmp(&e->ip.dst, &in_zero, sizeof(struct in_addr)))
        {
-               if (e->ip.flags & IPT_INV_DSTIP)
+               if (e->ip.invflags & IPT_INV_DSTIP)
                        printf(" !");
 
                printf(" -d %s/%s",
@@ -1315,6 +1476,7 @@ __fw3_ipt_rule_append(struct fw3_ipt_rule *r, bool repl, const char *fmt, ...)
        struct xtables_globals *g;
 
        int i, optc;
+       uint32_t id;
        bool inv = false;
        char buf[32];
        va_list ap;
@@ -1329,6 +1491,24 @@ __fw3_ipt_rule_append(struct fw3_ipt_rule *r, bool repl, const char *fmt, ...)
        optind = 0;
        opterr = 0;
 
+       if (r->id >= 0)
+       {
+               em = find_match(r, "id");
+
+               if (!em)
+               {
+                       warn("fw3_ipt_rule_append(): Can't find match '%s'", "id");
+                       goto free;
+               }
+
+               init_match(r, em, true);
+
+               id = FW3_ID_MAGIC | (r->id & ~FW3_ID_MASK);
+               memcpy(em->m->data, &id, sizeof(id));
+
+               em->mflags = 1;
+       }
+
        while ((optc = getopt_long(r->argc, r->argv, "-:m:j:", g->opts,
                                   NULL)) != -1)
        {
@@ -1469,3 +1649,63 @@ fw3_ipt_rule_create(struct fw3_ipt_handle *handle, struct fw3_protocol *proto,
 
        return r;
 }
+
+void
+xtables_register_match(struct xtables_match *me)
+{
+       int i;
+       static struct xtables_match **tmp;
+
+       if (!xext.register_match)
+               xext.register_match = dlsym(RTLD_NEXT, "xtables_register_match");
+
+       if (!xext.register_match)
+               return;
+
+       xext.register_match(me);
+
+       if (xext.retain)
+       {
+               for (i = 0; i < xext.mcount; i++)
+                       if (xext.matches[i] == me)
+                               return;
+
+               tmp = realloc(xext.matches, sizeof(me) * (xext.mcount + 1));
+
+               if (!tmp)
+                       return;
+
+               xext.matches = tmp;
+               xext.matches[xext.mcount++] = me;
+       }
+}
+
+void
+xtables_register_target(struct xtables_target *me)
+{
+       int i;
+       static struct xtables_target **tmp;
+
+       if (!xext.register_target)
+               xext.register_target = dlsym(RTLD_NEXT, "xtables_register_target");
+
+       if (!xext.register_target)
+               return;
+
+       xext.register_target(me);
+
+       if (xext.retain)
+       {
+               for (i = 0; i < xext.tcount; i++)
+                       if (xext.targets[i] == me)
+                               return;
+
+               tmp = realloc(xext.targets, sizeof(me) * (xext.tcount + 1));
+
+               if (!tmp)
+                       return;
+
+               xext.targets = tmp;
+               xext.targets[xext.tcount++] = me;
+       }
+}