Skip to content

Commit a377ad5

Browse files
committed
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>
1 parent 9336b9b commit a377ad5

File tree

8 files changed

+303
-176
lines changed

8 files changed

+303
-176
lines changed

contrib/ip_and_mac/Makefile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
# PostgreSQL type definitions for IP and MAC addresses.
1+
#
2+
# PostgreSQL types for IP and MAC addresses
3+
#
4+
# $Id: Makefile,v 1.2 1998/02/14 17:58:02 scrappy Exp $
25

36
all: ip.so mac.so
47

@@ -17,4 +20,6 @@ mac.o: mac.c mac.h
1720
install: ip.so mac.so
1821
install -c ip.so mac.so /usr/local/pgsql/modules
1922

23+
#
2024
# eof
25+
#

contrib/ip_and_mac/README

Lines changed: 38 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
PostgreSQL type extensions for IP and MAC addresses.
22
---------------------------------------------------
33

4+
$Id: README,v 1.2 1998/02/14 17:58:03 scrappy Exp $
5+
46
I needed to record IP and MAC level ethernet addresses in a data
57
base, and I really didn't want to store them as plain strings, with
68
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
911
as a very simple example of how to do this sort of thing, so here
1012
it is, in the hope that it will be useful.
1113

12-
IP addresses are implemented as an 8 byte struct (this may well be
14+
IP addresses are implemented as a 6 byte struct (this may be 1 byte
1315
more than is useful, but I figured that since it has to be at least 5,
14-
it might as well round well) that contains the four bytes of address
15-
and a mask width. Thus, a node address looks like '158.37.96.15/32'
16-
(or just '158.37.96.15', which is understood to mean the same thing).
17-
This address happens to be part of a subnet where I work;
18-
'158.37.96.0/24', which itself is a part of the larger subnet
19-
allocated to our institution, which is '158.37.96.0/21', which again,
20-
if you go by the book, is part of the class "B" net '158.37.0.0/16'.
16+
it might as well be an even number of bytes) that contains the four
17+
byte address and a mask width. The external representation of an IP
18+
address looks like '158.37.96.15/32' (or just '158.37.96.15', which is
19+
understood to mean the same thing). This address happens to be part
20+
of a subnet where I work; '158.37.96.0/24', which itself is a part of
21+
the larger subnet allocated to our site, which is '158.37.96.0/21',
22+
which again, if you go by the old book, is part of the class "B" net
23+
called '158.37.0.0/16'.
2124

2225
Input and output functions are supplied, along with the "normal" <,
23-
<=, =, >=, > and <> operators, which all do what you expect, and the
24-
similarity operator ~~, which checks whether two given addresses are
25-
either the same, or, failing that, whether one is a subnet
26-
specification and the other an address (or a smaller subnet) within
27-
that. Good for picking out records with addresses in a given subnet:
28-
note that '158.37.96.0/21' spans '158.37.96.0' to '158.37.103.255',
29-
which is not all that easily handled in its external representation.
30-
31-
MAC level ethernet addresses are also implemented as an 8 byte struct
32-
(I wish I knew what alignment needs are actually present -- I'm just
33-
not taking any chances here) that contains the address as unsigned
34-
chars. Several input forms are accepted: the following are all the
35-
same address: '08002b:010203', '08002b-010203', '0800.2b01.0203',
36-
'08-00-2b-01-02-03' and '08:00:2b:01:02:03'. Upper and lower case is
37-
accepted for the digits 'a' through 'f'. Output is always in the
38-
latter of the given forms.
39-
40-
Input and output functions are supplied, along with the = and <>
41-
operators, which do what you expect, and the similarity operator ~~,
42-
which checks whether two given addresses belong to hardware from the
43-
same manufacturer (first three bytes the same, that is). As an extra
26+
<=, =, >=, > and <> operators, which all do what you expect. In
27+
addition, there is a function to check whether a given address is a
28+
member of a given subnet: ipaddr_in_net(addr, net), and functions to
29+
return the netmask and the broadcast address of a given network:
30+
ipaddr_mask(net) and ipaddr_bcast(net).
31+
32+
MAC level ethernet addresses are implemented as a 6 byte struct that
33+
contains the address as unsigned chars. Several input forms are
34+
accepted; the following are all the same address: '08002b:010203',
35+
'08002b-010203', '0800.2b01.0203', '08-00-2b-01-02-03' and
36+
'08:00:2b:01:02:03'. Upper and lower case is accepted for the digits
37+
'a' through 'f'. Output is always in the latter of the given forms.
38+
39+
As with IP addresses, input and output functions are supplied as well
40+
as the "normal" operators, which do what you expect. As an extra
4441
feature, a function macaddr_manuf() is defined, which returns the name
45-
of the manufacturer as a string.
42+
of the manufacturer as a string. This is currently held in a
43+
hard-coded struct internal to the C module -- it might be smarter to
44+
put this information into an actual data base table, and look up the
45+
manufacturer there. (Another TODO, for both new data types, is to
46+
interface them to indices. If anyone can explain this to me in a way
47+
that is easier to understand than the current documentation, I would
48+
be most grateful!)
4649

47-
To install: fix the path names in the SQL files and the Makefile if
48-
you need to, then make, make install, slurp the SQL files into psql or
50+
I don't know what changes are needed to the Makefile for other systems
51+
than the one I'm running (NetBSD 1.3), but anyway: to install on a BSD
52+
system: fix the path names in the SQL files and the Makefile if you
53+
need to, then make, make install, slurp the SQL files into psql or
4954
whatever, and you're off. Enjoy!
5055

51-
Bergen, Norway, 1998-01-11, Tom Ivar Helbekkmo (tih@Hamartun.Priv.NO).
56+
Bergen, Norway, 1998-01-31, Tom Ivar Helbekkmo (tih@Hamartun.Priv.NO).

contrib/ip_and_mac/ip.c

Lines changed: 77 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
/*
22
* PostgreSQL type definitions for IP addresses.
3+
*
4+
* $Id: ip.c,v 1.2 1998/02/14 17:58:03 scrappy Exp $
35
*/
46

57
#include <stdio.h>
@@ -12,13 +14,8 @@
1214
*/
1315

1416
typedef struct ipaddr {
15-
unsigned char a;
16-
unsigned char b;
17-
unsigned char c;
18-
unsigned char d;
19-
unsigned char w;
20-
unsigned char pad1;
21-
short pad2;
17+
uint32 address;
18+
int16 width;
2219
} ipaddr;
2320

2421
/*
@@ -35,15 +32,24 @@ bool ipaddr_ge(ipaddr *a1, ipaddr *a2);
3532
bool ipaddr_gt(ipaddr *a1, ipaddr *a2);
3633

3734
bool ipaddr_ne(ipaddr *a1, ipaddr *a2);
35+
3836
int4 ipaddr_cmp(ipaddr *a1, ipaddr *a2);
39-
bool ipaddr_like(ipaddr *a1, ipaddr *a2);
37+
38+
bool ipaddr_in_net(ipaddr *a1, ipaddr *a2);
39+
ipaddr *ipaddr_mask(ipaddr *a);
40+
ipaddr *ipaddr_bcast(ipaddr *a);
4041

4142
/*
42-
* A utility macro used for sorting addresses numerically:
43+
* Build a mask of a given width:
4344
*/
4445

45-
#define Mag(addr) \
46-
((unsigned long)((addr->a<<24)|(addr->b<<16)|(addr->c<<8)|(addr->d)))
46+
unsigned long build_mask(unsigned char bits) {
47+
unsigned long mask = 0;
48+
int i;
49+
for (i = 0; i < bits; i++)
50+
mask = (mask >> 1) | 0x80000000;
51+
return mask;
52+
}
4753

4854
/*
4955
* IP address reader. Note how the count returned by sscanf()
@@ -79,11 +85,9 @@ ipaddr *ipaddr_in(char *str) {
7985

8086
result = (ipaddr *)palloc(sizeof(ipaddr));
8187

82-
result->a = a;
83-
result->b = b;
84-
result->c = c;
85-
result->d = d;
86-
result->w = w;
88+
result->address = (uint32) ((a<<24)|(b<<16)|(c<<8)|d);
89+
result->address &= build_mask(w);
90+
result->width = w;
8791

8892
return(result);
8993
}
@@ -101,112 +105,112 @@ char *ipaddr_out(ipaddr *addr) {
101105

102106
result = (char *)palloc(32);
103107

104-
if (Mag(addr) > 0) {
105-
if (addr->w == 32)
108+
if (addr->address > 0) {
109+
if (addr->width == 32)
106110
sprintf(result, "%d.%d.%d.%d",
107-
addr->a, addr->b, addr->c, addr->d);
111+
(addr->address >> 24) & 0xff,
112+
(addr->address >> 16) & 0xff,
113+
(addr->address >> 8) & 0xff,
114+
addr->address & 0xff);
108115
else
109116
sprintf(result, "%d.%d.%d.%d/%d",
110-
addr->a, addr->b, addr->c, addr->d, addr->w);
117+
(addr->address >> 24) & 0xff,
118+
(addr->address >> 16) & 0xff,
119+
(addr->address >> 8) & 0xff,
120+
addr->address & 0xff,
121+
addr->width);
111122
} else {
112123
result[0] = 0; /* special case for missing address */
113124
}
114125
return(result);
115126
}
116127

117128
/*
118-
* Boolean tests. The Mag() macro was defined above.
129+
* Boolean tests for magnitude.
119130
*/
120131

121132
bool ipaddr_lt(ipaddr *a1, ipaddr *a2) {
122-
unsigned long a1mag, a2mag;
123-
a1mag = Mag(a1);
124-
a2mag = Mag(a2);
125-
return (a1mag < a2mag);
133+
return (a1->address < a2->address);
126134
};
127135

