/* * PostgreSQL type definitions for the INET and CIDR types. * * src/backend/utils/adt/network.c * * Jon Postel RIP 16 Oct 1998 */ #include "postgres.h" #include #include #include #include "access/stratnum.h" #include "catalog/pg_opfamily.h" #include "catalog/pg_type.h" #include "common/hashfn.h" #include "common/ip.h" #include "lib/hyperloglog.h" #include "libpq/libpq-be.h" #include "libpq/pqformat.h" #include "miscadmin.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "nodes/supportnodes.h" #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/guc.h" #include "utils/inet.h" #include "utils/lsyscache.h" #include "utils/sortsupport.h" /* * An IPv4 netmask size is a value in the range of 0 - 32, which is * represented with 6 bits in inet/cidr abbreviated keys where possible. * * An IPv4 inet/cidr abbreviated key can use up to 25 bits for subnet * component. */ #define ABBREV_BITS_INET4_NETMASK_SIZE 6 #define ABBREV_BITS_INET4_SUBNET 25 /* sortsupport for inet/cidr */ typedef struct { int64 input_count; /* number of non-null values seen */ bool estimating; /* true if estimating cardinality */ hyperLogLogState abbr_card; /* cardinality estimator */ } network_sortsupport_state; static int32 network_cmp_internal(inet *a1, inet *a2); static int network_fast_cmp(Datum x, Datum y, SortSupport ssup); static bool network_abbrev_abort(int memtupcount, SortSupport ssup); static Datum network_abbrev_convert(Datum original, SortSupport ssup); static List *match_network_function(Node *leftop, Node *rightop, int indexarg, Oid funcid, Oid opfamily); static List *match_network_subset(Node *leftop, Node *rightop, bool is_eq, Oid opfamily); static bool addressOK(unsigned char *a, int bits, int family); static inet *internal_inetpl(inet *ip, int64 addend); /* * Common INET/CIDR input routine */ static inet * network_in(char *src, bool is_cidr, Node *escontext) { int bits; inet *dst; dst = (inet *) palloc0(sizeof(inet)); /* * First, check to see if this is an IPv6 or IPv4 address. IPv6 addresses * will have a : somewhere in them (several, in fact) so if there is one * present, assume it's V6, otherwise assume it's V4. */ if (strchr(src, ':') != NULL) ip_family(dst) = PGSQL_AF_INET6; else ip_family(dst) = PGSQL_AF_INET; bits = pg_inet_net_pton(ip_family(dst), src, ip_addr(dst), is_cidr ? ip_addrsize(dst) : -1); if ((bits < 0) || (bits > ip_maxbits(dst))) ereturn(escontext, NULL, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), /* translator: first %s is inet or cidr */ errmsg("invalid input syntax for type %s: \"%s\"", is_cidr ? "cidr" : "inet", src))); /* * Error check: CIDR values must not have any bits set beyond the masklen. */ if (is_cidr) { if (!addressOK(ip_addr(dst), bits, ip_family(dst))) ereturn(escontext, NULL, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid cidr value: \"%s\"", src), errdetail("Value has bits set to right of mask."))); } ip_bits(dst) = bits; SET_INET_VARSIZE(dst); return dst; } Datum inet_in(PG_FUNCTION_ARGS) { char *src = PG_GETARG_CSTRING(0); PG_RETURN_INET_P(network_in(src, false, fcinfo->context)); } Datum cidr_in(PG_FUNCTION_ARGS) { char *src = PG_GETARG_CSTRING(0); PG_RETURN_INET_P(network_in(src, true, fcinfo->context)); } /* * Common INET/CIDR output routine */ static char * network_out(inet *src, bool is_cidr) { char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")]; char *dst; int len; dst = pg_inet_net_ntop(ip_family(src), ip_addr(src), ip_bits(src), tmp, sizeof(tmp)); if (dst == NULL) ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("could not format inet value: %m"))); /* For CIDR, add /n if not present */ if (is_cidr && strchr(tmp, '/') == NULL) { len = strlen(tmp); snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(src)); } return pstrdup(tmp); } Datum inet_out(PG_FUNCTION_ARGS) { inet *src = PG_GETARG_INET_PP(0); PG_RETURN_CSTRING(network_out(src, false)); } Datum cidr_out(PG_FUNCTION_ARGS) { inet *src = PG_GETARG_INET_PP(0); PG_RETURN_CSTRING(network_out(src, true)); } /* * network_recv - converts external binary format to inet * * The external representation is (one byte apiece for) * family, bits, is_cidr, address length, address in network byte order. * * Presence of is_cidr is largely for historical reasons, though it might * allow some code-sharing on the client side. We send it correctly on * output, but ignore the value on input. */ static inet * network_recv(StringInfo buf, bool is_cidr) { inet *addr; char *addrptr; int bits; int nb, i; /* make sure any unused bits in a CIDR value are zeroed */ addr = (inet *) palloc0(sizeof(inet)); ip_family(addr) = pq_getmsgbyte(buf); if (ip_family(addr) != PGSQL_AF_INET && ip_family(addr) != PGSQL_AF_INET6) ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), /* translator: %s is inet or cidr */ errmsg("invalid address family in external \"%s\" value", is_cidr ? "cidr" : "inet"))); bits = pq_getmsgbyte(buf); if (bits < 0 || bits > ip_maxbits(addr)) ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), /* translator: %s is inet or cidr */ errmsg("invalid bits in external \"%s\" value", is_cidr ? "cidr" : "inet"))); ip_bits(addr) = bits; i = pq_getmsgbyte(buf); /* ignore is_cidr */ nb = pq_getmsgbyte(buf); if (nb != ip_addrsize(addr)) ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), /* translator: %s is inet or cidr */ errmsg("invalid length in external \"%s\" value", is_cidr ? "cidr" : "inet"))); addrptr = (char *) ip_addr(addr); for (i = 0; i < nb; i++) addrptr[i] = pq_getmsgbyte(buf); /* * Error check: CIDR values must not have any bits set beyond the masklen. */ if (is_cidr) { if (!addressOK(ip_addr(addr), bits, ip_family(addr))) ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("invalid external \"cidr\" value"), errdetail("Value has bits set to right of mask."))); } SET_INET_VARSIZE(addr); return addr; } Datum inet_recv(PG_FUNCTION_ARGS) { StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); PG_RETURN_INET_P(network_recv(buf, false)); } Datum cidr_recv(PG_FUNCTION_ARGS) { StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); PG_RETURN_INET_P(network_recv(buf, true)); } /* * network_send - converts inet to binary format */ static bytea * network_send(inet *addr, bool is_cidr) { StringInfoData buf; char *addrptr; int nb, i; pq_begintypsend(&buf); pq_sendbyte(&buf, ip_family(addr)); pq_sendbyte(&buf, ip_bits(addr)); pq_sendbyte(&buf, is_cidr); nb = ip_addrsize(addr); if (nb < 0) nb = 0; pq_sendbyte(&buf, nb); addrptr = (char *) ip_addr(addr); for (i = 0; i < nb; i++) pq_sendbyte(&buf, addrptr[i]); return pq_endtypsend(&buf); } Datum inet_send(PG_FUNCTION_ARGS) { inet *addr = PG_GETARG_INET_PP(0); PG_RETURN_BYTEA_P(network_send(addr, false)); } Datum cidr_send(PG_FUNCTION_ARGS) { inet *addr = PG_GETARG_INET_PP(0); PG_RETURN_BYTEA_P(network_send(addr, true)); } Datum inet_to_cidr(PG_FUNCTION_ARGS) { inet *src = PG_GETARG_INET_PP(0); int bits; bits = ip_bits(src); /* safety check */ if ((bits < 0) || (bits > ip_maxbits(src))) elog(ERROR, "invalid inet bit length: %d", bits); PG_RETURN_INET_P(cidr_set_masklen_internal(src, bits)); } Datum inet_set_masklen(PG_FUNCTION_ARGS) { inet *src = PG_GETARG_INET_PP(0); int bits = PG_GETARG_INT32(1); inet *dst; if (bits == -1) bits = ip_maxbits(src); if ((bits < 0) || (bits > ip_maxbits(src))) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid mask length: %d", bits))); /* clone the original data */ dst = (inet *) palloc(VARSIZE_ANY(src)); memcpy(dst, src, VARSIZE_ANY(src)); ip_bits(dst) = bits; PG_RETURN_INET_P(dst); } Datum cidr_set_masklen(PG_FUNCTION_ARGS) { inet *src = PG_GETARG_INET_PP(0); int bits = PG_GETARG_INT32(1); if (bits == -1) bits = ip_maxbits(src); if ((bits < 0) || (bits > ip_maxbits(src))) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid mask length: %d", bits))); PG_RETURN_INET_P(cidr_set_masklen_internal(src, bits)); } /* * Copy src and set mask length to 'bits' (which must be valid for the family) */ inet * cidr_set_masklen_internal(const inet *src, int bits) { inet *dst = (inet *) palloc0(sizeof(inet)); ip_family(dst) = ip_family(src); ip_bits(dst) = bits; if (bits > 0) { Assert(bits <= ip_maxbits(dst)); /* Clone appropriate bytes of the address, leaving the rest 0 */ memcpy(ip_addr(dst), ip_addr(src), (bits + 7) / 8); /* Clear any unwanted bits in the last partial byte */ if (bits % 8) ip_addr(dst)[bits / 8] &= ~(0xFF >> (bits % 8)); } /* Set varlena header correctly */ SET_INET_VARSIZE(dst); return dst; } /* * Basic comparison function for sorting and inet/cidr comparisons. * * Comparison is first on the common bits of the network part, then on * the length of the network part, and then on the whole unmasked address. * The effect is that the network part is the major sort key, and for * equal network parts we sort on the host part. Note this is only sane * for CIDR if address bits to the right of the mask are guaranteed zero; * otherwise logically-equal CIDRs might compare different. */ static int32 network_cmp_internal(inet *a1, inet *a2) { if (ip_family(a1) == ip_family(a2)) { int order; order = bitncmp(ip_addr(a1), ip_addr(a2), Min(ip_bits(a1), ip_bits(a2))); if (order != 0) return order; order = ((int) ip_bits(a1)) - ((int) ip_bits(a2)); if (order != 0) return order; return bitncmp(ip_addr(a1), ip_addr(a2), ip_maxbits(a1)); } return ip_family(a1) - ip_family(a2); } Datum network_cmp(PG_FUNCTION_ARGS) { inet *a1 = PG_GETARG_INET_PP(0); inet *a2 = PG_GETARG_INET_PP(1); PG_RETURN_INT32(network_cmp_internal(a1, a2)); } /* * SortSupport strategy routine */ Datum network_sortsupport(PG_FUNCTION_ARGS) { SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); ssup->comparator = network_fast_cmp; ssup->ssup_extra = NULL; if (ssup->abbreviate) { network_sortsupport_state *uss; MemoryContext oldcontext; oldcontext = MemoryContextSwitchTo(ssup->ssup_cxt); uss = palloc(sizeof(network_sortsupport_state)); uss->input_count = 0; uss->estimating = true; initHyperLogLog(&uss->abbr_card, 10); ssup->ssup_extra = uss; ssup->comparator = ssup_datum_unsigned_cmp; ssup->abbrev_converter = network_abbrev_convert; ssup->abbrev_abort = network_abbrev_abort; ssup->abbrev_full_comparator = network_fast_cmp; MemoryContextSwitchTo(oldcontext); } PG_RETURN_VOID(); } /* * SortSupport comparison func */ static int network_fast_cmp(Datum x, Datum y, SortSupport ssup) { inet *arg1 = DatumGetInetPP(x); inet *arg2 = DatumGetInetPP(y); return network_cmp_internal(arg1, arg2); } /* * Callback for estimating effectiveness of abbreviated key optimization. * * We pay no attention to the cardinality of the non-abbreviated data, because * there is no equality fast-path within authoritative inet comparator. */ static bool network_abbrev_abort(int memtupcount, SortSupport ssup) { network_sortsupport_state *uss = ssup->ssup_extra; double abbr_card; if (memtupcount < 10000 || uss->input_count < 10000 || !uss->estimating) return false; abbr_card = estimateHyperLogLog(&uss->abbr_card); /* * If we have >100k distinct values, then even if we were sorting many * billion rows we'd likely still break even, and the penalty of undoing * that many rows of abbrevs would probably not be worth it. At this point * we stop counting because we know that we're now fully committed. */ if (abbr_card > 100000.0) { #ifdef TRACE_SORT if (trace_sort) elog(LOG, "network_abbrev: estimation ends at cardinality %f" " after " INT64_FORMAT " values (%d rows)", abbr_card, uss->input_count, memtupcount); #endif uss->estimating = false; return false; } /* * Target minimum cardinality is 1 per ~2k of non-null inputs. 0.5 row * fudge factor allows us to abort earlier on genuinely pathological data * where we've had exactly one abbreviated value in the first 2k * (non-null) rows. */ if (abbr_card < uss->input_count / 2000.0 + 0.5) { #ifdef TRACE_SORT if (trace_sort) elog(LOG, "network_abbrev: aborting abbreviation at cardinality %f" " below threshold %f after " INT64_FORMAT " values (%d rows)", abbr_card, uss->input_count / 2000.0 + 0.5, uss->input_count, memtupcount); #endif return true; } #ifdef TRACE_SORT if (trace_sort) elog(LOG, "network_abbrev: cardinality %f after " INT64_FORMAT " values (%d rows)", abbr_card, uss->input_count, memtupcount); #endif return false; } /* * SortSupport conversion routine. Converts original inet/cidr representation * to abbreviated key representation that works with simple 3-way unsigned int * comparisons. The network_cmp_internal() rules for sorting inet/cidr datums * are followed by abbreviated comparisons by an encoding scheme that * conditions keys through careful use of padding. * * Some background: inet values have three major components (take for example * the address 1.2.3.4/24): * * * A network, or netmasked bits (1.2.3.0). * * A netmask size (/24). * * A subnet, or bits outside of the netmask (0.0.0.4). * * cidr values are the same except that with only the first two components -- * all their subnet bits *must* be zero (1.2.3.0/24). * * IPv4 and IPv6 are identical in this makeup, with the difference being that * IPv4 addresses have a maximum of 32 bits compared to IPv6's 64 bits, so in * IPv6 each part may be larger. * * inet/cidr types compare using these sorting rules. If inequality is detected * at any step, comparison is finished. If any rule is a tie, the algorithm * drops through to the next to break it: * * 1. IPv4 always appears before IPv6. * 2. Network bits are compared. * 3. Netmask size is compared. * 4. All bits are compared (having made it here, we know that both * netmasked bits and netmask size are equal, so we're in effect only * comparing subnet bits). * * When generating abbreviated keys for SortSupport, we pack as much as we can * into a datum while ensuring that when comparing those keys as integers, * these rules will be respected. Exact contents depend on IP family and datum * size. * * IPv4 * ---- * * 4 byte datums: * * Start with 1 bit for the IP family (IPv4 or IPv6; this bit is present in * every case below) followed by all but 1 of the netmasked bits. * * +----------+---------------------+ * | 1 bit IP | 31 bits network | (1 bit network * | family | (truncated) | omitted) * +----------+---------------------+ * * 8 byte datums: * * We have space to store all netmasked bits, followed by the netmask size, * followed by 25 bits of the subnet (25 bits is usually more than enough in * practice). cidr datums always have all-zero subnet bits. * * +----------+-----------------------+--------------+--------------------+ * | 1 bit IP | 32 bits network | 6 bits | 25 bits subnet | * | family | (full) | network size | (truncated) | * +----------+-----------------------+--------------+--------------------+ * * IPv6 * ---- * * 4 byte datums: * * +----------+---------------------+ * | 1 bit IP | 31 bits network | (up to 97 bits * | family | (truncated) | network omitted) * +----------+---------------------+ * * 8 byte datums: * * +----------+---------------------------------+ * | 1 bit IP | 63 bits network | (up to 65 bits * | family | (truncated) | network omitted) * +----------+---------------------------------+ */ static Datum network_abbrev_convert(Datum original, SortSupport ssup) { network_sortsupport_state *uss = ssup->ssup_extra; inet *authoritative = DatumGetInetPP(original); Datum res, ipaddr_datum, subnet_bitmask, network; int subnet_size; Assert(ip_family(authoritative) == PGSQL_AF_INET || ip_family(authoritative) == PGSQL_AF_INET6); /* * Get an unsigned integer representation of the IP address by taking its * first 4 or 8 bytes. Always take all 4 bytes of an IPv4 address. Take * the first 8 bytes of an IPv6 address with an 8 byte datum and 4 bytes * otherwise. * * We're consuming an array of unsigned char, so byteswap on little endian * systems (an inet's ipaddr field stores the most significant byte * first). */ if (ip_family(authoritative) == PGSQL_AF_INET) { uint32 ipaddr_datum32; memcpy(&ipaddr_datum32, ip_addr(authoritative), sizeof(uint32)); /* Must byteswap on little-endian machines */ #ifndef WORDS_BIGENDIAN ipaddr_datum = pg_bswap32(ipaddr_datum32); #else ipaddr_datum = ipaddr_datum32; #endif /* Initialize result without setting ipfamily bit */ res = (Datum) 0; } else { memcpy(&ipaddr_datum, ip_addr(authoritative), sizeof(Datum)); /* Must byteswap on little-endian machines */ ipaddr_datum = DatumBigEndianToNative(ipaddr_datum); /* Initialize result with ipfamily (most significant) bit set */ res = ((Datum) 1) << (SIZEOF_DATUM * BITS_PER_BYTE - 1); } /* * ipaddr_datum must be "split": high order bits go in "network" component * of abbreviated key (often with zeroed bits at the end due to masking), * while low order bits go in "subnet" component when there is space for * one. This is often accomplished by generating a temp datum subnet * bitmask, which we may reuse later when generating the subnet bits * themselves. (Note that subnet bits are only used with IPv4 datums on * platforms where datum is 8 bytes.) * * The number of bits in subnet is used to generate a datum subnet * bitmask. For example, with a /24 IPv4 datum there are 8 subnet bits * (since 32 - 24 is 8), so the final subnet bitmask is B'1111 1111'. We * need explicit handling for cases where the ipaddr bits cannot all fit * in a datum, though (otherwise we'd incorrectly mask the network * component with IPv6 values). */ subnet_size = ip_maxbits(authoritative) - ip_bits(authoritative); Assert(subnet_size >= 0); /* subnet size must work with prefix ipaddr cases */ subnet_size %= SIZEOF_DATUM * BITS_PER_BYTE; if (ip_bits(authoritative) == 0) { /* Fit as many ipaddr bits as possible into subnet */ subnet_bitmask = ((Datum) 0) - 1; network = 0; } else if (ip_bits(authoritative) < SIZEOF_DATUM * BITS_PER_BYTE) { /* Split ipaddr bits between network and subnet */ subnet_bitmask = (((Datum) 1) << subnet_size) - 1; network = ipaddr_datum & ~subnet_bitmask; } else { /* Fit as many ipaddr bits as possible into network */ subnet_bitmask = 0; network = ipaddr_datum; } #if SIZEOF_DATUM == 8 if (ip_family(authoritative) == PGSQL_AF_INET) { /* * IPv4 with 8 byte datums: keep all 32 netmasked bits, netmask size, * and most significant 25 subnet bits */ Datum netmask_size = (Datum) ip_bits(authoritative); Datum subnet; /* * Shift left 31 bits: 6 bits netmask size + 25 subnet bits. * * We don't make any distinction between network bits that are zero * due to masking and "true"/non-masked zero bits. An abbreviated * comparison that is resolved by comparing a non-masked and non-zero * bit to a masked/zeroed bit is effectively resolved based on * ip_bits(), even though the comparison won't reach the netmask_size * bits. */ network <<= (ABBREV_BITS_INET4_NETMASK_SIZE + ABBREV_BITS_INET4_SUBNET); /* Shift size to make room for subnet bits at the end */ netmask_size <<= ABBREV_BITS_INET4_SUBNET; /* Extract subnet bits without shifting them */ subnet = ipaddr_datum & subnet_bitmask; /* * If we have more than 25 subnet bits, we can't fit everything. Shift * subnet down to avoid clobbering bits that are only supposed to be * used for netmask_size. * * Discarding the least significant subnet bits like this is correct * because abbreviated comparisons that are resolved at the subnet * level must have had equal netmask_size/ip_bits() values in order to * get that far. */ if (subnet_size > ABBREV_BITS_INET4_SUBNET) subnet >>= subnet_size - ABBREV_BITS_INET4_SUBNET; /* * Assemble the final abbreviated key without clobbering the ipfamily * bit that must remain a zero. */ res |= network | netmask_size | subnet; } else #endif { /* * 4 byte datums, or IPv6 with 8 byte datums: Use as many of the * netmasked bits as will fit in final abbreviated key. Avoid * clobbering the ipfamily bit that was set earlier. */ res |= network >> 1; } uss->input_count += 1; /* Hash abbreviated key */ if (uss->estimating) { uint32 tmp; #if SIZEOF_DATUM == 8 tmp = (uint32) res ^ (uint32) ((uint64) res >> 32); #else /* SIZEOF_DATUM != 8 */ tmp = (uint32) res; #endif addHyperLogLog(&uss->abbr_card, DatumGetUInt32(hash_uint32(tmp))); } return res; } /* * Boolean ordering tests. */ Datum network_lt(PG_FUNCTION_ARGS) { inet *a1 = PG_GETARG_INET_PP(0); inet *a2 = PG_GETARG_INET_PP(1); PG_RETURN_BOOL(network_cmp_internal(a1, a2) < 0); } Datum network_le(PG_FUNCTION_ARGS) { inet *a1 = PG_GETARG_INET_PP(0); inet *a2 = PG_GETARG_INET_PP(1); PG_RETURN_BOOL(network_cmp_internal(a1, a2) <= 0); } Datum network_eq(PG_FUNCTION_ARGS) { inet *a1 = PG_GETARG_INET_PP(0); inet *a2 = PG_GETARG_INET_PP(1); PG_RETURN_BOOL(network_cmp_internal(a1, a2) == 0); } Datum network_ge(PG_FUNCTION_ARGS) { inet *a1 = PG_GETARG_INET_PP(0); inet *a2 = PG_GETARG_INET_PP(1); PG_RETURN_BOOL(network_cmp_internal(a1, a2) >= 0); } Datum network_gt(PG_FUNCTION_ARGS) { inet *a1 = PG_GETARG_INET_PP(0); inet *a2 = PG_GETARG_INET_PP(1); PG_RETURN_BOOL(network_cmp_internal(a1, a2) > 0); } Datum network_ne(PG_FUNCTION_ARGS) { inet *a1 = PG_GETARG_INET_PP(0); inet *a2 = PG_GETARG_INET_PP(1); PG_RETURN_BOOL(network_cmp_internal(a1, a2) != 0); } /* * MIN/MAX support functions. */ Datum network_smaller(PG_FUNCTION_ARGS) { inet *a1 = PG_GETARG_INET_PP(0); inet *a2 = PG_GETARG_INET_PP(1); if (network_cmp_internal(a1, a2) < 0) PG_RETURN_INET_P(a1); else PG_RETURN_INET_P(a2); } Datum network_larger(PG_FUNCTION_ARGS) { inet *a1 = PG_GETARG_INET_PP(0); inet *a2 = PG_GETARG_INET_PP(1); if (network_cmp_internal(a1, a2) > 0) PG_RETURN_INET_P(a1); else PG_RETURN_INET_P(a2); } /* * Support function for hash indexes on inet/cidr. */ Datum hashinet(PG_FUNCTION_ARGS) { inet *addr = PG_GETARG_INET_PP(0); int addrsize = ip_addrsize(addr); /* XXX this assumes there are no pad bytes in the data structure */ return hash_any((unsigned char *) VARDATA_ANY(addr), addrsize + 2); } Datum hashinetextended(PG_FUNCTION_ARGS) { inet *addr = PG_GETARG_INET_PP(0); int addrsize = ip_addrsize(addr); return hash_any_extended((unsigned char *) VARDATA_ANY(addr), addrsize + 2, PG_GETARG_INT64(1)); } /* * Boolean network-inclusion tests. */ Datum network_sub(PG_FUNCTION_ARGS) { inet *a1 = PG_GETARG_INET_PP(0); inet *a2 = PG_GETARG_INET_PP(1); if (ip_family(a1) == ip_family(a2)) { PG_RETURN_BOOL(ip_bits(a1) > ip_bits(a2) && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a2)) == 0); } PG_RETURN_BOOL(false); } Datum network_subeq(PG_FUNCTION_ARGS) { inet *a1 = PG_GETARG_INET_PP(0); inet *a2 = PG_GETARG_INET_PP(1); if (ip_family(a1) == ip_family(a2)) { PG_RETURN_BOOL(ip_bits(a1) >= ip_bits(a2) && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a2)) == 0); } PG_RETURN_BOOL(false); } Datum network_sup(PG_FUNCTION_ARGS) { inet *a1 = PG_GETARG_INET_PP(0); inet *a2 = PG_GETARG_INET_PP(1); if (ip_family(a1) == ip_family(a2)) { PG_RETURN_BOOL(ip_bits(a1) < ip_bits(a2) && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a1)) == 0); } PG_RETURN_BOOL(false); } Datum network_supeq(PG_FUNCTION_ARGS) { inet *a1 = PG_GETARG_INET_PP(0); inet *a2 = PG_GETARG_INET_PP(1); if (ip_family(a1) == ip_family(a2)) { PG_RETURN_BOOL(ip_bits(a1) <= ip_bits(a2) && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a1)) == 0); } PG_RETURN_BOOL(false); } Datum network_overlap(PG_FUNCTION_ARGS) { inet *a1 = PG_GETARG_INET_PP(0); inet *a2 = PG_GETARG_INET_PP(1); if (ip_family(a1) == ip_family(a2)) { PG_RETURN_BOOL(bitncmp(ip_addr(a1), ip_addr(a2), Min(ip_bits(a1), ip_bits(a2))) == 0); } PG_RETURN_BOOL(false); } /* * Planner support function for network subset/superset operators */ Datum network_subset_support(PG_FUNCTION_ARGS) { Node *rawreq = (Node *) PG_GETARG_POINTER(0); Node *ret = NULL; if (IsA(rawreq, SupportRequestIndexCondition)) { /* Try to convert operator/function call to index conditions */ SupportRequestIndexCondition *req = (SupportRequestIndexCondition *) rawreq; if (is_opclause(req->node)) { OpExpr *clause = (OpExpr *) req->node; Assert(list_length(clause->args) == 2); ret = (Node *) match_network_function((Node *) linitial(clause->args), (Node *) lsecond(clause->args), req->indexarg, req->funcid, req->opfamily); } else if (is_funcclause(req->node)) /* be paranoid */ { FuncExpr *clause = (FuncExpr *) req->node; Assert(list_length(clause->args) == 2); ret = (Node *) match_network_function((Node *) linitial(clause->args), (Node *) lsecond(clause->args), req->indexarg, req->funcid, req->opfamily); } } PG_RETURN_POINTER(ret); } /* * match_network_function * Try to generate an indexqual for a network subset/superset function. * * This layer is just concerned with identifying the function and swapping * the arguments if necessary. */ static List * match_network_function(Node *leftop, Node *rightop, int indexarg, Oid funcid, Oid opfamily) { switch (funcid) { case F_NETWORK_SUB: /* indexkey must be on the left */ if (indexarg != 0) return NIL; return match_network_subset(leftop, rightop, false, opfamily); case F_NETWORK_SUBEQ: /* indexkey must be on the left */ if (indexarg != 0) return NIL; return match_network_subset(leftop, rightop, true, opfamily); case F_NETWORK_SUP: /* indexkey must be on the right */ if (indexarg != 1) return NIL; return match_network_subset(rightop, leftop, false, opfamily); case F_NETWORK_SUPEQ: /* indexkey must be on the right */ if (indexarg != 1) return NIL; return match_network_subset(rightop, leftop, true, opfamily); default: /* * We'd only get here if somebody attached this support function * to an unexpected function. Maybe we should complain, but for * now, do nothing. */ return NIL; } } /* * match_network_subset * Try to generate an indexqual for a network subset function. */ static List * match_network_subset(Node *leftop, Node *rightop, bool is_eq, Oid opfamily) { List *result; Datum rightopval; Oid datatype = INETOID; Oid opr1oid; Oid opr2oid; Datum opr1right; Datum opr2right; Expr *expr; /* * Can't do anything with a non-constant or NULL comparison value. * * Note that since we restrict ourselves to cases with a hard constant on * the RHS, it's a-fortiori a pseudoconstant, and we don't need to worry * about verifying that. */ if (!IsA(rightop, Const) || ((Const *) rightop)->constisnull) return NIL; rightopval = ((Const *) rightop)->constvalue; /* * Must check that index's opfamily supports the operators we will want to * apply. * * We insist on the opfamily being the specific one we expect, else we'd * do the wrong thing if someone were to make a reverse-sort opfamily with * the same operators. */ if (opfamily != NETWORK_BTREE_FAM_OID) return NIL; /* * create clause "key >= network_scan_first( rightopval )", or ">" if the * operator disallows equality. * * Note: seeing that this function supports only fixed values for opfamily * and datatype, we could just hard-wire the operator OIDs instead of * looking them up. But for now it seems better to be general. */ if (is_eq) { opr1oid = get_opfamily_member(opfamily, datatype, datatype, BTGreaterEqualStrategyNumber); if (opr1oid == InvalidOid) elog(ERROR, "no >= operator for opfamily %u", opfamily); } else { opr1oid = get_opfamily_member(opfamily, datatype, datatype, BTGreaterStrategyNumber); if (opr1oid == InvalidOid) elog(ERROR, "no > operator for opfamily %u", opfamily); } opr1right = network_scan_first(rightopval); expr = make_opclause(opr1oid, BOOLOID, false, (Expr *) leftop, (Expr *) makeConst(datatype, -1, InvalidOid, /* not collatable */ -1, opr1right, false, false), InvalidOid, InvalidOid); result = list_make1(expr); /* create clause "key <= network_scan_last( rightopval )" */ opr2oid = get_opfamily_member(opfamily, datatype, datatype, BTLessEqualStrategyNumber); if (opr2oid == InvalidOid) elog(ERROR, "no <= operator for opfamily %u", opfamily); opr2right = network_scan_last(rightopval); expr = make_opclause(opr2oid, BOOLOID, false, (Expr *) leftop, (Expr *) makeConst(datatype, -1, InvalidOid, /* not collatable */ -1, opr2right, false, false), InvalidOid, InvalidOid); result = lappend(result, expr); return result; } /* * Extract data from a network datatype. */ Datum network_host(PG_FUNCTION_ARGS) { inet *ip = PG_GETARG_INET_PP(0); char *ptr; char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")]; /* force display of max bits, regardless of masklen... */ if (pg_inet_net_ntop(ip_family(ip), ip_addr(ip), ip_maxbits(ip), tmp, sizeof(tmp)) == NULL) ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("could not format inet value: %m"))); /* Suppress /n if present (shouldn't happen now) */ if ((ptr = strchr(tmp, '/')) != NULL) *ptr = '\0'; PG_RETURN_TEXT_P(cstring_to_text(tmp)); } /* * network_show implements the inet and cidr casts to text. This is not * quite the same behavior as network_out, hence we can't drop it in favor * of CoerceViaIO. */ Datum network_show(PG_FUNCTION_ARGS) { inet *ip = PG_GETARG_INET_PP(0); int len; char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")]; if (pg_inet_net_ntop(ip_family(ip), ip_addr(ip), ip_maxbits(ip), tmp, sizeof(tmp)) == NULL) ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("could not format inet value: %m"))); /* Add /n if not present (which it won't be) */ if (strchr(tmp, '/') == NULL) { len = strlen(tmp); snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(ip)); } PG_RETURN_TEXT_P(cstring_to_text(tmp)); } Datum inet_abbrev(PG_FUNCTION_ARGS) { inet *ip = PG_GETARG_INET_PP(0); char *dst; char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")]; dst = pg_inet_net_ntop(ip_family(ip), ip_addr(ip), ip_bits(ip), tmp, sizeof(tmp)); if (dst == NULL) ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("could not format inet value: %m"))); PG_RETURN_TEXT_P(cstring_to_text(tmp)); } Datum cidr_abbrev(PG_FUNCTION_ARGS) { inet *ip = PG_GETARG_INET_PP(0); char *dst; char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")]; dst = pg_inet_cidr_ntop(ip_family(ip), ip_addr(ip), ip_bits(ip), tmp, sizeof(tmp)); if (dst == NULL) ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("could not format cidr value: %m"))); PG_RETURN_TEXT_P(cstring_to_text(tmp)); } Datum network_masklen(PG_FUNCTION_ARGS) { inet *ip = PG_GETARG_INET_PP(0); PG_RETURN_INT32(ip_bits(ip)); } Datum network_family(PG_FUNCTION_ARGS) { inet *ip = PG_GETARG_INET_PP(0); switch (ip_family(ip)) { case PGSQL_AF_INET: PG_RETURN_INT32(4); break; case PGSQL_AF_INET6: PG_RETURN_INT32(6); break; default: PG_RETURN_INT32(0); break; } } Datum network_broadcast(PG_FUNCTION_ARGS) { inet *ip = PG_GETARG_INET_PP(0); inet *dst; int byte; int bits; int maxbytes; unsigned char mask; unsigned char *a, *b; /* make sure any unused bits are zeroed */ dst = (inet *) palloc0(sizeof(inet)); maxbytes = ip_addrsize(ip); bits = ip_bits(ip); a = ip_addr(ip); b = ip_addr(dst); for (byte = 0; byte < maxbytes; byte++) { if (bits >= 8) { mask = 0x00; bits -= 8; } else if (bits == 0) mask = 0xff; else { mask = 0xff >> bits; bits = 0; } b[byte] = a[byte] | mask; } ip_family(dst) = ip_family(ip); ip_bits(dst) = ip_bits(ip); SET_INET_VARSIZE(dst); PG_RETURN_INET_P(dst); } Datum network_network(PG_FUNCTION_ARGS) { inet *ip = PG_GETARG_INET_PP(0); inet *dst; int byte; int bits; unsigned char mask; unsigned char *a, *b; /* make sure any unused bits are zeroed */ dst = (inet *) palloc0(sizeof(inet)); bits = ip_bits(ip); a = ip_addr(ip); b = ip_addr(dst); byte = 0; while (bits) { if (bits >= 8) { mask = 0xff; bits -= 8; } else { mask = 0xff << (8 - bits); bits = 0; } b[byte] = a[byte] & mask; byte++; } ip_family(dst) = ip_family(ip); ip_bits(dst) = ip_bits(ip); SET_INET_VARSIZE(dst); PG_RETURN_INET_P(dst); } Datum network_netmask(PG_FUNCTION_ARGS) { inet *ip = PG_GETARG_INET_PP(0); inet *dst; int byte; int bits; unsigned char mask; unsigned char *b; /* make sure any unused bits are zeroed */ dst = (inet *) palloc0(sizeof(inet)); bits = ip_bits(ip); b = ip_addr(dst); byte = 0; while (bits) { if (bits >= 8) { mask = 0xff; bits -= 8; } else { mask = 0xff << (8 - bits); bits = 0; } b[byte] = mask; byte++; } ip_family(dst) = ip_family(ip); ip_bits(dst) = ip_maxbits(ip); SET_INET_VARSIZE(dst); PG_RETURN_INET_P(dst); } Datum network_hostmask(PG_FUNCTION_ARGS) { inet *ip = PG_GETARG_INET_PP(0); inet *dst; int byte; int bits; int maxbytes; unsigned char mask; unsigned char *b; /* make sure any unused bits are zeroed */ dst = (inet *) palloc0(sizeof(inet)); maxbytes = ip_addrsize(ip); bits = ip_maxbits(ip) - ip_bits(ip); b = ip_addr(dst); byte = maxbytes - 1; while (bits) { if (bits >= 8) { mask = 0xff; bits -= 8; } else { mask = 0xff >> (8 - bits); bits = 0; } b[byte] = mask; byte--; } ip_family(dst) = ip_family(ip); ip_bits(dst) = ip_maxbits(ip); SET_INET_VARSIZE(dst); PG_RETURN_INET_P(dst); } /* * Returns true if the addresses are from the same family, or false. Used to * check that we can create a network which contains both of the networks. */ Datum inet_same_family(PG_FUNCTION_ARGS) { inet *a1 = PG_GETARG_INET_PP(0); inet *a2 = PG_GETARG_INET_PP(1); PG_RETURN_BOOL(ip_family(a1) == ip_family(a2)); } /* * Returns the smallest CIDR which contains both of the inputs. */ Datum inet_merge(PG_FUNCTION_ARGS) { inet *a1 = PG_GETARG_INET_PP(0), *a2 = PG_GETARG_INET_PP(1); int commonbits; if (ip_family(a1) != ip_family(a2)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("cannot merge addresses from different families"))); commonbits = bitncommon(ip_addr(a1), ip_addr(a2), Min(ip_bits(a1), ip_bits(a2))); PG_RETURN_INET_P(cidr_set_masklen_internal(a1, commonbits)); } /* * Convert a value of a network datatype to an approximate scalar value. * This is used for estimating selectivities of inequality operators * involving network types. * * On failure (e.g., unsupported typid), set *failure to true; * otherwise, that variable is not changed. */ double convert_network_to_scalar(Datum value, Oid typid, bool *failure) { switch (typid) { case INETOID: case CIDROID: { inet *ip = DatumGetInetPP(value); int len; double res; int i; /* * Note that we don't use the full address for IPv6. */ if (ip_family(ip) == PGSQL_AF_INET) len = 4; else len = 5; res = ip_family(ip); for (i = 0; i < len; i++) { res *= 256; res += ip_addr(ip)[i]; } return res; } case MACADDROID: { macaddr *mac = DatumGetMacaddrP(value); double res; res = (mac->a << 16) | (mac->b << 8) | (mac->c); res *= 256 * 256 * 256; res += (mac->d << 16) | (mac->e << 8) | (mac->f); return res; } case MACADDR8OID: { macaddr8 *mac = DatumGetMacaddr8P(value); double res; res = (mac->a << 24) | (mac->b << 16) | (mac->c << 8) | (mac->d); res *= ((double) 256) * 256 * 256 * 256; res += (mac->e << 24) | (mac->f << 16) | (mac->g << 8) | (mac->h); return res; } } *failure = true; return 0; } /* * int * bitncmp(l, r, n) * compare bit masks l and r, for n bits. * return: * <0, >0, or 0 in the libc tradition. * note: * network byte order assumed. this means 192.5.5.240/28 has * 0x11110000 in its fourth octet. * author: * Paul Vixie (ISC), June 1996 */ int bitncmp(const unsigned char *l, const unsigned char *r, int n) { unsigned int lb, rb; int x, b; b = n / 8; x = memcmp(l, r, b); if (x || (n % 8) == 0) return x; lb = l[b]; rb = r[b]; for (b = n % 8; b > 0; b--) { if (IS_HIGHBIT_SET(lb) != IS_HIGHBIT_SET(rb)) { if (IS_HIGHBIT_SET(lb)) return 1; return -1; } lb <<= 1; rb <<= 1; } return 0; } /* * bitncommon: compare bit masks l and r, for up to n bits. * * Returns the number of leading bits that match (0 to n). */ int bitncommon(const unsigned char *l, const unsigned char *r, int n) { int byte, nbits; /* number of bits to examine in last byte */ nbits = n % 8; /* check whole bytes */ for (byte = 0; byte < n / 8; byte++) { if (l[byte] != r[byte]) { /* at least one bit in the last byte is not common */ nbits = 7; break; } } /* check bits in last partial byte */ if (nbits != 0) { /* calculate diff of first non-matching bytes */ unsigned int diff = l[byte] ^ r[byte]; /* compare the bits from the most to the least */ while ((diff >> (8 - nbits)) != 0) nbits--; } return (8 * byte) + nbits; } /* * Verify a CIDR address is OK (doesn't have bits set past the masklen) */ static bool addressOK(unsigned char *a, int bits, int family) { int byte; int nbits; int maxbits; int maxbytes; unsigned char mask; if (family == PGSQL_AF_INET) { maxbits = 32; maxbytes = 4; } else { maxbits = 128; maxbytes = 16; } Assert(bits <= maxbits); if (bits == maxbits) return true; byte = bits / 8; nbits = bits % 8; mask = 0xff; if (bits != 0) mask >>= nbits; while (byte < maxbytes) { if ((a[byte] & mask) != 0) return false; mask = 0xff; byte++; } return true; } /* * These functions are used by planner to generate indexscan limits * for clauses a << b and a <<= b */ /* return the minimal value for an IP on a given network */ Datum network_scan_first(Datum in) { return DirectFunctionCall1(network_network, in); } /* * return "last" IP on a given network. It's the broadcast address, * however, masklen has to be set to its max bits, since * 192.168.0.255/24 is considered less than 192.168.0.255/32 * * inet_set_masklen() hacked to max out the masklength to 128 for IPv6 * and 32 for IPv4 when given '-1' as argument. */ Datum network_scan_last(Datum in) { return DirectFunctionCall2(inet_set_masklen, DirectFunctionCall1(network_broadcast, in), Int32GetDatum(-1)); } /* * IP address that the client is connecting from (NULL if Unix socket) */ Datum inet_client_addr(PG_FUNCTION_ARGS) { Port *port = MyProcPort; char remote_host[NI_MAXHOST]; int ret; if (port == NULL) PG_RETURN_NULL(); switch (port->raddr.addr.ss_family) { case AF_INET: case AF_INET6: break; default: PG_RETURN_NULL(); } remote_host[0] = '\0'; ret = pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen, remote_host, sizeof(remote_host), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV); if (ret != 0) PG_RETURN_NULL(); clean_ipv6_addr(port->raddr.addr.ss_family, remote_host); PG_RETURN_INET_P(network_in(remote_host, false, NULL)); } /* * port that the client is connecting from (NULL if Unix socket) */ Datum inet_client_port(PG_FUNCTION_ARGS) { Port *port = MyProcPort; char remote_port[NI_MAXSERV]; int ret; if (port == NULL) PG_RETURN_NULL(); switch (port->raddr.addr.ss_family) { case AF_INET: case AF_INET6: break; default: PG_RETURN_NULL(); } remote_port[0] = '\0'; ret = pg_getnameinfo_all(&port->raddr.addr, port->raddr.salen, NULL, 0, remote_port, sizeof(remote_port), NI_NUMERICHOST | NI_NUMERICSERV); if (ret != 0) PG_RETURN_NULL(); PG_RETURN_DATUM(DirectFunctionCall1(int4in, CStringGetDatum(remote_port))); } /* * IP address that the server accepted the connection on (NULL if Unix socket) */ Datum inet_server_addr(PG_FUNCTION_ARGS) { Port *port = MyProcPort; char local_host[NI_MAXHOST]; int ret; if (port == NULL) PG_RETURN_NULL(); switch (port->laddr.addr.ss_family) { case AF_INET: case AF_INET6: break; default: PG_RETURN_NULL(); } local_host[0] = '\0'; ret = pg_getnameinfo_all(&port->laddr.addr, port->laddr.salen, local_host, sizeof(local_host), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV); if (ret != 0) PG_RETURN_NULL(); clean_ipv6_addr(port->laddr.addr.ss_family, local_host); PG_RETURN_INET_P(network_in(local_host, false, NULL)); } /* * port that the server accepted the connection on (NULL if Unix socket) */ Datum inet_server_port(PG_FUNCTION_ARGS) { Port *port = MyProcPort; char local_port[NI_MAXSERV]; int ret; if (port == NULL) PG_RETURN_NULL(); switch (port->laddr.addr.ss_family) { case AF_INET: case AF_INET6: break; default: PG_RETURN_NULL(); } local_port[0] = '\0'; ret = pg_getnameinfo_all(&port->laddr.addr, port->laddr.salen, NULL, 0, local_port, sizeof(local_port), NI_NUMERICHOST | NI_NUMERICSERV); if (ret != 0) PG_RETURN_NULL(); PG_RETURN_DATUM(DirectFunctionCall1(int4in, CStringGetDatum(local_port))); } Datum inetnot(PG_FUNCTION_ARGS) { inet *ip = PG_GETARG_INET_PP(0); inet *dst; dst = (inet *) palloc0(sizeof(inet)); { int nb = ip_addrsize(ip); unsigned char *pip = ip_addr(ip); unsigned char *pdst = ip_addr(dst); while (--nb >= 0) pdst[nb] = ~pip[nb]; } ip_bits(dst) = ip_bits(ip); ip_family(dst) = ip_family(ip); SET_INET_VARSIZE(dst); PG_RETURN_INET_P(dst); } Datum inetand(PG_FUNCTION_ARGS) { inet *ip = PG_GETARG_INET_PP(0); inet *ip2 = PG_GETARG_INET_PP(1); inet *dst; dst = (inet *) palloc0(sizeof(inet)); if (ip_family(ip) != ip_family(ip2)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("cannot AND inet values of different sizes"))); else { int nb = ip_addrsize(ip); unsigned char *pip = ip_addr(ip); unsigned char *pip2 = ip_addr(ip2); unsigned char *pdst = ip_addr(dst); while (--nb >= 0) pdst[nb] = pip[nb] & pip2[nb]; } ip_bits(dst) = Max(ip_bits(ip), ip_bits(ip2)); ip_family(dst) = ip_family(ip); SET_INET_VARSIZE(dst); PG_RETURN_INET_P(dst); } Datum inetor(PG_FUNCTION_ARGS) { inet *ip = PG_GETARG_INET_PP(0); inet *ip2 = PG_GETARG_INET_PP(1); inet *dst; dst = (inet *) palloc0(sizeof(inet)); if (ip_family(ip) != ip_family(ip2)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("cannot OR inet values of different sizes"))); else { int nb = ip_addrsize(ip); unsigned char *pip = ip_addr(ip); unsigned char *pip2 = ip_addr(ip2); unsigned char *pdst = ip_addr(dst); while (--nb >= 0) pdst[nb] = pip[nb] | pip2[nb]; } ip_bits(dst) = Max(ip_bits(ip), ip_bits(ip2)); ip_family(dst) = ip_family(ip); SET_INET_VARSIZE(dst); PG_RETURN_INET_P(dst); } static inet * internal_inetpl(inet *ip, int64 addend) { inet *dst; dst = (inet *) palloc0(sizeof(inet)); { int nb = ip_addrsize(ip); unsigned char *pip = ip_addr(ip); unsigned char *pdst = ip_addr(dst); int carry = 0; while (--nb >= 0) { carry = pip[nb] + (int) (addend & 0xFF) + carry; pdst[nb] = (unsigned char) (carry & 0xFF); carry >>= 8; /* * We have to be careful about right-shifting addend because * right-shift isn't portable for negative values, while simply * dividing by 256 doesn't work (the standard rounding is in the * wrong direction, besides which there may be machines out there * that round the wrong way). So, explicitly clear the low-order * byte to remove any doubt about the correct result of the * division, and then divide rather than shift. */ addend &= ~((int64) 0xFF); addend /= 0x100; } /* * At this point we should have addend and carry both zero if original * addend was >= 0, or addend -1 and carry 1 if original addend was < * 0. Anything else means overflow. */ if (!((addend == 0 && carry == 0) || (addend == -1 && carry == 1))) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("result is out of range"))); } ip_bits(dst) = ip_bits(ip); ip_family(dst) = ip_family(ip); SET_INET_VARSIZE(dst); return dst; } Datum inetpl(PG_FUNCTION_ARGS) { inet *ip = PG_GETARG_INET_PP(0); int64 addend = PG_GETARG_INT64(1); PG_RETURN_INET_P(internal_inetpl(ip, addend)); } Datum inetmi_int8(PG_FUNCTION_ARGS) { inet *ip = PG_GETARG_INET_PP(0); int64 addend = PG_GETARG_INT64(1); PG_RETURN_INET_P(internal_inetpl(ip, -addend)); } Datum inetmi(PG_FUNCTION_ARGS) { inet *ip = PG_GETARG_INET_PP(0); inet *ip2 = PG_GETARG_INET_PP(1); int64 res = 0; if (ip_family(ip) != ip_family(ip2)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("cannot subtract inet values of different sizes"))); else { /* * We form the difference using the traditional complement, increment, * and add rule, with the increment part being handled by starting the * carry off at 1. If you don't think integer arithmetic is done in * two's complement, too bad. */ int nb = ip_addrsize(ip); int byte = 0; unsigned char *pip = ip_addr(ip); unsigned char *pip2 = ip_addr(ip2); int carry = 1; while (--nb >= 0) { int lobyte; carry = pip[nb] + (~pip2[nb] & 0xFF) + carry; lobyte = carry & 0xFF; if (byte < sizeof(int64)) { res |= ((int64) lobyte) << (byte * 8); } else { /* * Input wider than int64: check for overflow. All bytes to * the left of what will fit should be 0 or 0xFF, depending on * sign of the now-complete result. */ if ((res < 0) ? (lobyte != 0xFF) : (lobyte != 0)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("result is out of range"))); } carry >>= 8; byte++; } /* * If input is narrower than int64, overflow is not possible, but we * have to do proper sign extension. */ if (carry == 0 && byte < sizeof(int64)) res |= ((uint64) (int64) -1) << (byte * 8); } PG_RETURN_INT64(res); } /* * clean_ipv6_addr --- remove any '%zone' part from an IPv6 address string * * XXX This should go away someday! * * This is a kluge needed because we don't yet support zones in stored inet * values. Since the result of getnameinfo() might include a zone spec, * call this to remove it anywhere we want to feed getnameinfo's output to * network_in. Beats failing entirely. * * An alternative approach would be to let network_in ignore %-parts for * itself, but that would mean we'd silently drop zone specs in user input, * which seems not such a good idea. */ void clean_ipv6_addr(int addr_family, char *addr) { if (addr_family == AF_INET6) { char *pct = strchr(addr, '%'); if (pct) *pct = '\0'; } }