1 /*-------------------------------------------------------------------------
4 * PostgreSQL type definitions for 8 byte (EUI-64) MAC addresses.
6 * EUI-48 (6 byte) MAC addresses are accepted as input and are stored in
7 * EUI-64 format, with the 4th and 5th bytes set to FF and FE, respectively.
9 * Output is always in 8 byte (EUI-64) format.
11 * The following code is written with the assumption that the OUI field
14 * Portions Copyright (c) 1998-2017, PostgreSQL Global Development Group
17 * src/backend/utils/adt/mac8.c
19 *-------------------------------------------------------------------------
24 #include "access/hash.h"
25 #include "libpq/pqformat.h"
26 #include "utils/builtins.h"
27 #include "utils/inet.h"
30 * Utility macros used for sorting and comparing:
32 #define hibits(addr) \
33 ((unsigned long)(((addr)->a<<24) | ((addr)->b<<16) | ((addr)->c<<8) | ((addr)->d)))
35 #define lobits(addr) \
36 ((unsigned long)(((addr)->e<<24) | ((addr)->f<<16) | ((addr)->g<<8) | ((addr)->h)))
38 static unsigned char hex2_to_uchar(const char *str, int offset);
40 static const int hexlookup[128] = {
41 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
42 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
43 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
44 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
45 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
46 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
47 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
48 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
51 static inline unsigned char
52 hex2_to_uchar(const char *str, int offset)
54 unsigned char ret = 0;
56 const char *ptr = str + offset;
58 /* Handle the first character */
59 if (*ptr < 0 || *ptr >= 127)
62 lookup = hexlookup[(unsigned char) *ptr];
63 if (lookup < 0 || lookup > 15)
68 /* Move to the second character */
71 if (*ptr < 0 || *ptr > 127)
74 lookup = hexlookup[(unsigned char) *ptr];
75 if (lookup < 0 || lookup > 15)
84 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
85 errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
88 /* We do not actually reach here */
93 * MAC address (EUI-48 and EUI-64) reader. Accepts several common notations.
96 macaddr8_in(PG_FUNCTION_ARGS)
98 const char *str = PG_GETARG_CSTRING(0);
99 const char *ptr = str;
112 /* skip leading spaces */
113 while (*ptr && isspace((unsigned char) *ptr))
116 /* digits must always come in pairs */
117 while (*ptr && *(ptr + 1))
120 * Attempt to decode each byte, which must be 2 hex digits in a row.
121 * If either digit is not hex, hex2_to_uchar will throw ereport() for
122 * us. Either 6 or 8 byte MAC addresses are supported.
125 /* Attempt to collect a byte */
131 a = hex2_to_uchar(str, ptr - str);
134 b = hex2_to_uchar(str, ptr - str);
137 c = hex2_to_uchar(str, ptr - str);
140 d = hex2_to_uchar(str, ptr - str);
143 e = hex2_to_uchar(str, ptr - str);
146 f = hex2_to_uchar(str, ptr - str);
149 g = hex2_to_uchar(str, ptr - str);
152 h = hex2_to_uchar(str, ptr - str);
155 /* must be trailing garbage... */
157 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
158 errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
162 /* Move forward to where the next byte should be */
165 /* Check for a spacer, these are valid, anything else is not */
166 if (*ptr == ':' || *ptr == '-' || *ptr == '.')
168 /* remember the spacer used, if it changes then it isn't valid */
172 /* Have to use the same spacer throughout */
173 else if (spacer != *ptr)
175 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
176 errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
179 /* move past the spacer */
183 /* allow trailing whitespace after if we have 6 or 8 bytes */
184 if (count == 6 || count == 8)
186 if (isspace((unsigned char) *ptr))
188 while (*++ptr && isspace((unsigned char) *ptr));
190 /* If we found a space and then non-space, it's invalid */
193 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
194 errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
200 /* Convert a 6 byte MAC address to macaddr8 */
212 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
213 errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
216 result = (macaddr8 *) palloc0(sizeof(macaddr8));
227 PG_RETURN_MACADDR8_P(result);
231 * MAC8 address (EUI-64) output function. Fixed format.
234 macaddr8_out(PG_FUNCTION_ARGS)
236 macaddr8 *addr = PG_GETARG_MACADDR8_P(0);
239 result = (char *) palloc(32);
241 snprintf(result, 32, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
242 addr->a, addr->b, addr->c, addr->d,
243 addr->e, addr->f, addr->g, addr->h);
245 PG_RETURN_CSTRING(result);
249 * macaddr8_recv - converts external binary format(EUI-48 and EUI-64) to macaddr8
251 * The external representation is just the eight bytes, MSB first.
254 macaddr8_recv(PG_FUNCTION_ARGS)
256 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
259 addr = (macaddr8 *) palloc0(sizeof(macaddr8));
261 addr->a = pq_getmsgbyte(buf);
262 addr->b = pq_getmsgbyte(buf);
263 addr->c = pq_getmsgbyte(buf);
272 addr->d = pq_getmsgbyte(buf);
273 addr->e = pq_getmsgbyte(buf);
276 addr->f = pq_getmsgbyte(buf);
277 addr->g = pq_getmsgbyte(buf);
278 addr->h = pq_getmsgbyte(buf);
280 PG_RETURN_MACADDR8_P(addr);
284 * macaddr8_send - converts macaddr8(EUI-64) to binary format
287 macaddr8_send(PG_FUNCTION_ARGS)
289 macaddr8 *addr = PG_GETARG_MACADDR8_P(0);
292 pq_begintypsend(&buf);
293 pq_sendbyte(&buf, addr->a);
294 pq_sendbyte(&buf, addr->b);
295 pq_sendbyte(&buf, addr->c);
296 pq_sendbyte(&buf, addr->d);
297 pq_sendbyte(&buf, addr->e);
298 pq_sendbyte(&buf, addr->f);
299 pq_sendbyte(&buf, addr->g);
300 pq_sendbyte(&buf, addr->h);
302 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
307 * macaddr8_cmp_internal - comparison function for sorting:
310 macaddr8_cmp_internal(macaddr8 *a1, macaddr8 *a2)
312 if (hibits(a1) < hibits(a2))
314 else if (hibits(a1) > hibits(a2))
316 else if (lobits(a1) < lobits(a2))
318 else if (lobits(a1) > lobits(a2))
325 macaddr8_cmp(PG_FUNCTION_ARGS)
327 macaddr8 *a1 = PG_GETARG_MACADDR8_P(0);
328 macaddr8 *a2 = PG_GETARG_MACADDR8_P(1);
330 PG_RETURN_INT32(macaddr8_cmp_internal(a1, a2));
334 * Boolean comparison functions.
338 macaddr8_lt(PG_FUNCTION_ARGS)
340 macaddr8 *a1 = PG_GETARG_MACADDR8_P(0);
341 macaddr8 *a2 = PG_GETARG_MACADDR8_P(1);
343 PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) < 0);
347 macaddr8_le(PG_FUNCTION_ARGS)
349 macaddr8 *a1 = PG_GETARG_MACADDR8_P(0);
350 macaddr8 *a2 = PG_GETARG_MACADDR8_P(1);
352 PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) <= 0);
356 macaddr8_eq(PG_FUNCTION_ARGS)
358 macaddr8 *a1 = PG_GETARG_MACADDR8_P(0);
359 macaddr8 *a2 = PG_GETARG_MACADDR8_P(1);
361 PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) == 0);
365 macaddr8_ge(PG_FUNCTION_ARGS)
367 macaddr8 *a1 = PG_GETARG_MACADDR8_P(0);
368 macaddr8 *a2 = PG_GETARG_MACADDR8_P(1);
370 PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) >= 0);
374 macaddr8_gt(PG_FUNCTION_ARGS)
376 macaddr8 *a1 = PG_GETARG_MACADDR8_P(0);
377 macaddr8 *a2 = PG_GETARG_MACADDR8_P(1);
379 PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) > 0);
383 macaddr8_ne(PG_FUNCTION_ARGS)
385 macaddr8 *a1 = PG_GETARG_MACADDR8_P(0);
386 macaddr8 *a2 = PG_GETARG_MACADDR8_P(1);
388 PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) != 0);
392 * Support function for hash indexes on macaddr8.
395 hashmacaddr8(PG_FUNCTION_ARGS)
397 macaddr8 *key = PG_GETARG_MACADDR8_P(0);
399 return hash_any((unsigned char *) key, sizeof(macaddr8));
403 * Arithmetic functions: bitwise NOT, AND, OR.
406 macaddr8_not(PG_FUNCTION_ARGS)
408 macaddr8 *addr = PG_GETARG_MACADDR8_P(0);
411 result = (macaddr8 *) palloc0(sizeof(macaddr8));
412 result->a = ~addr->a;
413 result->b = ~addr->b;
414 result->c = ~addr->c;
415 result->d = ~addr->d;
416 result->e = ~addr->e;
417 result->f = ~addr->f;
418 result->g = ~addr->g;
419 result->h = ~addr->h;
421 PG_RETURN_MACADDR8_P(result);
425 macaddr8_and(PG_FUNCTION_ARGS)
427 macaddr8 *addr1 = PG_GETARG_MACADDR8_P(0);
428 macaddr8 *addr2 = PG_GETARG_MACADDR8_P(1);
431 result = (macaddr8 *) palloc0(sizeof(macaddr8));
432 result->a = addr1->a & addr2->a;
433 result->b = addr1->b & addr2->b;
434 result->c = addr1->c & addr2->c;
435 result->d = addr1->d & addr2->d;
436 result->e = addr1->e & addr2->e;
437 result->f = addr1->f & addr2->f;
438 result->g = addr1->g & addr2->g;
439 result->h = addr1->h & addr2->h;
441 PG_RETURN_MACADDR8_P(result);
445 macaddr8_or(PG_FUNCTION_ARGS)
447 macaddr8 *addr1 = PG_GETARG_MACADDR8_P(0);
448 macaddr8 *addr2 = PG_GETARG_MACADDR8_P(1);
451 result = (macaddr8 *) palloc0(sizeof(macaddr8));
452 result->a = addr1->a | addr2->a;
453 result->b = addr1->b | addr2->b;
454 result->c = addr1->c | addr2->c;
455 result->d = addr1->d | addr2->d;
456 result->e = addr1->e | addr2->e;
457 result->f = addr1->f | addr2->f;
458 result->g = addr1->g | addr2->g;
459 result->h = addr1->h | addr2->h;
461 PG_RETURN_MACADDR8_P(result);
465 * Truncation function to allow comparing macaddr8 manufacturers.
468 macaddr8_trunc(PG_FUNCTION_ARGS)
470 macaddr8 *addr = PG_GETARG_MACADDR8_P(0);
473 result = (macaddr8 *) palloc0(sizeof(macaddr8));
484 PG_RETURN_MACADDR8_P(result);
488 * Set 7th bit for modified EUI-64 as used in IPv6.
491 macaddr8_set7bit(PG_FUNCTION_ARGS)
493 macaddr8 *addr = PG_GETARG_MACADDR8_P(0);
496 result = (macaddr8 *) palloc0(sizeof(macaddr8));
498 result->a = addr->a | 0x02;
507 PG_RETURN_MACADDR8_P(result);
510 /*----------------------------------------------------------
511 * Conversion operators.
512 *---------------------------------------------------------*/
515 macaddrtomacaddr8(PG_FUNCTION_ARGS)
517 macaddr *addr6 = PG_GETARG_MACADDR_P(0);
520 result = (macaddr8 *) palloc0(sizeof(macaddr8));
522 result->a = addr6->a;
523 result->b = addr6->b;
524 result->c = addr6->c;
527 result->f = addr6->d;
528 result->g = addr6->e;
529 result->h = addr6->f;
532 PG_RETURN_MACADDR8_P(result);
536 macaddr8tomacaddr(PG_FUNCTION_ARGS)
538 macaddr8 *addr = PG_GETARG_MACADDR8_P(0);
541 result = (macaddr *) palloc0(sizeof(macaddr));
543 if ((addr->d != 0xFF) || (addr->e != 0xFE))
545 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
546 errmsg("macaddr8 data out of range to convert to macaddr"),
547 errhint("Only addresses that have FF and FE as values in the "
548 "4th and 5th bytes, from the left, for example: "
549 "XX-XX-XX-FF-FE-XX-XX-XX, are eligible to be converted "
550 "from macaddr8 to macaddr.")));
559 PG_RETURN_MACADDR_P(result);