| /* |
| * 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 <sys/socket.h> |
| #include <netinet/in.h> |
| #include <arpa/inet.h> |
| |
| #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 int network_cmp_abbrev(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) |
| { |
| 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))) |
| ereport(ERROR, |
| (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))) |
| ereport(ERROR, |
| (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)); |
| } |
| |
| Datum |
| cidr_in(PG_FUNCTION_ARGS) |
| { |
| char *src = PG_GETARG_CSTRING(0); |
| |
| PG_RETURN_INET_P(network_in(src, true)); |
| } |
| |
| |
| /* |
| * 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 = network_cmp_abbrev; |
| 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); |
| } |
| |
| /* |
| * Abbreviated key comparison func |
| */ |
| static int |
| network_cmp_abbrev(Datum x, Datum y, SortSupport ssup) |
| { |
| if (x > y) |
| return 1; |
| else if (x == y) |
| return 0; |
| else |
| return -1; |
| } |
| |
| /* |
| * 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: |
| #ifdef HAVE_IPV6 |
| case AF_INET6: |
| #endif |
| 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)); |
| } |
| |
| |
| /* |
| * 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: |
| #ifdef HAVE_IPV6 |
| case AF_INET6: |
| #endif |
| 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: |
| #ifdef HAVE_IPV6 |
| case AF_INET6: |
| #endif |
| 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)); |
| } |
| |
| |
| /* |
| * 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: |
| #ifdef HAVE_IPV6 |
| case AF_INET6: |
| #endif |
| 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) |
| { |
| #ifdef HAVE_IPV6 |
| if (addr_family == AF_INET6) |
| { |
| char *pct = strchr(addr, '%'); |
| |
| if (pct) |
| *pct = '\0'; |
| } |
| #endif |
| } |