Bruce Momjian 92da1c3a6a I investigated carefully POSTGRES data base (in idea to use it for
our internal IP routing data base, and because  I have participated
in Ingres development here in Russia in RUBIN/DEMOS project -
through it was not freeware work - and it was very interesting for
me too see such good freeware data base as PostgreSQL), and I
modified 'ipaddr' data type library in accordance to our requests
and to allow SQL do indexing over ipaddr objects.

You can read description at 'http://relcom.EU.net/ipaddr.html' and
get sources at 'http://relcom.EU.net/ip_class.tar.gz'. It contains
sources, sql scripts for incorporating new data type into postgres
(including ipaddr_ops operator class incorporation) and 20,000
records based data test for the indexing.

I am not sure if it's proper mail list for this information, and
if it's interesting for anyone except me to get full-functional
ipaddress class. I am ready to make all modifications, bug fixing
and documentation for this data class if it's nessesary for it's
contribution to the Postgres data base.

Anyway, all my work was based at original 'ip&mac data type'
contribution, written by Tom Ivar Helbekkmo.

Be free to write me any questions or requests about this work.
==============================================================

Aleksei Roudnev, Network Operations Center, Relcom, Moscow (+7 095)
194-19-95 (Network Operations Center Hot Line),(+7 095) 239-10-10,
N 13729 (pager) (+7 095) 196-72-12 (Support), (+7 095) 194-33-28
(Fax)
1998-06-16 04:34:30 +00:00

449 lines
8.4 KiB
C

