From: Tom I Helbekkmo <tih@Hamartun.Priv.NO>
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.
This commit is contained in:
parent
9f8d3b668b
commit
674b22a2a4
20
contrib/ip_and_mac/Makefile
Normal file
20
contrib/ip_and_mac/Makefile
Normal file
@ -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
|
51
contrib/ip_and_mac/README
Normal file
51
contrib/ip_and_mac/README
Normal file
@ -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).
|
212
contrib/ip_and_mac/ip.c
Normal file
212
contrib/ip_and_mac/ip.c
Normal file
@ -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
|
||||
*/
|
131
contrib/ip_and_mac/ip.sql
Normal file
131
contrib/ip_and_mac/ip.sql
Normal file
@ -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
|
||||
--
|
202
contrib/ip_and_mac/mac.c
Normal file
202
contrib/ip_and_mac/mac.c
Normal file
@ -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
|
||||
*/
|
130
contrib/ip_and_mac/mac.h
Normal file
130
contrib/ip_and_mac/mac.h
Normal file
@ -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
|
||||
*/
|
88
contrib/ip_and_mac/mac.sql
Normal file
88
contrib/ip_and_mac/mac.sql
Normal file
@ -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
|
||||
--
|
Loading…
x
Reference in New Issue
Block a user