Thread: IPv6 address parsing for inet/cidr types (take II)

IPv6 address parsing for inet/cidr types (take II)

From
Michael Graff
Date:
The first posting was held for moderator approval, which never
happened, so I'll try this again after first subscribing.

This was done as part of my job at the Internet Software Consortium.

Major changes:

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

        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.

Regression tests pass, at least on my NetBSD machine.

--Michael

diff -ur ORIG/postgresql-7.3.2/src/backend/utils/adt/inet_net_ntop.c
postgresql-7.3.2/src/backend/utils/adt/inet_net_ntop.c
--- ORIG/postgresql-7.3.2/src/backend/utils/adt/inet_net_ntop.c    2002-09-01 19:47:04.000000000 -0700
+++ postgresql-7.3.2/src/backend/utils/adt/inet_net_ntop.c    2003-04-03 16:26:37.000000000 -0800
@@ -27,8 +27,12 @@

 #include <errno.h>

+#include "utils/inet.h"
 #include "utils/builtins.h"

+#define NS_IN6ADDRSZ 16
+#define NS_INT16SZ 2
+
 #ifdef SPRINTF_CHAR
 #define SPRINTF(x) strlen(sprintf/**/x)
 #else
@@ -36,9 +40,13 @@
 #endif

 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,
-                    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 *
@@ -55,8 +63,10 @@
 {
     switch (af)
     {
-        case AF_INET:
+        case PGSQL_AF_INET:
             return (inet_cidr_ntop_ipv4(src, bits, dst, size));
+        case PGSQL_AF_INET6:
+            return (inet_cidr_ntop_ipv6(src, bits, dst, size));
         default:
             errno = EAFNOSUPPORT;
             return (NULL);
@@ -136,6 +146,138 @@
     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_net_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;
+    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;
+
+    if (bits == 0) {
+        *cp++ = ':';
+        *cp++ = ':';
+        *cp = '\0';
+    } 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++ = ':';
+                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 (bits != 128) {
+        /* Format CIDR /width. */
+        SPRINTF((cp, "/%u", bits));
+    } else {
+        *cp = 0;
+    }
+    if (strlen(outbuf) + 1 > size)
+        goto emsgsize;
+    strcpy(dst, outbuf);
+
+    return (dst);
+
+emsgsize:
+    errno = EMSGSIZE;
+    return (NULL);
+}

 /*
  * char *
@@ -156,8 +298,10 @@
 {
     switch (af)
     {
-        case AF_INET:
+        case PGSQL_AF_INET:
             return (inet_net_ntop_ipv4(src, bits, dst, size));
+        case PGSQL_AF_INET6:
+            return (inet_net_ntop_ipv6(src, bits, dst, size));
         default:
             errno = EAFNOSUPPORT;
             return (NULL);
@@ -217,3 +361,133 @@
     errno = EMSGSIZE;
     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_cidr_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;
+
+            if (src[15] || bits == -1 || bits > 120)
+                n = 4;
+            else if (src[14] || bits > 112)
+                n = 3;
+            else
+                n = 2;
+            n = decoct(src+12, n, 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)
+        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);
+}
diff -ur ORIG/postgresql-7.3.2/src/backend/utils/adt/inet_net_pton.c
postgresql-7.3.2/src/backend/utils/adt/inet_net_pton.c
--- ORIG/postgresql-7.3.2/src/backend/utils/adt/inet_net_pton.c    2002-09-01 19:47:04.000000000 -0700
+++ postgresql-7.3.2/src/backend/utils/adt/inet_net_pton.c    2003-04-03 16:00:51.000000000 -0800
@@ -29,16 +29,14 @@
 #include <ctype.h>
 #include <errno.h>

-#include "utils/builtins.h"
+#include "utils/inet.h"

-#ifdef SPRINTF_CHAR
-#define SPRINTF(x) strlen(sprintf/**/x)
-#else
-#define SPRINTF(x) ((size_t)sprintf x)
-#endif
+#include "utils/builtins.h"

 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_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
