Commit 945543d9 authored by Bruce Momjian's avatar Bruce Momjian

Add ipv6 address parsing support to 'inet' and 'cidr' data types.

        Regression tests for IPv6 operations added.

        Documentation updated to document IPv6 bits.

        Stop treating IPv4 as an "unsigned int" and IPv6 as an array of
        characters.  Instead, always use the array of characters so we
        can have one function fits all.  This makes bitncmp(), addressOK(),
        and several other functions "just work" on both address families.

        add family() function which returns integer 4 or 6 for IPv4 or
        IPv6.  (See examples below)  Note that to add this new function
        you will need to dump/initdb/reload or find the correct magic
        to add the function to the postgresql function catalogs.

        IPv4 addresses always sort before IPv6.

        On disk we use AF_INET for IPv4, and AF_INET+1 for IPv6 addresses.
        This prevents the need for a dump and reload, but lets IPv6 parsing
        work on machines without AF_INET6.

        To select all IPv4 addresses from a table:

                select * from foo where family(addr) = 4 ...

        Order by and other bits should all work.

Michael Graff
parent 4dab978c
<!-- <!--
$Header: /cvsroot/pgsql/doc/src/sgml/datatype.sgml,v 1.117 2003/06/17 23:12:36 tgl Exp $ $Header: /cvsroot/pgsql/doc/src/sgml/datatype.sgml,v 1.118 2003/06/24 22:21:21 momjian Exp $
--> -->
<chapter id="datatype"> <chapter id="datatype">
...@@ -100,7 +100,7 @@ $Header: /cvsroot/pgsql/doc/src/sgml/datatype.sgml,v 1.117 2003/06/17 23:12:36 t ...@@ -100,7 +100,7 @@ $Header: /cvsroot/pgsql/doc/src/sgml/datatype.sgml,v 1.117 2003/06/17 23:12:36 t
<row> <row>
<entry><type>cidr</type></entry> <entry><type>cidr</type></entry>
<entry></entry> <entry></entry>
<entry>IP network address</entry> <entry>IPv4 or IPv6 network address</entry>
</row> </row>
<row> <row>
...@@ -124,7 +124,7 @@ $Header: /cvsroot/pgsql/doc/src/sgml/datatype.sgml,v 1.117 2003/06/17 23:12:36 t ...@@ -124,7 +124,7 @@ $Header: /cvsroot/pgsql/doc/src/sgml/datatype.sgml,v 1.117 2003/06/17 23:12:36 t
<row> <row>
<entry><type>inet</type></entry> <entry><type>inet</type></entry>
<entry></entry> <entry></entry>
<entry>IP host address</entry> <entry>IPv4 or IPv6 host address</entry>
</row> </row>
<row> <row>
...@@ -2425,7 +2425,7 @@ SELECT * FROM test1 WHERE a; ...@@ -2425,7 +2425,7 @@ SELECT * FROM test1 WHERE a;
</indexterm> </indexterm>
<para> <para>
<productname>PostgreSQL</> offers data types to store IP and MAC <productname>PostgreSQL</> offers data types to store IPv4, IPv6, and MAC
addresses, shown in <xref linkend="datatype-net-types-table">. It addresses, shown in <xref linkend="datatype-net-types-table">. It
is preferable to use these types over plain text types, because is preferable to use these types over plain text types, because
these types offer input error checking and several specialized these types offer input error checking and several specialized
...@@ -2446,14 +2446,14 @@ SELECT * FROM test1 WHERE a; ...@@ -2446,14 +2446,14 @@ SELECT * FROM test1 WHERE a;
<row> <row>
<entry><type>cidr</type></entry> <entry><type>cidr</type></entry>
<entry>12 bytes</entry> <entry>12 or 24 bytes</entry>
<entry>IPv4 networks</entry> <entry>IPv4 or IPv6 networks</entry>
</row> </row>
<row> <row>
<entry><type>inet</type></entry> <entry><type>inet</type></entry>
<entry>12 bytes</entry> <entry>12 or 24 bytes</entry>
<entry>IPv4 hosts and networks</entry> <entry>IPv4 and IPv6 hosts and networks</entry>
</row> </row>
<row> <row>
...@@ -2467,7 +2467,10 @@ SELECT * FROM test1 WHERE a; ...@@ -2467,7 +2467,10 @@ SELECT * FROM test1 WHERE a;
</table> </table>
<para> <para>
IPv6 is not yet supported. When sorting <type>inet</type> or <type>cidr</type> data types,
IPv4 addresses will always sort before IPv6 addresses, including
IPv4 addresses encapsulated or mapped into IPv6 addresses, such as
::10.2.3.4 or ::ffff::10.4.3.2.
</para> </para>
...@@ -2479,26 +2482,32 @@ SELECT * FROM test1 WHERE a; ...@@ -2479,26 +2482,32 @@ SELECT * FROM test1 WHERE a;
</indexterm> </indexterm>
<para> <para>
The <type>inet</type> type holds an IP host address, and The <type>inet</type> type holds an IPv4 or IPv6 host address, and
optionally the identity of the subnet it is in, all in one field. optionally the identity of the subnet it is in, all in one field.
The subnet identity is represented by stating how many bits of The subnet identity is represented by stating how many bits of
the host address represent the network address (the the host address represent the network address (the
<quote>netmask</quote>). If the netmask is 32, then the value <quote>netmask</quote>). If the netmask is 32 and the address is IPv4,
does not indicate a subnet, only a single host. Note that if you then the value does not indicate a subnet, only a single host.
In IPv6, the address length is 128 bits, so 128 bits will specify a
unique host address. Note that if you
want to accept networks only, you should use the want to accept networks only, you should use the
<type>cidr</type> type rather than <type>inet</type>. <type>cidr</type> type rather than <type>inet</type>.
</para> </para>
<para> <para>
The input format for this type is <replaceable The input format for this type is
class="parameter">x.x.x.x/y</replaceable> where <replaceable <replaceable class="parameter">address/y</replaceable>
class="parameter">x.x.x.x</replaceable> is an IP address and where
<replaceable class="parameter">y</replaceable> is the number of <replaceable class="parameter">address</replaceable>
bits in the netmask. If the <replaceable is an IPv4 or IPv6 address and
class="parameter">/y</replaceable> part is left off, then the <replaceable class="parameter">y</replaceable>
netmask is 32, and the value represents just a single host. is the number of bits in the netmask. If the
On display, the <replaceable class="parameter">/y</replaceable> <replaceable class="parameter">/y</replaceable>
portion is suppressed if the netmask is 32. part is left off, then the
netmask is 32 for IPv4 and 128 for IPv6, and the value represents
just a single host. On display, the
<replaceable class="parameter">/y</replaceable>
portion is suppressed if the netmask specifies a single host.
</para> </para>
</sect2> </sect2>
...@@ -2510,12 +2519,13 @@ SELECT * FROM test1 WHERE a; ...@@ -2510,12 +2519,13 @@ SELECT * FROM test1 WHERE a;
</indexterm> </indexterm>
<para> <para>
The <type>cidr</type> type holds an IP network specification. The <type>cidr</type> type holds an IPv4 or IPv6 network specification.
Input and output formats follow Classless Internet Domain Routing Input and output formats follow Classless Internet Domain Routing
conventions. conventions.
The format for specifying networks is <replaceable The format for specifying networks is <replaceable
class="parameter">x.x.x.x/y</> where <replaceable class="parameter">address/y</> where <replaceable
class="parameter">x.x.x.x</> is the network and <replaceable class="parameter">address</> is the network represented as an
IPv4 or IPv6 address, and <replaceable
class="parameter">y</> is the number of bits in the netmask. If class="parameter">y</> is the number of bits in the netmask. If
<replaceable class="parameter">y</> is omitted, it is calculated <replaceable class="parameter">y</> is omitted, it is calculated
using assumptions from the older classful network numbering system, except using assumptions from the older classful network numbering system, except
...@@ -2594,6 +2604,28 @@ SELECT * FROM test1 WHERE a; ...@@ -2594,6 +2604,28 @@ SELECT * FROM test1 WHERE a;
<entry>10.0.0.0/8</entry> <entry>10.0.0.0/8</entry>
<entry>10/8</entry> <entry>10/8</entry>
</row> </row>
<row>
<entry>10.1.2.3/32</entry>
<entry>10.1.2.3/32</entry>
<entry>10.1.2.3/32</entry>
<row>
<entry>2001:4f8:3:ba::/64</entry>
<entry>2001:4f8:3:ba::/64</entry>
<entry>2001:4f8:3:ba::/64</entry>
</row>
<row>
<entry>2001:4f8:3:ba:2e0:81ff:fe22:d1f1/128</entry>
<entry>2001:4f8:3:ba:2e0:81ff:fe22:d1f1/128</entry>
<entry>2001:4f8:3:ba:2e0:81ff:fe22:d1f1</entry>
</row>
<row>
<entry>::ffff:1.2.3.0/120</entry>
<entry>::ffff:1.2.3.0/120</entry>
<entry>::ffff:1.2.3/120</entry>
<row>
<entry>::ffff:1.2.3.0/128</entry>
<entry>::ffff:1.2.3.0/128</entry>
<entry>::ffff:1.2.3.0/128</entry>
</tbody> </tbody>
</tgroup> </tgroup>
</table> </table>
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
*/ */
#if defined(LIBC_SCCS) && !defined(lint) #if defined(LIBC_SCCS) && !defined(lint)
static const char rcsid[] = "$Id: inet_net_ntop.c,v 1.12 2002/09/02 02:47:04 momjian Exp $"; static const char rcsid[] = "$Id: inet_net_ntop.c,v 1.13 2003/06/24 22:21:22 momjian Exp $";
#endif #endif
#include "postgres.h" #include "postgres.h"
...@@ -27,8 +27,12 @@ static const char rcsid[] = "$Id: inet_net_ntop.c,v 1.12 2002/09/02 02:47:04 mom ...@@ -27,8 +27,12 @@ static const char rcsid[] = "$Id: inet_net_ntop.c,v 1.12 2002/09/02 02:47:04 mom
#include <errno.h> #include <errno.h>
#include "utils/inet.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#define NS_IN6ADDRSZ 16
#define NS_INT16SZ 2
#ifdef SPRINTF_CHAR #ifdef SPRINTF_CHAR
#define SPRINTF(x) strlen(sprintf/**/x) #define SPRINTF(x) strlen(sprintf/**/x)
#else #else
...@@ -39,6 +43,10 @@ static char *inet_net_ntop_ipv4(const u_char *src, int bits, ...@@ -39,6 +43,10 @@ static char *inet_net_ntop_ipv4(const u_char *src, int bits,
char *dst, size_t size); char *dst, size_t size);
static char *inet_cidr_ntop_ipv4(const u_char *src, int bits, static char *inet_cidr_ntop_ipv4(const u_char *src, int bits,
char *dst, size_t size); char *dst, size_t size);
static char *inet_net_ntop_ipv6(const u_char *src, int bits,
char *dst, size_t size);
static char *inet_cidr_ntop_ipv6(const u_char *src, int bits,
char *dst, size_t size);
/* /*
* char * * char *
...@@ -55,8 +63,10 @@ inet_cidr_ntop(int af, const void *src, int bits, char *dst, size_t size) ...@@ -55,8 +63,10 @@ inet_cidr_ntop(int af, const void *src, int bits, char *dst, size_t size)
{ {
switch (af) switch (af)
{ {
case AF_INET: case PGSQL_AF_INET:
return (inet_cidr_ntop_ipv4(src, bits, dst, size)); return (inet_cidr_ntop_ipv4(src, bits, dst, size));
case PGSQL_AF_INET6:
return (inet_cidr_ntop_ipv6(src, bits, dst, size));
default: default:
errno = EAFNOSUPPORT; errno = EAFNOSUPPORT;
return (NULL); return (NULL);
...@@ -136,6 +146,148 @@ emsgsize: ...@@ -136,6 +146,148 @@ emsgsize:
return (NULL); return (NULL);
} }
/*
* static char *
* inet_net_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).
* 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, char *dst, size_t size)
{
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 double_colon = 0;
unsigned char inbuf[16];
char outbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
char *cp;
int words;
u_char *s;
if (bits < 0 || bits > 128) {
errno = EINVAL;
return (NULL);
}
cp = outbuf;
double_colon = 0;
if (bits == 0) {
*cp++ = ':';
*cp++ = ':';
*cp = '\0';
double_colon = 1;
} else {
/* Copy src to private buffer. Zero host part. */
p = (bits + 7) / 8;
memcpy(inbuf, src, p);
memset(inbuf + p, 0, 16 - p);
b = bits % 8;
if (b != 0) {
m = ~0 << (8 - b);
inbuf[p-1] &= m;
}
s = inbuf;
/* how many words need to be displayed in output */
words = (bits + 15) / 16;
if (words == 1)
words = 2;
/* Find the longest substring of zero's */
zero_s = zero_l = tmp_zero_s = tmp_zero_l = 0;
for (i = 0; i < (words * 2); i += 2) {
if ((s[i] | s[i+1]) == 0) {
if (tmp_zero_l == 0)
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;
tmp_zero_l = 0;
}
}
}
if (tmp_zero_l && zero_l < tmp_zero_l) {
zero_s = tmp_zero_s;
zero_l = tmp_zero_l;
}
if (zero_l != words && zero_s == 0 && ((zero_l == 6) ||
((zero_l == 5 && s[10] == 0xff && s[11] == 0xff) ||
((zero_l == 7 && s[14] != 0 && s[15] != 1)))))
is_ipv4 = 1;
/* Format whole words. */
for (p = 0; p < words; p++) {
if (zero_l != 0 && p >= zero_s && p < zero_s + zero_l) {
/* Time to skip some zeros */
if (p == zero_s)
*cp++ = ':';
if (p == words - 1) {
*cp++ = ':';
double_colon = 1;
}
s++;
s++;
continue;
}
if (is_ipv4 && p > 5 ) {
*cp++ = (p == 6) ? ':' : '.';
cp += SPRINTF((cp, "%u", *s++));
/* we can potentially drop the last octet */
if (p != 7 || bits > 120) {
*cp++ = '.';
cp += SPRINTF((cp, "%u", *s++));
}
} else {
if (cp != outbuf)
*cp++ = ':';
cp += SPRINTF((cp, "%x", *s * 256 + s[1]));
s += 2;
}
}
}
if (!double_colon) {
if (bits < 128 - 32)
cp += SPRINTF((cp, "::", bits));
else if (bits < 128 - 16)
cp += SPRINTF((cp, ":0", bits));
}
/* Format CIDR /width. */
SPRINTF((cp, "/%u", bits));
if (strlen(outbuf) + 1 > size)
goto emsgsize;
strcpy(dst, outbuf);
return (dst);
emsgsize:
errno = EMSGSIZE;
return (NULL);
}
/* /*
* char * * char *
...@@ -156,8 +308,10 @@ inet_net_ntop(int af, const void *src, int bits, char *dst, size_t size) ...@@ -156,8 +308,10 @@ inet_net_ntop(int af, const void *src, int bits, char *dst, size_t size)
{ {
switch (af) switch (af)
{ {
case AF_INET: case PGSQL_AF_INET:
return (inet_net_ntop_ipv4(src, bits, dst, size)); return (inet_net_ntop_ipv4(src, bits, dst, size));
case PGSQL_AF_INET6:
return (inet_net_ntop_ipv6(src, bits, dst, size));
default: default:
errno = EAFNOSUPPORT; errno = EAFNOSUPPORT;
return (NULL); return (NULL);
...@@ -217,3 +371,127 @@ emsgsize: ...@@ -217,3 +371,127 @@ emsgsize:
errno = EMSGSIZE; errno = EMSGSIZE;
return (NULL); return (NULL);
} }
static int
decoct(const u_char *src, int bytes, char *dst, size_t size) {
char *odst = dst;
char *t;
int b;
for (b = 1; b <= bytes; b++) {
if (size < sizeof "255.")
return (0);
t = dst;
dst += SPRINTF((dst, "%u", *src++));
if (b != bytes) {
*dst++ = '.';
*dst = '\0';
}
size -= (size_t)(dst - t);
}
return (dst - odst);
}
static char *
inet_net_ntop_ipv6(const u_char *src, int bits, char *dst, size_t size)
{
/*
* Note that int32_t and int16_t need only be "at least" large enough
* to contain a value of the specified size. On some systems, like
* Crays, there is no such thing as an integer variable with 16 bits.
* Keep this in mind if you think this function should have been coded
* to use pointer overlays. All the world's not a VAX.
*/
char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255/128"];
char *tp;
struct { int base, len; } best, cur;
u_int words[NS_IN6ADDRSZ / NS_INT16SZ];
int i;
if ((bits < -1) || (bits > 128)) {
errno = EINVAL;
return (NULL);
}
/*
* Preprocess:
* Copy the input (bytewise) array into a wordwise array.
* Find the longest run of 0x00's in src[] for :: shorthanding.
*/
memset(words, '\0', sizeof words);
for (i = 0; i < NS_IN6ADDRSZ; i++)
words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
best.base = -1;
cur.base = -1;
for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
if (words[i] == 0) {
if (cur.base == -1)
cur.base = i, cur.len = 1;
else
cur.len++;
} else {
if (cur.base != -1) {
if (best.base == -1 || cur.len > best.len)
best = cur;
cur.base = -1;
}
}
}
if (cur.base != -1) {
if (best.base == -1 || cur.len > best.len)
best = cur;
}
if (best.base != -1 && best.len < 2)
best.base = -1;
/*
* Format the result.
*/
tp = tmp;
for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
/* Are we inside the best run of 0x00's? */
if (best.base != -1 && i >= best.base &&
i < (best.base + best.len)) {
if (i == best.base)
*tp++ = ':';
continue;
}
/* Are we following an initial run of 0x00s or any real hex? */
if (i != 0)
*tp++ = ':';
/* Is this address an encapsulated IPv4? */
if (i == 6 && best.base == 0 && (best.len == 6 ||
(best.len == 7 && words[7] != 0x0001) ||
(best.len == 5 && words[5] == 0xffff))) {
int n;
n = decoct(src+12, 4, tp, sizeof tmp - (tp - tmp));
if (n == 0) {
errno = EMSGSIZE;
return (NULL);
}
tp += strlen(tp);
break;
}
tp += SPRINTF((tp, "%x", words[i]));
}
/* Was it a trailing run of 0x00's? */
if (best.base != -1 && (best.base + best.len) ==
(NS_IN6ADDRSZ / NS_INT16SZ))
*tp++ = ':';
*tp = '\0';
if (bits != -1 && bits != 128)
tp += SPRINTF((tp, "/%u", bits));
/*
* Check for overflow, copy, and we're done.
*/
if ((size_t)(tp - tmp) > size) {
errno = EMSGSIZE;
return (NULL);
}
strcpy(dst, tmp);
return (dst);
}
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
*/ */
#if defined(LIBC_SCCS) && !defined(lint) #if defined(LIBC_SCCS) && !defined(lint)
static const char rcsid[] = "$Id: inet_net_pton.c,v 1.14 2002/09/02 02:47:04 momjian Exp $"; static const char rcsid[] = "$Id: inet_net_pton.c,v 1.15 2003/06/24 22:21:22 momjian Exp $";
#endif #endif
#include "postgres.h" #include "postgres.h"
...@@ -29,16 +29,14 @@ static const char rcsid[] = "$Id: inet_net_pton.c,v 1.14 2002/09/02 02:47:04 mom ...@@ -29,16 +29,14 @@ static const char rcsid[] = "$Id: inet_net_pton.c,v 1.14 2002/09/02 02:47:04 mom
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include "utils/builtins.h" #include "utils/inet.h"
#ifdef SPRINTF_CHAR #include "utils/builtins.h"
#define SPRINTF(x) strlen(sprintf/**/x)
#else
#define SPRINTF(x) ((size_t)sprintf x)
#endif
static int inet_net_pton_ipv4(const char *src, u_char *dst); 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_ipv4(const char *src, u_char *dst, size_t size);
static int inet_net_pton_ipv6(const char *src, u_char *dst);
static int inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size);
/* /*
* static int * static int
...@@ -63,10 +61,14 @@ inet_net_pton(int af, const char *src, void *dst, size_t size) ...@@ -63,10 +61,14 @@ inet_net_pton(int af, const char *src, void *dst, size_t size)
{ {
switch (af) switch (af)
{ {
case AF_INET: case PGSQL_AF_INET:
return size == -1 ? return size == -1 ?
inet_net_pton_ipv4(src, dst) : inet_net_pton_ipv4(src, dst) :
inet_cidr_pton_ipv4(src, dst, size); inet_cidr_pton_ipv4(src, dst, size);
case PGSQL_AF_INET6:
return size == -1 ?
inet_net_pton_ipv6(src, dst) :
inet_cidr_pton_ipv6(src, dst, size);
default: default:
errno = EAFNOSUPPORT; errno = EAFNOSUPPORT;
return (-1); return (-1);
...@@ -335,3 +337,199 @@ emsgsize: ...@@ -335,3 +337,199 @@ emsgsize:
errno = EMSGSIZE; errno = EMSGSIZE;
return (-1); return (-1);
} }
static int
getbits(const char *src, int *bitsp) {
static const char digits[] = "0123456789";
int n;
int val;
char ch;
val = 0;
n = 0;
while ((ch = *src++) != '\0') {
const char *pch;
pch = strchr(digits, ch);
if (pch != NULL) {
if (n++ != 0 && val == 0) /* no leading zeros */
return (0);
val *= 10;
val += (pch - digits);
if (val > 128) /* range */
return (0);
continue;
}
return (0);
}
if (n == 0)
return (0);
*bitsp = val;
return (1);
}
static int
getv4(const char *src, u_char *dst, int *bitsp) {
static const char digits[] = "0123456789";
u_char *odst = dst;
int n;
u_int val;
char ch;
val = 0;
n = 0;
while ((ch = *src++) != '\0') {
const char *pch;
pch = strchr(digits, ch);
if (pch != NULL) {
if (n++ != 0 && val == 0) /* no leading zeros */
return (0);
val *= 10;
val += (pch - digits);
if (val > 255) /* range */
return (0);
continue;
}
if (ch == '.' || ch == '/') {
if (dst - odst > 3) /* too many octets? */
return (0);
*dst++ = val;
if (ch == '/')
return (getbits(src, bitsp));
val = 0;
n = 0;
continue;
}
return (0);
}
if (n == 0)
return (0);
if (dst - odst > 3) /* too many octets? */
return (0);
*dst++ = val;
return (1);
}
static int
inet_net_pton_ipv6(const char *src, u_char *dst)
{
return inet_cidr_pton_ipv6(src, dst, 16);
}
#define NS_IN6ADDRSZ 16
#define NS_INT16SZ 2
#define NS_INADDRSZ 4
static int
inet_cidr_pton_ipv6(const char *src, u_char *dst, size_t size) {
static const char xdigits_l[] = "0123456789abcdef",
xdigits_u[] = "0123456789ABCDEF";
u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
const char *xdigits, *curtok;
int ch, saw_xdigit;
u_int val;
int digits;
int bits;
if (size < NS_IN6ADDRSZ)
goto emsgsize;
memset((tp = tmp), '\0', NS_IN6ADDRSZ);
endp = tp + NS_IN6ADDRSZ;
colonp = NULL;
/* Leading :: requires some special handling. */
if (*src == ':')
if (*++src != ':')
goto enoent;
curtok = src;
saw_xdigit = 0;
val = 0;
digits = 0;
bits = -1;
while ((ch = *src++) != '\0') {
const char *pch;
if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
pch = strchr((xdigits = xdigits_u), ch);
if (pch != NULL) {
val <<= 4;
val |= (pch - xdigits);
if (++digits > 4)
goto enoent;
saw_xdigit = 1;
continue;
}
if (ch == ':') {
curtok = src;
if (!saw_xdigit) {
if (colonp)
goto enoent;
colonp = tp;
continue;
} else if (*src == '\0')
goto enoent;
if (tp + NS_INT16SZ > endp)
return (0);
*tp++ = (u_char) (val >> 8) & 0xff;
*tp++ = (u_char) val & 0xff;
saw_xdigit = 0;
digits = 0;
val = 0;
continue;
}
if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
getv4(curtok, tp, &bits) > 0) {
tp += NS_INADDRSZ;
saw_xdigit = 0;
break; /* '\0' was seen by inet_pton4(). */
}
if (ch == '/' && getbits(src, &bits) > 0)
break;
goto enoent;
}
if (saw_xdigit) {
if (tp + NS_INT16SZ > endp)
goto enoent;
*tp++ = (u_char) (val >> 8) & 0xff;
*tp++ = (u_char) val & 0xff;
}
if (bits == -1)
bits = 128;
endp = tmp + 16;
if (colonp != NULL) {
/*
* Since some memmove()'s erroneously fail to handle
* overlapping regions, we'll do the shift by hand.
*/
const int n = tp - colonp;
int i;
if (tp == endp)
goto enoent;
for (i = 1; i <= n; i++) {
endp[- i] = colonp[n - i];
colonp[n - i] = 0;
}
tp = endp;
}
if (tp != endp)
goto enoent;
/*
* Copy out the result.
*/
memcpy(dst, tmp, NS_IN6ADDRSZ);
return (bits);
enoent:
errno = ENOENT;
return (-1);
emsgsize:
errno = EMSGSIZE;
return (-1);
}
/* /*
* PostgreSQL type definitions for the INET type. This * PostgreSQL type definitions for the INET and CIDR types.
* is for IP V4 CIDR notation, but prepared for V6: just
* add the necessary bits where the comments indicate.
* *
* $Header: /cvsroot/pgsql/src/backend/utils/adt/network.c,v 1.41 2003/05/13 18:03:07 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/utils/adt/network.c,v 1.42 2003/06/24 22:21:22 momjian Exp $
* *
* Jon Postel RIP 16 Oct 1998 * Jon Postel RIP 16 Oct 1998
*/ */
...@@ -23,16 +21,14 @@ ...@@ -23,16 +21,14 @@
static Datum text_network(text *src, int type); static Datum text_network(text *src, int type);
static int32 network_cmp_internal(inet *a1, inet *a2); static int32 network_cmp_internal(inet *a1, inet *a2);
static int v4bitncmp(unsigned long a1, unsigned long a2, int bits); static int bitncmp(void *l, void *r, int n);
static bool v4addressOK(unsigned long a1, int bits); static bool addressOK(unsigned char *a, int bits, int family);
static int ip_addrsize(inet *inetptr);
/* /*
* Access macros. Add IPV6 support. * Access macros.
*/ */
#define ip_addrsize(inetptr) \
(((inet_struct *)VARDATA(inetptr))->family == AF_INET ? 4 : -1)
#define ip_family(inetptr) \ #define ip_family(inetptr) \
(((inet_struct *)VARDATA(inetptr))->family) (((inet_struct *)VARDATA(inetptr))->family)
...@@ -42,8 +38,28 @@ static bool v4addressOK(unsigned long a1, int bits); ...@@ -42,8 +38,28 @@ static bool v4addressOK(unsigned long a1, int bits);
#define ip_type(inetptr) \ #define ip_type(inetptr) \
(((inet_struct *)VARDATA(inetptr))->type) (((inet_struct *)VARDATA(inetptr))->type)
#define ip_v4addr(inetptr) \ #define ip_addr(inetptr) \
(((inet_struct *)VARDATA(inetptr))->addr.ipv4_addr) (((inet_struct *)VARDATA(inetptr))->ip_addr)
#define ip_maxbits(inetptr) \
(ip_family(inetptr) == PGSQL_AF_INET ? 32 : 128)
/*
* Now, as a function!
* Return the number of bytes of storage needed for this data type.
*/
static int
ip_addrsize(inet *inetptr)
{
switch (ip_family(inetptr)) {
case PGSQL_AF_INET:
return 4;
case PGSQL_AF_INET6:
return 16;
default:
return -1;
}
}
/* Common input routine */ /* Common input routine */
static inet * static inet *
...@@ -52,32 +68,38 @@ network_in(char *src, int type) ...@@ -52,32 +68,38 @@ network_in(char *src, int type)
int bits; int bits;
inet *dst; inet *dst;
/* make sure any unused bits in a CIDR value are zeroed */
dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct)); dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));
/* First, try for an IP V4 address: */ /*
ip_family(dst) = AF_INET; * First, check to see if this is an IPv6 or IPv4 address. IPv6
bits = inet_net_pton(ip_family(dst), src, &ip_v4addr(dst), * 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 = inet_net_pton(ip_family(dst), src, ip_addr(dst),
type ? ip_addrsize(dst) : -1); type ? ip_addrsize(dst) : -1);
if ((bits < 0) || (bits > 32)) if ((bits < 0) || (bits > ip_maxbits(dst)))
{
/* Go for an IPV6 address here, before faulting out: */
elog(ERROR, "invalid %s value '%s'", elog(ERROR, "invalid %s value '%s'",
type ? "CIDR" : "INET", src); type ? "CIDR" : "INET", src);
}
/* /*
* Error check: CIDR values must not have any bits set beyond the * Error check: CIDR values must not have any bits set beyond
* masklen. XXX this code is not IPV6 ready. * the masklen.
*/ */
if (type) if (type)
{ {
if (!v4addressOK(ip_v4addr(dst), bits)) if (!addressOK(ip_addr(dst), bits, ip_family(dst)))
elog(ERROR, "invalid CIDR value '%s': has bits set to right of mask", src); elog(ERROR, "invalid CIDR value '%s': has bits set to right of mask", src);
} }
VARATT_SIZEP(dst) = VARHDRSZ VARATT_SIZEP(dst) = VARHDRSZ
+ ((char *) &ip_v4addr(dst) - (char *) VARDATA(dst)) + ((char *) ip_addr(dst) - (char *) VARDATA(dst))
+ ip_addrsize(dst); + ip_addrsize(dst);
ip_bits(dst) = bits; ip_bits(dst) = bits;
ip_type(dst) = type; ip_type(dst) = type;
...@@ -110,19 +132,11 @@ Datum ...@@ -110,19 +132,11 @@ Datum
inet_out(PG_FUNCTION_ARGS) inet_out(PG_FUNCTION_ARGS)
{ {
inet *src = PG_GETARG_INET_P(0); inet *src = PG_GETARG_INET_P(0);
char tmp[sizeof("255.255.255.255/32")]; char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
char *dst; char *dst;
int len; int len;
if (ip_family(src) == AF_INET) dst = inet_net_ntop(ip_family(src), ip_addr(src), ip_bits(src),
{
/* It's an IP V4 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),
tmp, sizeof(tmp)); tmp, sizeof(tmp));
if (dst == NULL) if (dst == NULL)
elog(ERROR, "unable to print address (%s)", strerror(errno)); elog(ERROR, "unable to print address (%s)", strerror(errno));
...@@ -132,10 +146,6 @@ inet_out(PG_FUNCTION_ARGS) ...@@ -132,10 +146,6 @@ inet_out(PG_FUNCTION_ARGS)
len = strlen(tmp); len = strlen(tmp);
snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(src)); snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(src));
} }
}
else
/* Go for an IPV6 address here, before faulting out: */
elog(ERROR, "unknown address family (%d)", ip_family(src));
PG_RETURN_CSTRING(pstrdup(tmp)); PG_RETURN_CSTRING(pstrdup(tmp));
} }
...@@ -172,7 +182,7 @@ inet_recv(PG_FUNCTION_ARGS) ...@@ -172,7 +182,7 @@ inet_recv(PG_FUNCTION_ARGS)
if (ip_family(addr) != AF_INET) if (ip_family(addr) != AF_INET)
elog(ERROR, "Invalid family in external inet"); elog(ERROR, "Invalid family in external inet");
bits = pq_getmsgbyte(buf); bits = pq_getmsgbyte(buf);
if (bits < 0 || bits > 32) if (bits < 0 || bits > ip_maxbits(addr))
elog(ERROR, "Invalid bits in external inet"); elog(ERROR, "Invalid bits in external inet");
ip_bits(addr) = bits; ip_bits(addr) = bits;
ip_type(addr) = pq_getmsgbyte(buf); ip_type(addr) = pq_getmsgbyte(buf);
...@@ -183,10 +193,10 @@ inet_recv(PG_FUNCTION_ARGS) ...@@ -183,10 +193,10 @@ inet_recv(PG_FUNCTION_ARGS)
elog(ERROR, "Invalid length in external inet"); elog(ERROR, "Invalid length in external inet");
VARATT_SIZEP(addr) = VARHDRSZ VARATT_SIZEP(addr) = VARHDRSZ
+ ((char *) &ip_v4addr(addr) - (char *) VARDATA(addr)) + ((char *)ip_addr(addr) - (char *) VARDATA(addr))
+ ip_addrsize(addr); + ip_addrsize(addr);
addrptr = (char *) &ip_v4addr(addr); addrptr = (char *)ip_addr(addr);
for (i = 0; i < nb; i++) for (i = 0; i < nb; i++)
addrptr[i] = pq_getmsgbyte(buf); addrptr[i] = pq_getmsgbyte(buf);
...@@ -196,7 +206,7 @@ inet_recv(PG_FUNCTION_ARGS) ...@@ -196,7 +206,7 @@ inet_recv(PG_FUNCTION_ARGS)
*/ */
if (ip_type(addr)) if (ip_type(addr))
{ {
if (!v4addressOK(ip_v4addr(addr), bits)) if (!addressOK(ip_addr(addr), bits, ip_family(addr)))
elog(ERROR, "invalid external CIDR value: has bits set to right of mask"); elog(ERROR, "invalid external CIDR value: has bits set to right of mask");
} }
...@@ -230,7 +240,7 @@ inet_send(PG_FUNCTION_ARGS) ...@@ -230,7 +240,7 @@ inet_send(PG_FUNCTION_ARGS)
if (nb < 0) if (nb < 0)
nb = 0; nb = 0;
pq_sendbyte(&buf, nb); pq_sendbyte(&buf, nb);
addrptr = (char *) &ip_v4addr(addr); addrptr = (char *)ip_addr(addr);
for (i = 0; i < nb; i++) for (i = 0; i < nb; i++)
pq_sendbyte(&buf, addrptr[i]); pq_sendbyte(&buf, addrptr[i]);
PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
...@@ -257,6 +267,7 @@ text_network(text *src, int type) ...@@ -257,6 +267,7 @@ text_network(text *src, int type)
PG_RETURN_INET_P(network_in(str, type)); PG_RETURN_INET_P(network_in(str, type));
} }
Datum Datum
text_cidr(PG_FUNCTION_ARGS) text_cidr(PG_FUNCTION_ARGS)
{ {
...@@ -276,7 +287,10 @@ inet_set_masklen(PG_FUNCTION_ARGS) ...@@ -276,7 +287,10 @@ inet_set_masklen(PG_FUNCTION_ARGS)
int bits = PG_GETARG_INT32(1); int bits = PG_GETARG_INT32(1);
inet *dst; inet *dst;
if ((bits < 0) || (bits > 32)) /* no support for v6 yet */ if ( bits == -1 )
bits = ip_maxbits(src);
if ((bits < 0) || (bits > ip_maxbits(src)))
elog(ERROR, "set_masklen - invalid value '%d'", bits); elog(ERROR, "set_masklen - invalid value '%d'", bits);
/* clone the original data */ /* clone the original data */
...@@ -302,26 +316,21 @@ inet_set_masklen(PG_FUNCTION_ARGS) ...@@ -302,26 +316,21 @@ inet_set_masklen(PG_FUNCTION_ARGS)
static int32 static int32
network_cmp_internal(inet *a1, inet *a2) network_cmp_internal(inet *a1, inet *a2)
{ {
if (ip_family(a1) == AF_INET && ip_family(a2) == AF_INET) if (ip_family(a1) == ip_family(a2))
{ {
int order; int order;
order = v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), order = bitncmp(ip_addr(a1), ip_addr(a2),
Min(ip_bits(a1), ip_bits(a2))); Min(ip_bits(a1), ip_bits(a2)));
if (order != 0) if (order != 0)
return order; return order;
order = ((int) ip_bits(a1)) - ((int) ip_bits(a2)); order = ((int) ip_bits(a1)) - ((int) ip_bits(a2));
if (order != 0) if (order != 0)
return order; return order;
return v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), 32); return bitncmp(ip_addr(a1), ip_addr(a2), ip_maxbits(a1));
}
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));
return 0; /* keep compiler quiet */
} }
return ip_family(a1) - ip_family(a2);
} }
Datum Datum
...@@ -399,18 +408,13 @@ network_sub(PG_FUNCTION_ARGS) ...@@ -399,18 +408,13 @@ network_sub(PG_FUNCTION_ARGS)
inet *a1 = PG_GETARG_INET_P(0); inet *a1 = PG_GETARG_INET_P(0);
inet *a2 = PG_GETARG_INET_P(1); inet *a2 = PG_GETARG_INET_P(1);
if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET)) if (ip_family(a1) == ip_family(a2))
{ {
PG_RETURN_BOOL(ip_bits(a1) > ip_bits(a2) PG_RETURN_BOOL(ip_bits(a1) > ip_bits(a2)
&& v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a2)) == 0); && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(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); PG_RETURN_BOOL(false);
}
} }
Datum Datum
...@@ -419,18 +423,13 @@ network_subeq(PG_FUNCTION_ARGS) ...@@ -419,18 +423,13 @@ network_subeq(PG_FUNCTION_ARGS)
inet *a1 = PG_GETARG_INET_P(0); inet *a1 = PG_GETARG_INET_P(0);
inet *a2 = PG_GETARG_INET_P(1); inet *a2 = PG_GETARG_INET_P(1);
if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET)) if (ip_family(a1) == ip_family(a2))
{ {
PG_RETURN_BOOL(ip_bits(a1) >= ip_bits(a2) PG_RETURN_BOOL(ip_bits(a1) >= ip_bits(a2)
&& v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a2)) == 0); && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(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); PG_RETURN_BOOL(false);
}
} }
Datum Datum
...@@ -439,18 +438,13 @@ network_sup(PG_FUNCTION_ARGS) ...@@ -439,18 +438,13 @@ network_sup(PG_FUNCTION_ARGS)
inet *a1 = PG_GETARG_INET_P(0); inet *a1 = PG_GETARG_INET_P(0);
inet *a2 = PG_GETARG_INET_P(1); inet *a2 = PG_GETARG_INET_P(1);
if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET)) if (ip_family(a1) == ip_family(a2))
{ {
PG_RETURN_BOOL(ip_bits(a1) < ip_bits(a2) PG_RETURN_BOOL(ip_bits(a1) < ip_bits(a2)
&& v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a1)) == 0); && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(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); PG_RETURN_BOOL(false);
}
} }
Datum Datum
...@@ -459,18 +453,13 @@ network_supeq(PG_FUNCTION_ARGS) ...@@ -459,18 +453,13 @@ network_supeq(PG_FUNCTION_ARGS)
inet *a1 = PG_GETARG_INET_P(0); inet *a1 = PG_GETARG_INET_P(0);
inet *a2 = PG_GETARG_INET_P(1); inet *a2 = PG_GETARG_INET_P(1);
if ((ip_family(a1) == AF_INET) && (ip_family(a2) == AF_INET)) if (ip_family(a1) == ip_family(a2))
{ {
PG_RETURN_BOOL(ip_bits(a1) <= ip_bits(a2) PG_RETURN_BOOL(ip_bits(a1) <= ip_bits(a2)
&& v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), ip_bits(a1)) == 0); && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(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); PG_RETURN_BOOL(false);
}
} }
/* /*
...@@ -482,19 +471,13 @@ network_host(PG_FUNCTION_ARGS) ...@@ -482,19 +471,13 @@ network_host(PG_FUNCTION_ARGS)
inet *ip = PG_GETARG_INET_P(0); inet *ip = PG_GETARG_INET_P(0);
text *ret; text *ret;
int len; int len;
char *ptr, char *ptr;
tmp[sizeof("255.255.255.255/32")]; char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
if (ip_family(ip) == AF_INET) /* force display of max bits, regardless of masklen... */
{ if (inet_net_ntop(ip_family(ip), ip_addr(ip), ip_maxbits(ip),
/* It's an IP V4 address: */ tmp, sizeof(tmp)) == NULL)
/* force display of 32 bits, regardless of masklen... */
if (inet_net_ntop(AF_INET, &ip_v4addr(ip), 32, tmp, sizeof(tmp)) == NULL)
elog(ERROR, "unable to print host (%s)", strerror(errno)); 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) */ /* Suppress /n if present (shouldn't happen now) */
if ((ptr = strchr(tmp, '/')) != NULL) if ((ptr = strchr(tmp, '/')) != NULL)
...@@ -514,13 +497,10 @@ network_show(PG_FUNCTION_ARGS) ...@@ -514,13 +497,10 @@ network_show(PG_FUNCTION_ARGS)
inet *ip = PG_GETARG_INET_P(0); inet *ip = PG_GETARG_INET_P(0);
text *ret; text *ret;
int len; int len;
char tmp[sizeof("255.255.255.255/32")]; char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
if (ip_family(ip) == AF_INET) if (inet_net_ntop(ip_family(ip), ip_addr(ip), ip_maxbits(ip),
{ tmp, sizeof(tmp)) == NULL)
/* 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)
elog(ERROR, "unable to print host (%s)", strerror(errno)); elog(ERROR, "unable to print host (%s)", strerror(errno));
/* Add /n if not present (which it won't be) */ /* Add /n if not present (which it won't be) */
if (strchr(tmp, '/') == NULL) if (strchr(tmp, '/') == NULL)
...@@ -528,10 +508,6 @@ network_show(PG_FUNCTION_ARGS) ...@@ -528,10 +508,6 @@ network_show(PG_FUNCTION_ARGS)
len = strlen(tmp); len = strlen(tmp);
snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(ip)); snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(ip));
} }
}
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 */ /* Return string as a text datum */
len = strlen(tmp); len = strlen(tmp);
...@@ -548,24 +524,18 @@ network_abbrev(PG_FUNCTION_ARGS) ...@@ -548,24 +524,18 @@ network_abbrev(PG_FUNCTION_ARGS)
text *ret; text *ret;
char *dst; char *dst;
int len; int len;
char tmp[sizeof("255.255.255.255/32")]; char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
if (ip_family(ip) == AF_INET)
{
/* It's an IP V4 address: */
if (ip_type(ip)) if (ip_type(ip))
dst = inet_cidr_ntop(AF_INET, &ip_v4addr(ip), ip_bits(ip), dst = inet_cidr_ntop(ip_family(ip), ip_addr(ip),
tmp, sizeof(tmp)); ip_bits(ip), tmp, sizeof(tmp));
else else
dst = inet_net_ntop(AF_INET, &ip_v4addr(ip), ip_bits(ip), dst = inet_net_ntop(ip_family(ip), ip_addr(ip),
tmp, sizeof(tmp)); ip_bits(ip), tmp, sizeof(tmp));
if (dst == NULL) if (dst == NULL)
elog(ERROR, "unable to print address (%s)", strerror(errno)); 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 */ /* Return string as a text datum */
len = strlen(tmp); len = strlen(tmp);
...@@ -583,40 +553,67 @@ network_masklen(PG_FUNCTION_ARGS) ...@@ -583,40 +553,67 @@ network_masklen(PG_FUNCTION_ARGS)
PG_RETURN_INT32(ip_bits(ip)); PG_RETURN_INT32(ip_bits(ip));
} }
Datum
network_family(PG_FUNCTION_ARGS)
{
inet *ip = PG_GETARG_INET_P(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 Datum
network_broadcast(PG_FUNCTION_ARGS) network_broadcast(PG_FUNCTION_ARGS)
{ {
inet *ip = PG_GETARG_INET_P(0); inet *ip = PG_GETARG_INET_P(0);
inet *dst; inet *dst;
int byte;
int bits;
int maxbytes;
unsigned char mask;
unsigned char *a, *b;
/* make sure any unused bits are zeroed */ /* make sure any unused bits are zeroed */
dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct)); dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));
if (ip_family(ip) == AF_INET) if (ip_family(ip) == PGSQL_AF_INET) {
{ maxbytes = 4;
/* It's an IP V4 address: */ } else {
unsigned long mask = 0xffffffff; maxbytes = 16;
}
/* bits = ip_bits(ip);
* Shifting by 32 or more bits does not yield portable results, so a = ip_addr(ip);
* don't try it. b = ip_addr(dst);
*/
if (ip_bits(ip) < 32) for (byte = 0 ; byte < maxbytes ; byte++) {
mask >>= ip_bits(ip); if (bits >= 8) {
else mask = 0x00;
mask = 0; bits -= 8;
} else if (bits == 0) {
mask = 0xff;
} else {
mask = 0xff >> bits;
bits = 0;
}
ip_v4addr(dst) = htonl(ntohl(ip_v4addr(ip)) | mask); b[byte] = a[byte] | 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); ip_family(dst) = ip_family(ip);
ip_bits(dst) = ip_bits(ip); ip_bits(dst) = ip_bits(ip);
ip_type(dst) = 0; ip_type(dst) = 0;
VARATT_SIZEP(dst) = VARHDRSZ VARATT_SIZEP(dst) = VARHDRSZ
+ ((char *) &ip_v4addr(dst) - (char *) VARDATA(dst)) + ((char *) ip_addr(dst) - (char *) VARDATA(dst))
+ ip_addrsize(dst); + ip_addrsize(dst);
PG_RETURN_INET_P(dst); PG_RETURN_INET_P(dst);
...@@ -627,35 +624,44 @@ network_network(PG_FUNCTION_ARGS) ...@@ -627,35 +624,44 @@ network_network(PG_FUNCTION_ARGS)
{ {
inet *ip = PG_GETARG_INET_P(0); inet *ip = PG_GETARG_INET_P(0);
inet *dst; inet *dst;
int byte;
int bits;
int maxbytes;
unsigned char mask;
unsigned char *a, *b;
/* make sure any unused bits are zeroed */ /* make sure any unused bits are zeroed */
dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct)); dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));
if (ip_family(ip) == AF_INET) if (ip_family(ip) == PGSQL_AF_INET) {
{ maxbytes = 4;
/* It's an IP V4 address: */ } else {
unsigned long mask = 0xffffffff; maxbytes = 16;
}
/* bits = ip_bits(ip);
* Shifting by 32 or more bits does not yield portable results, so a = ip_addr(ip);
* don't try it. b = ip_addr(dst);
*/
if (ip_bits(ip) > 0) byte = 0;
mask <<= (32 - ip_bits(ip)); while (bits) {
else if (bits >= 8) {
mask = 0; mask = 0xff;
bits -= 8;
} else {
mask = 0xff << (8 - bits);
bits = 0;
}
ip_v4addr(dst) = htonl(ntohl(ip_v4addr(ip)) & mask); b[byte] = a[byte] & mask;
byte++;
} }
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); ip_family(dst) = ip_family(ip);
ip_bits(dst) = ip_bits(ip); ip_bits(dst) = ip_bits(ip);
ip_type(dst) = 1; ip_type(dst) = 1;
VARATT_SIZEP(dst) = VARHDRSZ VARATT_SIZEP(dst) = VARHDRSZ
+ ((char *) &ip_v4addr(dst) - (char *) VARDATA(dst)) + ((char *) ip_addr(dst) - (char *) VARDATA(dst))
+ ip_addrsize(dst); + ip_addrsize(dst);
PG_RETURN_INET_P(dst); PG_RETURN_INET_P(dst);
...@@ -666,36 +672,43 @@ network_netmask(PG_FUNCTION_ARGS) ...@@ -666,36 +672,43 @@ network_netmask(PG_FUNCTION_ARGS)
{ {
inet *ip = PG_GETARG_INET_P(0); inet *ip = PG_GETARG_INET_P(0);
inet *dst; inet *dst;
int byte;
int bits;
int maxbytes;
unsigned char mask;
unsigned char *b;
/* make sure any unused bits are zeroed */ /* make sure any unused bits are zeroed */
dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct)); dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));
if (ip_family(ip) == AF_INET) if (ip_family(ip) == PGSQL_AF_INET) {
{ maxbytes = 4;
/* It's an IP V4 address: */ } else {
unsigned long mask = 0xffffffff; maxbytes = 16;
}
/*
* Shifting by 32 or more bits does not yield portable results, so
* don't try it.
*/
if (ip_bits(ip) > 0)
mask <<= (32 - ip_bits(ip));
else
mask = 0;
ip_v4addr(dst) = htonl(mask); 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;
}
ip_bits(dst) = 32; b[byte] = mask;
byte++;
} }
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); ip_family(dst) = ip_family(ip);
ip_bits(dst) = ip_bits(ip);
ip_type(dst) = 0; ip_type(dst) = 0;
VARATT_SIZEP(dst) = VARHDRSZ VARATT_SIZEP(dst) = VARHDRSZ
+ ((char *) &ip_v4addr(dst) - (char *) VARDATA(dst)) + ((char *)ip_addr(dst) - (char *) VARDATA(dst))
+ ip_addrsize(dst); + ip_addrsize(dst);
PG_RETURN_INET_P(dst); PG_RETURN_INET_P(dst);
...@@ -706,36 +719,43 @@ network_hostmask(PG_FUNCTION_ARGS) ...@@ -706,36 +719,43 @@ network_hostmask(PG_FUNCTION_ARGS)
{ {
inet *ip = PG_GETARG_INET_P(0); inet *ip = PG_GETARG_INET_P(0);
inet *dst; inet *dst;
int byte;
int bits;
int maxbytes;
unsigned char mask;
unsigned char *b;
/* make sure any unused bits are zeroed */ /* make sure any unused bits are zeroed */
dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct)); dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));
if (ip_family(ip) == AF_INET) if (ip_family(ip) == PGSQL_AF_INET) {
{ maxbytes = 4;
/* It's an IP V4 address: */ } else {
unsigned long mask = 0xffffffff; maxbytes = 16;
}
/*
* Only shift if the mask len is < 32 bits ..
*/
if (ip_bits(ip) < 32)
mask >>= ip_bits(ip);
else
mask = 0;
ip_v4addr(dst) = htonl(mask); 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;
}
ip_bits(dst) = 32; b[byte] = mask;
byte--;
} }
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); ip_family(dst) = ip_family(ip);
ip_bits(dst) = ip_bits(ip);
ip_type(dst) = 0; ip_type(dst) = 0;
VARATT_SIZEP(dst) = VARHDRSZ VARATT_SIZEP(dst) = VARHDRSZ
+ ((char *) &ip_v4addr(dst) - (char *) VARDATA(dst)) + ((char *)ip_addr(dst) - (char *) VARDATA(dst))
+ ip_addrsize(dst); + ip_addrsize(dst);
PG_RETURN_INET_P(dst); PG_RETURN_INET_P(dst);
...@@ -760,12 +780,26 @@ convert_network_to_scalar(Datum value, Oid typid) ...@@ -760,12 +780,26 @@ convert_network_to_scalar(Datum value, Oid typid)
case CIDROID: case CIDROID:
{ {
inet *ip = DatumGetInetP(value); inet *ip = DatumGetInetP(value);
int len;
double res;
int i;
if (ip_family(ip) == AF_INET) /*
return (double) ip_v4addr(ip); * Note that we don't use the full address
* here.
*/
if (ip_family(ip) == PGSQL_AF_INET)
len = 4;
else else
/* Go for an IPV6 address here, before faulting out: */ len = 5;
elog(ERROR, "unknown address family (%d)", ip_family(ip));
res = ip_family(ip);
for (i = 0 ; i < len ; i++) {
res *= 256;
res += ip_addr(ip)[i];
}
return res;
break; break;
} }
case MACADDROID: case MACADDROID:
...@@ -788,53 +822,80 @@ convert_network_to_scalar(Datum value, Oid typid) ...@@ -788,53 +822,80 @@ convert_network_to_scalar(Datum value, Oid typid)
return 0; return 0;
} }
/* /*
* Bitwise comparison for V4 addresses. Add V6 implementation! * int
* bitncmp(l, r, n)
* compare bit masks l and r, for n bits.
* return:
* -1, 1, 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
*/ */
static int static int
v4bitncmp(unsigned long a1, unsigned long a2, int bits) bitncmp(void *l, void *r, int n)
{ {
unsigned long mask; u_int lb, rb;
int x, b;
/* b = n / 8;
* Shifting by 32 or more bits does not yield portable results, so x = memcmp(l, r, b);
* don't try it. if (x)
*/ return (x);
if (bits > 0)
mask = (0xFFFFFFFFL << (32 - bits)) & 0xFFFFFFFFL; lb = ((const u_char *)l)[b];
else rb = ((const u_char *)r)[b];
mask = 0; for (b = n % 8; b > 0; b--) {
a1 = ntohl(a1); if ((lb & 0x80) != (rb & 0x80)) {
a2 = ntohl(a2); if (lb & 0x80)
if ((a1 & mask) < (a2 & mask))
return (-1);
else if ((a1 & mask) > (a2 & mask))
return (1); return (1);
return (-1);
}
lb <<= 1;
rb <<= 1;
}
return (0); return (0);
} }
/*
* Returns true if given address fits fully within the specified bit width.
*/
static bool static bool
v4addressOK(unsigned long a1, int bits) addressOK(unsigned char *a, int bits, int family)
{ {
unsigned long mask; 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;
}
#if 0
assert(bits <= maxbits);
#endif
/* if (bits == maxbits)
* Shifting by 32 or more bits does not yield portable results, so return 1;
* don't try it.
*/ byte = (bits + 7) / 8;
if (bits > 0) nbits = bits % 8;
mask = (0xFFFFFFFFL << (32 - bits)) & 0xFFFFFFFFL; mask = 0xff;
else if (bits != 0)
mask = 0; mask >>= nbits;
a1 = ntohl(a1);
if ((a1 & mask) == a1) while (byte < maxbytes) {
return true; if ((a[byte] & mask) != 0)
return false; return 0;
mask = 0xff;
byte++;
}
return 1;
} }
...@@ -852,15 +913,16 @@ network_scan_first(Datum in) ...@@ -852,15 +913,16 @@ network_scan_first(Datum in)
/* /*
* return "last" IP on a given network. It's the broadcast address, * return "last" IP on a given network. It's the broadcast address,
* however, masklen has to be set to 32, since * however, masklen has to be set to its max btis, since
* 192.168.0.255/24 is considered less than 192.168.0.255/32 * 192.168.0.255/24 is considered less than 192.168.0.255/32
* *
* NB: this is not IPv6 ready ... * inet_set_masklen() hacked to max out the masklength to 128 for IPv6
* and 32 for IPv4 when given '-1' as argument.
*/ */
Datum Datum
network_scan_last(Datum in) network_scan_last(Datum in)
{ {
return DirectFunctionCall2(inet_set_masklen, return DirectFunctionCall2(inet_set_masklen,
DirectFunctionCall1(network_broadcast, in), DirectFunctionCall1(network_broadcast, in),
Int32GetDatum(32)); Int32GetDatum(-1));
} }
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: pg_proc.h,v 1.304 2003/06/22 22:04:55 tgl Exp $ * $Id: pg_proc.h,v 1.305 2003/06/24 22:21:23 momjian Exp $
* *
* NOTES * NOTES
* The script catalog/genbki.sh reads this file and generates .bki * The script catalog/genbki.sh reads this file and generates .bki
...@@ -2369,6 +2369,8 @@ DESCR("is-supernet-or-equal"); ...@@ -2369,6 +2369,8 @@ DESCR("is-supernet-or-equal");
/* inet/cidr functions */ /* inet/cidr functions */
DATA(insert OID = 605 ( abbrev PGNSP PGUID 12 f f t f i 1 25 "869" network_abbrev - _null_ )); DATA(insert OID = 605 ( abbrev PGNSP PGUID 12 f f t f i 1 25 "869" network_abbrev - _null_ ));
DESCR("abbreviated display of inet/cidr value"); DESCR("abbreviated display of inet/cidr value");
DATA(insert OID = 711 ( family PGNSP PGUID 12 f f t f i 1 23 "869" network_family - _null_ ));
DESCR("return address family (4 for IPv4, 6 for IPv6)");
DATA(insert OID = 683 ( network PGNSP PGUID 12 f f t f i 1 650 "869" network_network - _null_ )); DATA(insert OID = 683 ( network PGNSP PGUID 12 f f t f i 1 650 "869" network_network - _null_ ));
DESCR("network part of address"); DESCR("network part of address");
DATA(insert OID = 696 ( netmask PGNSP PGUID 12 f f t f i 1 869 "869" network_netmask - _null_ )); DATA(insert OID = 696 ( netmask PGNSP PGUID 12 f f t f i 1 869 "869" network_netmask - _null_ ));
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: builtins.h,v 1.219 2003/05/26 00:11:28 tgl Exp $ * $Id: builtins.h,v 1.220 2003/06/24 22:21:23 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -644,6 +644,7 @@ extern Datum network_network(PG_FUNCTION_ARGS); ...@@ -644,6 +644,7 @@ extern Datum network_network(PG_FUNCTION_ARGS);
extern Datum network_netmask(PG_FUNCTION_ARGS); extern Datum network_netmask(PG_FUNCTION_ARGS);
extern Datum network_hostmask(PG_FUNCTION_ARGS); extern Datum network_hostmask(PG_FUNCTION_ARGS);
extern Datum network_masklen(PG_FUNCTION_ARGS); extern Datum network_masklen(PG_FUNCTION_ARGS);
extern Datum network_family(PG_FUNCTION_ARGS);
extern Datum network_broadcast(PG_FUNCTION_ARGS); extern Datum network_broadcast(PG_FUNCTION_ARGS);
extern Datum network_host(PG_FUNCTION_ARGS); extern Datum network_host(PG_FUNCTION_ARGS);
extern Datum network_show(PG_FUNCTION_ARGS); extern Datum network_show(PG_FUNCTION_ARGS);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: inet.h,v 1.13 2002/06/20 20:29:53 momjian Exp $ * $Id: inet.h,v 1.14 2003/06/24 22:21:23 momjian Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -23,13 +23,19 @@ typedef struct ...@@ -23,13 +23,19 @@ typedef struct
unsigned char family; unsigned char family;
unsigned char bits; unsigned char bits;
unsigned char type; unsigned char type;
union unsigned char ip_addr[16]; /* 128 bits of address */
{
unsigned int ipv4_addr; /* network byte order */
/* add IPV6 address type here */
} addr;
} inet_struct; } inet_struct;
/*
* Referencing all of the non-AF_INET types to AF_INET lets us work on
* machines which may not have the appropriate address family (like
* inet6 addresses when AF_INET6 isn't present) but doesn't cause a
* dump/reload requirement. Existing databases used AF_INET for the family
* type on disk.
*/
#define PGSQL_AF_INET (AF_INET + 0)
#define PGSQL_AF_INET6 (AF_INET + 1)
/* /*
* Both INET and CIDR addresses are represented within Postgres as varlena * Both INET and CIDR addresses are represented within Postgres as varlena
* objects, ie, there is a varlena header (basically a length word) in front * objects, ie, there is a varlena header (basically a length word) in front
......
...@@ -19,15 +19,22 @@ INSERT INTO INET_TBL (c, i) VALUES ('10.1', '10.1.2.3/16'); ...@@ -19,15 +19,22 @@ INSERT INTO INET_TBL (c, i) VALUES ('10.1', '10.1.2.3/16');
INSERT INTO INET_TBL (c, i) VALUES ('10', '10.1.2.3/8'); INSERT INTO INET_TBL (c, i) VALUES ('10', '10.1.2.3/8');
INSERT INTO INET_TBL (c, i) VALUES ('10', '11.1.2.3/8'); INSERT INTO INET_TBL (c, i) VALUES ('10', '11.1.2.3/8');
INSERT INTO INET_TBL (c, i) VALUES ('10', '9.1.2.3/8'); INSERT INTO INET_TBL (c, i) VALUES ('10', '9.1.2.3/8');
INSERT INTO INET_TBL (c, i) VALUES ('10:23::f1', '10:23::f1/64');
INSERT INTO INET_TBL (c, i) VALUES ('10:23::8000/113', '10:23::ffff');
INSERT INTO INET_TBL (c, i) VALUES ('::ffff:1.2.3.4', '::4.3.2.1/24');
-- check that CIDR rejects invalid input: -- check that CIDR rejects invalid input:
INSERT INTO INET_TBL (c, i) VALUES ('192.168.1.2/24', '192.168.1.226'); INSERT INTO INET_TBL (c, i) VALUES ('192.168.1.2/24', '192.168.1.226');
ERROR: invalid CIDR value '192.168.1.2/24': has bits set to right of mask ERROR: invalid CIDR value '192.168.1.2/24': has bits set to right of mask
INSERT INTO INET_TBL (c, i) VALUES ('1234::1234::1234', '::1.2.3.4');
ERROR: invalid CIDR value '1234::1234::1234'
-- check that CIDR rejects invalid input when converting from text: -- check that CIDR rejects invalid input when converting from text:
INSERT INTO INET_TBL (c, i) VALUES (cidr('192.168.1.2/24'), '192.168.1.226'); INSERT INTO INET_TBL (c, i) VALUES (cidr('192.168.1.2/24'), '192.168.1.226');
ERROR: invalid CIDR value '192.168.1.2/24': has bits set to right of mask ERROR: invalid CIDR value '192.168.1.2/24': has bits set to right of mask
INSERT INTO INET_TBL (c, i) VALUES (cidr('ffff:ffff:ffff:ffff::/24'), '::192.168.1.226');
ERROR: invalid CIDR value 'ffff:ffff:ffff:ffff::/24': has bits set to right of mask
SELECT '' AS ten, c AS cidr, i AS inet FROM INET_TBL; SELECT '' AS ten, c AS cidr, i AS inet FROM INET_TBL;
ten | cidr | inet ten | cidr | inet
-----+----------------+------------------ -----+--------------------+------------------
| 192.168.1.0/24 | 192.168.1.226/24 | 192.168.1.0/24 | 192.168.1.226/24
| 192.168.1.0/24 | 192.168.1.226 | 192.168.1.0/24 | 192.168.1.226
| 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.0/24
...@@ -42,32 +49,38 @@ SELECT '' AS ten, c AS cidr, i AS inet FROM INET_TBL; ...@@ -42,32 +49,38 @@ SELECT '' AS ten, c AS cidr, i AS inet FROM INET_TBL;
| 10.0.0.0/8 | 10.1.2.3/8 | 10.0.0.0/8 | 10.1.2.3/8
| 10.0.0.0/8 | 11.1.2.3/8 | 10.0.0.0/8 | 11.1.2.3/8
| 10.0.0.0/8 | 9.1.2.3/8 | 10.0.0.0/8 | 9.1.2.3/8
(14 rows) | 10:23::f1/128 | 10:23::f1/64
| 10:23::8000/113 | 10:23::ffff
| ::ffff:1.2.3.4/128 | ::4.3.2.1/24
(17 rows)
-- now test some support functions -- now test some support functions
SELECT '' AS ten, i AS inet, host(i), text(i) FROM INET_TBL; SELECT '' AS ten, i AS inet, host(i), text(i), family(i) FROM INET_TBL;
ten | inet | host | text ten | inet | host | text | family
-----+------------------+---------------+------------------ -----+------------------+---------------+------------------+--------
| 192.168.1.226/24 | 192.168.1.226 | 192.168.1.226/24 | 192.168.1.226/24 | 192.168.1.226 | 192.168.1.226/24 | 4
| 192.168.1.226 | 192.168.1.226 | 192.168.1.226/32 | 192.168.1.226 | 192.168.1.226 | 192.168.1.226/32 | 4
| 192.168.1.0/24 | 192.168.1.0 | 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.0 | 192.168.1.0/24 | 4
| 192.168.1.0/25 | 192.168.1.0 | 192.168.1.0/25 | 192.168.1.0/25 | 192.168.1.0 | 192.168.1.0/25 | 4
| 192.168.1.255/24 | 192.168.1.255 | 192.168.1.255/24 | 192.168.1.255/24 | 192.168.1.255 | 192.168.1.255/24 | 4
| 192.168.1.255/25 | 192.168.1.255 | 192.168.1.255/25 | 192.168.1.255/25 | 192.168.1.255 | 192.168.1.255/25 | 4
| 10.1.2.3/8 | 10.1.2.3 | 10.1.2.3/8 | 10.1.2.3/8 | 10.1.2.3 | 10.1.2.3/8 | 4
| 10.1.2.3/8 | 10.1.2.3 | 10.1.2.3/8 | 10.1.2.3/8 | 10.1.2.3 | 10.1.2.3/8 | 4
| 10.1.2.3 | 10.1.2.3 | 10.1.2.3/32 | 10.1.2.3 | 10.1.2.3 | 10.1.2.3/32 | 4
| 10.1.2.3/24 | 10.1.2.3 | 10.1.2.3/24 | 10.1.2.3/24 | 10.1.2.3 | 10.1.2.3/24 | 4
| 10.1.2.3/16 | 10.1.2.3 | 10.1.2.3/16 | 10.1.2.3/16 | 10.1.2.3 | 10.1.2.3/16 | 4
| 10.1.2.3/8 | 10.1.2.3 | 10.1.2.3/8 | 10.1.2.3/8 | 10.1.2.3 | 10.1.2.3/8 | 4
| 11.1.2.3/8 | 11.1.2.3 | 11.1.2.3/8 | 11.1.2.3/8 | 11.1.2.3 | 11.1.2.3/8 | 4
| 9.1.2.3/8 | 9.1.2.3 | 9.1.2.3/8 | 9.1.2.3/8 | 9.1.2.3 | 9.1.2.3/8 | 4
(14 rows) | 10:23::f1/64 | 10:23::f1 | 10:23::f1/64 | 6
| 10:23::ffff | 10:23::ffff | 10:23::ffff/128 | 6
| ::4.3.2.1/24 | ::4.3.2.1 | ::4.3.2.1/24 | 6
(17 rows)
SELECT '' AS ten, c AS cidr, broadcast(c), SELECT '' AS ten, c AS cidr, broadcast(c),
i AS inet, broadcast(i) FROM INET_TBL; i AS inet, broadcast(i) FROM INET_TBL;
ten | cidr | broadcast | inet | broadcast ten | cidr | broadcast | inet | broadcast
-----+----------------+------------------+------------------+------------------ -----+--------------------+------------------+------------------+---------------------------------------
| 192.168.1.0/24 | 192.168.1.255/24 | 192.168.1.226/24 | 192.168.1.255/24 | 192.168.1.0/24 | 192.168.1.255/24 | 192.168.1.226/24 | 192.168.1.255/24
| 192.168.1.0/24 | 192.168.1.255/24 | 192.168.1.226 | 192.168.1.226 | 192.168.1.0/24 | 192.168.1.255/24 | 192.168.1.226 | 192.168.1.226
| 192.168.1.0/24 | 192.168.1.255/24 | 192.168.1.0/24 | 192.168.1.255/24 | 192.168.1.0/24 | 192.168.1.255/24 | 192.168.1.0/24 | 192.168.1.255/24
...@@ -82,12 +95,15 @@ SELECT '' AS ten, c AS cidr, broadcast(c), ...@@ -82,12 +95,15 @@ SELECT '' AS ten, c AS cidr, broadcast(c),
| 10.0.0.0/8 | 10.255.255.255/8 | 10.1.2.3/8 | 10.255.255.255/8 | 10.0.0.0/8 | 10.255.255.255/8 | 10.1.2.3/8 | 10.255.255.255/8
| 10.0.0.0/8 | 10.255.255.255/8 | 11.1.2.3/8 | 11.255.255.255/8 | 10.0.0.0/8 | 10.255.255.255/8 | 11.1.2.3/8 | 11.255.255.255/8
| 10.0.0.0/8 | 10.255.255.255/8 | 9.1.2.3/8 | 9.255.255.255/8 | 10.0.0.0/8 | 10.255.255.255/8 | 9.1.2.3/8 | 9.255.255.255/8
(14 rows) | 10:23::f1/128 | 10:23::f1 | 10:23::f1/64 | 10:23::ffff:ffff:ffff:ffff/64
| 10:23::8000/113 | 10:23::ffff/113 | 10:23::ffff | 10:23::ffff
| ::ffff:1.2.3.4/128 | ::ffff:1.2.3.4 | ::4.3.2.1/24 | 0:ff:ffff:ffff:ffff:ffff:ffff:ffff/24
(17 rows)
SELECT '' AS ten, c AS cidr, network(c) AS "network(cidr)", SELECT '' AS ten, c AS cidr, network(c) AS "network(cidr)",
i AS inet, network(i) AS "network(inet)" FROM INET_TBL; i AS inet, network(i) AS "network(inet)" FROM INET_TBL;
ten | cidr | network(cidr) | inet | network(inet) ten | cidr | network(cidr) | inet | network(inet)
-----+----------------+----------------+------------------+------------------ -----+--------------------+--------------------+------------------+------------------
| 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.226/24 | 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.226/24 | 192.168.1.0/24
| 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.226 | 192.168.1.226/32 | 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.226 | 192.168.1.226/32
| 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.0/24 | 192.168.1.0/24
...@@ -102,12 +118,15 @@ SELECT '' AS ten, c AS cidr, network(c) AS "network(cidr)", ...@@ -102,12 +118,15 @@ SELECT '' AS ten, c AS cidr, network(c) AS "network(cidr)",
| 10.0.0.0/8 | 10.0.0.0/8 | 10.1.2.3/8 | 10.0.0.0/8 | 10.0.0.0/8 | 10.0.0.0/8 | 10.1.2.3/8 | 10.0.0.0/8
| 10.0.0.0/8 | 10.0.0.0/8 | 11.1.2.3/8 | 11.0.0.0/8 | 10.0.0.0/8 | 10.0.0.0/8 | 11.1.2.3/8 | 11.0.0.0/8
| 10.0.0.0/8 | 10.0.0.0/8 | 9.1.2.3/8 | 9.0.0.0/8 | 10.0.0.0/8 | 10.0.0.0/8 | 9.1.2.3/8 | 9.0.0.0/8
(14 rows) | 10:23::f1/128 | 10:23::f1/128 | 10:23::f1/64 | 10:23::/64
| 10:23::8000/113 | 10:23::8000/113 | 10:23::ffff | 10:23::ffff/128
| ::ffff:1.2.3.4/128 | ::ffff:1.2.3.4/128 | ::4.3.2.1/24 | ::/24
(17 rows)
SELECT '' AS ten, c AS cidr, masklen(c) AS "masklen(cidr)", SELECT '' AS ten, c AS cidr, masklen(c) AS "masklen(cidr)",
i AS inet, masklen(i) AS "masklen(inet)" FROM INET_TBL; i AS inet, masklen(i) AS "masklen(inet)" FROM INET_TBL;
ten | cidr | masklen(cidr) | inet | masklen(inet) ten | cidr | masklen(cidr) | inet | masklen(inet)
-----+----------------+---------------+------------------+--------------- -----+--------------------+---------------+------------------+---------------
| 192.168.1.0/24 | 24 | 192.168.1.226/24 | 24 | 192.168.1.0/24 | 24 | 192.168.1.226/24 | 24
| 192.168.1.0/24 | 24 | 192.168.1.226 | 32 | 192.168.1.0/24 | 24 | 192.168.1.226 | 32
| 192.168.1.0/24 | 24 | 192.168.1.0/24 | 24 | 192.168.1.0/24 | 24 | 192.168.1.0/24 | 24
...@@ -122,7 +141,10 @@ SELECT '' AS ten, c AS cidr, masklen(c) AS "masklen(cidr)", ...@@ -122,7 +141,10 @@ SELECT '' AS ten, c AS cidr, masklen(c) AS "masklen(cidr)",
| 10.0.0.0/8 | 8 | 10.1.2.3/8 | 8 | 10.0.0.0/8 | 8 | 10.1.2.3/8 | 8
| 10.0.0.0/8 | 8 | 11.1.2.3/8 | 8 | 10.0.0.0/8 | 8 | 11.1.2.3/8 | 8
| 10.0.0.0/8 | 8 | 9.1.2.3/8 | 8 | 10.0.0.0/8 | 8 | 9.1.2.3/8 | 8
(14 rows) | 10:23::f1/128 | 128 | 10:23::f1/64 | 64
| 10:23::8000/113 | 113 | 10:23::ffff | 128
| ::ffff:1.2.3.4/128 | 128 | ::4.3.2.1/24 | 24
(17 rows)
SELECT '' AS four, c AS cidr, masklen(c) AS "masklen(cidr)", SELECT '' AS four, c AS cidr, masklen(c) AS "masklen(cidr)",
i AS inet, masklen(i) AS "masklen(inet)" FROM INET_TBL i AS inet, masklen(i) AS "masklen(inet)" FROM INET_TBL
...@@ -150,7 +172,7 @@ SELECT '' AS ten, i, c, ...@@ -150,7 +172,7 @@ SELECT '' AS ten, i, c,
i >> c AS sup, i >>= c AS spe i >> c AS sup, i >>= c AS spe
FROM INET_TBL; FROM INET_TBL;
ten | i | c | lt | le | eq | ge | gt | ne | sb | sbe | sup | spe ten | i | c | lt | le | eq | ge | gt | ne | sb | sbe | sup | spe
-----+------------------+----------------+----+----+----+----+----+----+----+-----+-----+----- -----+------------------+--------------------+----+----+----+----+----+----+----+-----+-----+-----
| 192.168.1.226/24 | 192.168.1.0/24 | f | f | f | t | t | t | f | t | f | t | 192.168.1.226/24 | 192.168.1.0/24 | f | f | f | t | t | t | f | t | f | t
| 192.168.1.226 | 192.168.1.0/24 | f | f | f | t | t | t | t | t | f | f | 192.168.1.226 | 192.168.1.0/24 | f | f | f | t | t | t | t | t | f | f
| 192.168.1.0/24 | 192.168.1.0/24 | f | t | t | t | f | f | f | t | f | t | 192.168.1.0/24 | 192.168.1.0/24 | f | t | t | t | f | f | f | t | f | t
...@@ -165,7 +187,10 @@ SELECT '' AS ten, i, c, ...@@ -165,7 +187,10 @@ SELECT '' AS ten, i, c,
| 10.1.2.3/8 | 10.0.0.0/8 | f | f | f | t | t | t | f | t | f | t | 10.1.2.3/8 | 10.0.0.0/8 | f | f | f | t | t | t | f | t | f | t
| 11.1.2.3/8 | 10.0.0.0/8 | f | f | f | t | t | t | f | f | f | f | 11.1.2.3/8 | 10.0.0.0/8 | f | f | f | t | t | t | f | f | f | f
| 9.1.2.3/8 | 10.0.0.0/8 | t | t | f | f | f | t | f | f | f | f | 9.1.2.3/8 | 10.0.0.0/8 | t | t | f | f | f | t | f | f | f | f
(14 rows) | 10:23::f1/64 | 10:23::f1/128 | t | t | f | f | f | t | f | f | t | t
| 10:23::ffff | 10:23::8000/113 | f | f | f | t | t | t | t | t | f | f
| ::4.3.2.1/24 | ::ffff:1.2.3.4/128 | t | t | f | f | f | t | f | f | t | t
(17 rows)
-- check the conversion to/from text and set_netmask -- check the conversion to/from text and set_netmask
SELECT '' AS ten, set_masklen(inet(text(i)), 24) FROM INET_TBL; SELECT '' AS ten, set_masklen(inet(text(i)), 24) FROM INET_TBL;
...@@ -185,7 +210,10 @@ SELECT '' AS ten, set_masklen(inet(text(i)), 24) FROM INET_TBL; ...@@ -185,7 +210,10 @@ SELECT '' AS ten, set_masklen(inet(text(i)), 24) FROM INET_TBL;
| 10.1.2.3/24 | 10.1.2.3/24
| 11.1.2.3/24 | 11.1.2.3/24
| 9.1.2.3/24 | 9.1.2.3/24
(14 rows) | 10:23::f1/24
| 10:23::ffff/24
| ::4.3.2.1/24
(17 rows)
-- check that index works correctly -- check that index works correctly
CREATE INDEX inet_idx1 ON inet_tbl(i); CREATE INDEX inet_idx1 ON inet_tbl(i);
......
...@@ -20,16 +20,20 @@ INSERT INTO INET_TBL (c, i) VALUES ('10.1', '10.1.2.3/16'); ...@@ -20,16 +20,20 @@ INSERT INTO INET_TBL (c, i) VALUES ('10.1', '10.1.2.3/16');
INSERT INTO INET_TBL (c, i) VALUES ('10', '10.1.2.3/8'); INSERT INTO INET_TBL (c, i) VALUES ('10', '10.1.2.3/8');
INSERT INTO INET_TBL (c, i) VALUES ('10', '11.1.2.3/8'); INSERT INTO INET_TBL (c, i) VALUES ('10', '11.1.2.3/8');
INSERT INTO INET_TBL (c, i) VALUES ('10', '9.1.2.3/8'); INSERT INTO INET_TBL (c, i) VALUES ('10', '9.1.2.3/8');
INSERT INTO INET_TBL (c, i) VALUES ('10:23::f1', '10:23::f1/64');
INSERT INTO INET_TBL (c, i) VALUES ('10:23::8000/113', '10:23::ffff');
INSERT INTO INET_TBL (c, i) VALUES ('::ffff:1.2.3.4', '::4.3.2.1/24');
-- check that CIDR rejects invalid input: -- check that CIDR rejects invalid input:
INSERT INTO INET_TBL (c, i) VALUES ('192.168.1.2/24', '192.168.1.226'); INSERT INTO INET_TBL (c, i) VALUES ('192.168.1.2/24', '192.168.1.226');
INSERT INTO INET_TBL (c, i) VALUES ('1234::1234::1234', '::1.2.3.4');
-- check that CIDR rejects invalid input when converting from text: -- check that CIDR rejects invalid input when converting from text:
INSERT INTO INET_TBL (c, i) VALUES (cidr('192.168.1.2/24'), '192.168.1.226'); INSERT INTO INET_TBL (c, i) VALUES (cidr('192.168.1.2/24'), '192.168.1.226');
INSERT INTO INET_TBL (c, i) VALUES (cidr('ffff:ffff:ffff:ffff::/24'), '::192.168.1.226');
SELECT '' AS ten, c AS cidr, i AS inet FROM INET_TBL; SELECT '' AS ten, c AS cidr, i AS inet FROM INET_TBL;
-- now test some support functions -- now test some support functions
SELECT '' AS ten, i AS inet, host(i), text(i) FROM INET_TBL; SELECT '' AS ten, i AS inet, host(i), text(i), family(i) FROM INET_TBL;
SELECT '' AS ten, c AS cidr, broadcast(c), SELECT '' AS ten, c AS cidr, broadcast(c),
i AS inet, broadcast(i) FROM INET_TBL; i AS inet, broadcast(i) FROM INET_TBL;
SELECT '' AS ten, c AS cidr, network(c) AS "network(cidr)", SELECT '' AS ten, c AS cidr, network(c) AS "network(cidr)",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment