提交 a377ad58 编写于 作者: M Marc G. Fournier

I'm including an update to my user defined IP and MAC address type

implementation that's in contrib/ip_and_mac/.  This one works right
with 6.3, avoids the problems I ran into earlier with LIKE, and
includes a bit of extra functionality.

From: Tom I Helbekkmo <tih@Hamartun.Priv.NO>
上级 9336b9b2
# PostgreSQL type definitions for IP and MAC addresses.
#
# PostgreSQL types for IP and MAC addresses
#
# $Id: Makefile,v 1.2 1998/02/14 17:58:02 scrappy Exp $
all: ip.so mac.so
......@@ -17,4 +20,6 @@ mac.o: mac.c mac.h
install: ip.so mac.so
install -c ip.so mac.so /usr/local/pgsql/modules
#
# eof
#
PostgreSQL type extensions for IP and MAC addresses.
---------------------------------------------------
$Id: README,v 1.2 1998/02/14 17:58:03 scrappy Exp $
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
......@@ -9,43 +11,46 @@ 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
IP addresses are implemented as a 6 byte struct (this may be 1 byte
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'.
it might as well be an even number of bytes) that contains the four
byte address and a mask width. The external representation of an IP
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 site, which is '158.37.96.0/21',
which again, if you go by the old book, is part of the class "B" net
called '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
<=, =, >=, > and <> operators, which all do what you expect. In
addition, there is a function to check whether a given address is a
member of a given subnet: ipaddr_in_net(addr, net), and functions to
return the netmask and the broadcast address of a given network:
ipaddr_mask(net) and ipaddr_bcast(net).
MAC level ethernet addresses are implemented as a 6 byte struct 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.
As with IP addresses, input and output functions are supplied as well
as the "normal" operators, which do what you expect. As an extra
feature, a function macaddr_manuf() is defined, which returns the name
of the manufacturer as a string.
of the manufacturer as a string. This is currently held in a
hard-coded struct internal to the C module -- it might be smarter to
put this information into an actual data base table, and look up the
manufacturer there. (Another TODO, for both new data types, is to
interface them to indices. If anyone can explain this to me in a way
that is easier to understand than the current documentation, I would
be most grateful!)
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
I don't know what changes are needed to the Makefile for other systems
than the one I'm running (NetBSD 1.3), but anyway: to install on a BSD
system: 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).
Bergen, Norway, 1998-01-31, Tom Ivar Helbekkmo (tih@Hamartun.Priv.NO).
/*
* PostgreSQL type definitions for IP addresses.
*
* $Id: ip.c,v 1.2 1998/02/14 17:58:03 scrappy Exp $
*/
#include <stdio.h>
......@@ -12,13 +14,8 @@
*/
typedef struct ipaddr {
unsigned char a;
unsigned char b;
unsigned char c;
unsigned char d;
unsigned char w;
unsigned char pad1;
short pad2;
uint32 address;
int16 width;
} ipaddr;
/*
......@@ -35,15 +32,24 @@ 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);
bool ipaddr_in_net(ipaddr *a1, ipaddr *a2);
ipaddr *ipaddr_mask(ipaddr *a);
ipaddr *ipaddr_bcast(ipaddr *a);
/*
* A utility macro used for sorting addresses numerically:
* Build a mask of a given width:
*/
#define Mag(addr) \
((unsigned long)((addr->a<<24)|(addr->b<<16)|(addr->c<<8)|(addr->d)))
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;
}
/*
* IP address reader. Note how the count returned by sscanf()
......@@ -79,11 +85,9 @@ ipaddr *ipaddr_in(char *str) {
result = (ipaddr *)palloc(sizeof(ipaddr));
result->a = a;
result->b = b;
result->c = c;
result->d = d;
result->w = w;
result->address = (uint32) ((a<<24)|(b<<16)|(c<<8)|d);
result->address &= build_mask(w);
result->width = w;
return(result);
}
......@@ -101,13 +105,20 @@ char *ipaddr_out(ipaddr *addr) {
result = (char *)palloc(32);
if (Mag(addr) > 0) {
if (addr->w == 32)
if (addr->address > 0) {
if (addr->width == 32)
sprintf(result, "%d.%d.%d.%d",
addr->a, addr->b, addr->c, addr->d);
(addr->address >> 24) & 0xff,
(addr->address >> 16) & 0xff,
(addr->address >> 8) & 0xff,
addr->address & 0xff);
else
sprintf(result, "%d.%d.%d.%d/%d",
addr->a, addr->b, addr->c, addr->d, addr->w);
(addr->address >> 24) & 0xff,
(addr->address >> 16) & 0xff,
(addr->address >> 8) & 0xff,
addr->address & 0xff,
addr->width);
} else {
result[0] = 0; /* special case for missing address */
}
......@@ -115,49 +126,31 @@ char *ipaddr_out(ipaddr *addr) {
}
/*
* Boolean tests. The Mag() macro was defined above.
* Boolean tests for magnitude.
*/
bool ipaddr_lt(ipaddr *a1, ipaddr *a2) {
unsigned long a1mag, a2mag;
a1mag = Mag(a1);
a2mag = Mag(a2);
return (a1mag < a2mag);
return (a1->address < a2->address);
};
bool ipaddr_le(ipaddr *a1, ipaddr *a2) {
unsigned long a1mag, a2mag;
a1mag = Mag(a1);
a2mag = Mag(a2);
return (a1mag <= a2mag);
return (a1->address <= a2->address);
};
bool ipaddr_eq(ipaddr *a1, ipaddr *a2) {
unsigned long a1mag, a2mag;
a1mag = Mag(a1);
a2mag = Mag(a2);
return ((a1mag == a2mag) && (a1->w == a2->w));
return (a1->address == a2->address);
};
bool ipaddr_ge(ipaddr *a1, ipaddr *a2) {
unsigned long a1mag, a2mag;
a1mag = Mag(a1);
a2mag = Mag(a2);
return (a1mag >= a2mag);
return (a1->address >= a2->address);
};
bool ipaddr_gt(ipaddr *a1, ipaddr *a2) {
unsigned long a1mag, a2mag;
a1mag = Mag(a1);
a2mag = Mag(a2);
return (a1mag > a2mag);
return (a1->address > a2->address);
};
bool ipaddr_ne(ipaddr *a1, ipaddr *a2) {
unsigned long a1mag, a2mag;
a1mag = Mag(a1);
a2mag = Mag(a2);
return ((a1mag != a2mag) || (a1->w != a2->w));
return (a1->address != a2->address);
};
/*
......@@ -165,48 +158,59 @@ bool ipaddr_ne(ipaddr *a1, ipaddr *a2) {
*/
int4 ipaddr_cmp(ipaddr *a1, ipaddr *a2) {
unsigned long a1mag = Mag(a1), a2mag = Mag(a2);
if (a1mag < a2mag)
if (a1->address < a2->address)
return -1;
else if (a1mag > a2mag)
else if (a1->address > a2->address)
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.
* Test whether an address is within a given subnet:
*/
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))
bool ipaddr_in_net(ipaddr *a1, ipaddr *a2) {
uint32 maskbits;
if (a1->width < a2->width)
return FALSE;
if ((a1->w == 32) && (a2->w == 32))
if ((a1->width == 32) && (a2->width == 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));
}
maskbits = build_mask(a2->width);
if ((a1->address & maskbits) == (a2->address & maskbits))
return TRUE;
return FALSE;
}
/*
* Pick out just the mask of a network:
*/
ipaddr *ipaddr_mask(ipaddr *a) {
ipaddr *result;
result = (ipaddr *)palloc(sizeof(ipaddr));
result->address = build_mask(a->width);
result->width = 32;
return result;
}
/*
* Return the broadcast address of a network:
*/
ipaddr *ipaddr_bcast(ipaddr *a) {
ipaddr *result;
result = (ipaddr *)palloc(sizeof(ipaddr));
result->address = a->address;
result->address |= (build_mask(32 - a->width) >> a->width);
result->width = 32;
return result;
}
/*
* eof
*/
--
-- PostgreSQL code for IP addresses.
--
-- $Id: ip.sql,v 1.2 1998/02/14 17:58:04 scrappy Exp $
--
load '/usr/local/pgsql/modules/ip.so';
......@@ -19,7 +21,7 @@ create function ipaddr_out(opaque)
language 'c';
create type ipaddr (
internallength = 8,
internallength = 6,
externallength = variable,
input = ipaddr_in,
output = ipaddr_out
......@@ -59,11 +61,21 @@ create function ipaddr_ne(ipaddr, ipaddr)
as '/usr/local/pgsql/modules/ip.so'
language 'c';
create function ipaddr_like(ipaddr, ipaddr)
create function ipaddr_in_net(ipaddr, ipaddr)
returns bool
as '/usr/local/pgsql/modules/ip.so'
language 'c';
create function ipaddr_mask(ipaddr)
returns ipaddr
as '/usr/local/pgsql/modules/ip.so'
language 'c';
create function ipaddr_bcast(ipaddr)
returns ipaddr
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
......@@ -71,20 +83,18 @@ create function ipaddr_like(ipaddr, ipaddr)
-- will be implicitly defined when those are, further down.
--
create operator <= (
create operator < (
leftarg = ipaddr,
rightarg = ipaddr,
-- commutator = >,
-- negator = >,
procedure = ipaddr_le
-- negator = >=,
procedure = ipaddr_lt
);
create operator < (
create operator <= (
leftarg = ipaddr,
rightarg = ipaddr,
-- commutator = >=,
-- negator = >=,
procedure = ipaddr_lt
-- negator = >,
procedure = ipaddr_le
);
create operator = (
......@@ -98,7 +108,6 @@ create operator = (
create operator >= (
leftarg = ipaddr,
rightarg = ipaddr,
commutator = <,
negator = <,
procedure = ipaddr_ge
);
......@@ -106,7 +115,6 @@ create operator >= (
create operator > (
leftarg = ipaddr,
rightarg = ipaddr,
commutator = <=,
negator = <=,
procedure = ipaddr_gt
);
......@@ -114,18 +122,10 @@ create operator > (
create operator <> (
leftarg = ipaddr,
rightarg = ipaddr,
commutator = <>,
negator = =,
procedure = ipaddr_ne
);
create operator ~~ (
leftarg = ipaddr,
rightarg = ipaddr,
commutator = ~~,
procedure = ipaddr_like
);
--
-- eof
--
/*
* PostgreSQL type definitions for MAC addresses.
*
* $Id: mac.c,v 1.2 1998/02/14 17:58:05 scrappy Exp $
*/
#include <stdio.h>
......@@ -20,7 +22,6 @@ typedef struct macaddr {
unsigned char d;
unsigned char e;
unsigned char f;
short pad;
} macaddr;
/*
......@@ -30,21 +31,26 @@ typedef struct macaddr {
macaddr *macaddr_in(char *str);
char *macaddr_out(macaddr *addr);
bool macaddr_lt(macaddr *a1, macaddr *a2);
bool macaddr_le(macaddr *a1, macaddr *a2);
bool macaddr_eq(macaddr *a1, macaddr *a2);
bool macaddr_ge(macaddr *a1, macaddr *a2);
bool macaddr_gt(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) \
#define hibits(addr) \
((unsigned long)((addr->a<<16)|(addr->b<<8)|(addr->c)))
#define MagH(addr) \
#define lobits(addr) \
((unsigned long)((addr->c<<16)|(addr->e<<8)|(addr->f)))
/*
......@@ -107,7 +113,7 @@ char *macaddr_out(macaddr *addr) {
result = (char *)palloc(32);
if ((MagM(addr) > 0) || (MagH(addr) > 0)) {
if ((hibits(addr) > 0) || (lobits(addr) > 0)) {
sprintf(result, "%02x:%02x:%02x:%02x:%02x:%02x",
addr->a, addr->b, addr->c, addr->d, addr->e, addr->f);
} else {
......@@ -120,16 +126,32 @@ char *macaddr_out(macaddr *addr) {
* Boolean tests.
*/
bool macaddr_lt(macaddr *a1, macaddr *a2) {
return((hibits(a1) < hibits(a2)) ||
((hibits(a1) == hibits(a2)) && lobits(a1) < lobits(a2)));
};
bool macaddr_le(macaddr *a1, macaddr *a2) {
return((hibits(a1) < hibits(a2)) ||
((hibits(a1) == hibits(a2)) && lobits(a1) <= lobits(a2)));
};
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));
return ((hibits(a1) == hibits(a2)) && (lobits(a1) == lobits(a2)));
};
bool macaddr_ge(macaddr *a1, macaddr *a2) {
return((hibits(a1) > hibits(a2)) ||
((hibits(a1) == hibits(a2)) && lobits(a1) >= lobits(a2)));
};
bool macaddr_gt(macaddr *a1, macaddr *a2) {
return((hibits(a1) > hibits(a2)) ||
((hibits(a1) == hibits(a2)) && lobits(a1) > lobits(a2)));
};
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));
return ((hibits(a1) != hibits(a2)) || (lobits(a1) != lobits(a2)));
};
/*
......@@ -137,37 +159,18 @@ bool macaddr_ne(macaddr *a1, macaddr *a2) {
*/
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)
if (hibits(a1) < hibits(a2))
return -1;
else if (a1magm > a2magm)
else if (hibits(a1) > hibits(a2))
return 1;
else if (a1magh < a2magh)
else if (lobits(a1) < lobits(a2))
return -1;
else if (a1magh > a2magh)
else if (lobits(a1) > lobits(a2))
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".
*/
......
/*
* PostgreSQL type definitions for MAC addresses.
*
* $Id: mac.h,v 1.2 1998/02/14 17:58:07 scrappy Exp $
*/
typedef struct manufacturer {
......
--
-- PostgreSQL code for MAC addresses.
--
-- $Id: mac.sql,v 1.2 1998/02/14 17:58:08 scrappy Exp $
--
load '/usr/local/pgsql/modules/mac.so';
......@@ -19,38 +21,67 @@ create function macaddr_out(opaque)
language 'c';
create type macaddr (
internallength = 8,
internallength = 6,
externallength = variable,
input = macaddr_in,
output = macaddr_out
);
--
-- The various boolean tests:
-- The boolean tests:
--
create function macaddr_lt(macaddr, macaddr)
returns bool
as '/usr/local/pgsql/modules/mac.so'
language 'c';
create function macaddr_le(macaddr, macaddr)
returns bool
as '/usr/local/pgsql/modules/mac.so'
language 'c';
create function macaddr_eq(macaddr, macaddr)
returns bool
as '/usr/local/pgsql/modules/mac.so'
language 'c';
create function macaddr_ne(macaddr, macaddr)
create function macaddr_ge(macaddr, macaddr)
returns bool
as '/usr/local/pgsql/modules/mac.so'
language 'c';
create function macaddr_gt(macaddr, macaddr)
returns bool
as '/usr/local/pgsql/modules/mac.so'
language 'c';
create function macaddr_like(macaddr, macaddr)
create function macaddr_ne(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.
-- 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 = macaddr,
rightarg = macaddr,
-- negator = >=,
procedure = macaddr_lt
);
create operator <= (
leftarg = macaddr,
rightarg = macaddr,
-- negator = >,
procedure = macaddr_le
);
create operator = (
leftarg = macaddr,
rightarg = macaddr,
......@@ -59,19 +90,25 @@ create operator = (
procedure = macaddr_eq
);
create operator <> (
create operator >= (
leftarg = macaddr,
rightarg = macaddr,
commutator = <>,
negator = =,
procedure = macaddr_ne
negator = <,
procedure = macaddr_ge
);
create operator > (
leftarg = macaddr,
rightarg = macaddr,
negator = <=,
procedure = macaddr_gt
);
create operator ~~ (
create operator <> (
leftarg = macaddr,
rightarg = macaddr,
commutator = ~~,
procedure = macaddr_like
negator = =,
procedure = macaddr_ne
);
--
......
--
-- A quick test of the IP address code
--
-- $Id: test.sql,v 1.1 1998/02/14 17:58:09 scrappy Exp $
--
-- temporary table:
create table addresses (address ipaddr);
-- sample data from two subnets:
insert into addresses values ('158.37.96.15');
insert into addresses values ('158.37.96.16');
insert into addresses values ('158.37.96.17');
insert into addresses values ('158.37.97.15');
insert into addresses values ('158.37.97.16');
insert into addresses values ('158.37.97.17');
insert into addresses values ('158.37.98.15');
insert into addresses values ('158.37.98.16');
insert into addresses values ('158.37.98.17');
insert into addresses values ('158.37.96.150');
insert into addresses values ('158.37.96.160');
insert into addresses values ('158.37.96.170');
insert into addresses values ('158.37.97.150');
insert into addresses values ('158.37.97.160');
insert into addresses values ('158.37.97.170');
insert into addresses values ('158.37.98.150');
insert into addresses values ('158.37.98.160');
insert into addresses values ('158.37.98.170');
-- show them all:
select * from addresses;
-- select the ones in subnet 96:
select * from addresses where ipaddr_in_net(address, '158.37.96.0/24');
-- select the ones not in subnet 96:
select * from addresses where not ipaddr_in_net(address, '158.37.96.0/24');
-- select the ones in subnet 97:
select * from addresses where ipaddr_in_net(address, '158.37.97.0/24');
-- select the ones not in subnet 97:
select * from addresses where not ipaddr_in_net(address, '158.37.97.0/24');
-- select the ones in subnet 96 or 97, sorted:
select * from addresses where ipaddr_in_net(address, '158.37.96.0/23')
order by address;
-- now some networks:
create table networks (network ipaddr);
-- now the subnets mentioned above:
insert into networks values ('158.37.96.0/24');
insert into networks values ('158.37.97.0/24');
insert into networks values ('158.37.98.0/24');
-- select the netmasks of the net containing each:
select address, ipaddr_mask(network) from addresses, networks
where ipaddr_in_net(address, network);
-- select the broadcast address of the net containing each:
select address, ipaddr_bcast(network) from addresses, networks
where ipaddr_in_net(address, network);
-- tidy up:
drop table addresses;
drop table networks;
--
-- eof
--
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册