From 268d7d8794f3f8a6c2d6f08dc4351e767990e683 Mon Sep 17 00:00:00 2001 From: "R. Christian McDonald" Date: Tue, 2 Jun 2026 11:57:11 -0400 Subject: [PATCH] ipv6: support 32-bit platforms without __uint128_t IPv6 support (2.0.0) relied on the __uint128_t builtin, which i386 and armv7 lack, so the build hard-errored there. Add src/uint128.h: on 64-bit it typedefs __uint128_t and inlines the bare operators (unchanged codegen); on 32-bit it falls back to a portable {hi, lo} struct with explicit arithmetic. --- Makefile.am | 1 + configure.ac | 5 +- src/iprange6.h | 53 +++++----- src/iprange6_main.c | 24 ++--- src/ipset6.c | 4 +- src/ipset6.h | 20 ++-- src/ipset6_binary.c | 36 +++---- src/ipset6_common.c | 8 +- src/ipset6_diff.c | 24 ++--- src/ipset6_exclude.c | 16 +-- src/ipset6_load.c | 12 +-- src/ipset6_optimize.c | 16 +-- src/ipset6_print.c | 22 ++-- src/uint128.h | 230 ++++++++++++++++++++++++++++++++++++++++++ 15 files changed, 356 insertions(+), 121 deletions(-) create mode 100644 src/uint128.h diff --git a/Makefile.am b/Makefile.am index 64353db..fa9043f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -34,6 +34,7 @@ iprange_SOURCES = \ src/iprange.c \ src/iprange.h \ src/iprange6.h \ + src/uint128.h \ src/iprange6_main.c \ src/ipset.c \ src/ipset.h \ diff --git a/configure.ac b/configure.ac index 0015482..992b67b 100644 --- a/configure.ac +++ b/configure.ac @@ -73,8 +73,9 @@ CC="${PTHREAD_CC}" AC_TYPE_UINT32_T AC_C_INLINE -AC_CHECK_TYPE([__uint128_t], [], - [AC_MSG_ERROR([Your compiler does not support __uint128_t, required for IPv6 support.])], +AC_CHECK_TYPE([__uint128_t], + [AC_MSG_NOTICE([Native __uint128_t detected — using hardware 128-bit integers for IPv6])], + [AC_MSG_NOTICE([__uint128_t not available — using portable 128-bit arithmetic for IPv6])], [/* no includes needed */]) test "${with_compare_with_common}" = "yes" && AC_DEFINE([COMPARE_WITH_COMMON], [1], [compare settings]) diff --git a/src/iprange6.h b/src/iprange6.h index 2bf0871..448f28c 100644 --- a/src/iprange6.h +++ b/src/iprange6.h @@ -2,10 +2,11 @@ #define IPRANGE_IPRANGE6_H #include "iprange.h" +#include "uint128.h" #include /* IPv6 address type: 128-bit unsigned integer in host byte order */ -typedef __uint128_t ipv6_addr_t; +typedef uint128_t ipv6_addr_t; /* IPv6 network address type: one field for the net address, one for broadcast */ typedef struct network_addr6 { @@ -14,11 +15,11 @@ typedef struct network_addr6 { } network_addr6_t; /* Maximum IPv6 address */ -#define IPV6_ADDR_MAX ((ipv6_addr_t)((__uint128_t)(-1))) +#define IPV6_ADDR_MAX U128_MAX /* IPv4-mapped IPv6 prefix: ::ffff:0:0/96 */ -#define IPV6_MAPPED_PREFIX ((ipv6_addr_t)0xFFFF00000000ULL) -#define IPV6_MAPPED_MASK ((ipv6_addr_t)0xFFFFFFFFULL) +#define IPV6_MAPPED_PREFIX (u128_from_u64(0xFFFF00000000ULL)) +#define IPV6_MAPPED_MASK (u128_from_u64(0xFFFFFFFFULL)) #define IP6STR_MAX_LEN 46 @@ -28,18 +29,18 @@ typedef struct network_addr6 { /*----------------------------------------------------------------------*/ static inline ipv6_addr_t in6_addr_to_ipv6(const struct in6_addr *in6) { - ipv6_addr_t result = 0; + ipv6_addr_t result = U128_ZERO; int i; for(i = 0; i < 16; i++) - result = (result << 8) | in6->s6_addr[i]; + result = u128_or(u128_shl(result, 8), u128_from_u64(in6->s6_addr[i])); return result; } static inline void ipv6_to_in6_addr(ipv6_addr_t addr, struct in6_addr *in6) { int i; for(i = 15; i >= 0; i--) { - in6->s6_addr[i] = (uint8_t)(addr & 0xFF); - addr >>= 8; + in6->s6_addr[i] = (uint8_t)u128_lo64(addr); + addr = u128_shr(addr, 8); } } @@ -48,24 +49,24 @@ static inline void ipv6_to_in6_addr(ipv6_addr_t addr, struct in6_addr *in6) { /*----------------------------------------------*/ static inline ipv6_addr_t netmask6(int prefix) { if(prefix == 0) - return (ipv6_addr_t)0; + return U128_ZERO; if(prefix >= 128) return IPV6_ADDR_MAX; - return IPV6_ADDR_MAX << (128 - prefix); + return u128_shl(U128_MAX, 128 - prefix); } /*----------------------------------------------------*/ /* Compute broadcast address given address and prefix */ /*----------------------------------------------------*/ static inline ipv6_addr_t broadcast6(ipv6_addr_t addr, int prefix) { - return addr | ~netmask6(prefix); + return u128_or(addr, u128_not(netmask6(prefix))); } /*--------------------------------------------------*/ /* Compute network address given address and prefix */ /*--------------------------------------------------*/ static inline ipv6_addr_t network6(ipv6_addr_t addr, int prefix) { - return addr & netmask6(prefix); + return u128_and(addr, netmask6(prefix)); } /*------------------------------------------------------------------*/ @@ -73,9 +74,9 @@ static inline ipv6_addr_t network6(ipv6_addr_t addr, int prefix) { /*------------------------------------------------------------------*/ static inline ipv6_addr_t set_bit6(ipv6_addr_t addr, int bitno, int val) { if(val) - return addr | ((__uint128_t)1 << (128 - bitno)); + return u128_or(addr, u128_shl(U128_ONE, 128 - bitno)); else - return addr & ~((__uint128_t)1 << (128 - bitno)); + return u128_and(addr, u128_not(u128_shl(U128_ONE, 128 - bitno))); } /*-----------------------------------------------------------*/ @@ -121,8 +122,8 @@ static inline network_addr6_t str2netaddr6(char *ipstr, int *err) { || parsed_prefix < 0 || parsed_prefix > 128)) { if(err) (*err)++; fprintf(stderr, "%s: Invalid IPv6 prefix /%s\n", PROG, prefixstr); - netaddr.addr = 0; - netaddr.broadcast = 0; + netaddr.addr = U128_ZERO; + netaddr.broadcast = U128_ZERO; return netaddr; } prefix = (int)parsed_prefix; @@ -131,8 +132,8 @@ static inline network_addr6_t str2netaddr6(char *ipstr, int *err) { if(!str_to_ipv6(ipstr, &addr)) { if(err) (*err)++; fprintf(stderr, "%s: Invalid IPv6 address %s\n", PROG, ipstr); - netaddr.addr = 0; - netaddr.broadcast = 0; + netaddr.addr = U128_ZERO; + netaddr.broadcast = U128_ZERO; return netaddr; } @@ -149,21 +150,21 @@ static inline network_addr6_t str2netaddr6(char *ipstr, int *err) { /* Check if an IPv6 address is IPv4-mapped (::ffff:x.x.x.x) */ /*-----------------------------------------------------------*/ static inline int is_ipv4_mapped(ipv6_addr_t addr) { - return (addr >> 32) == 0xFFFF; + return u128_eq(u128_shr(addr, 32), u128_from_u64(0xFFFF)); } /*-----------------------------------------------------------*/ /* Convert IPv4 to IPv4-mapped IPv6 */ /*-----------------------------------------------------------*/ static inline ipv6_addr_t ipv4_to_mapped6(in_addr_t ipv4) { - return IPV6_MAPPED_PREFIX | (ipv6_addr_t)ipv4; + return u128_or(IPV6_MAPPED_PREFIX, u128_from_u64(ipv4)); } /*-----------------------------------------------------------*/ /* Extract IPv4 from IPv4-mapped IPv6 */ /*-----------------------------------------------------------*/ static inline in_addr_t mapped6_to_ipv4(ipv6_addr_t addr) { - return (in_addr_t)(addr & IPV6_MAPPED_MASK); + return (in_addr_t)u128_lo64(u128_and(addr, IPV6_MAPPED_MASK)); } /*-----------------------------------------------------------*/ @@ -171,18 +172,18 @@ static inline in_addr_t mapped6_to_ipv4(ipv6_addr_t addr) { /* Returns pointer to start of number within buf */ /* buf must be at least 40 bytes */ /*-----------------------------------------------------------*/ -static inline char *u128_to_dec(char *buf, size_t buflen, __uint128_t val) { +static inline char *u128_to_dec(char *buf, size_t buflen, uint128_t val) { char *p = buf + buflen - 1; *p = '\0'; - if(val == 0) { + if(u128_is_zero(val)) { *(--p) = '0'; return p; } - while(val > 0) { - *(--p) = '0' + (char)(val % 10); - val /= 10; + while(!u128_is_zero(val)) { + *(--p) = '0' + (char)u128_mod10(val); + val = u128_div10(val); } return p; } diff --git a/src/iprange6_main.c b/src/iprange6_main.c index 0484406..07c9e8d 100644 --- a/src/iprange6_main.c +++ b/src/iprange6_main.c @@ -42,9 +42,9 @@ static void free_pathnames6(char **files, size_t entries) free(files); } -static __uint128_t ipset6_report_unique_ips(ipset6 *ips, size_t *entries) +static uint128_t ipset6_report_unique_ips(ipset6 *ips, size_t *entries) { - __uint128_t unique_ips = ipset6_unique_ips(ips); + uint128_t unique_ips = ipset6_unique_ips(ips); if(entries) *entries = ips->entries; return unique_ips; } @@ -337,7 +337,7 @@ int iprange6_run(int argc, char **argv, int mode, IPSET_PRINT_CMD print, if(mode == MODE_COMBINE) ipset6_print(root, print); else if(mode == MODE_COUNT_UNIQUE_MERGED) { - __uint128_t unique_ips = ipset6_report_unique_ips(root, NULL); + uint128_t unique_ips = ipset6_report_unique_ips(root, NULL); if(unlikely(header)) printf("entries,unique_ips\n"); printf("%zu,%s\n", root->entries, u128_to_dec(u128buf, sizeof(u128buf), unique_ips)); } @@ -381,7 +381,7 @@ int iprange6_run(int argc, char **argv, int mode, IPSET_PRINT_CMD print, ips6 = ipset6_diff(root, second); if(!quiet) ipset6_print(ips6, print); - if(ips6->unique_ips) ret = 1; + if(!u128_is_zero(ips6->unique_ips)) ret = 1; else ret = 0; } else if(mode == MODE_COMPARE) { @@ -400,8 +400,8 @@ int iprange6_run(int argc, char **argv, int mode, IPSET_PRINT_CMD print, for(ips2 = ips6; ips2; ips2 = ips2->next) { ipset6 *comips; size_t entries1, entries2; - __uint128_t unique1 = ipset6_report_unique_ips(ips6, &entries1); - __uint128_t unique2 = ipset6_report_unique_ips(ips2, &entries2); + uint128_t unique1 = ipset6_report_unique_ips(ips6, &entries1); + uint128_t unique2 = ipset6_report_unique_ips(ips2, &entries2); if(ips6 == ips2) continue; @@ -416,7 +416,7 @@ int iprange6_run(int argc, char **argv, int mode, IPSET_PRINT_CMD print, u128_to_dec(u128buf, sizeof(u128buf), unique1)); printf("%s,", u128_to_dec(u128buf, sizeof(u128buf), unique2)); printf("%s,", u128_to_dec(u128buf, sizeof(u128buf), comips->unique_ips)); - printf("%s\n", u128_to_dec(u128buf, sizeof(u128buf), unique1 + unique2 - comips->unique_ips)); + printf("%s\n", u128_to_dec(u128buf, sizeof(u128buf), u128_sub(u128_add(unique1, unique2), comips->unique_ips))); ipset6_free(comips); } } @@ -437,8 +437,8 @@ int iprange6_run(int argc, char **argv, int mode, IPSET_PRINT_CMD print, for(ips6 = root; ips6; ips6 = ips6->next) { for(ips2 = second; ips2; ips2 = ips2->next) { size_t entries1, entries2; - __uint128_t unique1 = ipset6_report_unique_ips(ips6, &entries1); - __uint128_t unique2 = ipset6_report_unique_ips(ips2, &entries2); + uint128_t unique1 = ipset6_report_unique_ips(ips6, &entries1); + uint128_t unique2 = ipset6_report_unique_ips(ips2, &entries2); ipset6 *combined = ipset6_combine(ips6, ips2); if(!combined) { @@ -451,7 +451,7 @@ int iprange6_run(int argc, char **argv, int mode, IPSET_PRINT_CMD print, u128_to_dec(u128buf, sizeof(u128buf), unique1)); printf("%s,", u128_to_dec(u128buf, sizeof(u128buf), unique2)); printf("%s,", u128_to_dec(u128buf, sizeof(u128buf), combined->unique_ips)); - printf("%s\n", u128_to_dec(u128buf, sizeof(u128buf), unique1 + unique2 - combined->unique_ips)); + printf("%s\n", u128_to_dec(u128buf, sizeof(u128buf), u128_sub(u128_add(unique1, unique2), combined->unique_ips))); ipset6_free(combined); } } @@ -468,7 +468,7 @@ int iprange6_run(int argc, char **argv, int mode, IPSET_PRINT_CMD print, for(ips6 = root; ips6; ips6 = ips6->next) { size_t entries; - __uint128_t unique_ips = ipset6_report_unique_ips(ips6, &entries); + uint128_t unique_ips = ipset6_report_unique_ips(ips6, &entries); if(ips6 == first) continue; @@ -481,7 +481,7 @@ int iprange6_run(int argc, char **argv, int mode, IPSET_PRINT_CMD print, ipset6_optimize(comips); printf("%s,%zu,%s,", ips6->filename, entries, u128_to_dec(u128buf, sizeof(u128buf), unique_ips)); - printf("%s\n", u128_to_dec(u128buf, sizeof(u128buf), unique_ips + first->unique_ips - comips->unique_ips)); + printf("%s\n", u128_to_dec(u128buf, sizeof(u128buf), u128_sub(u128_add(unique_ips, first->unique_ips), comips->unique_ips))); ipset6_free(comips); } } diff --git a/src/ipset6.c b/src/ipset6.c index a34833d..79aa360 100644 --- a/src/ipset6.c +++ b/src/ipset6.c @@ -24,7 +24,7 @@ ipset6 *ipset6_create(const char *filename, size_t entries) { ips->lines = 0; ips->entries = 0; ips->entries_max = entries; - ips->unique_ips = 0; + ips->unique_ips = U128_ZERO; ips->next = NULL; ips->prev = NULL; ips->flags = 0; @@ -87,7 +87,7 @@ void ipset6_grow_internal(ipset6 *ips, size_t free_entries_needed) { } } -inline __uint128_t ipset6_unique_ips(ipset6 *ips) { +inline uint128_t ipset6_unique_ips(ipset6 *ips) { if(unlikely(!(ips->flags & IPSET_FLAG_OPTIMIZED))) ipset6_optimize(ips); diff --git a/src/ipset6.h b/src/ipset6.h index aa9c9c5..c4cd11e 100644 --- a/src/ipset6.h +++ b/src/ipset6.h @@ -9,7 +9,7 @@ typedef struct ipset6 { size_t lines; size_t entries; size_t entries_max; - __uint128_t unique_ips; + uint128_t unique_ips; uint32_t flags; @@ -25,7 +25,7 @@ extern void ipset6_free_all(ipset6 *ips); extern size_t prefix6_counters[129]; -extern __uint128_t ipset6_unique_ips(ipset6 *ips); +extern uint128_t ipset6_unique_ips(ipset6 *ips); static inline int ipset6_entries_allocation_overflows(size_t entries) { return (entries > (SIZE_MAX / sizeof(network_addr6_t))); @@ -56,26 +56,26 @@ static inline void ipset6_added_entry(ipset6 *ips) { ips->lines++; - /* overflow-safe unique_ips: 2^128 doesn't fit in __uint128_t, saturate at max */ - if(lo == 0 && hi == IPV6_ADDR_MAX) + /* overflow-safe unique_ips: 2^128 doesn't fit in uint128_t, saturate at max */ + if(u128_is_zero(lo) && u128_eq(hi, IPV6_ADDR_MAX)) ips->unique_ips = IPV6_ADDR_MAX; else { - __uint128_t size = hi - lo + 1; - if(ips->unique_ips > IPV6_ADDR_MAX - size) + uint128_t size = u128_add(u128_sub(hi, lo), U128_ONE); + if(u128_gt(ips->unique_ips, u128_sub(IPV6_ADDR_MAX, size))) ips->unique_ips = IPV6_ADDR_MAX; else - ips->unique_ips += size; + ips->unique_ips = u128_add(ips->unique_ips, size); } if(likely(ips->flags & IPSET_FLAG_OPTIMIZED && entries > 0)) { /* overflow-safe adjacency: broadcast + 1 wraps at IPV6_ADDR_MAX */ - if(unlikely(ips->netaddrs[entries - 1].broadcast != IPV6_ADDR_MAX && - lo == (ips->netaddrs[entries - 1].broadcast + 1))) { + if(unlikely(!u128_eq(ips->netaddrs[entries - 1].broadcast, IPV6_ADDR_MAX) && + u128_eq(lo, u128_inc(ips->netaddrs[entries - 1].broadcast)))) { ips->netaddrs[entries - 1].broadcast = hi; return; } - if(likely(lo > ips->netaddrs[entries - 1].broadcast)) { + if(likely(u128_gt(lo, ips->netaddrs[entries - 1].broadcast))) { ips->entries++; return; } diff --git a/src/ipset6_binary.c b/src/ipset6_binary.c index bc6caa9..c23a3e2 100644 --- a/src/ipset6_binary.c +++ b/src/ipset6_binary.c @@ -10,15 +10,15 @@ static void binary6_write_failed(void) { exit(1); } -static int binary6_validate_payload(ipset6 *ips, int header_optimized, size_t entries, __uint128_t expected_unique_ips, int *payload_is_optimized) +static int binary6_validate_payload(ipset6 *ips, int header_optimized, size_t entries, uint128_t expected_unique_ips, int *payload_is_optimized) { size_t i; - __uint128_t actual_unique_ips = 0; + uint128_t actual_unique_ips = U128_ZERO; *payload_is_optimized = 1; if(!entries) { - if(unlikely(expected_unique_ips != 0)) { + if(unlikely(!u128_is_zero(expected_unique_ips))) { fprintf(stderr, "%s: %s: unique IPs do not match the binary payload\n", PROG, ips->filename); return 1; } @@ -26,7 +26,7 @@ static int binary6_validate_payload(ipset6 *ips, int header_optimized, size_t en } for(i = 0; i < entries; i++) { - if(unlikely(ips->netaddrs[ips->entries + i].addr > ips->netaddrs[ips->entries + i].broadcast)) { + if(unlikely(u128_gt(ips->netaddrs[ips->entries + i].addr, ips->netaddrs[ips->entries + i].broadcast))) { fprintf(stderr, "%s: %s: invalid binary record %zu has addr > broadcast\n", PROG, ips->filename, i + 1); return 1; } @@ -36,9 +36,9 @@ static int binary6_validate_payload(ipset6 *ips, int header_optimized, size_t en network_addr6_t *prev = &ips->netaddrs[ips->entries + i - 1]; network_addr6_t *curr = &ips->netaddrs[ips->entries + i]; - if(curr->addr < prev->addr - || curr->addr <= prev->broadcast - || (prev->broadcast != IPV6_ADDR_MAX && curr->addr == (prev->broadcast + 1))) { + if(u128_lt(curr->addr, prev->addr) + || u128_le(curr->addr, prev->broadcast) + || (!u128_eq(prev->broadcast, IPV6_ADDR_MAX) && u128_eq(curr->addr, u128_inc(prev->broadcast)))) { *payload_is_optimized = 0; break; } @@ -46,8 +46,8 @@ static int binary6_validate_payload(ipset6 *ips, int header_optimized, size_t en if(*payload_is_optimized) { for(i = 0; i < entries; i++) { - __uint128_t size = ips->netaddrs[ips->entries + i].broadcast - ips->netaddrs[ips->entries + i].addr + 1; - actual_unique_ips += size; + uint128_t size = u128_add(u128_sub(ips->netaddrs[ips->entries + i].broadcast, ips->netaddrs[ips->entries + i].addr), U128_ONE); + actual_unique_ips = u128_add(actual_unique_ips, size); } } else { @@ -57,7 +57,7 @@ static int binary6_validate_payload(ipset6 *ips, int header_optimized, size_t en actual_unique_ips = expected_unique_ips; } - if(unlikely(expected_unique_ips != actual_unique_ips)) { + if(unlikely(!u128_eq(expected_unique_ips, actual_unique_ips))) { fprintf(stderr, "%s: %s: unique IPs do not match the binary payload\n", PROG, ips->filename); return 1; } @@ -91,9 +91,9 @@ static int parse_binary6_size_field(ipset6 *ips, const char *field, const char * return 0; } -static int parse_binary6_u128_field(ipset6 *ips, const char *field, const char *value, __uint128_t *parsed_value) +static int parse_binary6_u128_field(ipset6 *ips, const char *field, const char *value, uint128_t *parsed_value) { - __uint128_t result = 0; + uint128_t result = U128_ZERO; const char *s = value; if(!s || *s < '0' || *s > '9') { @@ -102,9 +102,9 @@ static int parse_binary6_u128_field(ipset6 *ips, const char *field, const char * } while(*s >= '0' && *s <= '9') { - __uint128_t prev = result; - result = result * 10 + (*s - '0'); - if(unlikely(result < prev)) { + uint128_t prev = result; + result = u128_add(u128_mul_u64(result, 10), u128_from_u64(*s - '0')); + if(unlikely(u128_lt(result, prev))) { fprintf(stderr, "%s: %s: %s value overflow\n", PROG, ips->filename, field); return 1; } @@ -123,7 +123,7 @@ static int parse_binary6_u128_field(ipset6 *ips, const char *field, const char * int ipset6_load_binary_v20(FILE *fp, ipset6 *ips, int first_line_missing) { char buffer[MAX_LINE + 1], *s; size_t entries, bytes, lines, expected_bytes, record_size; - __uint128_t unique_ips; + uint128_t unique_ips; uint32_t endian; size_t loaded; int header_optimized = 0; @@ -230,7 +230,7 @@ int ipset6_load_binary_v20(FILE *fp, ipset6 *ips, int first_line_missing) { return 1; } - if(unique_ips < entries && unique_ips != 0) { + if(u128_lt(unique_ips, u128_from_u64(entries)) && !u128_is_zero(unique_ips)) { fprintf(stderr, "%s: %s: unique IPs cannot be less than entries (%zu)\n", PROG, ips->filename, entries); return 1; } @@ -263,7 +263,7 @@ int ipset6_load_binary_v20(FILE *fp, ipset6 *ips, int first_line_missing) { ips->entries += loaded; ips->lines += lines; - ips->unique_ips += unique_ips; + ips->unique_ips = u128_add(ips->unique_ips, unique_ips); ips->flags &= ~IPSET_FLAG_OPTIMIZED; if(header_optimized && payload_is_optimized) ips->flags |= IPSET_FLAG_OPTIMIZED; diff --git a/src/ipset6_common.c b/src/ipset6_common.c index f284144..d84cf1a 100644 --- a/src/ipset6_common.c +++ b/src/ipset6_common.c @@ -33,7 +33,7 @@ inline ipset6 *ipset6_common(ipset6 *ips1, ipset6 *ips2) { hi2 = ips2->netaddrs[0].broadcast; while(i1 < n1 && i2 < n2) { - if(lo1 > hi2) { + if(u128_gt(lo1, hi2)) { i2++; if(i2 < n2) { lo2 = ips2->netaddrs[i2].addr; @@ -42,7 +42,7 @@ inline ipset6 *ipset6_common(ipset6 *ips1, ipset6 *ips2) { continue; } - if(lo2 > hi1) { + if(u128_gt(lo2, hi1)) { i1++; if(i1 < n1) { lo1 = ips1->netaddrs[i1].addr; @@ -51,9 +51,9 @@ inline ipset6 *ipset6_common(ipset6 *ips1, ipset6 *ips2) { continue; } - lo = (lo1 > lo2) ? lo1 : lo2; + lo = u128_gt(lo1, lo2) ? lo1 : lo2; - if(hi1 < hi2) { + if(u128_lt(hi1, hi2)) { hi = hi1; i1++; if(i1 < n1) { diff --git a/src/ipset6_diff.c b/src/ipset6_diff.c index f629ae3..7f07d9c 100644 --- a/src/ipset6_diff.c +++ b/src/ipset6_diff.c @@ -53,7 +53,7 @@ inline ipset6 *ipset6_diff(ipset6 *ips1, ipset6 *ips2) { hi2 = ips2->netaddrs[0].broadcast; while(i1 < n1 && i2 < n2) { - if(lo1 > hi2) { + if(u128_gt(lo1, hi2)) { ipset6_add_ip_range(ips, lo2, hi2); i2++; if(i2 < n2) { @@ -62,7 +62,7 @@ inline ipset6 *ipset6_diff(ipset6 *ips1, ipset6 *ips2) { } continue; } - if(lo2 > hi1) { + if(u128_gt(lo2, hi1)) { ipset6_add_ip_range(ips, lo1, hi1); i1++; if(i1 < n1) { @@ -72,14 +72,14 @@ inline ipset6 *ipset6_diff(ipset6 *ips1, ipset6 *ips2) { continue; } - if(lo1 > lo2) - ipset6_add_ip_range(ips, lo2, lo1 - 1); - else if(lo2 > lo1) - ipset6_add_ip_range(ips, lo1, lo2 - 1); + if(u128_gt(lo1, lo2)) + ipset6_add_ip_range(ips, lo2, u128_dec(lo1)); + else if(u128_gt(lo2, lo1)) + ipset6_add_ip_range(ips, lo1, u128_dec(lo2)); - if(hi1 > hi2) { - if(hi2 == IPV6_ADDR_MAX) { i1++; i2++; } - else { lo1 = hi2 + 1; i2++; } + if(u128_gt(hi1, hi2)) { + if(u128_eq(hi2, IPV6_ADDR_MAX)) { i1++; i2++; } + else { lo1 = u128_inc(hi2); i2++; } if(i2 < n2) { lo2 = ips2->netaddrs[i2].addr; hi2 = ips2->netaddrs[i2].broadcast; @@ -90,9 +90,9 @@ inline ipset6 *ipset6_diff(ipset6 *ips1, ipset6 *ips2) { } continue; } - else if(hi2 > hi1) { - if(hi1 == IPV6_ADDR_MAX) { i1++; i2++; } - else { lo2 = hi1 + 1; i1++; } + else if(u128_gt(hi2, hi1)) { + if(u128_eq(hi1, IPV6_ADDR_MAX)) { i1++; i2++; } + else { lo2 = u128_inc(hi1); i1++; } if(i1 < n1) { lo1 = ips1->netaddrs[i1].addr; hi1 = ips1->netaddrs[i1].broadcast; diff --git a/src/ipset6_exclude.c b/src/ipset6_exclude.c index c7bb969..75dbaaf 100644 --- a/src/ipset6_exclude.c +++ b/src/ipset6_exclude.c @@ -43,7 +43,7 @@ inline ipset6 *ipset6_exclude(ipset6 *ips1, ipset6 *ips2) { hi2 = ips2->netaddrs[0].broadcast; while(i1 < n1 && i2 < n2) { - if(lo1 > hi2) { + if(u128_gt(lo1, hi2)) { i2++; if(i2 < n2) { lo2 = ips2->netaddrs[i2].addr; @@ -52,7 +52,7 @@ inline ipset6 *ipset6_exclude(ipset6 *ips1, ipset6 *ips2) { continue; } - if(lo2 > hi1) { + if(u128_gt(lo2, hi1)) { ipset6_add_ip_range(ips, lo1, hi1); i1++; if(i1 < n1) { @@ -62,12 +62,12 @@ inline ipset6 *ipset6_exclude(ipset6 *ips1, ipset6 *ips2) { continue; } - if(lo1 < lo2) { - ipset6_add_ip_range(ips, lo1, lo2 - 1); + if(u128_lt(lo1, lo2)) { + ipset6_add_ip_range(ips, lo1, u128_dec(lo2)); lo1 = lo2; } - if(hi1 == hi2) { + if(u128_eq(hi1, hi2)) { i1++; if(i1 < n1) { lo1 = ips1->netaddrs[i1].addr; @@ -79,7 +79,7 @@ inline ipset6 *ipset6_exclude(ipset6 *ips1, ipset6 *ips2) { hi2 = ips2->netaddrs[i2].broadcast; } } - else if(hi1 < hi2) { + else if(u128_lt(hi1, hi2)) { i1++; if(i1 < n1) { lo1 = ips1->netaddrs[i1].addr; @@ -89,7 +89,7 @@ inline ipset6 *ipset6_exclude(ipset6 *ips1, ipset6 *ips2) { else { /* hi2 + 1 would overflow if hi2 == IPV6_ADDR_MAX, but that means * ips2 covers everything from lo1..max, so nothing remains in ips1 */ - if(hi2 == IPV6_ADDR_MAX) { + if(u128_eq(hi2, IPV6_ADDR_MAX)) { i1++; if(i1 < n1) { lo1 = ips1->netaddrs[i1].addr; @@ -97,7 +97,7 @@ inline ipset6 *ipset6_exclude(ipset6 *ips1, ipset6 *ips2) { } } else { - lo1 = hi2 + 1; + lo1 = u128_inc(hi2); } i2++; if(i2 < n2) { diff --git a/src/ipset6_load.c b/src/ipset6_load.c index 6295ef2..b64ca9f 100644 --- a/src/ipset6_load.c +++ b/src/ipset6_load.c @@ -152,8 +152,8 @@ static network_addr6_t parse_address6(char *ipstr, int *err) { else if(addr_class == 4) { network_addr_t v4 = str2netaddr(ipstr, err); if(*err) { - netaddr.addr = 0; - netaddr.broadcast = 0; + netaddr.addr = U128_ZERO; + netaddr.broadcast = U128_ZERO; return netaddr; } @@ -164,8 +164,8 @@ static network_addr6_t parse_address6(char *ipstr, int *err) { if(err) (*err)++; fprintf(stderr, "%s: Cannot parse address: %s\n", PROG, ipstr); - netaddr.addr = 0; - netaddr.broadcast = 0; + netaddr.addr = U128_ZERO; + netaddr.broadcast = U128_ZERO; return netaddr; } @@ -275,8 +275,8 @@ ipset6 *ipset6_load(const char *filename) { continue; } - ipv6_addr_t lo = (net1.addr < net2.addr) ? net1.addr : net2.addr; - ipv6_addr_t hi = (net1.broadcast > net2.broadcast) ? net1.broadcast : net2.broadcast; + ipv6_addr_t lo = u128_lt(net1.addr, net2.addr) ? net1.addr : net2.addr; + ipv6_addr_t hi = u128_gt(net1.broadcast, net2.broadcast) ? net1.broadcast : net2.broadcast; ipset6_add_ip_range(ips, lo, hi); } break; diff --git a/src/ipset6_optimize.c b/src/ipset6_optimize.c index f11a7d4..d0c1734 100644 --- a/src/ipset6_optimize.c +++ b/src/ipset6_optimize.c @@ -6,10 +6,10 @@ static int compar_netaddr6(const void *p1, const void *p2) { const network_addr6_t *na1 = (const network_addr6_t *)p1; const network_addr6_t *na2 = (const network_addr6_t *)p2; - if(na1->addr < na2->addr) return -1; - if(na1->addr > na2->addr) return 1; - if(na1->broadcast > na2->broadcast) return -1; - if(na1->broadcast < na2->broadcast) return 1; + if(u128_lt(na1->addr, na2->addr)) return -1; + if(u128_gt(na1->addr, na2->addr)) return 1; + if(u128_gt(na1->broadcast, na2->broadcast)) return -1; + if(u128_lt(na1->broadcast, na2->broadcast)) return 1; return 0; } @@ -25,7 +25,7 @@ inline void ipset6_optimize(ipset6 *ips) { if(unlikely(n == 0)) { ips->flags |= IPSET_FLAG_OPTIMIZED; - ips->unique_ips = 0; + ips->unique_ips = U128_ZERO; return; } @@ -39,17 +39,17 @@ inline void ipset6_optimize(ipset6 *ips) { ips->netaddrs = naddrs; ips->entries = 0; - ips->unique_ips = 0; + ips->unique_ips = U128_ZERO; ips->lines = 0; lo = oaddrs[0].addr; hi = oaddrs[0].broadcast; for(i = 1; i < n; i++) { - if(oaddrs[i].broadcast <= hi) + if(u128_le(oaddrs[i].broadcast, hi)) continue; /* overflow-safe adjacency check: hi + 1 would overflow if hi == max */ - if(oaddrs[i].addr <= hi || (hi != IPV6_ADDR_MAX && oaddrs[i].addr == hi + 1)) { + if(u128_le(oaddrs[i].addr, hi) || (!u128_eq(hi, IPV6_ADDR_MAX) && u128_eq(oaddrs[i].addr, u128_inc(hi)))) { hi = oaddrs[i].broadcast; continue; } diff --git a/src/ipset6_print.c b/src/ipset6_print.c index 32794b0..0dafe25 100644 --- a/src/ipset6_print.c +++ b/src/ipset6_print.c @@ -41,7 +41,7 @@ inline void print_addr6(ipv6_addr_t addr, int prefix) { inline void print_addr6_range(ipv6_addr_t lo, ipv6_addr_t hi) { char buf[IP6STR_MAX_LEN + 1]; - if(unlikely(lo > hi)) { + if(unlikely(u128_gt(lo, hi))) { ipv6_addr_t t = hi; fprintf(stderr, "%s: WARNING: invalid range reversed start=%s", PROG, ip6str_r(buf, lo)); fprintf(stderr, " end=%s\n", ip6str_r(buf, hi)); @@ -49,7 +49,7 @@ inline void print_addr6_range(ipv6_addr_t lo, ipv6_addr_t hi) { lo = t; } - if(lo == hi) { + if(u128_eq(lo, hi)) { printf("%s%s-", print_prefix_ips, ip6str_r(buf, lo)); printf("%s%s\n", ip6str_r(buf, hi), print_suffix_ips); } @@ -72,7 +72,7 @@ inline void print_addr6_single(ipv6_addr_t x) { inline int split_range6(ipv6_addr_t addr, int prefix, ipv6_addr_t lo, ipv6_addr_t hi, void (*print)(ipv6_addr_t, int)) { ipv6_addr_t bc, lower_half, upper_half; - if(unlikely(lo > hi)) { + if(unlikely(u128_gt(lo, hi))) { ipv6_addr_t t = hi; char buf[IP6STR_MAX_LEN + 1]; fprintf(stderr, "%s: WARNING: invalid range reversed start=%s", PROG, ip6str_r(buf, lo)); @@ -88,13 +88,13 @@ inline int split_range6(ipv6_addr_t addr, int prefix, ipv6_addr_t lo, ipv6_addr_ bc = broadcast6(addr, prefix); - if(unlikely(lo < addr || hi > bc)) { + if(unlikely(u128_lt(lo, addr) || u128_gt(hi, bc))) { char buf[IP6STR_MAX_LEN + 1]; fprintf(stderr, "%s: Out of range limits for IPv6 network %s/%d\n", PROG, ip6str_r(buf, addr), prefix); return 0; } - if(lo == addr && hi == bc && prefix6_enabled[prefix]) { + if(u128_eq(lo, addr) && u128_eq(hi, bc) && prefix6_enabled[prefix]) { print(addr, prefix); return 1; } @@ -103,9 +103,9 @@ inline int split_range6(ipv6_addr_t addr, int prefix, ipv6_addr_t lo, ipv6_addr_ lower_half = addr; upper_half = set_bit6(addr, prefix, 1); - if(hi < upper_half) + if(u128_lt(hi, upper_half)) return split_range6(lower_half, prefix, lo, hi, print); - else if(lo >= upper_half) + else if(u128_ge(lo, upper_half)) return split_range6(upper_half, prefix, lo, hi, print); else return ( @@ -136,7 +136,7 @@ void ipset6_print(ipset6 *ips, IPSET_PRINT_CMD print) { n = ips->entries; for(i = 0; i < n; i++) - total += split_range6((__uint128_t)0, 0, ips->netaddrs[i].addr, ips->netaddrs[i].broadcast, print_addr6); + total += split_range6(U128_ZERO, 0, ips->netaddrs[i].addr, ips->netaddrs[i].broadcast, print_addr6); break; case PRINT_SINGLE_IPS: @@ -146,7 +146,7 @@ void ipset6_print(ipset6 *ips, IPSET_PRINT_CMD print) { ipv6_addr_t end = ips->netaddrs[i].broadcast; ipv6_addr_t x; - if(unlikely(start > end)) { + if(unlikely(u128_gt(start, end))) { char buf[IP6STR_MAX_LEN + 1]; fprintf(stderr, "%s: WARNING: invalid range reversed start=%s", PROG, ip6str_r(buf, start)); fprintf(stderr, " end=%s\n", ip6str_r(buf, end)); @@ -154,13 +154,13 @@ void ipset6_print(ipset6 *ips, IPSET_PRINT_CMD print) { end = start; start = x; } - if(unlikely(end - start > IPV6_SINGLE_IP_CAP)) { + if(unlikely(u128_gt(u128_sub(end, start), u128_from_u64(IPV6_SINGLE_IP_CAP)))) { char buf[IP6STR_MAX_LEN + 1]; fprintf(stderr, "%s: too big range eliminated start=%s", PROG, ip6str_r(buf, start)); fprintf(stderr, " end=%s\n", ip6str_r(buf, end)); continue; } - for(x = start; x >= start && x <= end; x++) { + for(x = start; u128_ge(x, start) && u128_le(x, end); x = u128_inc(x)) { print_addr6_single(x); total++; } diff --git a/src/uint128.h b/src/uint128.h new file mode 100644 index 0000000..0da5873 --- /dev/null +++ b/src/uint128.h @@ -0,0 +1,230 @@ +#ifndef IPRANGE_UINT128_H +#define IPRANGE_UINT128_H + +#include + +/* Define IPRANGE_FORCE_PORTABLE_U128 to compile the portable (struct-based) + * 128-bit path even on platforms that have native __uint128_t. This lets the + * 32-bit code path be exercised by the test suite on a 64-bit host. */ +#if defined(__SIZEOF_INT128__) && !defined(IPRANGE_FORCE_PORTABLE_U128) + +/* ================================================================ + * Native 128-bit path (64-bit platforms with compiler support). + * Every function compiles to the native operator — zero overhead. + * ================================================================ */ + +typedef __uint128_t uint128_t; + +#define U128_ZERO ((uint128_t)0) +#define U128_ONE ((uint128_t)1) +#define U128_MAX ((uint128_t)((__uint128_t)(-1))) + +static inline uint128_t u128_from_u64(uint64_t v) { return (uint128_t)v; } +static inline uint128_t u128_from_u32(uint32_t v) { return (uint128_t)v; } + +static inline uint64_t u128_hi64(uint128_t a) { return (uint64_t)(a >> 64); } +static inline uint64_t u128_lo64(uint128_t a) { return (uint64_t)a; } + +static inline int u128_is_zero(uint128_t a) { return a == 0; } +static inline int u128_eq(uint128_t a, uint128_t b) { return a == b; } +static inline int u128_lt(uint128_t a, uint128_t b) { return a < b; } +static inline int u128_gt(uint128_t a, uint128_t b) { return a > b; } +static inline int u128_le(uint128_t a, uint128_t b) { return a <= b; } +static inline int u128_ge(uint128_t a, uint128_t b) { return a >= b; } + +static inline uint128_t u128_add(uint128_t a, uint128_t b) { return a + b; } +static inline uint128_t u128_sub(uint128_t a, uint128_t b) { return a - b; } +static inline uint128_t u128_inc(uint128_t a) { return a + 1; } +static inline uint128_t u128_dec(uint128_t a) { return a - 1; } + +static inline uint128_t u128_and(uint128_t a, uint128_t b) { return a & b; } +static inline uint128_t u128_or(uint128_t a, uint128_t b) { return a | b; } +static inline uint128_t u128_not(uint128_t a) { return ~a; } +static inline uint128_t u128_shl(uint128_t a, int n) { return a << n; } +static inline uint128_t u128_shr(uint128_t a, int n) { return a >> n; } + +static inline uint128_t u128_mul_u64(uint128_t a, uint64_t b) { return a * b; } +static inline uint128_t u128_div10(uint128_t a) { return a / 10; } +static inline int u128_mod10(uint128_t a) { return (int)(a % 10); } + +#else /* !__SIZEOF_INT128__ */ + +/* ================================================================ + * Portable 128-bit path (32-bit platforms without __uint128_t). + * Uses a struct of two uint64_t with explicit arithmetic. + * ================================================================ */ + +/* Field order matches the byte layout of a native __uint128_t on the same + * endianness, so that an array of these structs is binary-compatible with one + * of native __uint128_t. This lets binary ipset files written by a 64-bit + * (native) build load correctly on a 32-bit (portable) build of the same + * endianness, and vice versa. All code refers to the fields by name, so the + * order is irrelevant to arithmetic; only the in-memory layout changes. */ +#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +typedef struct { uint64_t hi; uint64_t lo; } uint128_t; +#else +typedef struct { uint64_t lo; uint64_t hi; } uint128_t; +#endif + +#define U128_ZERO ((uint128_t){ .hi = 0, .lo = 0 }) +#define U128_ONE ((uint128_t){ .hi = 0, .lo = 1 }) +#define U128_MAX ((uint128_t){ .hi = UINT64_MAX, .lo = UINT64_MAX }) + +static inline uint128_t u128_from_u64(uint64_t v) { + uint128_t r = { .hi = 0, .lo = v }; + return r; +} + +static inline uint128_t u128_from_u32(uint32_t v) { + uint128_t r = { .hi = 0, .lo = (uint64_t)v }; + return r; +} + +static inline uint64_t u128_hi64(uint128_t a) { return a.hi; } +static inline uint64_t u128_lo64(uint128_t a) { return a.lo; } + +static inline int u128_is_zero(uint128_t a) { + return a.hi == 0 && a.lo == 0; +} + +static inline int u128_eq(uint128_t a, uint128_t b) { + return a.hi == b.hi && a.lo == b.lo; +} + +static inline int u128_lt(uint128_t a, uint128_t b) { + return a.hi < b.hi || (a.hi == b.hi && a.lo < b.lo); +} + +static inline int u128_gt(uint128_t a, uint128_t b) { + return a.hi > b.hi || (a.hi == b.hi && a.lo > b.lo); +} + +static inline int u128_le(uint128_t a, uint128_t b) { return !u128_gt(a, b); } +static inline int u128_ge(uint128_t a, uint128_t b) { return !u128_lt(a, b); } + +static inline uint128_t u128_add(uint128_t a, uint128_t b) { + uint128_t r; + r.lo = a.lo + b.lo; + r.hi = a.hi + b.hi + (r.lo < a.lo); + return r; +} + +static inline uint128_t u128_sub(uint128_t a, uint128_t b) { + uint128_t r; + r.lo = a.lo - b.lo; + r.hi = a.hi - b.hi - (a.lo < b.lo); + return r; +} + +static inline uint128_t u128_inc(uint128_t a) { + uint128_t r; + r.lo = a.lo + 1; + r.hi = a.hi + (r.lo == 0); + return r; +} + +static inline uint128_t u128_dec(uint128_t a) { + uint128_t r; + r.hi = a.hi - (a.lo == 0); + r.lo = a.lo - 1; + return r; +} + +static inline uint128_t u128_and(uint128_t a, uint128_t b) { + uint128_t r = { .hi = a.hi & b.hi, .lo = a.lo & b.lo }; + return r; +} + +static inline uint128_t u128_or(uint128_t a, uint128_t b) { + uint128_t r = { .hi = a.hi | b.hi, .lo = a.lo | b.lo }; + return r; +} + +static inline uint128_t u128_not(uint128_t a) { + uint128_t r = { .hi = ~a.hi, .lo = ~a.lo }; + return r; +} + +static inline uint128_t u128_shl(uint128_t a, int n) { + uint128_t r; + if(n == 0) return a; + if(n >= 128) { r.hi = 0; r.lo = 0; return r; } + if(n >= 64) { + r.hi = a.lo << (n - 64); + r.lo = 0; + } + else { + r.hi = (a.hi << n) | (a.lo >> (64 - n)); + r.lo = a.lo << n; + } + return r; +} + +static inline uint128_t u128_shr(uint128_t a, int n) { + uint128_t r; + if(n == 0) return a; + if(n >= 128) { r.hi = 0; r.lo = 0; return r; } + if(n >= 64) { + r.lo = a.hi >> (n - 64); + r.hi = 0; + } + else { + r.lo = (a.lo >> n) | (a.hi << (64 - n)); + r.hi = a.hi >> n; + } + return r; +} + +/* multiply uint128 by uint64, keeping low 128 bits */ +static inline uint128_t u128_mul_u64(uint128_t a, uint64_t b) { + uint32_t al = (uint32_t)a.lo; + uint32_t ah = (uint32_t)(a.lo >> 32); + uint32_t bl = (uint32_t)b; + uint32_t bh = (uint32_t)(b >> 32); + uint64_t p0, p1, p2, p3, carry; + uint128_t r; + + p0 = (uint64_t)al * bl; + p1 = (uint64_t)al * bh; + p2 = (uint64_t)ah * bl; + p3 = (uint64_t)ah * bh; + + carry = (p0 >> 32) + (uint32_t)p1 + (uint32_t)p2; + r.lo = ((uint64_t)(uint32_t)p0) | (carry << 32); + r.hi = p3 + (p1 >> 32) + (p2 >> 32) + (carry >> 32) + a.hi * b; + return r; +} + +/* long division by 10 using 32-bit digits */ +static inline uint128_t u128_div10(uint128_t a) { + uint128_t q; + uint32_t d3, d2, d1, d0, q3, q2, q1, q0; + uint64_t r, tmp; + + d3 = (uint32_t)(a.hi >> 32); + d2 = (uint32_t)a.hi; + d1 = (uint32_t)(a.lo >> 32); + d0 = (uint32_t)a.lo; + + tmp = (uint64_t)d3; q3 = (uint32_t)(tmp / 10); r = tmp % 10; + tmp = (r << 32) | d2; q2 = (uint32_t)(tmp / 10); r = tmp % 10; + tmp = (r << 32) | d1; q1 = (uint32_t)(tmp / 10); r = tmp % 10; + tmp = (r << 32) | d0; q0 = (uint32_t)(tmp / 10); + + q.hi = ((uint64_t)q3 << 32) | q2; + q.lo = ((uint64_t)q1 << 32) | q0; + return q; +} + +static inline int u128_mod10(uint128_t a) { + uint64_t r; + r = ((uint64_t)(uint32_t)(a.hi >> 32)) % 10; + r = ((r << 32) | (uint32_t)a.hi) % 10; + r = ((r << 32) | (uint32_t)(a.lo >> 32)) % 10; + r = ((r << 32) | (uint32_t)a.lo) % 10; + return (int)r; +} + +#endif /* __SIZEOF_INT128__ */ + +#endif /* IPRANGE_UINT128_H */