@@ -63,10 +61,14 @@
 {
     switch (af)
     {
-        case AF_INET:
+        case PGSQL_AF_INET:
             return size == -1 ?
                 inet_net_pton_ipv4(src, dst) :
                 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:
             errno = EAFNOSUPPORT;
             return (-1);
@@ -335,3 +337,199 @@
     errno = EMSGSIZE;
     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);
+}
diff -ur ORIG/postgresql-7.3.2/src/backend/utils/adt/network.c postgresql-7.3.2/src/backend/utils/adt/network.c
--- ORIG/postgresql-7.3.2/src/backend/utils/adt/network.c    2002-09-01 19:47:04.000000000 -0700
+++ postgresql-7.3.2/src/backend/utils/adt/network.c    2003-04-04 13:16:31.000000000 -0800
@@ -1,7 +1,5 @@
 /*
- *    PostgreSQL type definitions for the INET type.    This
- *    is for IP V4 CIDR notation, but prepared for V6: just
- *    add the necessary bits where the comments indicate.
+ *    PostgreSQL type definitions for the INET and CIDR types.
  *
  *    $Header: /cvsroot/pgsql-server/src/backend/utils/adt/network.c,v 1.35 2002/09/02 02:47:04 momjian Exp $
  *
@@ -15,23 +13,22 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>

+#include <assert.h>
+
 #include "catalog/pg_type.h"
 #include "utils/builtins.h"
 #include "utils/inet.h"

-
 static Datum text_network(text *src, int type);
 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 bitncmp(void *l, void *r, int n);
+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) \
     (((inet_struct *)VARDATA(inetptr))->family)

@@ -41,43 +38,70 @@
 #define ip_type(inetptr) \
     (((inet_struct *)VARDATA(inetptr))->type)

-#define ip_v4addr(inetptr) \
-    (((inet_struct *)VARDATA(inetptr))->addr.ipv4_addr)
+#define ip_addr(inetptr) \
+    (((inet_struct *)VARDATA(inetptr))->ip_addr)
+
+/*
+ * 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 */
 static inet *
 network_in(char *src, int type)
 {
-    int            bits;
+    int        bits;
+    int        maxbits;
     inet       *dst;

     dst = (inet *) palloc(VARHDRSZ + sizeof(inet_struct));
     /* make sure any unused bits in a CIDR value are zeroed */
     MemSet(dst, 0, VARHDRSZ + sizeof(inet_struct));

-    /* First, try for an IP V4 address: */
-    ip_family(dst) = AF_INET;
-    bits = inet_net_pton(ip_family(dst), src, &ip_v4addr(dst),
-                         type ? ip_addrsize(dst) : -1);
-    if ((bits < 0) || (bits > 32))
-    {
-        /* Go for an IPV6 address here, before faulting out: */
-        elog(ERROR, "invalid %s value '%s'",
-             type ? "CIDR" : "INET", src);
+    /*
+     * First, check to see if this is an IPv6 or IPv4 address.  IPv6
+     * addresses will have a : somewhere in them (several, in fact) so
+     * if there is one present, assume it's V6, otherwise assume it's V4.
+     */
+
+    if (strchr(src, ':') != NULL) {
+        ip_family(dst) = PGSQL_AF_INET6;
+        maxbits = 128;
+    } else {
+        ip_family(dst) = PGSQL_AF_INET;
+        maxbits = 32;
     }

+    bits = inet_net_pton(ip_family(dst), src, ip_addr(dst),
+                 type ? ip_addrsize(dst) : -1);
+    if ((bits < 0) || (bits > maxbits))
+        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.
+     * Error check: CIDR values must not have any bits set beyond
+     * the masklen.
      */
     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);
     }

     VARATT_SIZEP(dst) = VARHDRSZ
-        + ((char *) &ip_v4addr(dst) - (char *) VARDATA(dst))
+        + ((char *) &ip_addr(dst) - (char *) VARDATA(dst))
         + ip_addrsize(dst);
     ip_bits(dst) = bits;
     ip_type(dst) = type;
