summaryrefslogtreecommitdiffstats
path: root/src/backend/utils/adt/mac8.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/mac8.c')
-rw-r--r--src/backend/utils/adt/mac8.c577
1 files changed, 577 insertions, 0 deletions
diff --git a/src/backend/utils/adt/mac8.c b/src/backend/utils/adt/mac8.c
new file mode 100644
index 0000000..b7b2968
--- /dev/null
+++ b/src/backend/utils/adt/mac8.c
@@ -0,0 +1,577 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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-2020, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/mac8.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "common/hashfn.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 unsigned char *ptr, const unsigned char *str);
+
+static const signed char 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,
+};
+
+/*
+ * hex2_to_uchar - convert 2 hex digits to a byte (unsigned char)
+ *
+ * This will ereport() if the end of the string is reached ('\0' found), or if
+ * either character is not a valid hex digit.
+ *
+ * ptr is the pointer to where the digits to convert are in the string, str is
+ * the entire string, which is used only for error reporting.
+ */
+static inline unsigned char
+hex2_to_uchar(const unsigned char *ptr, const unsigned char *str)
+{
+ unsigned char ret = 0;
+ signed char lookup;
+
+ /* Handle the first character */
+ if (*ptr > 127)
+ goto invalid_input;
+
+ lookup = hexlookup[*ptr];
+ if (lookup < 0)
+ goto invalid_input;
+
+ ret = lookup << 4;
+
+ /* Move to the second character */
+ ptr++;
+
+ if (*ptr > 127)
+ goto invalid_input;
+
+ lookup = hexlookup[*ptr];
+ if (lookup < 0)
+ 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 unsigned char *str = (unsigned char *) PG_GETARG_CSTRING(0);
+ const unsigned 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;
+ unsigned char spacer = '\0';
+
+ /* skip leading spaces */
+ while (*ptr && isspace(*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(ptr, str);
+ break;
+ case 2:
+ b = hex2_to_uchar(ptr, str);
+ break;
+ case 3:
+ c = hex2_to_uchar(ptr, str);
+ break;
+ case 4:
+ d = hex2_to_uchar(ptr, str);
+ break;
+ case 5:
+ e = hex2_to_uchar(ptr, str);
+ break;
+ case 6:
+ f = hex2_to_uchar(ptr, str);
+ break;
+ case 7:
+ g = hex2_to_uchar(ptr, str);
+ break;
+ case 8:
+ h = hex2_to_uchar(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(*ptr))
+ {
+ while (*++ptr && isspace(*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));
+}
+
+Datum
+hashmacaddr8extended(PG_FUNCTION_ARGS)
+{
+ macaddr8 *key = PG_GETARG_MACADDR8_P(0);
+
+ return hash_any_extended((unsigned char *) key, sizeof(macaddr8),
+ PG_GETARG_INT64(1));
+}
+
+/*
+ * 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);
+}