Use netmasks instead of prefix lengths internally
authorJo-Philipp Wich <jow@openwrt.org>
Fri, 18 Jul 2014 13:43:56 +0000 (15:43 +0200)
committerJo-Philipp Wich <jow@openwrt.org>
Sat, 19 Jul 2014 09:48:39 +0000 (11:48 +0200)
Iptables supports using non-continuous netmasks like FFFF::FFFF which would
match the first and last 16bit of an IPv6 address while ignoring the parts
in between which is useful fordeclaring rules targeting hosts on rotating
prefixes.

Instead of storing parsed netmasks as bitcount internally, use a full mask
which is passed to iptables as-is.

Also support a new shorthand notation "addr/-N" which will construct a mask
that matches the *last* N bits of an address - useful for matching the host
part only of an IPv4 address, e.g.

  option dest_ip '::c23f:eff:fe7a:a094/-64'

This will convert to a netmask of "::ffff:ffff:ffff:ffff".

Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
ipsets.c
iptables.c
options.c
options.h
redirects.c
ubus.c
utils.c
utils.h

index 5b319a58e8fbf5120f3dbc08494906aadb79ce30..993cc1f9fb07f69569dab612ec7dd6ac483aae33 100644 (file)
--- a/ipsets.c
+++ b/ipsets.c
@@ -303,7 +303,7 @@ create_ipset(struct fw3_ipset *ipset, struct fw3_state *state)
 
        if (ipset->iprange.set)
        {
-               fw3_pr(" range %s", fw3_address_to_string(&ipset->iprange, false));
+               fw3_pr(" range %s", fw3_address_to_string(&ipset->iprange, false, true));
        }
        else if (ipset->portrange.set)
        {
index 58ec752af8df55e68f0cc1fd3215f412dd7c781c..03987af81248b0bde0019f995979159658d18373 100644 (file)
@@ -606,34 +606,6 @@ fw3_ipt_rule_in_out(struct fw3_ipt_rule *r,
 }
 
 
-static void
-ip4prefix2mask(int prefix, struct in_addr *mask)
-{
-       if (prefix > 0)
-               mask->s_addr = htonl(~((1 << (32 - prefix)) - 1));
-       else
-               mask->s_addr = 0;
-}
-
-#ifndef DISABLE_IPV6
-static void
-ip6prefix2mask(int prefix, struct in6_addr *mask)
-{
-       char *p = (char *)mask;
-
-       if (prefix > 0)
-       {
-               memset(p, 0xff, prefix / 8);
-               memset(p + (prefix / 8) + 1, 0, (128 - prefix) / 8);
-               p[prefix / 8] = 0xff << (8 - (prefix & 7));
-       }
-       else
-       {
-               memset(mask, 0, sizeof(*mask));
-       }
-}
-#endif
-
 void
 fw3_ipt_rule_src_dest(struct fw3_ipt_rule *r,
                       struct fw3_address *src, struct fw3_address *dest)
@@ -648,13 +620,13 @@ fw3_ipt_rule_src_dest(struct fw3_ipt_rule *r,
                if (src->range)
                {
                        fw3_ipt_rule_addarg(r, src->invert, "--src-range",
-                                           fw3_address_to_string(src, false));
+                                           fw3_address_to_string(src, false, false));
                }
 #ifndef DISABLE_IPV6
                else if (r->h->family == FW3_FAMILY_V6)
                {
                        r->e6.ipv6.src = src->address.v6;
-                       ip6prefix2mask(src->mask, &r->e6.ipv6.smsk);
+                       r->e6.ipv6.smsk = src->mask.v6;
 
                        int i;
                        for (i = 0; i < 4; i++)
@@ -667,7 +639,7 @@ fw3_ipt_rule_src_dest(struct fw3_ipt_rule *r,
                else
                {
                        r->e.ip.src = src->address.v4;
-                       ip4prefix2mask(src->mask, &r->e.ip.smsk);
+                       r->e.ip.smsk = src->mask.v4;
 
                        r->e.ip.src.s_addr &= r->e.ip.smsk.s_addr;
 
@@ -681,13 +653,13 @@ fw3_ipt_rule_src_dest(struct fw3_ipt_rule *r,
                if (dest->range)
                {
                        fw3_ipt_rule_addarg(r, dest->invert, "--dst-range",
-                                           fw3_address_to_string(dest, false));
+                                           fw3_address_to_string(dest, false, false));
                }
 #ifndef DISABLE_IPV6
                else if (r->h->family == FW3_FAMILY_V6)
                {
                        r->e6.ipv6.dst = dest->address.v6;
-                       ip6prefix2mask(dest->mask, &r->e6.ipv6.dmsk);
+                       r->e6.ipv6.dmsk = dest->mask.v6;
 
                        int i;
                        for (i = 0; i < 4; i++)
@@ -700,7 +672,7 @@ fw3_ipt_rule_src_dest(struct fw3_ipt_rule *r,
                else
                {
                        r->e.ip.dst = dest->address.v4;
-                       ip4prefix2mask(dest->mask, &r->e.ip.dmsk);
+                       r->e.ip.dmsk = dest->mask.v4;
 
                        r->e.ip.dst.s_addr &= r->e.ip.dmsk.s_addr;
 
@@ -1003,7 +975,7 @@ fw3_ipt_rule_extra(struct fw3_ipt_rule *r, const char *extra)
 static void
 rule_print6(struct ip6t_entry *e)
 {
-       char buf[INET6_ADDRSTRLEN];
+       char buf1[INET6_ADDRSTRLEN], buf2[INET6_ADDRSTRLEN];
        char *pname;
 
        if (e->ipv6.flags & IP6T_F_PROTO)
@@ -1040,8 +1012,9 @@ rule_print6(struct ip6t_entry *e)
                if (e->ipv6.flags & IP6T_INV_SRCIP)
                        printf(" !");
 
-               printf(" -s %s/%u", inet_ntop(AF_INET6, &e->ipv6.src, buf, sizeof(buf)),
-                                   xtables_ip6mask_to_cidr(&e->ipv6.smsk));
+               printf(" -s %s/%s",
+                      inet_ntop(AF_INET6, &e->ipv6.src, buf1, sizeof(buf1)),
+                      inet_ntop(AF_INET6, &e->ipv6.smsk, buf2, sizeof(buf2)));
        }
 
        if (memcmp(&e->ipv6.dst, &in6addr_any, sizeof(struct in6_addr)))
@@ -1049,8 +1022,9 @@ rule_print6(struct ip6t_entry *e)
                if (e->ipv6.flags & IP6T_INV_DSTIP)
                        printf(" !");
 
-               printf(" -d %s/%u", inet_ntop(AF_INET6, &e->ipv6.dst, buf, sizeof(buf)),
-                                   xtables_ip6mask_to_cidr(&e->ipv6.dmsk));
+               printf(" -d %s/%s",
+                      inet_ntop(AF_INET6, &e->ipv6.dst, buf1, sizeof(buf1)),
+                      inet_ntop(AF_INET6, &e->ipv6.dmsk, buf2, sizeof(buf2)));
        }
 }
 #endif
@@ -1059,7 +1033,7 @@ static void
 rule_print4(struct ipt_entry *e)
 {
        struct in_addr in_zero = { 0 };
-       char buf[sizeof("255.255.255.255\0")];
+       char buf1[sizeof("255.255.255.255\0")], buf2[sizeof("255.255.255.255\0")];
        char *pname;
 
        if (e->ip.proto)
@@ -1096,8 +1070,9 @@ rule_print4(struct ipt_entry *e)
                if (e->ip.flags & IPT_INV_SRCIP)
                        printf(" !");
 
-               printf(" -s %s/%u", inet_ntop(AF_INET, &e->ip.src, buf, sizeof(buf)),
-                                   xtables_ipmask_to_cidr(&e->ip.smsk));
+               printf(" -s %s/%s",
+                      inet_ntop(AF_INET, &e->ip.src, buf1, sizeof(buf1)),
+                      inet_ntop(AF_INET, &e->ip.smsk, buf2, sizeof(buf2)));
        }
 
        if (memcmp(&e->ip.dst, &in_zero, sizeof(struct in_addr)))
@@ -1105,8 +1080,9 @@ rule_print4(struct ipt_entry *e)
                if (e->ip.flags & IPT_INV_DSTIP)
                        printf(" !");
 
-               printf(" -d %s/%u", inet_ntop(AF_INET, &e->ip.dst, buf, sizeof(buf)),
-                                   xtables_ipmask_to_cidr(&e->ip.dmsk));
+               printf(" -d %s/%s",
+                      inet_ntop(AF_INET, &e->ip.dst, buf1, sizeof(buf1)),
+                      inet_ntop(AF_INET, &e->ip.dmsk, buf2, sizeof(buf2)));
        }
 }
 
index 25668fcec9ef4addd3f5012b4d894b00211a6d91..292f5fc322f7d93facb08f8c1a2458708c80bfa3 100644 (file)
--- a/options.c
+++ b/options.c
@@ -244,8 +244,8 @@ fw3_parse_address(void *ptr, const char *val, bool is_list)
        struct fw3_address addr = { };
        struct in_addr v4;
        struct in6_addr v6;
-       char *p, *s, *e;
-       int i, m = -1;
+       char *p = NULL, *m = NULL, *s, *e;
+       int bits = -1;
 
        if (*val == '!')
        {
@@ -258,71 +258,76 @@ fw3_parse_address(void *ptr, const char *val, bool is_list)
        if (!s)
                return false;
 
-       if ((p = strchr(s, '/')) != NULL)
-       {
+       if ((m = strchr(s, '/')) != NULL)
+               *m++ = 0;
+       else if ((p = strchr(s, '-')) != NULL)
                *p++ = 0;
-               m = strtoul(p, &e, 10);
 
-               if ((e == p) || (*e != 0))
-               {
-                       if (strchr(s, ':') || !inet_pton(AF_INET, p, &v4))
-                       {
-                               free(s);
-                               return false;
-                       }
-
-                       for (i = 0, m = 32; !(v4.s_addr & 1) && (i < 32); i++)
-                       {
-                               m--;
-                               v4.s_addr >>= 1;
-                       }
-               }
-       }
-       else if ((p = strchr(s, '-')) != NULL)
+       if (inet_pton(AF_INET6, s, &v6))
        {
-               *p++ = 0;
+               addr.family = FW3_FAMILY_V6;
+               addr.address.v6 = v6;
 
-               if (inet_pton(AF_INET6, p, &v6))
+               if (m && !inet_pton(AF_INET6, m, &addr.mask.v6))
                {
-                       addr.family = FW3_FAMILY_V6;
-                       addr.address2.v6 = v6;
-                       addr.range = true;
+                       bits = strtol(m, &e, 10);
+
+                       if ((*e != 0) || !fw3_bitlen2netmask(addr.family, bits, &v6))
+                               goto fail;
+
+                       addr.mask.v6 = v6;
                }
-               else if (inet_pton(AF_INET, p, &v4))
+               else if (p)
                {
-                       addr.family = FW3_FAMILY_V4;
-                       addr.address2.v4 = v4;
+                       if (!inet_pton(AF_INET6, p, &addr.mask.v6))
+                               goto fail;
+
                        addr.range = true;
                }
                else
                {
-                       free(s);
-                       return false;
+                       memset(addr.mask.v6.s6_addr, 0xFF, 16);
                }
        }
-
-       if (inet_pton(AF_INET6, s, &v6))
-       {
-               addr.family = FW3_FAMILY_V6;
-               addr.address.v6 = v6;
-               addr.mask = (m >= 0) ? m : 128;
-       }
        else if (inet_pton(AF_INET, s, &v4))
        {
                addr.family = FW3_FAMILY_V4;
                addr.address.v4 = v4;
-               addr.mask = (m >= 0) ? m : 32;
+
+               if (m && !inet_pton(AF_INET, m, &addr.mask.v4))
+               {
+                       bits = strtol(m, &e, 10);
+
+                       if ((*e != 0) || !fw3_bitlen2netmask(addr.family, bits, &v4))
+                               goto fail;
+
+                       addr.mask.v4 = v4;
+               }
+               else if (p)
+               {
+                       if (!inet_pton(AF_INET, p, &addr.mask.v4))
+                               goto fail;
+
+                       addr.range = true;
+               }
+               else
+               {
+                       addr.mask.v4.s_addr = 0xFFFFFFFF;
+               }
        }
        else
        {
-               free(s);
-               return false;
+               goto fail;
        }
 
        free(s);
        addr.set = true;
        put_value(ptr, &addr, sizeof(addr), is_list);
        return true;
+
+fail:
+       free(s);
+       return false;
 }
 
 bool
@@ -1070,7 +1075,7 @@ fw3_parse_blob_options(void *s, const struct fw3_option *opts,
 
 
 const char *
-fw3_address_to_string(struct fw3_address *address, bool allow_invert)
+fw3_address_to_string(struct fw3_address *address, bool allow_invert, bool as_cidr)
 {
        char *p, ip[INET6_ADDRSTRLEN];
        static char buf[INET6_ADDRSTRLEN * 2 + 2];
@@ -1088,13 +1093,21 @@ fw3_address_to_string(struct fw3_address *address, bool allow_invert)
        if (address->range)
        {
                inet_ntop(address->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
-                         &address->address2.v4, ip, sizeof(ip));
+                         &address->mask.v4, ip, sizeof(ip));
 
                p += sprintf(p, "-%s", ip);
        }
+       else if (!as_cidr)
+       {
+               inet_ntop(address->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
+                         &address->mask.v4, ip, sizeof(ip));
+
+               p += sprintf(p, "/%s", ip);
+       }
        else
        {
-               p += sprintf(p, "/%u", address->mask);
+               p += sprintf(p, "/%u", fw3_netmask2bitlen(address->family,
+                                                         &address->mask.v6));
        }
 
        return buf;
index 0a2fa7f45c54005b4d3e624e128b9aa9a49018d3..28de48ef62826fa129717bb452a96850dfc6a3d8 100644 (file)
--- a/options.h
+++ b/options.h
@@ -175,7 +175,6 @@ struct fw3_address
        bool invert;
        bool resolved;
        enum fw3_family family;
-       int mask;
        union {
                struct in_addr v4;
                struct in6_addr v6;
@@ -185,7 +184,7 @@ struct fw3_address
                struct in_addr v4;
                struct in6_addr v6;
                struct ether_addr mac;
-       } address2;
+       } mask;
 };
 
 struct fw3_mac
@@ -563,6 +562,6 @@ bool fw3_parse_blob_options(void *s, const struct fw3_option *opts,
                        struct blob_attr *a);
 
 const char * fw3_address_to_string(struct fw3_address *address,
-                                   bool allow_invert);
+                                   bool allow_invert, bool as_cidr);
 
 #endif
index 080e2c17d49fc1117d1467260ab81cda0eb9b8d6..5dea21f7c9cff72a4e8b0e8c0bae837305cb39cb 100644 (file)
@@ -116,14 +116,11 @@ check_families(struct uci_element *e, struct fw3_redirect *r)
 static bool
 compare_addr(struct fw3_address *a, struct fw3_address *b)
 {
-       uint32_t mask;
-
        if (a->family != FW3_FAMILY_V4 || b->family != FW3_FAMILY_V4)
                return false;
 
-       mask = htonl(~((1 << (32 - a->mask)) - 1));
-
-       return ((a->address.v4.s_addr & mask) == (b->address.v4.s_addr & mask));
+       return ((a->address.v4.s_addr & a->mask.v4.s_addr) ==
+               (b->address.v4.s_addr & a->mask.v4.s_addr));
 }
 
 static bool
@@ -603,8 +600,8 @@ expand_redirect(struct fw3_ipt_handle *handle, struct fw3_state *state,
                                else
                                        ref_addr = *ext_addr;
 
-                               ref_addr.mask = 32;
-                               ext_addr->mask = 32;
+                               ref_addr.mask.v4.s_addr = 0xFFFFFFFF;
+                               ext_addr->mask.v4.s_addr = 0xFFFFFFFF;
 
                                print_reflection(handle, state, redir, num, proto,
                                                                 &ref_addr, int_addr, ext_addr);
diff --git a/ubus.c b/ubus.c
index 0d83e0f497b919b26e732ae93f7681bac0d56714..3031e84e9f8158da421ee1468c881549d11d636f 100644 (file)
--- a/ubus.c
+++ b/ubus.c
@@ -94,10 +94,10 @@ parse_subnet(enum fw3_family family, struct blob_attr *dict, int rem)
        {
                if (!strcmp(blobmsg_name(cur), "address"))
                        inet_pton(family == FW3_FAMILY_V4 ? AF_INET : AF_INET6,
-                                 blobmsg_data(cur), &addr->address.v6);
+                                 blobmsg_get_string(cur), &addr->address.v6);
 
                else if (!strcmp(blobmsg_name(cur), "mask"))
-                       addr->mask = be32_to_cpu(*(uint32_t *)blobmsg_data(cur));
+                       fw3_bitlen2netmask(family, blobmsg_get_u32(cur), &addr->mask.v6);
        }
 
        return addr;
diff --git a/utils.c b/utils.c
index 4f309559088ee27da28fbbcf26b419ec76274948..fa4a73f9e692723490a66b5696d6cf8103e74244 100644 (file)
--- a/utils.c
+++ b/utils.c
@@ -514,7 +514,7 @@ write_zone_uci(struct uci_context *ctx, struct fw3_zone *z,
                if (!sub)
                        continue;
 
-               ptr.value = fw3_address_to_string(sub, true);
+               ptr.value = fw3_address_to_string(sub, true, false);
                uci_add_list(ctx, &ptr);
        }
 
@@ -569,7 +569,7 @@ write_ipset_uci(struct uci_context *ctx, struct fw3_ipset *s,
        {
                ptr.o      = NULL;
                ptr.option = "iprange";
-               ptr.value  = fw3_address_to_string(&s->iprange, false);
+               ptr.value  = fw3_address_to_string(&s->iprange, false, false);
                uci_set(ctx, &ptr);
        }
 
@@ -710,3 +710,60 @@ fw3_hotplug(bool add, void *zone, void *device)
        /* unreached */
        return false;
 }
+
+int
+fw3_netmask2bitlen(int family, void *mask)
+{
+       int bits;
+       struct in_addr *v4;
+       struct in6_addr *v6;
+
+       if (family == FW3_FAMILY_V6)
+               for (bits = 0, v6 = mask;
+                    bits < 128 && (v6->s6_addr[bits / 8] << (bits % 8)) & 128;
+                    bits++);
+       else
+               for (bits = 0, v4 = mask;
+                    bits < 32 && (ntohl(v4->s_addr) << bits) & 0x80000000;
+                    bits++);
+
+       return bits;
+}
+
+bool
+fw3_bitlen2netmask(int family, int bits, void *mask)
+{
+       int i;
+       struct in_addr *v4;
+       struct in6_addr *v6;
+
+       if (family == FW3_FAMILY_V6)
+       {
+               if (bits < -128 || bits > 128)
+                       return false;
+
+               v6 = mask;
+               i = abs(bits);
+
+               memset(v6->s6_addr, 0xff, i / 8);
+               memset(v6->s6_addr + (i / 8) + 1, 0, (128 - i) / 8);
+               v6->s6_addr[i / 8] = 0xff << (8 - (i & 7));
+
+               if (bits < 0)
+                       for (i = 0; i < 16; i++)
+                               v6->s6_addr[i] = ~v6->s6_addr[i];
+       }
+       else
+       {
+               if (bits < -32 || bits > 32)
+                       return false;
+
+               v4 = mask;
+               v4->s_addr = htonl(~((1 << (32 - abs(bits))) - 1));
+
+               if (bits < 0)
+                       v4->s_addr = ~v4->s_addr;
+       }
+
+       return true;
+}
diff --git a/utils.h b/utils.h
index cd478f9d80886855ecf88e3df3319130c50233f0..d2e1aa68e8eacc9b8d196b83d89a55a102e6c9bd 100644 (file)
--- a/utils.h
+++ b/utils.h
@@ -95,4 +95,8 @@ void fw3_free_list(struct list_head *head);
 
 bool fw3_hotplug(bool add, void *zone, void *device);
 
+int fw3_netmask2bitlen(int family, void *mask);
+
+bool fw3_bitlen2netmask(int family, int bits, void *mask);
+
 #endif