128136
bool ipaddr_le(ipaddr *a1, ipaddr *a2) {
129-
unsigned long a1mag, a2mag;
130-
a1mag = Mag(a1);
131-
a2mag = Mag(a2);
132-
return (a1mag <= a2mag);
137+
return (a1->address <= a2->address);
133138
};
134139

135140
bool ipaddr_eq(ipaddr *a1, ipaddr *a2) {
136-
unsigned long a1mag, a2mag;
137-
a1mag = Mag(a1);
138-
a2mag = Mag(a2);
139-
return ((a1mag == a2mag) && (a1->w == a2->w));
141+
return (a1->address == a2->address);
140142
};
141143

142144
bool ipaddr_ge(ipaddr *a1, ipaddr *a2) {
143-
unsigned long a1mag, a2mag;
144-
a1mag = Mag(a1);
145-
a2mag = Mag(a2);
146-
return (a1mag >= a2mag);
145+
return (a1->address >= a2->address);
147146
};
148147

149148
bool ipaddr_gt(ipaddr *a1, ipaddr *a2) {
150-
unsigned long a1mag, a2mag;
151-
a1mag = Mag(a1);
152-
a2mag = Mag(a2);
153-
return (a1mag > a2mag);
149+
return (a1->address > a2->address);
154150
};
155151

156152
bool ipaddr_ne(ipaddr *a1, ipaddr *a2) {
157-
unsigned long a1mag, a2mag;
158-
a1mag = Mag(a1);
159-
a2mag = Mag(a2);
160-
return ((a1mag != a2mag) || (a1->w != a2->w));
153+
return (a1->address != a2->address);
161154
};
162155

163156
/*
164157
* Comparison function for sorting:
165158
*/
166159

167160
int4 ipaddr_cmp(ipaddr *a1, ipaddr *a2) {
168-
unsigned long a1mag = Mag(a1), a2mag = Mag(a2);
169-
if (a1mag < a2mag)
161+
if (a1->address < a2->address)
170162
return -1;
171-
else if (a1mag > a2mag)
163+
else if (a1->address > a2->address)
172164
return 1;
173165
else
174166
return 0;
175167
}
176168

177169
/*
178-
* Our "similarity" operator checks whether two addresses are
179-
* either the same node address, or, failing that, whether one
180-
* of them contains the other. This will be true if they have
181-
* the same high bits down as far as the shortest mask reaches.
170+
* Test whether an address is within a given subnet:
182171
*/
183172

184-
unsigned long build_mask(unsigned char bits) {
185-
unsigned long mask = 0;
186-
int i;
187-
for (i = 0; i < bits; i++)
188-
mask = (mask >> 1) | 0x80000000;
189-
return mask;
190-
}
191-
192-
bool ipaddr_like(ipaddr *a1, ipaddr *a2) {
193-
unsigned long a1bits, a2bits, maskbits;
194-
if ((a1->w == 0) || (a2->w == 0))
173+
bool ipaddr_in_net(ipaddr *a1, ipaddr *a2) {
174+
uint32 maskbits;
175+
if (a1->width < a2->width)
195176
return FALSE;
196-
if ((a1->w == 32) && (a2->w == 32))
177+
if ((a1->width == 32) && (a2->width == 32))
197178
return ipaddr_eq(a1, a2);
198-
a1bits = Mag(a1);
199-
a2bits = Mag(a2);
200-
if (a1->w > a2->w) {
201-
maskbits = build_mask(a2->w);
202-
return ((a1bits & maskbits) == (a2bits & maskbits));
203-
} else {
204-
maskbits = build_mask(a1->w);
205-
return ((a2bits & maskbits) == (a1bits & maskbits));
206-
}
179+
maskbits = build_mask(a2->width);
180+
if ((a1->address & maskbits) == (a2->address & maskbits))
181+
return TRUE;
207182
return FALSE;
208183
}
209184

185+
/*
186+
* Pick out just the mask of a network:
187+
*/
188+
189+
ipaddr *ipaddr_mask(ipaddr *a) {
190+
ipaddr *result;
191+
192+
result = (ipaddr *)palloc(sizeof(ipaddr));
193+
result->address = build_mask(a->width);
194+
result->width = 32;
195+
196+
return result;
197+
}
198+
199+
/*
200+
* Return the broadcast address of a network:
201+
*/
202+
203+
ipaddr *ipaddr_bcast(ipaddr *a) {
204+
ipaddr *result;
205+
206+
result = (ipaddr *)palloc(sizeof(ipaddr));
207+
result->address = a->address;
208+
result->address |= (build_mask(32 - a->width) >> a->width);
209+
result->width = 32;
210+
211+
return result;
212+
}
213+
210214
/*
211215
* eof
212216
*/

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy