From bba31cce0521e014109fc805671d4cff7ee9dbf6 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Fri, 18 Jul 2014 15:43:56 +0200 Subject: [PATCH] Use netmasks instead of prefix lengths internally 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 --- ipsets.c | 2 +- iptables.c | 64 +++++++++++---------------------- options.c | 101 +++++++++++++++++++++++++++++----------------------- options.h | 5 ++- redirects.c | 11 +++--- ubus.c | 4 +-- utils.c | 61 +++++++++++++++++++++++++++++-- utils.h | 4 +++ 8 files changed, 149 insertions(+), 103 deletions(-) diff --git a/ipsets.c b/ipsets.c index 5b319a5..993cc1f 100644 --- 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) { diff --git a/iptables.c b/iptables.c index 58ec752..03987af 100644 --- a/iptables.c +++ b/iptables.c @@ -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))); } } diff --git a/options.c b/options.c index 25668fc..292f5fc 100644 --- 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; diff --git a/options.h b/options.h index 0a2fa7f..28de48e 100644 --- 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 diff --git a/redirects.c b/redirects.c index 080e2c1..5dea21f 100644 --- a/redirects.c +++ b/redirects.c @@ -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 0d83e0f..3031e84 100644 --- 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 4f30955..fa4a73f 100644 --- 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 cd478f9..d2e1aa6 100644 --- 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 -- 2.30.2