@@ -110,32 +134,20 @@
 inet_out(PG_FUNCTION_ARGS)
 {
     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;
     int            len;

-    if (ip_family(src) == AF_INET)
+    dst = inet_net_ntop(ip_family(src), ip_addr(src), ip_bits(src),
+                tmp, sizeof(tmp));
+    if (dst == NULL)
+        elog(ERROR, "unable to print address (%s)", strerror(errno));
+    /* For CIDR, add /n if not present */
+    if (ip_type(src) && strchr(tmp, '/') == NULL)
     {
-        /* 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));
-        if (dst == NULL)
-            elog(ERROR, "unable to print address (%s)", strerror(errno));
-        /* For CIDR, add /n if not present */
-        if (ip_type(src) && strchr(tmp, '/') == NULL)
-        {
-            len = strlen(tmp);
-            snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(src));
-        }
+        len = strlen(tmp);
+        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));
 }
@@ -181,8 +193,14 @@
     int            bits = PG_GETARG_INT32(1);
     inet       *dst;

-    if ((bits < 0) || (bits > 32))        /* no support for v6 yet */
-        elog(ERROR, "set_masklen - invalid value '%d'", bits);
+    if (ip_family(src) == PGSQL_AF_INET) {
+        if ((bits < 0) || (bits > 32))
+            elog(ERROR, "set_masklen - invalid value '%d'", bits);
+    }
+    else if (ip_family(src) == PGSQL_AF_INET6) {
+        if ((bits < 0) || (bits > 128))
+            elog(ERROR, "set_masklen - invalid value '%d'", bits);
+    }

     /* clone the original data */
     dst = (inet *) palloc(VARHDRSZ + sizeof(inet_struct));
@@ -207,26 +225,21 @@
 static int32
 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;

-        order = v4bitncmp(ip_v4addr(a1), ip_v4addr(a2),
-                          Min(ip_bits(a1), ip_bits(a2)));
+        order = bitncmp(ip_addr(a1), ip_addr(a2),
+                Min(ip_bits(a1), ip_bits(a2)));
         if (order != 0)
             return order;
         order = ((int) ip_bits(a1)) - ((int) ip_bits(a2));
         if (order != 0)
             return order;
-        return v4bitncmp(ip_v4addr(a1), ip_v4addr(a2), 32);
-    }
-    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 bitncmp(ip_addr(a1), ip_addr(a2), 32);
     }
+
+    return ip_family(a1) - ip_family(a2);
 }

 Datum
@@ -304,18 +317,13 @@
     inet       *a1 = PG_GETARG_INET_P(0);
     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)
-           && v4bitncmp(ip_v4addr(a1), ip_v4addr(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);
+           && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a2)) == 0);
     }
+
+    PG_RETURN_BOOL(false);
 }

 Datum
@@ -324,18 +332,13 @@
     inet       *a1 = PG_GETARG_INET_P(0);
     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)
-           && v4bitncmp(ip_v4addr(a1), ip_v4addr(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);
+           && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a2)) == 0);
     }
+
+    PG_RETURN_BOOL(false);
 }

 Datum
@@ -344,18 +347,13 @@
     inet       *a1 = PG_GETARG_INET_P(0);
     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)
-           && v4bitncmp(ip_v4addr(a1), ip_v4addr(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);
+           && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a1)) == 0);
     }
+
+    PG_RETURN_BOOL(false);
 }

 Datum
@@ -364,18 +362,13 @@
     inet       *a1 = PG_GETARG_INET_P(0);
     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)
-           && v4bitncmp(ip_v4addr(a1), ip_v4addr(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);
+           && bitncmp(ip_addr(a1), ip_addr(a2), ip_bits(a1)) == 0);
     }
+
+    PG_RETURN_BOOL(false);
 }

 /*
@@ -387,19 +380,18 @@
     inet       *ip = PG_GETARG_INET_P(0);
     text       *ret;
     int            len;
-    char       *ptr,
-                tmp[sizeof("255.255.255.255/32")];
-
-    if (ip_family(ip) == AF_INET)
-    {
-        /* 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));
-    }
-    else
-        /* Go for an IPV6 address here, before faulting out: */
-        elog(ERROR, "unknown address family (%d)", ip_family(ip));
+    int        bits;
+    char       *ptr;
+    char        tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+
+    if (ip_family(ip) == PGSQL_AF_INET)
+        bits = 32;
+    else
+        bits = 128;
+    /* force display of 32 bits, regardless of masklen... */
+    if (inet_net_ntop(ip_family(ip), ip_addr(ip), bits,
+              tmp, sizeof(tmp)) == NULL)
+        elog(ERROR, "unable to print host (%s)", strerror(errno));

     /* Suppress /n if present (shouldn't happen now) */
     if ((ptr = strchr(tmp, '/')) != NULL)
@@ -419,24 +411,22 @@
     inet       *ip = PG_GETARG_INET_P(0);
     text       *ret;
     int            len;
-    char        tmp[sizeof("255.255.255.255/32")];
+    char        tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];
+    int bits;

-    if (ip_family(ip) == AF_INET)
+    if (ip_family(ip) == PGSQL_AF_INET)
+        bits = 32;
+    else
+        bits = 128;
+    if (inet_net_ntop(ip_family(ip), ip_addr(ip), bits,
+              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)
     {
-        /* 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));
-        /* Add /n if not present (which it won't be) */
-        if (strchr(tmp, '/') == NULL)
-        {
-            len = strlen(tmp);
-            snprintf(tmp + len, sizeof(tmp) - len, "/%u", ip_bits(ip));
-        }
+        len = strlen(tmp);
+        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 */
     len = strlen(tmp);
@@ -453,24 +443,18 @@
     text       *ret;
     char       *dst;
     int            len;
-    char        tmp[sizeof("255.255.255.255/32")];
-
-    if (ip_family(ip) == AF_INET)
-    {
-        /* It's an IP V4 address: */
-        if (ip_type(ip))
-            dst = inet_cidr_ntop(AF_INET, &ip_v4addr(ip), ip_bits(ip),
-                                 tmp, sizeof(tmp));
-        else
-            dst = inet_net_ntop(AF_INET, &ip_v4addr(ip), ip_bits(ip),
-                                tmp, sizeof(tmp));
+    char        tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128")];

-        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));
+    if (ip_type(ip))
+        dst = inet_cidr_ntop(ip_family(ip), ip_addr(ip),
+                     ip_bits(ip), tmp, sizeof(tmp));
+    else
+        dst = inet_net_ntop(ip_family(ip), ip_addr(ip),
+                    ip_bits(ip), tmp, sizeof(tmp));
+
+    if (dst == NULL)
+        elog(ERROR, "unable to print address (%s)",
+             strerror(errno));

     /* Return string as a text datum */
     len = strlen(tmp);
@@ -489,40 +473,70 @@
 }

 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
 network_broadcast(PG_FUNCTION_ARGS)
 {
     inet       *ip = PG_GETARG_INET_P(0);
     inet       *dst;
+    int byte;
+    int bits;
+    int maxbits;
+    int maxbytes;
+    unsigned char mask;
+    unsigned char *a, *b;

     dst = (inet *) palloc(VARHDRSZ + sizeof(inet_struct));
     /* make sure any unused bits are zeroed */
     MemSet(dst, 0, VARHDRSZ + sizeof(inet_struct));

-    if (ip_family(ip) == AF_INET)
-    {
-        /* It's an IP V4 address: */
-        unsigned long mask = 0xffffffff;
-
-        /*
-         * Shifting by 32 or more bits does not yield portable results, so
-         * don't try it.
-         */
-        if (ip_bits(ip) < 32)
-            mask >>= ip_bits(ip);
-        else
-            mask = 0;
-
-        ip_v4addr(dst) = htonl(ntohl(ip_v4addr(ip)) | mask);
+    if (ip_family(ip) == PGSQL_AF_INET) {
+        maxbits = 32;
+        maxbytes = 4;
+    } else {
+        maxbits = 128;
+        maxbytes = 16;
+    }
+
+    bits = ip_bits(ip);
+    a = ip_addr(ip);
+    b = ip_addr(dst);
+
+    for (byte = 0 ; byte < maxbytes ; byte++) {
+        if (bits >= 8) {
+            mask = 0x00;
+            bits -= 8;
+        } else if (bits == 0) {
+            mask = 0xff;
+        } else {
+            mask = 0xff >> bits;
+            bits = 0;
+        }
+
+        b[byte] = a[byte] | mask;
     }
-    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_bits(dst) = ip_bits(ip);
     ip_type(dst) = 0;
     VARATT_SIZEP(dst) = VARHDRSZ
-        + ((char *) &ip_v4addr(dst) - (char *) VARDATA(dst))
+        + ((char *)ip_addr(dst) - (char *)VARDATA(dst))
         + ip_addrsize(dst);

     PG_RETURN_INET_P(dst);
@@ -533,36 +547,48 @@
 {
     inet       *ip = PG_GETARG_INET_P(0);
     inet       *dst;
+    int byte;
+    int bits;
+    int maxbits;
+    int maxbytes;
+    unsigned char mask;
+    unsigned char *a, *b;

     dst = (inet *) palloc(VARHDRSZ + sizeof(inet_struct));
     /* make sure any unused bits are zeroed */
     MemSet(dst, 0, VARHDRSZ + sizeof(inet_struct));

-    if (ip_family(ip) == AF_INET)
-    {
-        /* It's an IP V4 address: */
-        unsigned long mask = 0xffffffff;
-
-        /*
-         * 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(ntohl(ip_v4addr(ip)) & mask);
+    if (ip_family(ip) == PGSQL_AF_INET) {
+        maxbits = 32;
+        maxbytes = 4;
+    } else {
+        maxbits = 128;
+        maxbytes = 16;
+    }
+
+    bits = ip_bits(ip);
+    a = ip_addr(ip);
+    b = ip_addr(dst);
+
+    byte = 0;
+    while (bits) {
+        if (bits >= 8) {
+            mask = 0xff;
+            bits -= 8;
+        } else {
+            mask = 0xff << (8 - bits);
+            bits = 0;
+        }
+
+        b[byte] = a[byte] & mask;
+        byte++;
     }
-    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_bits(dst) = ip_bits(ip);
     ip_type(dst) = 1;
     VARATT_SIZEP(dst) = VARHDRSZ
-        + ((char *) &ip_v4addr(dst) - (char *) VARDATA(dst))
+        + ((char *)ip_addr(dst) - (char *)VARDATA(dst))
         + ip_addrsize(dst);

     PG_RETURN_INET_P(dst);
@@ -573,37 +599,47 @@
 {
     inet       *ip = PG_GETARG_INET_P(0);
     inet       *dst;
+    int byte;
+    int bits;
+    int maxbits;
+    int maxbytes;
+    unsigned char mask;
+    unsigned char *b;

     dst = (inet *) palloc(VARHDRSZ + sizeof(inet_struct));
     /* make sure any unused bits are zeroed */
     MemSet(dst, 0, VARHDRSZ + sizeof(inet_struct));

-    if (ip_family(ip) == AF_INET)
-    {
-        /* It's an IP V4 address: */
-        unsigned long mask = 0xffffffff;
-
-        /*
-         * 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);
-
-        ip_bits(dst) = 32;
+    if (ip_family(ip) == PGSQL_AF_INET) {
+        maxbits = 32;
+        maxbytes = 4;
+    } else {
+        maxbits = 128;
+        maxbytes = 16;
+    }
+
+    bits = ip_bits(ip);
+    b = ip_addr(dst);
+
+    byte = 0;
+    while (bits) {
+        if (bits >= 8) {
+            mask = 0xff;
+            bits -= 8;
+        } else {
+            mask = 0xff << (8 - bits);
+            bits = 0;
+        }
+
+        b[byte] = mask;
+        byte++;
     }
-    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_bits(dst) = ip_bits(ip);
     ip_type(dst) = 0;
     VARATT_SIZEP(dst) = VARHDRSZ
-        + ((char *) &ip_v4addr(dst) - (char *) VARDATA(dst))
+        + ((char *)ip_addr(dst) - (char *)VARDATA(dst))
         + ip_addrsize(dst);

     PG_RETURN_INET_P(dst);
@@ -629,12 +665,26 @@
         case CIDROID:
             {
                 inet       *ip = DatumGetInetP(value);
-
-                if (ip_family(ip) == AF_INET)
-                    return (double) ip_v4addr(ip);
+                int len;
+                double res;
+                int i;
+
+                /*
+                 * Note that we don't use the full address
+                 * here.
+                 */
+                if (ip_family(ip) == PGSQL_AF_INET)
+                    len = 4;
                 else
-                    /* Go for an IPV6 address here, before faulting out: */
-                    elog(ERROR, "unknown address family (%d)", ip_family(ip));
+                    len = 5;
+
+                res = ip_family(ip);
+                for (i = 0 ; i < len ; i++) {
+                    res *= 256;
+                    res += ip_addr(ip)[i];
+                }
+                return res;
+
                 break;
             }
         case MACADDROID:
@@ -657,53 +707,78 @@
     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
-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;

-    /*
-     * Shifting by 32 or more bits does not yield portable results, so
-     * don't try it.
-     */
-    if (bits > 0)
-        mask = (0xFFFFFFFFL << (32 - bits)) & 0xFFFFFFFFL;
-    else
-        mask = 0;
-    a1 = ntohl(a1);
-    a2 = ntohl(a2);
-    if ((a1 & mask) < (a2 & mask))
-        return (-1);
-    else if ((a1 & mask) > (a2 & mask))
-        return (1);
+    b = n / 8;
+    x = memcmp(l, r, b);
+    if (x)
+        return (x);
+
+    lb = ((const u_char *)l)[b];
+    rb = ((const u_char *)r)[b];
+    for (b = n % 8; b > 0; b--) {
+        if ((lb & 0x80) != (rb & 0x80)) {
+            if (lb & 0x80)
+                return (1);
+            return (-1);
+        }
+        lb <<= 1;
+        rb <<= 1;
+    }
     return (0);
 }

-/*
- * Returns true if given address fits fully within the specified bit width.
- */
 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;
+    }
+    assert(bits <= maxbits);
+
+    if (bits == maxbits)
+        return 1;
+
+    byte = (bits + 7) / 8;
+    nbits = bits % 8;
+    mask = 0xff;
+    if (bits != 0)
+        mask >>= nbits;
+
+    while (byte < maxbytes) {
+        if ((a[byte] & mask) != 0)
+            return 0;
+        mask = 0xff;
+        byte++;
+    }

-    /*
-     * Shifting by 32 or more bits does not yield portable results, so
-     * don't try it.
-     */
-    if (bits > 0)
-        mask = (0xFFFFFFFFL << (32 - bits)) & 0xFFFFFFFFL;
-    else
-        mask = 0;
-    a1 = ntohl(a1);
-    if ((a1 & mask) == a1)
-        return true;
-    return false;
+    return 1;
 }


diff -ur ORIG/postgresql-7.3.2/src/include/catalog/pg_proc.h postgresql-7.3.2/src/include/catalog/pg_proc.h
--- ORIG/postgresql-7.3.2/src/include/catalog/pg_proc.h    2002-11-02 10:41:22.000000000 -0800
+++ postgresql-7.3.2/src/include/catalog/pg_proc.h    2003-04-03 16:16:10.000000000 -0800
@@ -2353,6 +2353,8 @@
 DESCR("show address octets only");
 DATA(insert OID = 730 (  text                PGNSP PGUID 12 f f t f i 1 25 "869"  network_show - _null_ ));
 DESCR("show all parts of inet/cidr value");
+DATA(insert OID = 731 (  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 = 1713 (  inet                PGNSP PGUID 12 f f t f i 1 869 "25"  text_inet - _null_ ));
 DESCR("text to inet");
 DATA(insert OID = 1714 (  cidr                PGNSP PGUID 12 f f t f i 1 650 "25"  text_cidr - _null_ ));
diff -ur ORIG/postgresql-7.3.2/src/include/utils/builtins.h postgresql-7.3.2/src/include/utils/builtins.h
--- ORIG/postgresql-7.3.2/src/include/utils/builtins.h    2002-11-02 10:41:22.000000000 -0800
+++ postgresql-7.3.2/src/include/utils/builtins.h    2003-04-03 16:05:43.000000000 -0800
@@ -566,6 +566,7 @@
 extern Datum network_network(PG_FUNCTION_ARGS);
 extern Datum network_netmask(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_host(PG_FUNCTION_ARGS);
 extern Datum network_show(PG_FUNCTION_ARGS);
diff -ur ORIG/postgresql-7.3.2/src/include/utils/inet.h postgresql-7.3.2/src/include/utils/inet.h
--- ORIG/postgresql-7.3.2/src/include/utils/inet.h    2002-06-20 13:29:53.000000000 -0700
+++ postgresql-7.3.2/src/include/utils/inet.h    2003-04-04 13:07:21.000000000 -0800
@@ -23,14 +23,20 @@
     unsigned char family;
     unsigned char bits;
     unsigned char type;
-    union
-    {
-        unsigned int ipv4_addr; /* network byte order */
-        /* add IPV6 address type here */
-    }            addr;
+    unsigned char ip_addr[16]; /* 128 bits of address */
 } 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
  * objects, ie, there is a varlena header (basically a length word) in front
  * of the struct type depicted above.

Re: IPv6 address parsing for inet/cidr types (take II)

From
Tom Lane
Date:
Michael Graff <explorer@flame.org> writes:
> The first posting was held for moderator approval, which never
> happened, so I'll try this again after first subscribing.

As I recall, Paul Vixie's first attempt at this was rejected because it
undid a lot of painfully-arrived-at decisions about the I/O behavior of
these datatypes.  You need to tell us exactly what you did about those
issues.  (No, I don't have time to read the code to find out...)

You also need to supply some documentation updates --- code updates
alone are incomplete.  I'd not have had to ask the question above if
this patch included proper documentation.  The "Network Address Data
Types" and "Network Address Type Functions" pages both need to be fixed.

Some additions to the inet regression test would seem in order, too.

            regards, tom lane


Re: IPv6 address parsing for inet/cidr types (take II)

From
Peter Eisentraut
Date:
Michael Graff writes:

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

I believe it was decided that ipv6 should be a separate data type (or
two).

--
Peter Eisentraut   peter_e@gmx.net


Re: IPv6 address parsing for inet/cidr types (take II)

From
Michael Graff
Date:
Tom Lane <tgl@sss.pgh.pa.us> writes:

> As I recall, Paul Vixie's first attempt at this was rejected because it
> undid a lot of painfully-arrived-at decisions about the I/O behavior of
> these datatypes.  You need to tell us exactly what you did about those
> issues.  (No, I don't have time to read the code to find out...)

It shouldn't change the behavior much at all -- all I do is
return a different data length for ipv6 addresses (16 bytes rather
than 4) but the inet and cidr behavior remain unchanged, as well as
the on-disk storage model for ipv4 addresses.  ipv6 uses a different
family type.

> You also need to supply some documentation updates --- code updates
> alone are incomplete.  I'd not have had to ask the question above if
> this patch included proper documentation.  The "Network Address Data
> Types" and "Network Address Type Functions" pages both need to be fixed.
>
> Some additions to the inet regression test would seem in order, too.

I'd gladly do those, but wanted feedback on the work in progress
before I did them.  Paul asked me to mail out what I had asap, and
I've not made the documentation or regression test changes yet
(although I have the regression tests for my own use, of course.)

One other poster suggested they should be two data types, which I half
agree with.  There are advantages of being able to use IPv4 or IPv6
addresses in the same column, so I wouldn't have to have two tables
for host <-> address mappings, for instance.  I'm undecided on which
is better, but so far I've used the inet with ipv4 and 6 data type
once and found them useful under one data type.

--Michael

Re: IPv6 address parsing for inet/cidr types (take II)

From
Peter Eisentraut
Date:
Michael Graff writes:

> One other poster suggested they should be two data types, which I half
> agree with.  There are advantages of being able to use IPv4 or IPv6
> addresses in the same column, so I wouldn't have to have two tables
> for host <-> address mappings, for instance.  I'm undecided on which
> is better, but so far I've used the inet with ipv4 and 6 data type
> once and found them useful under one data type.

Perhaps we can make "inet" take both and then define domains "inet4" and
"inet6" over it that only take one kind.

--
Peter Eisentraut   peter_e@gmx.net


Re: IPv6 address parsing for inet/cidr types (take II)

From
Tom Lane
Date:
Peter Eisentraut <peter_e@gmx.net> writes:
> Michael Graff writes:
>> One other poster suggested they should be two data types, which I half
>> agree with.  There are advantages of being able to use IPv4 or IPv6
>> addresses in the same column, so I wouldn't have to have two tables
>> for host <-> address mappings, for instance.  I'm undecided on which
>> is better, but so far I've used the inet with ipv4 and 6 data type
>> once and found them useful under one data type.

> Perhaps we can make "inet" take both and then define domains "inet4" and
> "inet6" over it that only take one kind.

I had originally felt strongly that there should be only one datatype
... but IIRC the thread Peter referred to convinced me that they should
indeed be two types, or at least that there's a darn good argument for
that viewpoint.  Michael, have you reviewed the archives?  I'd be
interested to hear your take on that discussion.

Single basic datatype plus two domains seems like a reasonable approach
if we feel that both viewpoints have merit.  But I wonder whether they
both do ...

            regards, tom lane


Re: IPv6 address parsing for inet/cidr types (take II)

From
Bruce Momjian
Date:
You guys can make any decision you like --- I am just glad we are
getting this done, and with IPv6 connections in 7.4, not having an IPv6
data type would have been an embarrassment.

---------------------------------------------------------------------------

Tom Lane wrote:
> Peter Eisentraut <peter_e@gmx.net> writes:
> > Michael Graff writes:
> >> One other poster suggested they should be two data types, which I half
> >> agree with.  There are advantages of being able to use IPv4 or IPv6
> >> addresses in the same column, so I wouldn't have to have two tables
> >> for host <-> address mappings, for instance.  I'm undecided on which
> >> is better, but so far I've used the inet with ipv4 and 6 data type
> >> once and found them useful under one data type.
>
> > Perhaps we can make "inet" take both and then define domains "inet4" and
> > "inet6" over it that only take one kind.
>
> I had originally felt strongly that there should be only one datatype
> ... but IIRC the thread Peter referred to convinced me that they should
> indeed be two types, or at least that there's a darn good argument for
> that viewpoint.  Michael, have you reviewed the archives?  I'd be
> interested to hear your take on that discussion.
>
> Single basic datatype plus two domains seems like a reasonable approach
> if we feel that both viewpoints have merit.  But I wonder whether they
> both do ...
>
>             regards, tom lane
>
>
> ---------------------------(end of broadcast)---------------------------
> TIP 6: Have you searched our list archives?
>
> http://archives.postgresql.org
>

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073


Re: IPv6 address parsing for inet/cidr types (take II)

From
Michael Graff
Date:
Tom Lane <tgl@sss.pgh.pa.us> writes:

> I had originally felt strongly that there should be only one datatype
> ... but IIRC the thread Peter referred to convinced me that they should
> indeed be two types, or at least that there's a darn good argument for
> that viewpoint.  Michael, have you reviewed the archives?  I'd be
> interested to hear your take on that discussion.
>
> Single basic datatype plus two domains seems like a reasonable approach
> if we feel that both viewpoints have merit.  But I wonder whether they
> both do ...

Arguments for a single data type:

        An address is an address, and it seems to be a waste to have
        a column for IPv4 and one for IPv6, when the latter or the
        former will nearly always be empty.

        Parsing, text output, masklen(), show(), abbrev(), network(),
        and host() all work identically on IPv4 or IPv6 addresses, and
        return the proper data.

Arguments against a single data type:

        lt(), gt(), cmp(), etc. all work fine when comparing the same
        family, but get a little odd (that is, fail) when comparing
        different families.

        broadcast() really doesn't mean much on an IPv6 address.

The way I'm using the data types right now I tend to prefer one column
that can hold either type, so I don't have to worry about which is
which.  I can use family() to make certain the families are compatible
when comparing them.

I've reviewed the archives on the topic, and tend to believe one
data type still makes sense.  If the IPv6 support won't be accepted
unless changes are made, I will have to learn more about pgsql.  The
last time I hacked on it was when it had the unfortunate name of
Postgres, about half a year before it became Postgres95 or something
like that.  :)

Here's the latest patches, which now include documentation changes
(although they are likely rough) and regression tests (which caught a
bug or two, imagine that!)


Attachment