diff --git a/contrib/ip_and_mac/Makefile b/contrib/ip_and_mac/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..924a20029e41660775634477b85e1224c26fa759
--- /dev/null
+++ b/contrib/ip_and_mac/Makefile
@@ -0,0 +1,20 @@
+#	PostgreSQL type definitions for IP and MAC addresses.
+
+all: ip.so mac.so
+
+ip.so: ip.o
+	ld -Bshareable -o ip.so ip.o
+
+ip.o: ip.c
+	cc -g -O -fPIC -I/usr/local/pgsql/include -c ip.c
+
+mac.so: mac.o
+	ld -Bshareable -o mac.so mac.o
+
+mac.o: mac.c mac.h
+	cc -g -O -fPIC -I/usr/local/pgsql/include -c mac.c
+
+install: ip.so mac.so
+	install -c ip.so mac.so /usr/local/pgsql/modules
+
+#	eof
diff --git a/contrib/ip_and_mac/README b/contrib/ip_and_mac/README
new file mode 100644
index 0000000000000000000000000000000000000000..a2da4fc46d525a59775f1ed55ba9eaa60b720571
--- /dev/null
+++ b/contrib/ip_and_mac/README
@@ -0,0 +1,51 @@
+PostgreSQL type extensions for IP and MAC addresses.
+---------------------------------------------------
+
+I needed to record IP and MAC level ethernet addresses in a data
+base, and I really didn't want to store them as plain strings, with
+no enforced error checking, so I put together the accompanying code
+as my first experiment with adding a data type to PostgreSQL.  I
+then thought that this might be useful to others, both directly and
+as a very simple example of how to do this sort of thing, so here
+it is, in the hope that it will be useful.
+
+IP addresses are implemented as an 8 byte struct (this may well be
+more than is useful, but I figured that since it has to be at least 5,
+it might as well round well) that contains the four bytes of address
+and a mask width.  Thus, a node address looks like '158.37.96.15/32'
+(or just '158.37.96.15', which is understood to mean the same thing).
+This address happens to be part of a subnet where I work;
+'158.37.96.0/24', which itself is a part of the larger subnet
+allocated to our institution, which is '158.37.96.0/21', which again,
+if you go by the book, is part of the class "B" net '158.37.0.0/16'.
+
+Input and output functions are supplied, along with the "normal" <,
+<=, =, >=, > and <> operators, which all do what you expect, and the
+similarity operator ~~, which checks whether two given addresses are
+either the same, or, failing that, whether one is a subnet
+specification and the other an address (or a smaller subnet) within
+that.  Good for picking out records with addresses in a given subnet:
+note that '158.37.96.0/21' spans '158.37.96.0' to '158.37.103.255',
+which is not all that easily handled in its external representation.
+
+MAC level ethernet addresses are also implemented as an 8 byte struct
+(I wish I knew what alignment needs are actually present -- I'm just
+not taking any chances here) that contains the address as unsigned
+chars.  Several input forms are accepted: the following are all the
+same address: '08002b:010203', '08002b-010203', '0800.2b01.0203',
+'08-00-2b-01-02-03' and '08:00:2b:01:02:03'.  Upper and lower case is
+accepted for the digits 'a' through 'f'.  Output is always in the
+latter of the given forms.
+
+Input and output functions are supplied, along with the = and <>
+operators, which do what you expect, and the similarity operator ~~,
+which checks whether two given addresses belong to hardware from the
+same manufacturer (first three bytes the same, that is).  As an extra
+feature, a function macaddr_manuf() is defined, which returns the name
+of the manufacturer as a string.
+
+To install: fix the path names in the SQL files and the Makefile if
+you need to, then make, make install, slurp the SQL files into psql or
+whatever, and you're off.  Enjoy!
+
+Bergen, Norway, 1998-01-11, Tom Ivar Helbekkmo (tih@Hamartun.Priv.NO).
diff --git a/contrib/ip_and_mac/ip.c b/contrib/ip_and_mac/ip.c
new file mode 100644
index 0000000000000000000000000000000000000000..de967aa8eda722c4e5fd2079aa103a0da327952a
--- /dev/null
+++ b/contrib/ip_and_mac/ip.c
@@ -0,0 +1,212 @@
+/*
+ *	PostgreSQL type definitions for IP addresses.
+ */
+
+#include <stdio.h>
+
+#include <postgres.h>
+#include <utils/palloc.h>
+
+/*
+ *	This is the internal storage format for IP addresses:
+ */
+
+typedef struct ipaddr {
+  unsigned char a;
+  unsigned char b;
+  unsigned char c;
+  unsigned char d;
+  unsigned char w;
+  unsigned char pad1;
+  short pad2;
+} ipaddr;
+
+/*
+ *	Various forward declarations:
+ */
+
+ipaddr *ipaddr_in(char *str);
+char *ipaddr_out(ipaddr *addr);
+
+bool ipaddr_lt(ipaddr *a1, ipaddr *a2);
+bool ipaddr_le(ipaddr *a1, ipaddr *a2);
+bool ipaddr_eq(ipaddr *a1, ipaddr *a2);
+bool ipaddr_ge(ipaddr *a1, ipaddr *a2);
+bool ipaddr_gt(ipaddr *a1, ipaddr *a2);
+
+bool ipaddr_ne(ipaddr *a1, ipaddr *a2);
+int4 ipaddr_cmp(ipaddr *a1, ipaddr *a2);
+bool ipaddr_like(ipaddr *a1, ipaddr *a2);
+
+/*
+ *	A utility macro used for sorting addresses numerically:
+ */
+
+#define Mag(addr) \
+  ((unsigned long)((addr->a<<24)|(addr->b<<16)|(addr->c<<8)|(addr->d)))
+
+/*
+ *	IP address reader.  Note how the count returned by sscanf()
+ *	is used to determine whether the mask size was specified.
+ */
+
+ipaddr *ipaddr_in(char *str) {
+  int a, b, c, d, w;
+  ipaddr *result;
+  int count;
+
+  if (strlen(str) > 0) {
+
+    count = sscanf(str, "%d.%d.%d.%d/%d", &a, &b, &c, &d, &w);
+
+    if (count < 4) {
+      elog(ERROR, "ipaddr_in: error in parsing \"%s\"", str);
+      return(NULL);
+    }
+
+    if (count == 4)
+      w = 32;
+
+    if ((a < 0) || (a > 255) || (b < 0) || (b > 255) ||
+	(c < 0) || (c > 255) || (d < 0) || (d > 255) ||
+	(w < 0) || (w > 32)) {
+      elog(ERROR, "ipaddr_in: illegal address \"%s\"", str);
+      return(NULL);
+    }
+  } else {
+    a = b = c = d = w = 0;	/* special case for missing address */
+  }
+
+  result = (ipaddr *)palloc(sizeof(ipaddr));
+
+  result->a = a;
+  result->b = b;
+  result->c = c;
+  result->d = d;
+  result->w = w;
+
+  return(result);
+}
+
+/*
+ *	IP address output function.  Note mask size specification
+ *	generated only for subnets, not for plain host addresses.
+ */
+
+char *ipaddr_out(ipaddr *addr) {
+  char *result;
+
+  if (addr == NULL)
+    return(NULL);
+
+  result = (char *)palloc(32);
+
+  if (Mag(addr) > 0) {
+    if (addr->w == 32)
+      sprintf(result, "%d.%d.%d.%d",
+	      addr->a, addr->b, addr->c, addr->d);
+    else
+      sprintf(result, "%d.%d.%d.%d/%d",
+	      addr->a, addr->b, addr->c, addr->d, addr->w);
+  } else {
+    result[0] = 0;		/* special case for missing address */
+  }
+  return(result);
+}
+
+/*
+ *	Boolean tests.  The Mag() macro was defined above.
+ */
+
+bool ipaddr_lt(ipaddr *a1, ipaddr *a2) {
+  unsigned long a1mag, a2mag;
+  a1mag = Mag(a1);
+  a2mag = Mag(a2);
+  return (a1mag < a2mag);
+};
+
+bool ipaddr_le(ipaddr *a1, ipaddr *a2) {
+  unsigned long a1mag, a2mag;
+  a1mag = Mag(a1);
+  a2mag = Mag(a2);
+  return (a1mag <= a2mag);
+};
+
+bool ipaddr_eq(ipaddr *a1, ipaddr *a2) {
+  unsigned long a1mag, a2mag;
+  a1mag = Mag(a1);
+  a2mag = Mag(a2);
+  return ((a1mag == a2mag) && (a1->w == a2->w));
+};
+
+bool ipaddr_ge(ipaddr *a1, ipaddr *a2) {
+  unsigned long a1mag, a2mag;
+  a1mag = Mag(a1);
+  a2mag = Mag(a2);
+  return (a1mag >= a2mag);
+};
+
+bool ipaddr_gt(ipaddr *a1, ipaddr *a2) {
+  unsigned long a1mag, a2mag;
+  a1mag = Mag(a1);
+  a2mag = Mag(a2);
+  return (a1mag > a2mag);
+};
+
+bool ipaddr_ne(ipaddr *a1, ipaddr *a2) {
+  unsigned long a1mag, a2mag;
+  a1mag = Mag(a1);
+  a2mag = Mag(a2);
+  return ((a1mag != a2mag) || (a1->w != a2->w));
+};
+
+/*
+ *	Comparison function for sorting:
+ */
+
+int4 ipaddr_cmp(ipaddr *a1, ipaddr *a2) {
+  unsigned long a1mag = Mag(a1), a2mag = Mag(a2);
+  if (a1mag < a2mag)
+    return -1;
+  else if (a1mag > a2mag)
+    return 1;
+  else
+    return 0;
+}
+
+/*
+ *	Our "similarity" operator checks whether two addresses are
+ *	either the same node address, or, failing that, whether one
+ *	of them contains the other.  This will be true if they have
+ *	the same high bits down as far as the shortest mask reaches.
+ */
+
+unsigned long build_mask(unsigned char bits) {
+  unsigned long mask = 0;
+  int i;
+  for (i = 0; i < bits; i++)
+    mask = (mask >> 1) | 0x80000000;
+  return mask;
+}
+
+bool ipaddr_like(ipaddr *a1, ipaddr *a2) {
+  unsigned long a1bits, a2bits, maskbits;
+  if ((a1->w == 0) || (a2->w == 0))
+    return FALSE;
+  if ((a1->w == 32) && (a2->w == 32))
+    return ipaddr_eq(a1, a2);
+  a1bits = Mag(a1);
+  a2bits = Mag(a2);
+  if (a1->w > a2->w) {
+    maskbits = build_mask(a2->w);
+    return ((a1bits & maskbits) == (a2bits & maskbits));
+  } else {
+    maskbits = build_mask(a1->w);
+    return ((a2bits & maskbits) == (a1bits & maskbits));
+  }
+  return FALSE;
+}
+
+/*
+ *	eof
+ */
diff --git a/contrib/ip_and_mac/ip.sql b/contrib/ip_and_mac/ip.sql
new file mode 100644
index 0000000000000000000000000000000000000000..bcfe1a24d90ff8a7f7a800f77854180beb0930d3
--- /dev/null
+++ b/contrib/ip_and_mac/ip.sql
@@ -0,0 +1,131 @@
+--
+--	PostgreSQL code for IP addresses.
+--
+
+load '/usr/local/pgsql/modules/ip.so';
+
+--
+--	Input and output functions and the type itself:
+--
+
+create function ipaddr_in(opaque)
+	returns opaque
+	as '/usr/local/pgsql/modules/ip.so'
+	language 'c';
+
+create function ipaddr_out(opaque)
+	returns opaque
+	as '/usr/local/pgsql/modules/ip.so'
+	language 'c';
+
+create type ipaddr (
+	internallength = 8,
+	externallength = variable,
+	input = ipaddr_in,
+	output = ipaddr_out
+);
+
+--
+--	The various boolean tests:
+--
+
+create function ipaddr_lt(ipaddr, ipaddr)
+	returns bool
+	as '/usr/local/pgsql/modules/ip.so'
+	language 'c';
+
+create function ipaddr_le(ipaddr, ipaddr)
+	returns bool
+	as '/usr/local/pgsql/modules/ip.so'
+	language 'c';
+
+create function ipaddr_eq(ipaddr, ipaddr)
+	returns bool
+	as '/usr/local/pgsql/modules/ip.so'
+	language 'c';
+
+create function ipaddr_ge(ipaddr, ipaddr)
+	returns bool
+	as '/usr/local/pgsql/modules/ip.so'
+	language 'c';
+
+create function ipaddr_gt(ipaddr, ipaddr)
+	returns bool
+	as '/usr/local/pgsql/modules/ip.so'
+	language 'c';
+
+create function ipaddr_ne(ipaddr, ipaddr)
+	returns bool
+	as '/usr/local/pgsql/modules/ip.so'
+	language 'c';
+
+create function ipaddr_like(ipaddr, ipaddr)
+	returns bool
+	as '/usr/local/pgsql/modules/ip.so'
+	language 'c';
+
+--
+--	Now the operators.  Note how some of the parameters to some
+--	of the 'create operator' commands are commented out.  This
+--	is because they reference as yet undefined operators, and
+--	will be implicitly defined when those are, further down.
+--
+
+create operator <= (
+	leftarg = ipaddr,
+	rightarg = ipaddr,
+--	commutator = >,
+--	negator = >,
+	procedure = ipaddr_le
+);
+
+create operator < (
+	leftarg = ipaddr,
+	rightarg = ipaddr,
+--	commutator = >=,
+--	negator = >=,
+	procedure = ipaddr_lt
+);
+
+create operator = (
+	leftarg = ipaddr,
+	rightarg = ipaddr,
+	commutator = =,
+--	negator = <>,
+	procedure = ipaddr_eq
+);
+
+create operator >= (
+	leftarg = ipaddr,
+	rightarg = ipaddr,
+	commutator = <,
+	negator = <,
+	procedure = ipaddr_ge
+);
+
+create operator > (
+	leftarg = ipaddr,
+	rightarg = ipaddr,
+	commutator = <=,
+	negator = <=,
+	procedure = ipaddr_gt
+);
+
+create operator <> (
+	leftarg = ipaddr,
+	rightarg = ipaddr,
+	commutator = <>,
+	negator = =,
+	procedure = ipaddr_ne
+);
+
+create operator ~~ (
+	leftarg = ipaddr,
+	rightarg = ipaddr,
+	commutator = ~~,
+	procedure = ipaddr_like
+);
+
+--
+--	eof
+--
diff --git a/contrib/ip_and_mac/mac.c b/contrib/ip_and_mac/mac.c
new file mode 100644
index 0000000000000000000000000000000000000000..3317eab24eb0453b8b254abe0164bc39f8b0b995
--- /dev/null
+++ b/contrib/ip_and_mac/mac.c
@@ -0,0 +1,202 @@
+/*
+ *	PostgreSQL type definitions for MAC addresses.
+ */
+
+#include <stdio.h>
+
+#include <postgres.h>
+#include <utils/palloc.h>
+
+#include "mac.h"
+
+/*
+ *	This is the internal storage format for MAC addresses:
+ */
+
+typedef struct macaddr {
+  unsigned char a;
+  unsigned char b;
+  unsigned char c;
+  unsigned char d;
+  unsigned char e;
+  unsigned char f;
+  short pad;
+} macaddr;
+
+/*
+ *	Various forward declarations:
+ */
+
+macaddr *macaddr_in(char *str);
+char *macaddr_out(macaddr *addr);
+
+bool macaddr_eq(macaddr *a1, macaddr *a2);
+bool macaddr_ne(macaddr *a1, macaddr *a2);
+
+int4 macaddr_cmp(macaddr *a1, macaddr *a2);
+bool macaddr_like(macaddr *a1, macaddr *a2);
+text *macaddr_manuf(macaddr *addr);
+
+/*
+ *	Utility macros used for sorting and comparing:
+ */
+
+#define MagM(addr) \
+  ((unsigned long)((addr->a<<16)|(addr->b<<8)|(addr->c)))
+
+#define MagH(addr) \
+  ((unsigned long)((addr->c<<16)|(addr->e<<8)|(addr->f)))
+
+/*
+ *	MAC address reader.  Accepts several common notations.
+ */
+
+macaddr *macaddr_in(char *str) {
+  int a, b, c, d, e, f;
+  macaddr *result;
+  int count;
+
+  if (strlen(str) > 0) {
+
+    count = sscanf(str, "%x:%x:%x:%x:%x:%x", &a, &b, &c, &d, &e, &f);
+    if (count != 6)
+      count = sscanf(str, "%x-%x-%x-%x-%x-%x", &a, &b, &c, &d, &e, &f);
+    if (count != 6)
+      count = sscanf(str, "%2x%2x%2x:%2x%2x%2x", &a, &b, &c, &d, &e, &f);
+    if (count != 6)
+      count = sscanf(str, "%2x%2x%2x-%2x%2x%2x", &a, &b, &c, &d, &e, &f);
+    if (count != 6)
+      count = sscanf(str, "%2x%2x.%2x%2x.%2x%2x", &a, &b, &c, &d, &e, &f);
+    
+    if (count != 6) {
+      elog(ERROR, "macaddr_in: error in parsing \"%s\"", str);
+      return(NULL);
+    }
+    
+    if ((a < 0) || (a > 255) || (b < 0) || (b > 255) ||
+	(c < 0) || (c > 255) || (d < 0) || (d > 255) ||
+	(e < 0) || (e > 255) || (f < 0) || (f > 255)) {
+      elog(ERROR, "macaddr_in: illegal address \"%s\"", str);
+      return(NULL);
+    }
+  } else {
+    a = b = c = d = e = f = 0;	/* special case for missing address */
+  }
+
+  result = (macaddr *)palloc(sizeof(macaddr));
+
+  result->a = a;
+  result->b = b;
+  result->c = c;
+  result->d = d;
+  result->e = e;
+  result->f = f;
+
+  return(result);
+}
+
+/*
+ *	MAC address output function.  Fixed format.
+ */
+
+char *macaddr_out(macaddr *addr) {
+  char *result;
+
+  if (addr == NULL)
+    return(NULL);
+
+  result = (char *)palloc(32);
+
+  if ((MagM(addr) > 0) || (MagH(addr) > 0)) {
+    sprintf(result, "%02x:%02x:%02x:%02x:%02x:%02x",
+	    addr->a, addr->b, addr->c, addr->d, addr->e, addr->f);
+  } else {
+    result[0] = 0;		/* special case for missing address */
+  }
+  return(result);
+}
+
+/*
+ *	Boolean tests.
+ */
+
+bool macaddr_eq(macaddr *a1, macaddr *a2) {
+  return((a1->a == a2->a) && (a1->b == a2->b) &&
+	 (a1->c == a2->c) && (a1->d == a2->d) &&
+	 (a1->e == a2->e) && (a1->f == a2->f));
+};
+
+bool macaddr_ne(macaddr *a1, macaddr *a2) {
+  return((a1->a != a2->a) || (a1->b != a2->b) ||
+	 (a1->c != a2->c) || (a1->d != a2->d) ||
+	 (a1->e != a2->e) || (a1->f != a2->f));
+};
+
+/*
+ *	Comparison function for sorting:
+ */
+
+int4 macaddr_cmp(macaddr *a1, macaddr *a2) {
+  unsigned long a1magm, a1magh, a2magm, a2magh;
+  a1magm = MagM(a1);
+  a1magh = MagH(a1);
+  a2magm = MagM(a2);
+  a2magh = MagH(a2);
+  if (a1magm < a2magm)
+    return -1;
+  else if (a1magm > a2magm)
+    return 1;
+  else if (a1magh < a2magh)
+    return -1;
+  else if (a1magh > a2magh)
+    return 1;
+  else
+    return 0;
+}
+
+/*
+ *	Similarity means having the same manufacurer, which means
+ *	having the same first three bytes of address:
+ */
+
+bool macaddr_like(macaddr *a1, macaddr *a2) {
+  unsigned long a1magm, a2magm;
+  a1magm = MagM(a1);
+  a2magm = MagM(a2);
+  if ((a1magm == 0) || (a2magm == 0))
+    return FALSE;
+  return (a1magm == a2magm);
+}
+
+/*
+ *	The special manufacturer fetching function.  See "mac.h".
+ */
+
+text *macaddr_manuf(macaddr *addr) {
+  manufacturer *manuf;
+  int length;
+  text *result;
+
+  for (manuf = manufacturers; manuf->name != NULL; manuf++) {
+    if ((manuf->a == addr->a) &&
+	(manuf->b == addr->b) &&
+	(manuf->c == addr->c))
+      break;
+  }
+  if (manuf->name == NULL) {
+    result = palloc(VARHDRSZ + 1);
+    memset(result, 0, VARHDRSZ + 1);
+    VARSIZE(result) = VARHDRSZ + 1;
+  } else {
+    length = strlen(manuf->name) + 1;
+    result = palloc(length + VARHDRSZ);
+    memset(result, 0, length + VARHDRSZ);
+    VARSIZE(result) = length + VARHDRSZ;
+    memcpy(VARDATA(result), manuf->name, length);
+  }
+  return result;
+}
+
+/*
+ *	eof
+ */
diff --git a/contrib/ip_and_mac/mac.h b/contrib/ip_and_mac/mac.h
new file mode 100644
index 0000000000000000000000000000000000000000..39f12d83bb3d90e3f43912475368c4534cc27c99
--- /dev/null
+++ b/contrib/ip_and_mac/mac.h
@@ -0,0 +1,130 @@
+/*
+ *	PostgreSQL type definitions for MAC addresses.
+ */
+
+typedef struct manufacturer {
+  unsigned char a;
+  unsigned char b;
+  unsigned char c;
+  char *name;
+} manufacturer;
+
+manufacturer manufacturers[] = {
+  {0x00, 0x00, 0x0C, "Cisco"},
+  {0x00, 0x00, 0x0E, "Fujitsu"},
+  {0x00, 0x00, 0x0F, "NeXT"},
+  {0x00, 0x00, 0x10, "Sytek"},
+  {0x00, 0x00, 0x1D, "Cabletron"},
+  {0x00, 0x00, 0x20, "DIAB"},
+  {0x00, 0x00, 0x22, "Visual Technology"},
+  {0x00, 0x00, 0x2A, "TRW"},
+  {0x00, 0x00, 0x32, "GPT Limited"},
+  {0x00, 0x00, 0x5A, "S & Koch"},
+  {0x00, 0x00, 0x5E, "IANA"},
+  {0x00, 0x00, 0x65, "Network General"},
+  {0x00, 0x00, 0x6B, "MIPS"},
+  {0x00, 0x00, 0x77, "MIPS"},
+  {0x00, 0x00, 0x7A, "Ardent"},
+  {0x00, 0x00, 0x89, "Cayman Systems"},
+  {0x00, 0x00, 0x93, "Proteon"},
+  {0x00, 0x00, 0x9F, "Ameristar Technology"},
+  {0x00, 0x00, 0xA2, "Wellfleet"},
+  {0x00, 0x00, 0xA3, "Network Application Technology"},
+  {0x00, 0x00, 0xA6, "Network General"},
+  {0x00, 0x00, 0xA7, "NCD"},
+  {0x00, 0x00, 0xA9, "Network Systems"},
+  {0x00, 0x00, 0xAA, "Xerox"},
+  {0x00, 0x00, 0xB3, "CIMLinc"},
+  {0x00, 0x00, 0xB7, "Dove Fastnet"},
+  {0x00, 0x00, 0xBC, "Allen-Bradley"},
+  {0x00, 0x00, 0xC0, "Western Digital"},
+  {0x00, 0x00, 0xC5, "Farallon"},
+  {0x00, 0x00, 0xC6, "Hewlett-Packard"},
+  {0x00, 0x00, 0xC8, "Altos"},
+  {0x00, 0x00, 0xC9, "Emulex"},
+  {0x00, 0x00, 0xD7, "Dartmouth College"},
+  {0x00, 0x00, 0xD8, "3Com (?)"},
+  {0x00, 0x00, 0xDD, "Gould"},
+  {0x00, 0x00, 0xDE, "Unigraph"},
+  {0x00, 0x00, 0xE2, "Acer Counterpoint"},
+  {0x00, 0x00, 0xEF, "Alantec"},
+  {0x00, 0x00, 0xFD, "High Level Hardware"},
+  {0x00, 0x01, 0x02, "BBN internal usage"},
+  {0x00, 0x20, 0xAF, "3Com"},
+  {0x00, 0x17, 0x00, "Kabel"},
+  {0x00, 0x80, 0x64, "Wyse Technology"},
+  {0x00, 0x80, 0x2B, "IMAC (?)"},
+  {0x00, 0x80, 0x2D, "Xylogics, Inc."},
+  {0x00, 0x80, 0x8C, "Frontier Software Development"},
+  {0x00, 0x80, 0xC2, "IEEE 802.1 Committee"},
+  {0x00, 0x80, 0xD3, "Shiva"},
+  {0x00, 0xAA, 0x00, "Intel"},
+  {0x00, 0xDD, 0x00, "Ungermann-Bass"},
+  {0x00, 0xDD, 0x01, "Ungermann-Bass"},
+  {0x02, 0x07, 0x01, "Racal InterLan"},
+  {0x02, 0x04, 0x06, "BBN internal usage"},
+  {0x02, 0x60, 0x86, "Satelcom MegaPac"},
+  {0x02, 0x60, 0x8C, "3Com"},
+  {0x02, 0xCF, 0x1F, "CMC"},
+  {0x08, 0x00, 0x02, "3Com"},
+  {0x08, 0x00, 0x03, "ACC"},
+  {0x08, 0x00, 0x05, "Symbolics"},
+  {0x08, 0x00, 0x08, "BBN"},
+  {0x08, 0x00, 0x09, "Hewlett-Packard"},
+  {0x08, 0x00, 0x0A, "Nestar Systems"},
+  {0x08, 0x00, 0x0B, "Unisys"},
+  {0x08, 0x00, 0x11, "Tektronix"},
+  {0x08, 0x00, 0x14, "Excelan"},
+  {0x08, 0x00, 0x17, "NSC"},
+  {0x08, 0x00, 0x1A, "Data General"},
+  {0x08, 0x00, 0x1B, "Data General"},
+  {0x08, 0x00, 0x1E, "Apollo"},
+  {0x08, 0x00, 0x20, "Sun"},
+  {0x08, 0x00, 0x22, "NBI"},
+  {0x08, 0x00, 0x25, "CDC"},
+  {0x08, 0x00, 0x26, "Norsk Data"},
+  {0x08, 0x00, 0x27, "PCS Computer Systems GmbH"},
+  {0x08, 0x00, 0x28, "Texas Instruments"},
+  {0x08, 0x00, 0x2B, "DEC"},
+  {0x08, 0x00, 0x2E, "Metaphor"},
+  {0x08, 0x00, 0x2F, "Prime Computer"},
+  {0x08, 0x00, 0x36, "Intergraph"},
+  {0x08, 0x00, 0x37, "Fujitsu-Xerox"},
+  {0x08, 0x00, 0x38, "Bull"},
+  {0x08, 0x00, 0x39, "Spider Systems"},
+  {0x08, 0x00, 0x41, "DCA Digital Comm. Assoc."},
+  {0x08, 0x00, 0x45, "Xylogics (?)"},
+  {0x08, 0x00, 0x46, "Sony"},
+  {0x08, 0x00, 0x47, "Sequent"},
+  {0x08, 0x00, 0x49, "Univation"},
+  {0x08, 0x00, 0x4C, "Encore"},
+  {0x08, 0x00, 0x4E, "BICC"},
+  {0x08, 0x00, 0x56, "Stanford University"},
+  {0x08, 0x00, 0x58, "DECsystem 20 (?)"},
+  {0x08, 0x00, 0x5A, "IBM"},
+  {0x08, 0x00, 0x67, "Comdesign"},
+  {0x08, 0x00, 0x68, "Ridge"},
+  {0x08, 0x00, 0x69, "Silicon Graphics"},
+  {0x08, 0x00, 0x6E, "Concurrent"},
+  {0x08, 0x00, 0x75, "DDE"},
+  {0x08, 0x00, 0x7C, "Vitalink"},
+  {0x08, 0x00, 0x80, "XIOS"},
+  {0x08, 0x00, 0x86, "Imagen/QMS"},
+  {0x08, 0x00, 0x87, "Xyplex"},
+  {0x08, 0x00, 0x89, "Kinetics"},
+  {0x08, 0x00, 0x8B, "Pyramid"},
+  {0x08, 0x00, 0x8D, "XyVision"},
+  {0x08, 0x00, 0x90, "Retix Inc"},
+  {0x48, 0x44, 0x53, "HDS (?)"},
+  {0x80, 0x00, 0x10, "AT&T"},
+  {0xAA, 0x00, 0x00, "DEC"},
+  {0xAA, 0x00, 0x01, "DEC"},
+  {0xAA, 0x00, 0x02, "DEC"},
+  {0xAA, 0x00, 0x03, "DEC"},
+  {0xAA, 0x00, 0x04, "DEC"},
+  {0x00, 0x00, 0x00, NULL}
+};
+
+/*
+ *	eof
+ */
diff --git a/contrib/ip_and_mac/mac.sql b/contrib/ip_and_mac/mac.sql
new file mode 100644
index 0000000000000000000000000000000000000000..9e5ec61277bd868775082ffbfe4dcbbfc251305c
--- /dev/null
+++ b/contrib/ip_and_mac/mac.sql
@@ -0,0 +1,88 @@
+--
+--	PostgreSQL code for MAC addresses.
+--
+
+load '/usr/local/pgsql/modules/mac.so';
+
+--
+--	Input and output functions and the type itself:
+--
+
+create function macaddr_in(opaque)
+	returns opaque
+	as '/usr/local/pgsql/modules/mac.so'
+	language 'c';
+
+create function macaddr_out(opaque)
+	returns opaque
+	as '/usr/local/pgsql/modules/mac.so'
+	language 'c';
+
+create type macaddr (
+	internallength = 8,
+	externallength = variable,
+	input = macaddr_in,
+	output = macaddr_out
+);
+
+--
+--	The various boolean tests:
+--
+
+create function macaddr_eq(macaddr, macaddr)
+	returns bool
+	as '/usr/local/pgsql/modules/mac.so'
+	language 'c';
+
+create function macaddr_ne(macaddr, macaddr)
+	returns bool
+	as '/usr/local/pgsql/modules/mac.so'
+	language 'c';
+
+create function macaddr_like(macaddr, macaddr)
+	returns bool
+	as '/usr/local/pgsql/modules/mac.so'
+	language 'c';
+
+--
+--	Now the operators.  Note how the "negator = <>" in the
+--	definition of the equivalence operator is commented out.
+--	It gets defined implicitly when "<>" is defined, with
+--	"=" as its negator.
+--
+
+create operator = (
+	leftarg = macaddr,
+	rightarg = macaddr,
+	commutator = =,
+--	negator = <>,
+	procedure = macaddr_eq
+);
+
+create operator <> (
+	leftarg = macaddr,
+	rightarg = macaddr,
+	commutator = <>,
+	negator = =,
+	procedure = macaddr_ne
+);
+
+create operator ~~ (
+	leftarg = macaddr,
+	rightarg = macaddr,
+	commutator = ~~,
+	procedure = macaddr_like
+);
+
+--
+--	Finally, the special manufacurer matching function:
+--
+
+create function macaddr_manuf(macaddr)
+	returns text
+	as '/usr/local/pgsql/modules/mac.so'
+	language 'c';
+
+--
+--	eof
+--