1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-04 20:11:56 +03:00

Add support for EUI-64 MAC addresses as macaddr8

This adds in support for EUI-64 MAC addresses by adding a new data type
called 'macaddr8' (using our usual convention of indicating the number
of bytes stored).

This was largely a copy-and-paste from the macaddr data type, with
appropriate adjustments for having 8 bytes instead of 6 and adding
support for converting a provided EUI-48 (6 byte format) to the EUI-64
format.  Conversion from EUI-48 to EUI-64 inserts FFFE as the 4th and
5th bytes but does not perform the IPv6 modified EUI-64 action of
flipping the 7th bit, but we add a function to perform that specific
action for the user as it may be commonly done by users who wish to
calculate their IPv6 address based on their network prefix and 48-bit
MAC address.

Author: Haribabu Kommi, with a good bit of rework of macaddr8_in by me.
Reviewed by: Vitaly Burovoy, Kuntal Ghosh

Discussion: https://postgr.es/m/CAJrrPGcUi8ZH+KkK+=TctNQ+EfkeCEHtMU_yo1mvX8hsk_ghNQ@mail.gmail.com
This commit is contained in:
Stephen Frost
2017-03-15 11:16:25 -04:00
parent 42bdaebf16
commit c7a9fa399d
37 changed files with 1826 additions and 20 deletions

View File