/*
* PostgreSQL type definitions for IP addresses.
*
* $Id: ip.c,v 1.4 1998/06/16 04:34:29 momjian Exp $
*/
#include <stdio.h>
#include <postgres.h>
#include <utils/palloc.h>
/*
* This is the internal storage format for IP addresses:
*/
typedef struct ipaddr
{
uint32 address;
int16 width;
} 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_in_net(ipaddr * a1, ipaddr * a2);
ipaddr *ipaddr_mask(ipaddr * a);
ipaddr *ipaddr_bcast(ipaddr * a);
/*
* Build a mask of a given width:
*/
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()
* 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 == 3 ) {
d = 0;
count = 4;
};
if (count == 4)
{
w = 32;
if ( a >= 192 && a < 224 && d == 0 ) w = 24;
if ( a >= 128 && a < 192 && d == 0 && c == 0 ) w = 16;
if ( a > 0 && a < 128 && c == 0 && b == 0 && a < 128 ) w = 8;
if ( a == 0 && b == 0 && c == 0 && d == 0 ) w = 0;
};
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 = 255; /* special case for missing address */
}
result = (ipaddr *) palloc(sizeof(ipaddr));
result->address = (uint32) ((a << 24) | (b << 16) | (c << 8) | d);
result->width = 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;
int a, b, c, d, w;
if (addr == NULL)
return (NULL);
result = (char *) palloc(32);
w = addr->width;
a = (addr->address >> 24) & 0xff;
b = (addr->address >> 16) & 0xff;
c = (addr->address >> 8) & 0xff;
d = (addr->address >> 0) & 0xff;
/* Check by missing address (w > 32 ) */
if ( w >= 0 && w <= 32 )
{
/* In case of NATURAL network don't output the prefix */
if ( (a == 0 && b == 0 && c == 0 && d == 0 && w == 0 ) ||
(a < 128 && b == 0 && c == 0 && d == 0 && w == 8 ) ||
(a < 192 && c == 0 && d == 0 && w == 16 ) ||
(a < 224 && d == 0 && w == 24 ) ||
( d != 0 ) ) w = -1;
if (w == -1 )
sprintf(result, "%d.%d.%d.%d",a,b,c,d);
else
sprintf(result, "%d.%d.%d.%d/%d",a,b,c,d,w);
}
else
{
result[0] = 0; /* special case for missing address */
}
return (result);
}
/*
* Print ipaddr by format
* %A - address
* %M - maska
* %P - prefix
* %B - negated maska
*/
# define TXT_LEN_0 4
text *
ipaddr_print(ipaddr * addr, text *fmt)
{
text *result;
char *p, *op;
uint32 aaa;
int a, b, c, d;
if (addr == NULL)
return (NULL);
result = (text *) palloc( sizeof(text) + 64 );
/* Check by missing address (w > 32 ) */
for ( p = fmt->vl_dat, op = result->vl_dat; *p && (p - fmt->vl_dat) < (fmt->vl_len - TXT_LEN_0) && (op - result->vl_dat) < 48; p++) {
if ( *p != '%' ) {
*op++ = *p;
continue;
};
p++;
if ( *p == 'A' )
{
aaa = addr->address;
goto pta;
};
if ( *p == 'M' ) {
aaa = build_mask(addr->width);
goto pta;
}
if ( *p == 'B' ) {
aaa = build_mask(32 - addr->width) >> addr->width;
goto pta;
}
if ( *p == 'P' ) {
sprintf(op,"%d",addr->width);
while ( *op) op++;
continue;
};
*op++ = *p;
continue;
pta:
a = (aaa >> 24) & 0xff;
b = (aaa >> 16) & 0xff;
c = (aaa >> 8) & 0xff;
d = (aaa >> 0) & 0xff;
sprintf(op, "%d.%d.%d.%d",a,b,c,d);
while ( *op ) op++;
continue;
};
*op = 0;
result->vl_len = (op - result->vl_dat) + TXT_LEN_0;
return (result);
}
/*
* Boolean tests for magnitude.
*/
bool
ipaddr_lt(ipaddr * a1, ipaddr * a2)
{
if ( a1->address == a2->address ) return(a1->width < a2->width);
return (a1->address < a2->address);
};
bool
ipaddr_le(ipaddr * a1, ipaddr * a2)
{
if ( a1->address == a2->address ) return(a1->width <= a2->width);
return (a1->address <= a2->address);
};
bool
ipaddr_eq(ipaddr * a1, ipaddr * a2)
{
if ( a1->address == a2->address ) return(a1->width == a2->width);
return (a1->address == a2->address);
};
bool
ipaddr_ge(ipaddr * a1, ipaddr * a2)
{
if ( a1->address == a2->address ) return(a1->width >= a2->width);
return (a1->address >= a2->address);
};
bool
ipaddr_gt(ipaddr * a1, ipaddr * a2)
{
if ( a1->address == a2->address ) return(a1->width > a2->width);
return (a1->address > a2->address);
};
bool
ipaddr_ne(ipaddr * a1, ipaddr * a2)
{
if ( a1->address == a2->address ) return(a1->width != a2->width);
return (a1->address != a2->address);
};
/*
* Comparison function for sorting:
*/
int4
ipaddr_cmp(ipaddr * a1, ipaddr * a2)
{
if (a1->address < a2->address)
return -1;
else if (a1->address > a2->address)
return 1;
else
{
if (a1->width < a2->width)
return -1;
else if (a1->width > a2->width)
return 1;
}
return 0;
}
/*
* The number of hosts in the network
*/
int4
ipaddr_len(ipaddr * a)
{
if ( a->width > 32 || a->width < 0 ) return(0);
return(1 << (32 - a->width));
}
/*
* The number of network bits
*/
int4
ipaddr_pref(ipaddr * a)
{
if ( a->width > 32 || a->width < 0 ) return(0);
return(a->width);
}
/*
* The host addr as an integer
*/
int4
ipaddr_integer(ipaddr * a)
{
return(a->address);
}
/*
* Test whether an address is within a given subnet:
*/
bool
ipaddr_in_net(ipaddr * a1, ipaddr * a2)
{
uint32 maskbits;
if (a1->width < a2->width)
return FALSE;
if ((a1->width == 32) && (a2->width == 32))
return ipaddr_eq(a1, a2);
maskbits = build_mask(a2->width);
if ((a1->address & maskbits) == (a2->address & maskbits))
return TRUE;
return FALSE;
}
/*
* Test whether an address is the network or a host in the network:
*/
bool
ipaddr_is_net(ipaddr * a)
{
uint32 maskbits;
if (a->width == 32)
return FALSE;
maskbits = build_mask(a->width);
if ( (a->address & maskbits) == a->address )
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;
}
/*
* Return the base network of the address/network:
*/
ipaddr *
ipaddr_net(ipaddr * a)
{
ipaddr *result;
result = (ipaddr *) palloc(sizeof(ipaddr));
result->address = a->address;
result->address &= build_mask(a->width);
result->width = a->width;
return result;
}
/*
* Compose ipaddr from ADDR and PREFIX
*/
ipaddr *
ipaddr_compose(int4 addr, int4 pref)
{
ipaddr *result;
result = (ipaddr *) palloc(sizeof(ipaddr));
if ( pref < 0 || pref > 32 ) {
pref = 255;
addr = 0;
};
result->address = addr;
result->width = pref;
return result;
}
/*
* Plus and Minus operators
*/
ipaddr *
ipaddr_plus(ipaddr * a, int4 i)
{
ipaddr *result;
result = (ipaddr *) palloc(sizeof(ipaddr));
result->address = a->address + i;
result->width = a->width;
return result;
}
ipaddr *
ipaddr_minus(ipaddr * a, int4 i)
{
ipaddr *result;
result = (ipaddr *) palloc(sizeof(ipaddr));
result->address = a->address - i;
result->width = a->width;
return result;
}
/*
* eof
*/