diff -ruB postgresql-7.1.2/src/backend/utils/adt/inet_net_ntop.c postgresql-7.1.2.ipv6/src/backend/utils/adt/inet_net_ntop.c --- postgresql-7.1.2/src/backend/utils/adt/inet_net_ntop.c Wed Mar 21 19:59:51 2001 +++ postgresql-7.1.2.ipv6/src/backend/utils/adt/inet_net_ntop.c Sun Aug 5 23:11:51 2001 @@ -40,6 +40,8 @@ char *dst, size_t size); static char *inet_cidr_ntop_ipv4(const u_char *src, int bits, char *dst, size_t size); +static char *inet_cidr_ntop_ipv6(const u_char *src, int bits, int fakebits, + char *dst, size_t size); /* * char * @@ -50,14 +52,17 @@ * pointer to dst, or NULL if an error occurred (check errno). * author: * Paul Vixie (ISC), July 1996 + * modified by Vadim Kogan (UCB), June 2001 to add IPv6 support */ char * inet_cidr_ntop(int af, const void *src, int bits, char *dst, size_t size) { switch (af) { - case AF_INET: + case AF_INET: return (inet_cidr_ntop_ipv4(src, bits, dst, size)); + case AF_INET6: + return (inet_cidr_ntop_ipv6(src, bits, bits, dst, size)); default: errno = EAFNOSUPPORT; return (NULL); @@ -137,6 +142,181 @@ return (NULL); } +/* + * static char * + * inet_cidr_ntop_ipv6(src, bits, fakebits, dst, size) + * convert IPv6 network number from network to presentation format. + * generates CIDR style result always. Picks the shortest representation + * unless the IP is really IPv4. + * always prints specified number of bits (bits), but appends fake number + * of bits (fakebits) at the end to allow for use as inet_net_ntop_ipv6() + * return: + * pointer to dst, or NULL if an error occurred (check errno). + * note: + * network byte order assumed. this means 192.5.5.240/28 has + * 0x11110000 in its fourth octet. + * author: + * Vadim Kogan (UCB), June 2001 + * Original version (IPv4) by Paul Vixie (ISC), July 1996 + */ +static char * +inet_cidr_ntop_ipv6(const u_char *src, int bits, int fakebits, char *dst, size_t size) +{ + char *odst = dst; + char *t; + u_int m; + int b; + int p; + int zero_s, zero_l, tmp_zero_s, tmp_zero_l; + int i; + int is_ipv4 = 0; + int inet = 0; + + if (bits < 0 || bits > 128) + { + errno = EINVAL; + return (NULL); + } + if (bits == 0) + { + if (size < sizeof "0") + goto emsgsize; + *dst++ = '0'; + *dst = '\0'; + } + + if( fakebits == -1 ) { + /* Always show all 128 bits if called for inet, not cidr */ + fakebits = bits; + bits = 128; + inet = 1; + } + + /* Find the longest substring of zero's */ + zero_s = zero_l = tmp_zero_s = tmp_zero_l = 0; + for (i = 0; i < bits / 8; i += 2) + { + if ((src[i] | src[i+1]) == 0) + { + if( !tmp_zero_l ) + tmp_zero_s = i / 2; + tmp_zero_l++; + } + else + { + if( tmp_zero_l && zero_l < tmp_zero_l ) + { + zero_s = tmp_zero_s; + zero_l = tmp_zero_l; + } + } + if ((tmp_zero_s == 0 && tmp_zero_l == 6) || (zero_s == 0 && zero_l == 5 && src[i] == 0xff && src[i+1] == 0xff)) + is_ipv4 = 1; + } + if (tmp_zero_l && zero_l < tmp_zero_l) + { + zero_s = tmp_zero_s; + zero_l = tmp_zero_l; + } + if (is_ipv4 && zero_l > 6) + is_ipv4 = 0; + + /* I _think_ one should not omit 0 if it's just one :0: */ + if( zero_l == 1 ) + zero_l = 0; + + /* Format whole words. */ + for (b = bits / 16, p = 0; b > 0; b--, p++) + { + if (zero_l && p >= zero_s && p < zero_s + zero_l) + { + /* Time to skip some zeros */ + if (p == zero_s || p == ( bits - 1 ) / 16) + { + if (size < sizeof( ":" )) + goto emsgsize; + *dst++ = ':'; + size--; + } + src++; + src++; + continue; + } + + t = dst; + if (is_ipv4 && p > 5 ) + { + if (size < sizeof ".255.255") + goto emsgsize; + if (p == 6) + *dst++ = ':'; + else + *dst++ = '.'; + dst += SPRINTF((dst, "%u", *src++)); + *dst++ = '.'; + dst += SPRINTF((dst, "%u", *src++)); + } + else + { + /* This is an overkill, but it's safe */ + if (size < sizeof ":FFFF") + goto emsgsize; + if (dst != odst) + *dst++ = ':'; + dst += SPRINTF((dst, "%X", *src * 256 + src[1])); + src += 2; + } + size -= (size_t) (dst - t); + } + + /* Format partial word. */ + b = bits % 16; + if (b > 0) + { + t = dst; + if (is_ipv4 && p > 5 ) + { + if (size < sizeof ".255.255") + goto emsgsize; + if (p == 6) + *dst++ = ':'; + else + *dst++ = '.'; + if (b > 8) + { + dst += SPRINTF((dst, "%u", *src++)); + *dst++ = '.'; + b -= 8; + } + m = ~0 << (8 - b); + dst += SPRINTF((dst, "%u", *src++ & m)); + } + else + { + if (size < sizeof ":FFFF") + goto emsgsize; + if (dst != odst) + *dst++ = ':'; + m = ((1 << b) - 1) << (8 - b); + dst += SPRINTF((dst, "%u", *src & m)); + } + size -= (size_t) (dst - t); + } + + /* Format CIDR /width. */ + if( !inet || (inet && fakebits != 128) ) { + if (size < sizeof "/128") + goto emsgsize; + dst += SPRINTF((dst, "/%u", fakebits)); + } + + return (odst); + +emsgsize: + errno = EMSGSIZE; + return (NULL); +} + /* * char * @@ -151,14 +331,17 @@ * an included netmask. * author: * Paul Vixie (ISC), October 1998 + * modified by Vadim Kogan (UCB), June 2001 to add IPv6 support */ char * inet_net_ntop(int af, const void *src, int bits, char *dst, size_t size) { switch (af) { - case AF_INET: + case AF_INET: return (inet_net_ntop_ipv4(src, bits, dst, size)); + case AF_INET6: + return (inet_cidr_ntop_ipv6(src, bits, -1, dst, size)); default: errno = EAFNOSUPPORT; return (NULL); diff -ruB postgresql-7.1.2/src/backend/utils/adt/inet_net_pton.c postgresql-7.1.2.ipv6/src/backend/utils/adt/inet_net_pton.c --- postgresql-7.1.2/src/backend/utils/adt/inet_net_pton.c Sun Dec 3 12:45:36 2000 +++ postgresql-7.1.2.ipv6/src/backend/utils/adt/inet_net_pton.c Sun Aug 5 23:11:51 2001 @@ -40,6 +40,7 @@ static int inet_net_pton_ipv4(const char *src, u_char *dst); static int inet_cidr_pton_ipv4(const char *src, u_char *dst, size_t size); +static int inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size, int check); /* * static int @@ -64,10 +65,14 @@ { switch (af) { - case AF_INET: + case AF_INET: return size == -1 ? inet_net_pton_ipv4(src, dst) : inet_cidr_pton_ipv4(src, dst, size); + case AF_INET6: + return size == -1 ? + inet_cidr_pton_ipv6(src, dst, 16, 0) : + inet_cidr_pton_ipv6(src, dst, size, 1); default: errno = EAFNOSUPPORT; return (-1); @@ -221,6 +226,259 @@ *dst++ = '\0'; } return (bits); + +enoent: + errno = ENOENT; + return (-1); + +emsgsize: + errno = EMSGSIZE; + return (-1); +} + +/* + * static int + * inet_cidr_pton_ipv6(src, dst, size) + * convert IPv6 network number from presentation to network format. + * accepts hex words (w/ or w/o decimal octets for IPv4), and /CIDR. + * "size" is in bytes and describes "dst". If "check" is non-zero, + * the function will fail if the specified address is not a valid net. + * return: + * number of bits, either imputed classfully or specified with /CIDR, + * or -1 if some failure occurred (check errno). ENOENT means it was + * not an IPv6 network specification. + * note: + * network byte order assumed. this means 192.5.5.240/28 has + * 0x11110000 in its fourth octet. + * author: + * Vadim Kogan (UCB), June 2001 + */ +static int +inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size, int check) +{ + int part1_l, part2_l, tot_l; + unsigned int part1[4], part2[4], *buf = part1, *buf_l = &part1_l; + unsigned char ch; + unsigned int *real_dst = (unsigned int *) dst; + const char *last_col = src; + unsigned int acc = 0, half = 0; + int lead_col = *src == ':'; + int ndig = 0; /* FIXME: other functions here don't seem to check number of digits they see, + so an overflow may happen and invalid IP will be accepted */ + int preflen = -1; + int i, j; + + part1_l = part2_l = tot_l = 0; + memset( part1, 0, sizeof( part1 ) ); + memset( part2, 0, sizeof( part2 ) ); + + while( (ch = *src++) != '\0' ) { + if( ch == ':' ) { + last_col = src - 1; + if( ndig > 4 ) + goto enoent; + ndig = 0; + if( half ) { + *buf = *buf << 16 | acc; + buf++; + *buf_l += 16; + tot_l += 16; + half = 0; + } else if( !lead_col ) { + *buf = acc; + *buf_l += 16; + tot_l += 16; + half = 1; + } + if( *src == ':' ) { + if( buf >= part2 && buf < part2 + 3 ) { + goto enoent; + } + if( half ) { + *buf <<= 16; + half = 0; + } + last_col = src; + src++; + buf = part2; + buf_l = &part2_l; + } else if( lead_col ) + goto enoent; + acc = 0; + lead_col = 0; + } else if( ch == '.' ) { + const char *newsrc = last_col + 1; + unsigned int oacc = 0, ndots = 0; + if( last_col > src - 3 ) + goto enoent; + if( tot_l > 96 ) + goto enoent; + /* From now on it's just ipv4 address */ + acc = 0; + ndig = 0; + while( (ch = *newsrc++) != '/' && ch != '\0' ) { + if( ch >= '0' && ch <= '9' ) { + ndig++; + oacc = oacc * 10 + ch - '0'; + } else if( ch == '.' ) { + if( ++ndots > 3 ) + goto enoent; + if( ndig > 3 || oacc > 255 ) + goto enoent; + acc = acc << 8 | oacc; + oacc = 0; + ndig = 0; + } else + goto enoent; + } + if( newsrc[ -1 ] == '.' ) + goto enoent; + acc = acc << 8 | oacc; + acc <<= (3 - ndots) * 8; + if( half ) { + *buf = *buf << 16 | acc >> 16; + buf++; + *buf = acc << 16; + } else + *buf = acc; + *buf_l += 32; + tot_l += 32; + half = -1; + src = newsrc - 1; + } else if( ch >= '0' && ch <= '9' ) { + ndig++; + acc = acc << 4 | (ch - '0'); + } else if( ch >= 'a' && ch <= 'f' ) { + ndig++; + acc = acc << 4 | (ch - 'a' + 10); + } else if( ch >= 'A' && ch <= 'F' ) { + ndig++; + acc = acc << 4 | (ch - 'A' + 10); + } else if( ch == '/' ) { + if( half == 1 ) { + *buf = *buf << 16 | acc; + *buf_l += 16; + tot_l += 16; + } else if( half == 0 ) { + *buf = acc << 16; + *buf_l += 16; + tot_l += 16; + } /* else it was ipv4 at the end */ + half = -1; + acc = 0; + ndig = 0; + while( (ch = *src++) != '\0' ) { + if( ch >= '0' && ch <= '9' ) { + ndig++; + acc = acc * 10 + ch - '0'; + } else + goto enoent; + } + if( ndig > 3 || acc > 128 ) + goto enoent; + preflen = acc; + goto parse_done; + } else + goto enoent; + } + if( half == 1 ) { + *buf = *buf << 16 | acc; + *buf_l += 16; + tot_l += 16; + } else if( half == 0 ) { + *buf = acc << 16; + *buf_l += 16; + tot_l += 16; + } /* else it was ipv4 at the end */ + parse_done: + if( ch != '\0' ) + goto enoent; + + if( preflen == -1 ) { + /* If there is no preflen, it must be either IPv4 with implied mask or part1_l/128 */ + /* First, let's check whether it is IPv4, we need to find the bits 80-96 */ + unsigned int tmp = 0; + if( part1_l >= 96 ) { + if( ( part1[ 0 ] | part1[ 1 ] ) != 0 ) + tmp = 1; + else + tmp = part1[ 2 ]; + } else if( part1_l >= 80 ) { + if( ( part1[ 0 ] | part1[ 1 ] | part1[ 3 ] ) != 0 ) + tmp = 1; + else + tmp = part2[ 0 ] >> 16; + } else if( part2_l > 32 ) { + if( ( part1[ 0 ] | part1[ 1 ] | part1[ 2 ] ) != 0 ) + tmp = 1; + else + /* FIXME: this is not checking the beginning of part2!!! */ + tmp = part2[ ( part2_l - 16 ) / 32 - 1 ] >> ( part2_l % 32 ? 16 : 0 ); + } + /* else part2_l <= 32 - we must have only IPv4 in part2 */ + if( tmp & 0xffff0000 ) + tmp = 1; + if( tmp == 0 || tmp == 0xffff ) + /* we have an IPv4 (unless it's :: or ::1) */ + /* we will calculate the actual preflen later */ + /* preflen = 0x10000; */ + /* FIXME: need to infer preflen from IPv4 address */ + preflen = 128; + else + /* we have IPv6 (if it's not 128, we'll count the bits later) */ + preflen = part2_l > 0 ? 128 : part1_l; /* FIXME: Need to infer preflen from IPv6 */ + } + + + /* Making sure the buffer is zeroed */ + for( i = 0; i < 4; i++ ) real_dst[ i ] = 0; + /* First, let's put what we have in part1 to work */ + for( i = 0; part1_l && i <= (part1_l - 1) / 32; i++ ) { + real_dst[ i ] = part1[ i ]; + } + if( part2_l > 0 ) { + /* Ok, so we have part2 too. Let's see how much we need to skip */ + int skip = 128 - tot_l; + if( skip % 16 || skip < 16 ) + goto enoent; + tot_l = 128; /* Since we have part2 */ + i = ( part1_l + skip ) / 32; + if( ( part1_l + skip ) % 32 ) { + int front = 1; + for( j = 0; j <= (part2_l - 1 ) / 16; j++ ) { + real_dst[ i ] |= front ? part2[ j / 2 ] >> 16 : part2[ j / 2 ] << 16; + if( front ) i++; + front = !front; + } + } else { + for( j = 0; j < part2_l / 32; j++, i++ ) + real_dst[ i ] = part2[ j ]; + } + } + if( check && tot_l > preflen ) { + /* We have to go check the bits by hand */ + if( preflen > 96 ) { + if( real_dst[ 3 ] & ( ~0L >> ( preflen - 96 ) ) ) + goto enoent; + } else if( preflen > 64 ) { + if( real_dst[ 3 ] | ( real_dst[ 2 ] & ( ~0L >> ( preflen - 64 ) ) ) ) + goto enoent; + } else if( preflen > 32 ) { + if( real_dst[ 3 ] | real_dst[ 2 ] | ( real_dst[ 1 ] & ( ~0L >> ( preflen - 32 ) ) ) ) + goto enoent; + } else if( preflen > 0 ) { + if( real_dst[ 3 ] | real_dst[ 2 ] | real_dst[ 1 ] | ( real_dst[ 0 ] & ( ~0L >> preflen ) ) ) + goto enoent; + } else /* preflen == 0 */ + if( real_dst[ 3 ] | real_dst[ 2 ] | real_dst[ 1 ] | real_dst[ 0 ] ) + goto enoent; + goto enoent; + } + + /* Fixing the byteorder stuff */ + for( i = 0; i < 4; i++ ) real_dst[ i ] = htonl( real_dst[ i ] ); + + return preflen <= 128 ? preflen : 128; /* Quick fix for now */ enoent: errno = ENOENT; diff -ruB postgresql-7.1.2/src/backend/utils/adt/network.c postgresql-7.1.2.ipv6/src/backend/utils/adt/network.c --- postgresql-7.1.2/src/backend/utils/adt/network.c Wed Mar 21 19:59:52 2001 +++ postgresql-7.1.2.ipv6/src/backend/utils/adt/network.c Sun Aug 5 23:11:51 2001 @@ -20,16 +20,25 @@ #include "utils/inet.h" +#ifndef INET6_ADDRSTRLEN +#define INET6_ADDRSTRLEN 46 +#endif + +#define INET6_CIDRSTRLEN (INET6_ADDRSTRLEN + 4) + static int32 network_cmp_internal(inet *a1, inet *a2); static int v4bitncmp(unsigned long a1, unsigned long a2, int bits); static bool v4addressOK(unsigned long a1, int bits); +static int v6bitncmp(unsigned int *a1, unsigned int *a2, int bits); +static bool v6addressOK(unsigned int *a1, int bits); /* - * Access macros. Add IPV6 support. + * Access macros. */ #define ip_addrsize(inetptr) \ - (((inet_struct *)VARDATA(inetptr))->family == AF_INET ? 4 : -1) + (((inet_struct *)VARDATA(inetptr))->family == AF_INET ? 4 : \ + (((inet_struct *)VARDATA(inetptr))->family == AF_INET6 ? 16 : -1 )) #define ip_family(inetptr) \ (((inet_struct *)VARDATA(inetptr))->family) @@ -37,12 +46,31 @@ #define ip_bits(inetptr) \ (((inet_struct *)VARDATA(inetptr))->bits) +#define ip_ipv4bits(inetptr) \ + (ip_bits(inetptr) - (ip_family(inetptr) == AF_INET6 ? 96 : 0)) + #define ip_type(inetptr) \ (((inet_struct *)VARDATA(inetptr))->type) #define ip_v4addr(inetptr) \ (((inet_struct *)VARDATA(inetptr))->addr.ipv4_addr) +#define ip_v6addr(inetptr) \ + (((inet_struct *)VARDATA(inetptr))->addr.ipv6_addr) + +#define ip_v4inv6addr(inetptr) \ + (((inet_struct *)VARDATA(inetptr))->addr.ipv6_addr[ 3 ]) + +#define ip_v4addr_always(inetptr) \ + (ip_family(inetptr) == AF_INET ? ip_v4addr(inetptr) : ip_v4inv6addr(inetptr)) + +#define ip_is_ipv4(inetptr) \ + (ip_family(inetptr) == AF_INET ? 1 : \ + (ip_family(inetptr) == AF_INET6 && \ + ip_v6addr(inetptr)[0] == 0 && \ + ip_v6addr(inetptr)[1] == 0 && \ + (ip_v6addr(inetptr)[2] == 0 || ntohl( ip_v6addr(inetptr)[2] ) == 0xFFFF) )) + /* Common input routine */ static inet * network_in(char *src, int type) @@ -61,17 +89,25 @@ if ((bits < 0) || (bits > 32)) { /* Go for an IPV6 address here, before faulting out: */ - elog(ERROR, "invalid %s value '%s'", - type ? "CIDR" : "INET", src); + ip_family(dst) = AF_INET6; + bits = inet_net_pton(ip_family(dst), src, &ip_v6addr(dst), + type ? ip_addrsize(dst) : -1); + if ((bits < 0) || (bits > 128)) { + elog(ERROR, "invalid %s value '%s'", + type ? "CIDR" : "INET", src); + } } /* * Error check: CIDR values must not have any bits set beyond the - * masklen. XXX this code is not IPV6 ready. + * masklen. */ if (type) { - if (!v4addressOK(ip_v4addr(dst), bits)) + if ( + !(ip_family(dst) == AF_INET && v4addressOK(ip_v4addr(dst), bits)) && + !(ip_family(dst) == AF_INET6 && v6addressOK(ip_v6addr(dst), bits)) + ) elog(ERROR, "invalid CIDR value '%s': has bits set to right of mask", src); } @@ -109,19 +145,19 @@ inet_out(PG_FUNCTION_ARGS) { inet *src = PG_GETARG_INET_P(0); - char tmp[sizeof("255.255.255.255/32")]; + char tmp[INET6_CIDRSTRLEN]; char *dst; int len; - if (ip_family(src) == AF_INET) + if (ip_family(src) == AF_INET || ip_family(src) == AF_INET6) { - /* It's an IP V4 address: */ + /* It's an IP V4 or IP V6 address: */ /* * Use inet style for both inet and cidr, since we don't want * abbreviated CIDR style here. */ - dst = inet_net_ntop(AF_INET, &ip_v4addr(src), ip_bits(src), + dst = inet_net_ntop(ip_family(src), &ip_v4addr(src), ip_bits(src), tmp, sizeof(tmp)); if (dst == NULL) elog(ERROR, "unable to print address (%s)", strerror(errno)); @@ -133,7 +169,6 @@ } } else - /* Go for an IPV6 address here, before faulting out: */ elog(ERROR, "unknown address family (%d)", ip_family(src)); PG_RETURN_CSTRING(pstrdup(tmp)); @@ -175,9 +210,55 @@ return order; return v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), 32); } + else if (ip_family(a1) == AF_INET6 && ip_family(a2) == AF_INET6) + { + int order; + + order = v6bitncmp(ip_v6addr(a1), ip_v6addr(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 v6bitncmp(ip_v6addr(a1), ip_v6addr(a2), 128); + } + else if (ip_is_ipv4(a1) && ip_is_ipv4(a2)) + { + /* Be smart and compare IP V4 with IP V6 */ + int order; + + + order = v4bitncmp(ip_v4addr_always(a1), ip_v4addr_always(a2), + Min(ip_ipv4bits(a1), ip_ipv4bits(a2))); + if (order != 0) + return order; + order = ((int) ip_ipv4bits(a1)) - ((int) ip_ipv4bits(a2)); + if (order != 0) + return order; + return v4bitncmp(ip_v4addr_always(a1), ip_v4addr_always(a2), 32); + } else { - /* Go for an IPV6 address here, before faulting out: */ + elog( DEBUG, "----" ); + if( ip_family(a1) == AF_INET ) + elog( DEBUG, "Pure IPv4" ); + else + elog( DEBUG, "0: 0x%X, 1: 0x%X, 2: 0x%X, 3: 0x%X", + ip_v6addr(a1)[0], + ip_v6addr(a1)[1], + ip_v6addr(a1)[2], + ip_v6addr(a1)[3] ); + + if( ip_family(a2) == AF_INET ) + elog( DEBUG, "Pure IPv4" ); + else + elog( DEBUG, "0: 0x%X, 1: 0x%X, 2: 0x%X, 3: 0x%X", + ip_v6addr(a2)[0], + ip_v6addr(a2)[1], + ip_v6addr(a2)[2], + ip_v6addr(a2)[3] ); + elog(ERROR, "cannot compare address families %d and %d", ip_family(a1), ip_family(a2)); return 0; /* keep compiler quiet */ @@ -264,9 +345,18 @@ PG_RETURN_BOOL(ip_bits(a1) > ip_bits(a2) && v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a2)) == 0); } + else if ((ip_family(a1) == AF_INET6) && (ip_family(a2) == AF_INET6)) + { + PG_RETURN_BOOL(ip_bits(a1) > ip_bits(a2) + && v6bitncmp(ip_v6addr(a1), ip_v6addr(a2), ip_bits(a2)) == 0); + } + else if (ip_is_ipv4(a1) && ip_is_ipv4(a2)) + { + PG_RETURN_BOOL(ip_ipv4bits(a1) > ip_ipv4bits(a2) + && v4bitncmp(ip_v4addr_always(a1), ip_v4addr_always(a2), ip_ipv4bits(a2)) == 0); + } else { - /* Go for an IPV6 address here, before faulting out: */ elog(ERROR, "cannot compare address families %d and %d", ip_family(a1), ip_family(a2)); PG_RETURN_BOOL(false); @@ -284,9 +374,18 @@ PG_RETURN_BOOL(ip_bits(a1) >= ip_bits(a2) && v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a2)) == 0); } + else if ((ip_family(a1) == AF_INET6) && (ip_family(a2) == AF_INET6)) + { + PG_RETURN_BOOL(ip_bits(a1) >= ip_bits(a2) + && v6bitncmp(ip_v6addr(a1), ip_v6addr(a2), ip_bits(a2)) == 0); + } + else if (ip_is_ipv4(a1) && ip_is_ipv4(a2)) + { + PG_RETURN_BOOL(ip_ipv4bits(a1) >= ip_ipv4bits(a2) + && v4bitncmp(ip_v4addr_always(a1), ip_v4addr_always(a2), ip_ipv4bits(a2)) == 0); + } else { - /* Go for an IPV6 address here, before faulting out: */ elog(ERROR, "cannot compare address families %d and %d", ip_family(a1), ip_family(a2)); PG_RETURN_BOOL(false); @@ -304,9 +403,18 @@ PG_RETURN_BOOL(ip_bits(a1) < ip_bits(a2) && v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a1)) == 0); } + else if ((ip_family(a1) == AF_INET6) && (ip_family(a2) == AF_INET6)) + { + PG_RETURN_BOOL(ip_bits(a1) < ip_bits(a2) + && v6bitncmp(ip_v6addr(a1), ip_v6addr(a2), ip_bits(a2)) == 0); + } + else if (ip_is_ipv4(a1) && ip_is_ipv4(a2)) + { + PG_RETURN_BOOL(ip_ipv4bits(a1) < ip_ipv4bits(a2) + && v4bitncmp(ip_v4addr_always(a1), ip_v4addr_always(a2), ip_ipv4bits(a1)) == 0); + } else { - /* Go for an IPV6 address here, before faulting out: */ elog(ERROR, "cannot compare address families %d and %d", ip_family(a1), ip_family(a2)); PG_RETURN_BOOL(false); @@ -324,9 +432,18 @@ PG_RETURN_BOOL(ip_bits(a1) <= ip_bits(a2) && v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a1)) == 0); } + else if ((ip_family(a1) == AF_INET6) && (ip_family(a2) == AF_INET6)) + { + PG_RETURN_BOOL(ip_bits(a1) <= ip_bits(a2) + && v6bitncmp(ip_v6addr(a1), ip_v6addr(a2), ip_bits(a2)) == 0); + } + else if (ip_is_ipv4(a1) && ip_is_ipv4(a2)) + { + PG_RETURN_BOOL(ip_ipv4bits(a1) <= ip_ipv4bits(a2) + && v4bitncmp(ip_v4addr_always(a1), ip_v4addr_always(a2), ip_ipv4bits(a1)) == 0); + } else { - /* Go for an IPV6 address here, before faulting out: */ elog(ERROR, "cannot compare address families %d and %d", ip_family(a1), ip_family(a2)); PG_RETURN_BOOL(false); @@ -343,17 +460,16 @@ text *ret; int len; char *ptr, - tmp[sizeof("255.255.255.255/32")]; + tmp[INET6_CIDRSTRLEN]; - if (ip_family(ip) == AF_INET) + if (ip_family(ip) == AF_INET || ip_family(ip) == AF_INET6) { - /* It's an IP V4 address: */ - /* force display of 32 bits, regardless of masklen... */ - if (inet_net_ntop(AF_INET, &ip_v4addr(ip), 32, tmp, sizeof(tmp)) == NULL) + /* It's an IP V4 or IP V6 address: */ + /* force display of 32/128 bits, regardless of masklen... */ + if (inet_net_ntop(ip_family(ip), &ip_v4addr(ip), (ip_family(ip) == AF_INET ? 32 : 128), tmp, sizeof(tmp)) == NULL) elog(ERROR, "unable to print host (%s)", strerror(errno)); } else - /* Go for an IPV6 address here, before faulting out: */ elog(ERROR, "unknown address family (%d)", ip_family(ip)); /* Suppress /n if present (shouldn't happen now) */ @@ -374,13 +490,13 @@ inet *ip = PG_GETARG_INET_P(0); text *ret; int len; - char tmp[sizeof("255.255.255.255/32")]; + char tmp[INET6_CIDRSTRLEN]; - if (ip_family(ip) == AF_INET) + if (ip_family(ip) == AF_INET || ip_family(ip) == AF_INET6) { - /* It's an IP V4 address: */ - /* force display of 32 bits, regardless of masklen... */ - if (inet_net_ntop(AF_INET, &ip_v4addr(ip), 32, tmp, sizeof(tmp)) == NULL) + /* It's an IP V4 or IP V6 address: */ + /* force display of 32/128 bits, regardless of masklen... */ + if (inet_net_ntop(ip_family(ip), &ip_v4addr(ip), (ip_family(ip) == AF_INET ? 32 : 128), tmp, sizeof(tmp)) == NULL) elog(ERROR, "unable to print host (%s)", strerror(errno)); /* Add /n if not present (which it won't be) */ if (strchr(tmp, '/') == NULL) @@ -390,7 +506,6 @@ } } else - /* Go for an IPV6 address here, before faulting out: */ elog(ERROR, "unknown address family (%d)", ip_family(ip)); /* Return string as a text datum */ @@ -408,23 +523,22 @@ text *ret; char *dst; int len; - char tmp[sizeof("255.255.255.255/32")]; + char tmp[INET6_CIDRSTRLEN]; - if (ip_family(ip) == AF_INET) + if (ip_family(ip) == AF_INET || ip_family(ip) == AF_INET6) { - /* It's an IP V4 address: */ + /* It's an IP V4 or IP V6 address: */ if (ip_type(ip)) - dst = inet_cidr_ntop(AF_INET, &ip_v4addr(ip), ip_bits(ip), + dst = inet_cidr_ntop(ip_family(ip), &ip_v4addr(ip), ip_bits(ip), tmp, sizeof(tmp)); else - dst = inet_net_ntop(AF_INET, &ip_v4addr(ip), ip_bits(ip), + dst = inet_net_ntop(ip_family(ip), &ip_v4addr(ip), ip_bits(ip), tmp, sizeof(tmp)); if (dst == NULL) elog(ERROR, "unable to print address (%s)", strerror(errno)); } else - /* Go for an IPV6 address here, before faulting out: */ elog(ERROR, "unknown address family (%d)", ip_family(ip)); /* Return string as a text datum */ @@ -469,8 +583,19 @@ ip_v4addr(dst) = htonl(ntohl(ip_v4addr(ip)) | mask); } + else if (ip_family(ip) == AF_INET6) { + /* It's an IP V6 address: */ + + /* "There are no broadcast addresses in IPv6, their function being + superseded by multicast addresses." (RFC2373) */ + /* I think use of solicited-node address (FF02:0:0:0:0:1:FFXX:XXXX) here is appropriate */ + unsigned long mask = 0x00ffffff; + ip_v6addr(dst)[0] = htonl(0xff02); + ip_v6addr(dst)[1] = 0; + ip_v6addr(dst)[2] = htonl(1); + ip_v6addr(dst)[3] = htonl(0xff000000 | (ntohl(ip_v4addr_always(ip)) & mask)); + } else - /* Go for an IPV6 address here, before faulting out: */ elog(ERROR, "unknown address family (%d)", ip_family(ip)); ip_family(dst) = ip_family(ip); @@ -509,8 +634,41 @@ ip_v4addr(dst) = htonl(ntohl(ip_v4addr(ip)) & mask); } + else if(ip_family(ip) == AF_INET6) { + /* It's an IP V6 address: */ + unsigned long mask = 0xffffffff; + + /* Ok, let's do this in 4 steps... */ + if(ip_bits(ip) > 96) + { + mask <<= (128 - ip_bits(ip)); + ip_v6addr(dst)[0] = htonl(ntohl(ip_v6addr(ip)[0]) & mask); + memset(ip_v6addr(dst) + 1, 0, sizeof(int) * 3); + } + else if(ip_bits(ip) > 64) + { + ip_v6addr(dst)[0] = ip_v6addr(ip)[0]; + mask <<= (96 - ip_bits(ip)); + ip_v6addr(dst)[1] = htonl(ntohl(ip_v6addr(ip)[1]) & mask); + memset(ip_v6addr(dst) + 2, 0, sizeof(int) * 2); + } + else if(ip_bits(ip) > 32) + { + memcpy(ip_v6addr(dst), ip_v6addr(ip), sizeof(int) * 2); + mask <<= (64 - ip_bits(ip)); + ip_v6addr(dst)[2] = htonl(ntohl(ip_v6addr(ip)[2]) & mask); + ip_v6addr(dst)[3] = 0; + } + else if(ip_bits(ip) > 0 ) + { + memcpy(ip_v6addr(dst), ip_v6addr(ip), sizeof(int) * 3); + mask <<= (32 - ip_bits(ip)); + ip_v6addr(dst)[3] = htonl(ntohl(ip_v6addr(ip)[3]) & mask); + } + else + memcpy(ip_v6addr(dst), ip_v6addr(ip), sizeof(int) * 4); + } else - /* Go for an IPV6 address here, before faulting out: */ elog(ERROR, "unknown address family (%d)", ip_family(ip)); ip_family(dst) = ip_family(ip); @@ -551,8 +709,44 @@ ip_bits(dst) = 32; } + else if (ip_family(ip) == AF_INET6) + { + /* It's an IP V6 address: */ + unsigned long mask = 0xffffffff; + + /* Ok, let's do this in 4 steps... */ + if(ip_bits(ip) > 96) + { + mask <<= (128 - ip_bits(ip)); + ip_v6addr(dst)[0] = htonl(mask); + memset(ip_v6addr(dst) + 1, 0, sizeof(int) * 3); + } + else if(ip_bits(ip) > 64) + { + ip_v6addr(dst)[0] = 0xffffffff; + mask <<= (96 - ip_bits(ip)); + ip_v6addr(dst)[1] = htonl(mask); + memset(ip_v6addr(dst) + 2, 0, sizeof(int) * 2); + } + else if(ip_bits(ip) > 32) + { + memset(ip_v6addr(dst), 0xff, sizeof(int) * 2); + mask <<= (64 - ip_bits(ip)); + ip_v6addr(dst)[2] = htonl(mask); + ip_v6addr(dst)[3] = 0; + } + else if(ip_bits(ip) > 0 ) + { + memset(ip_v6addr(dst), 0xff, sizeof(int) * 3); + mask <<= (32 - ip_bits(ip)); + ip_v6addr(dst)[3] = htonl(mask); + } + else + memset(ip_v6addr(dst), 0xff, sizeof(int) * 4); + + ip_bits(dst) = 128; + } else - /* Go for an IPV6 address here, before faulting out: */ elog(ERROR, "unknown address family (%d)", ip_family(ip)); ip_family(dst) = ip_family(ip); @@ -565,7 +759,7 @@ } /* - * Bitwise comparison for V4 addresses. Add V6 implementation! + * Bitwise comparison for IP V4 addresses. */ static int @@ -591,6 +785,34 @@ } /* + * Bitwise comparison for IP V6 addresses. + */ + +static int +v6bitncmp(unsigned int *a1, unsigned int *a2, int bits) +{ + unsigned int mask; + unsigned int tmp1, tmp2; + int i; + + for( i = 0; bits > 0 && i < 4; i++, bits -= 32 ) { + if( bits > 32 ) + mask = 0xffffffff; + else if( bits > 0 ) + mask = 0xffffffff << (32 - bits); + else + mask = 0; + tmp1 = ntohl(a1[i]); + tmp2 = ntohl(a2[i]); + if( ( tmp1 & mask ) < ( tmp2 & mask ) ) + return -1; + else if( ( tmp1 & mask ) > ( tmp2 & mask ) ) + return 1; + } + return 0; +} + +/* * Returns true if given address fits fully within the specified bit width. */ static bool @@ -610,4 +832,27 @@ if ((a1 & mask) == a1) return true; return false; +} + +/* + * Returns true if given address fits fully within the specified bit width. + */ +static bool +v6addressOK(unsigned int *a1, int bits) +{ + unsigned int tmp1, mask; + int i; + + for( i = 0; bits > 0 && i < 4; i++, bits -= 32 ) { + if( bits > 32 ) + mask = 0xffffffff; + else if( bits > 0 ) + mask = 0xffffffff << (32 - bits); + else + mask = 0; + tmp1 = ntohl(a1[i]); + if( ( tmp1 & mask ) != tmp1 ) + return false; + } + return true; } diff -ruB postgresql-7.1.2/src/include/utils/inet.h postgresql-7.1.2.ipv6/src/include/utils/inet.h --- postgresql-7.1.2/src/include/utils/inet.h Wed Mar 21 20:01:12 2001 +++ postgresql-7.1.2.ipv6/src/include/utils/inet.h Sun Aug 5 23:10:25 2001 @@ -27,6 +27,7 @@ { unsigned int ipv4_addr; /* network byte order */ /* add IPV6 address type here */ + unsigned int ipv6_addr[4]; } addr; } inet_struct;