@@ -0,0 +1,560 @@
/*-------------------------------------------------------------------------
*
* mac8.c
* PostgreSQL type definitions for 8 byte (EUI-64) MAC addresses.
*
* EUI-48 (6 byte) MAC addresses are accepted as input and are stored in
* EUI-64 format, with the 4th and 5th bytes set to FF and FE, respectively.
*
* Output is always in 8 byte (EUI-64) format.
*
* The following code is written with the assumption that the OUI field
* size is 24 bits.
*
* Portions Copyright (c) 1998-2017, PostgreSQL Global Development Group
*
* IDENTIFICATION
* src/backend/utils/adt/mac8.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/hash.h"
#include "libpq/pqformat.h"
#include "utils/builtins.h"
#include "utils/inet.h"
/*
* Utility macros used for sorting and comparing:
*/
#define hibits(addr) \
((unsigned long)(((addr)->a<<24) | ((addr)->b<<16) | ((addr)->c<<8) | ((addr)->d)))
#define lobits(addr) \
((unsigned long)(((addr)->e<<24) | ((addr)->f<<16) | ((addr)->g<<8) | ((addr)->h)))
static unsigned char hex2_to_uchar(const char *str, int offset);
static const int hexlookup[128] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
};
static inline unsigned char
hex2_to_uchar(const char *str, int offset)
{
unsigned char ret = 0;
int lookup;
const char *ptr = str + offset;
/* Handle the first character */
if (*ptr < 0 || *ptr >= 127)
goto invalid_input;
lookup = hexlookup[(unsigned char) *ptr];
if (lookup < 0 || lookup > 15)
goto invalid_input;
ret = lookup << 4;
/* Move to the second character */
ptr++;
if (*ptr < 0 || *ptr > 127)
goto invalid_input;
lookup = hexlookup[(unsigned char) *ptr];
if (lookup < 0 || lookup > 15)
goto invalid_input;
ret += lookup;
return ret;
invalid_input:
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
str)));
/* We do not actually reach here */
return 0;
}
/*
* MAC address (EUI-48 and EUI-64) reader. Accepts several common notations.
*/
Datum
macaddr8_in(PG_FUNCTION_ARGS)
{
const char *str = PG_GETARG_CSTRING(0);
const char *ptr = str;
macaddr8 *result;
unsigned char a = 0,
b = 0,
c = 0,
d = 0,
e = 0,
f = 0,
g = 0,
h = 0;
int count = 0;
char spacer = '\0';
/* skip leading spaces */
while (*ptr && isspace((unsigned char) *ptr))
ptr++;
/* digits must always come in pairs */
while (*ptr && *(ptr + 1))
{
/*
* Attempt to decode each byte, which must be 2 hex digits in a row.
* If either digit is not hex, hex2_to_uchar will throw ereport() for
* us. Either 6 or 8 byte MAC addresses are supported.
*/
/* Attempt to collect a byte */
count++;
switch (count)
{
case 1:
a = hex2_to_uchar(str, ptr - str);
break;
case 2:
b = hex2_to_uchar(str, ptr - str);
break;
case 3:
c = hex2_to_uchar(str, ptr - str);
break;
case 4:
d = hex2_to_uchar(str, ptr - str);
break;
case 5:
e = hex2_to_uchar(str, ptr - str);
break;
case 6:
f = hex2_to_uchar(str, ptr - str);
break;
case 7:
g = hex2_to_uchar(str, ptr - str);
break;
case 8:
h = hex2_to_uchar(str, ptr - str);
break;
default:
/* must be trailing garbage... */
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
str)));
}
/* Move forward to where the next byte should be */
ptr += 2;
/* Check for a spacer, these are valid, anything else is not */
if (*ptr == ':' || *ptr == '-' || *ptr == '.')
{
/* remember the spacer used, if it changes then it isn't valid */
if (spacer == '\0')
spacer = *ptr;
/* Have to use the same spacer throughout */
else if (spacer != *ptr)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
str)));
/* move past the spacer */
ptr++;
}
/* allow trailing whitespace after if we have 6 or 8 bytes */
if (count == 6 || count == 8)
{
if (isspace((unsigned char) *ptr))
{
while (*++ptr && isspace((unsigned char) *ptr));
/* If we found a space and then non-space, it's invalid */
if (*ptr)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
str)));
}
}
}
/* Convert a 6 byte MAC address to macaddr8 */
if (count == 6)
{
h = f;
g = e;
f = d;
d = 0xFF;
e = 0xFE;
}
else if (count != 8)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
str)));
result = (macaddr8 *) palloc0(sizeof(macaddr8));
result->a = a;
result->b = b;
result->c = c;
result->d = d;
result->e = e;
result->f = f;
result->g = g;
result->h = h;
PG_RETURN_MACADDR8_P(result);
}
/*
* MAC8 address (EUI-64) output function. Fixed format.
*/
Datum
macaddr8_out(PG_FUNCTION_ARGS)
{
macaddr8 *addr = PG_GETARG_MACADDR8_P(0);
char *result;
result = (char *) palloc(32);
snprintf(result, 32, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
addr->a, addr->b, addr->c, addr->d,
addr->e, addr->f, addr->g, addr->h);
PG_RETURN_CSTRING(result);
}
/*
* macaddr8_recv - converts external binary format(EUI-48 and EUI-64) to macaddr8
*
* The external representation is just the eight bytes, MSB first.
*/
Datum
macaddr8_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
macaddr8 *addr;
addr = (macaddr8 *) palloc0(sizeof(macaddr8));
addr->a = pq_getmsgbyte(buf);
addr->b = pq_getmsgbyte(buf);
addr->c = pq_getmsgbyte(buf);
if (buf->len == 6)
{
addr->d = 0xFF;
addr->e = 0xFE;
}
else
{
addr->d = pq_getmsgbyte(buf);
addr->e = pq_getmsgbyte(buf);
}
addr->f = pq_getmsgbyte(buf);
addr->g = pq_getmsgbyte(buf);
addr->h = pq_getmsgbyte(buf);
PG_RETURN_MACADDR8_P(addr);
}
/*
* macaddr8_send - converts macaddr8(EUI-64) to binary format
*/
Datum
macaddr8_send(PG_FUNCTION_ARGS)
{
macaddr8 *addr = PG_GETARG_MACADDR8_P(0);
StringInfoData buf;
pq_begintypsend(&buf);
pq_sendbyte(&buf, addr->a);
pq_sendbyte(&buf, addr->b);
pq_sendbyte(&buf, addr->c);
pq_sendbyte(&buf, addr->d);
pq_sendbyte(&buf, addr->e);
pq_sendbyte(&buf, addr->f);
pq_sendbyte(&buf, addr->g);
pq_sendbyte(&buf, addr->h);
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
/*
* macaddr8_cmp_internal - comparison function for sorting:
*/
static int32
macaddr8_cmp_internal(macaddr8 *a1, macaddr8 *a2)
{
if (hibits(a1) < hibits(a2))
return -1;
else if (hibits(a1) > hibits(a2))
return 1;
else if (lobits(a1) < lobits(a2))
return -1;
else if (lobits(a1) > lobits(a2))
return 1;
else
return 0;
}
Datum
macaddr8_cmp(PG_FUNCTION_ARGS)
{
macaddr8 *a1 = PG_GETARG_MACADDR8_P(0);
macaddr8 *a2 = PG_GETARG_MACADDR8_P(1);
PG_RETURN_INT32(macaddr8_cmp_internal(a1, a2));
}
/*
* Boolean comparison functions.
*/
Datum
macaddr8_lt(PG_FUNCTION_ARGS)
{
macaddr8 *a1 = PG_GETARG_MACADDR8_P(0);
macaddr8 *a2 = PG_GETARG_MACADDR8_P(1);
PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) < 0);
}
Datum
macaddr8_le(PG_FUNCTION_ARGS)
{
macaddr8 *a1 = PG_GETARG_MACADDR8_P(0);
macaddr8 *a2 = PG_GETARG_MACADDR8_P(1);
PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) <= 0);
}
Datum
macaddr8_eq(PG_FUNCTION_ARGS)
{
macaddr8 *a1 = PG_GETARG_MACADDR8_P(0);
macaddr8 *a2 = PG_GETARG_MACADDR8_P(1);
PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) == 0);
}
Datum
macaddr8_ge(PG_FUNCTION_ARGS)
{
macaddr8 *a1 = PG_GETARG_MACADDR8_P(0);
macaddr8 *a2 = PG_GETARG_MACADDR8_P(1);
PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) >= 0);
}
Datum
macaddr8_gt(PG_FUNCTION_ARGS)
{
macaddr8 *a1 = PG_GETARG_MACADDR8_P(0);
macaddr8 *a2 = PG_GETARG_MACADDR8_P(1);
PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) > 0);
}
Datum
macaddr8_ne(PG_FUNCTION_ARGS)
{
macaddr8 *a1 = PG_GETARG_MACADDR8_P(0);
macaddr8 *a2 = PG_GETARG_MACADDR8_P(1);
PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) != 0);
}
/*
* Support function for hash indexes on macaddr8.
*/
Datum
hashmacaddr8(PG_FUNCTION_ARGS)
{
macaddr8 *key = PG_GETARG_MACADDR8_P(0);
return hash_any((unsigned char *) key, sizeof(macaddr8));
}
/*
* Arithmetic functions: bitwise NOT, AND, OR.
*/
Datum
macaddr8_not(PG_FUNCTION_ARGS)
{
macaddr8 *addr = PG_GETARG_MACADDR8_P(0);
macaddr8 *result;
result = (macaddr8 *) palloc0(sizeof(macaddr8));
result->a = ~addr->a;
result->b = ~addr->b;
result->c = ~addr->c;
result->d = ~addr->d;
result->e = ~addr->e;
result->f = ~addr->f;
result->g = ~addr->g;
result->h = ~addr->h;
PG_RETURN_MACADDR8_P(result);
}
Datum
macaddr8_and(PG_FUNCTION_ARGS)
{
macaddr8 *addr1 = PG_GETARG_MACADDR8_P(0);
macaddr8 *addr2 = PG_GETARG_MACADDR8_P(1);
macaddr8 *result;
result = (macaddr8 *) palloc0(sizeof(macaddr8));
result->a = addr1->a & addr2->a;
result->b = addr1->b & addr2->b;
result->c = addr1->c & addr2->c;
result->d = addr1->d & addr2->d;
result->e = addr1->e & addr2->e;
result->f = addr1->f & addr2->f;
result->g = addr1->g & addr2->g;
result->h = addr1->h & addr2->h;
PG_RETURN_MACADDR8_P(result);
}
Datum
macaddr8_or(PG_FUNCTION_ARGS)
{
macaddr8 *addr1 = PG_GETARG_MACADDR8_P(0);
macaddr8 *addr2 = PG_GETARG_MACADDR8_P(1);
macaddr8 *result;
result = (macaddr8 *) palloc0(sizeof(macaddr8));
result->a = addr1->a | addr2->a;
result->b = addr1->b | addr2->b;
result->c = addr1->c | addr2->c;
result->d = addr1->d | addr2->d;
result->e = addr1->e | addr2->e;
result->f = addr1->f | addr2->f;
result->g = addr1->g | addr2->g;
result->h = addr1->h | addr2->h;
PG_RETURN_MACADDR8_P(result);
}
/*
* Truncation function to allow comparing macaddr8 manufacturers.
*/
Datum
macaddr8_trunc(PG_FUNCTION_ARGS)
{
macaddr8 *addr = PG_GETARG_MACADDR8_P(0);
macaddr8 *result;
result = (macaddr8 *) palloc0(sizeof(macaddr8));
result->a = addr->a;
result->b = addr->b;
result->c = addr->c;
result->d = 0;
result->e = 0;
result->f = 0;
result->g = 0;
result->h = 0;
PG_RETURN_MACADDR8_P(result);
}
/*
* Set 7th bit for modified EUI-64 as used in IPv6.
*/
Datum
macaddr8_set7bit(PG_FUNCTION_ARGS)
{
macaddr8 *addr = PG_GETARG_MACADDR8_P(0);
macaddr8 *result;
result = (macaddr8 *) palloc0(sizeof(macaddr8));
result->a = addr->a | 0x02;
result->b = addr->b;
result->c = addr->c;
result->d = addr->d;
result->e = addr->e;
result->f = addr->f;
result->g = addr->g;
result->h = addr->h;
PG_RETURN_MACADDR8_P(result);
}
/*----------------------------------------------------------
* Conversion operators.
*---------------------------------------------------------*/
Datum
macaddrtomacaddr8(PG_FUNCTION_ARGS)
{
macaddr *addr6 = PG_GETARG_MACADDR_P(0);
macaddr8 *result;
result = (macaddr8 *) palloc0(sizeof(macaddr8));
result->a = addr6->a;
result->b = addr6->b;
result->c = addr6->c;
result->d = 0xFF;
result->e = 0xFE;
result->f = addr6->d;
result->g = addr6->e;
result->h = addr6->f;
PG_RETURN_MACADDR8_P(result);
}
Datum
macaddr8tomacaddr(PG_FUNCTION_ARGS)
{
macaddr8 *addr = PG_GETARG_MACADDR8_P(0);
macaddr *result;
result = (macaddr *) palloc0(sizeof(macaddr));
if ((addr->d != 0xFF) || (addr->e != 0xFE))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("macaddr8 data out of range to convert to macaddr"),
errhint("Only addresses that have FF and FE as values in the "
"4th and 5th bytes, from the left, for example: "
"XX-XX-XX-FF-FE-XX-XX-XX, are eligible to be converted "
"from macaddr8 to macaddr.")));
result->a = addr->a;
result->b = addr->b;
result->c = addr->c;
result->d = addr->f;
result->e = addr->g;
result->f = addr->h;
PG_RETURN_MACADDR_P(result);
}