PostgreSQL Source Code  git master
mac8.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * mac8.c
4  * PostgreSQL type definitions for 8 byte (EUI-64) MAC addresses.
5  *
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.
8  *
9  * Output is always in 8 byte (EUI-64) format.
10  *
11  * The following code is written with the assumption that the OUI field
12  * size is 24 bits.
13  *
14  * Portions Copyright (c) 1998-2019, PostgreSQL Global Development Group
15  *
16  * IDENTIFICATION
17  * src/backend/utils/adt/mac8.c
18  *
19  *-------------------------------------------------------------------------
20  */
21 
22 #include "postgres.h"
23 
24 #include "libpq/pqformat.h"
25 #include "utils/builtins.h"
26 #include "utils/hashutils.h"
27 #include "utils/inet.h"
28 
29 /*
30  * Utility macros used for sorting and comparing:
31  */
32 #define hibits(addr) \
33  ((unsigned long)(((addr)->a<<24) | ((addr)->b<<16) | ((addr)->c<<8) | ((addr)->d)))
34 
35 #define lobits(addr) \
36  ((unsigned long)(((addr)->e<<24) | ((addr)->f<<16) | ((addr)->g<<8) | ((addr)->h)))
37 
38 static unsigned char hex2_to_uchar(const unsigned char *str, const unsigned char *ptr);
39 
40 static const signed char 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,
49 };
50 
51 /*
52  * hex2_to_uchar - convert 2 hex digits to a byte (unsigned char)
53  *
54  * This will ereport() if the end of the string is reached ('\0' found), or if
55  * either character is not a valid hex digit.
56  *
57  * ptr is the pointer to where the digits to convert are in the string, str is
58  * the entire string, which is used only for error reporting.
59  */
60 static inline unsigned char
61 hex2_to_uchar(const unsigned char *ptr, const unsigned char *str)
62 {
63  unsigned char ret = 0;
64  signed char lookup;
65 
66  /* Handle the first character */
67  if (*ptr > 127)
68  goto invalid_input;
69 
70  lookup = hexlookup[*ptr];
71  if (lookup < 0)
72  goto invalid_input;
73 
74  ret = lookup << 4;
75 
76  /* Move to the second character */
77  ptr++;
78 
79  if (*ptr > 127)
80  goto invalid_input;
81 
82  lookup = hexlookup[*ptr];
83  if (lookup < 0)
84  goto invalid_input;
85 
86  ret += lookup;
87 
88  return ret;
89 
90 invalid_input:
91  ereport(ERROR,
92  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
93  errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
94  str)));
95 
96  /* We do not actually reach here */
97  return 0;
98 }
99 
100 /*
101  * MAC address (EUI-48 and EUI-64) reader. Accepts several common notations.
102  */
103 Datum
105 {
106  const unsigned char *str = (unsigned char *) PG_GETARG_CSTRING(0);
107  const unsigned char *ptr = str;
108  macaddr8 *result;
109  unsigned char a = 0,
110  b = 0,
111  c = 0,
112  d = 0,
113  e = 0,
114  f = 0,
115  g = 0,
116  h = 0;
117  int count = 0;
118  unsigned char spacer = '\0';
119 
120  /* skip leading spaces */
121  while (*ptr && isspace(*ptr))
122  ptr++;
123 
124  /* digits must always come in pairs */
125  while (*ptr && *(ptr + 1))
126  {
127  /*
128  * Attempt to decode each byte, which must be 2 hex digits in a row.
129  * If either digit is not hex, hex2_to_uchar will throw ereport() for
130  * us. Either 6 or 8 byte MAC addresses are supported.
131  */
132 
133  /* Attempt to collect a byte */
134  count++;
135 
136  switch (count)
137  {
138  case 1:
139  a = hex2_to_uchar(ptr, str);
140  break;
141  case 2:
142  b = hex2_to_uchar(ptr, str);
143  break;
144  case 3:
145  c = hex2_to_uchar(ptr, str);
146  break;
147  case 4:
148  d = hex2_to_uchar(ptr, str);
149  break;
150  case 5:
151  e = hex2_to_uchar(ptr, str);
152  break;
153  case 6:
154  f = hex2_to_uchar(ptr, str);
155  break;
156  case 7:
157  g = hex2_to_uchar(ptr, str);
158  break;
159  case 8:
160  h = hex2_to_uchar(ptr, str);
161  break;
162  default:
163  /* must be trailing garbage... */
164  ereport(ERROR,
165  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
166  errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
167  str)));
168  }
169 
170  /* Move forward to where the next byte should be */
171  ptr += 2;
172 
173  /* Check for a spacer, these are valid, anything else is not */
174  if (*ptr == ':' || *ptr == '-' || *ptr == '.')
175  {
176  /* remember the spacer used, if it changes then it isn't valid */
177  if (spacer == '\0')
178  spacer = *ptr;
179 
180  /* Have to use the same spacer throughout */
181  else if (spacer != *ptr)
182  ereport(ERROR,
183  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
184  errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
185  str)));
186 
187  /* move past the spacer */
188  ptr++;
189  }
190 
191  /* allow trailing whitespace after if we have 6 or 8 bytes */
192  if (count == 6 || count == 8)
193  {
194  if (isspace(*ptr))
195  {
196  while (*++ptr && isspace(*ptr));
197 
198  /* If we found a space and then non-space, it's invalid */
199  if (*ptr)
200  ereport(ERROR,
201  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
202  errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
203  str)));
204  }
205  }
206  }
207 
208  /* Convert a 6 byte MAC address to macaddr8 */
209  if (count == 6)
210  {
211  h = f;
212  g = e;
213  f = d;
214 
215  d = 0xFF;
216  e = 0xFE;
217  }
218  else if (count != 8)
219  ereport(ERROR,
220  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
221  errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
222  str)));
223 
224  result = (macaddr8 *) palloc0(sizeof(macaddr8));
225 
226  result->a = a;
227  result->b = b;
228  result->c = c;
229  result->d = d;
230  result->e = e;
231  result->f = f;
232  result->g = g;
233  result->h = h;
234 
235  PG_RETURN_MACADDR8_P(result);
236 }
237 
238 /*
239  * MAC8 address (EUI-64) output function. Fixed format.
240  */
241 Datum
243 {
244  macaddr8 *addr = PG_GETARG_MACADDR8_P(0);
245  char *result;
246 
247  result = (char *) palloc(32);
248 
249  snprintf(result, 32, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
250  addr->a, addr->b, addr->c, addr->d,
251  addr->e, addr->f, addr->g, addr->h);
252 
253  PG_RETURN_CSTRING(result);
254 }
255 
256 /*
257  * macaddr8_recv - converts external binary format(EUI-48 and EUI-64) to macaddr8
258  *
259  * The external representation is just the eight bytes, MSB first.
260  */
261 Datum
263 {
265  macaddr8 *addr;
266 
267  addr = (macaddr8 *) palloc0(sizeof(macaddr8));
268 
269  addr->a = pq_getmsgbyte(buf);
270  addr->b = pq_getmsgbyte(buf);
271  addr->c = pq_getmsgbyte(buf);
272 
273  if (buf->len == 6)
274  {
275  addr->d = 0xFF;
276  addr->e = 0xFE;
277  }
278  else
279  {
280  addr->d = pq_getmsgbyte(buf);
281  addr->e = pq_getmsgbyte(buf);
282  }
283 
284  addr->f = pq_getmsgbyte(buf);
285  addr->g = pq_getmsgbyte(buf);
286  addr->h = pq_getmsgbyte(buf);
287 
288  PG_RETURN_MACADDR8_P(addr);
289 }
290 
291 /*
292  * macaddr8_send - converts macaddr8(EUI-64) to binary format
293  */
294 Datum
296 {
297  macaddr8 *addr = PG_GETARG_MACADDR8_P(0);
299 
300  pq_begintypsend(&buf);
301  pq_sendbyte(&buf, addr->a);
302  pq_sendbyte(&buf, addr->b);
303  pq_sendbyte(&buf, addr->c);
304  pq_sendbyte(&buf, addr->d);
305  pq_sendbyte(&buf, addr->e);
306  pq_sendbyte(&buf, addr->f);
307  pq_sendbyte(&buf, addr->g);
308  pq_sendbyte(&buf, addr->h);
309 
311 }
312 
313 
314 /*
315  * macaddr8_cmp_internal - comparison function for sorting:
316  */
317 static int32
319 {
320  if (hibits(a1) < hibits(a2))
321  return -1;
322  else if (hibits(a1) > hibits(a2))
323  return 1;
324  else if (lobits(a1) < lobits(a2))
325  return -1;
326  else if (lobits(a1) > lobits(a2))
327  return 1;
328  else
329  return 0;
330 }
331 
332 Datum
334 {
337 
339 }
340 
341 /*
342  * Boolean comparison functions.
343  */
344 
345 Datum
347 {
350 
352 }
353 
354 Datum
356 {
359 
361 }
362 
363 Datum
365 {
368 
370 }
371 
372 Datum
374 {
377 
379 }
380 
381 Datum
383 {
386 
388 }
389 
390 Datum
392 {
395 
397 }
398 
399 /*
400  * Support function for hash indexes on macaddr8.
401  */
402 Datum
404 {
406 
407  return hash_any((unsigned char *) key, sizeof(macaddr8));
408 }
409 
410 Datum
412 {
414 
415  return hash_any_extended((unsigned char *) key, sizeof(macaddr8),
416  PG_GETARG_INT64(1));
417 }
418 
419 /*
420  * Arithmetic functions: bitwise NOT, AND, OR.
421  */
422 Datum
424 {
425  macaddr8 *addr = PG_GETARG_MACADDR8_P(0);
426  macaddr8 *result;
427 
428  result = (macaddr8 *) palloc0(sizeof(macaddr8));
429  result->a = ~addr->a;
430  result->b = ~addr->b;
431  result->c = ~addr->c;
432  result->d = ~addr->d;
433  result->e = ~addr->e;
434  result->f = ~addr->f;
435  result->g = ~addr->g;
436  result->h = ~addr->h;
437 
438  PG_RETURN_MACADDR8_P(result);
439 }
440 
441 Datum
443 {
444  macaddr8 *addr1 = PG_GETARG_MACADDR8_P(0);
445  macaddr8 *addr2 = PG_GETARG_MACADDR8_P(1);
446  macaddr8 *result;
447 
448  result = (macaddr8 *) palloc0(sizeof(macaddr8));
449  result->a = addr1->a & addr2->a;
450  result->b = addr1->b & addr2->b;
451  result->c = addr1->c & addr2->c;
452  result->d = addr1->d & addr2->d;
453  result->e = addr1->e & addr2->e;
454  result->f = addr1->f & addr2->f;
455  result->g = addr1->g & addr2->g;
456  result->h = addr1->h & addr2->h;
457 
458  PG_RETURN_MACADDR8_P(result);
459 }
460 
461 Datum
463 {
464  macaddr8 *addr1 = PG_GETARG_MACADDR8_P(0);
465  macaddr8 *addr2 = PG_GETARG_MACADDR8_P(1);
466  macaddr8 *result;
467 
468  result = (macaddr8 *) palloc0(sizeof(macaddr8));
469  result->a = addr1->a | addr2->a;
470  result->b = addr1->b | addr2->b;
471  result->c = addr1->c | addr2->c;
472  result->d = addr1->d | addr2->d;
473  result->e = addr1->e | addr2->e;
474  result->f = addr1->f | addr2->f;
475  result->g = addr1->g | addr2->g;
476  result->h = addr1->h | addr2->h;
477 
478  PG_RETURN_MACADDR8_P(result);
479 }
480 
481 /*
482  * Truncation function to allow comparing macaddr8 manufacturers.
483  */
484 Datum
486 {
487  macaddr8 *addr = PG_GETARG_MACADDR8_P(0);
488  macaddr8 *result;
489 
490  result = (macaddr8 *) palloc0(sizeof(macaddr8));
491 
492  result->a = addr->a;
493  result->b = addr->b;
494  result->c = addr->c;
495  result->d = 0;
496  result->e = 0;
497  result->f = 0;
498  result->g = 0;
499  result->h = 0;
500 
501  PG_RETURN_MACADDR8_P(result);
502 }
503 
504 /*
505  * Set 7th bit for modified EUI-64 as used in IPv6.
506  */
507 Datum
509 {
510  macaddr8 *addr = PG_GETARG_MACADDR8_P(0);
511  macaddr8 *result;
512 
513  result = (macaddr8 *) palloc0(sizeof(macaddr8));
514 
515  result->a = addr->a | 0x02;
516  result->b = addr->b;
517  result->c = addr->c;
518  result->d = addr->d;
519  result->e = addr->e;
520  result->f = addr->f;
521  result->g = addr->g;
522  result->h = addr->h;
523 
524  PG_RETURN_MACADDR8_P(result);
525 }
526 
527 /*----------------------------------------------------------
528  * Conversion operators.
529  *---------------------------------------------------------*/
530 
531 Datum
533 {
534  macaddr *addr6 = PG_GETARG_MACADDR_P(0);
535  macaddr8 *result;
536 
537  result = (macaddr8 *) palloc0(sizeof(macaddr8));
538 
539  result->a = addr6->a;
540  result->b = addr6->b;
541  result->c = addr6->c;
542  result->d = 0xFF;
543  result->e = 0xFE;
544  result->f = addr6->d;
545  result->g = addr6->e;
546  result->h = addr6->f;
547 
548 
549  PG_RETURN_MACADDR8_P(result);
550 }
551 
552 Datum
554 {
555  macaddr8 *addr = PG_GETARG_MACADDR8_P(0);
556  macaddr *result;
557 
558  result = (macaddr *) palloc0(sizeof(macaddr));
559 
560  if ((addr->d != 0xFF) || (addr->e != 0xFE))
561  ereport(ERROR,
562  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
563  errmsg("macaddr8 data out of range to convert to macaddr"),
564  errhint("Only addresses that have FF and FE as values in the "
565  "4th and 5th bytes from the left, for example "
566  "xx:xx:xx:ff:fe:xx:xx:xx, are eligible to be converted "
567  "from macaddr8 to macaddr.")));
568 
569  result->a = addr->a;
570  result->b = addr->b;
571  result->c = addr->c;
572  result->d = addr->f;
573  result->e = addr->g;
574  result->f = addr->h;
575 
576  PG_RETURN_MACADDR_P(result);
577 }
Datum macaddr8_cmp(PG_FUNCTION_ARGS)
Definition: mac8.c:333
Datum macaddr8_send(PG_FUNCTION_ARGS)
Definition: mac8.c:295
Datum macaddr8_set7bit(PG_FUNCTION_ARGS)
Definition: mac8.c:508
int errhint(const char *fmt,...)
Definition: elog.c:974
Datum hash_any(const unsigned char *k, int keylen)
Definition: hashfn.c:148
#define lobits(addr)
Definition: mac8.c:35
Datum hashmacaddr8extended(PG_FUNCTION_ARGS)
Definition: mac8.c:411
Datum macaddr8_in(PG_FUNCTION_ARGS)
Definition: mac8.c:104
void pq_begintypsend(StringInfo buf)
Definition: pqformat.c:328
unsigned char f
Definition: inet.h:101
StringInfoData * StringInfo
Definition: stringinfo.h:43
#define PG_RETURN_INT32(x)
Definition: fmgr.h:344
int errcode(int sqlerrcode)
Definition: elog.c:570
#define PG_GETARG_POINTER(n)
Definition: fmgr.h:271
Datum hash_any_extended(const unsigned char *k, int keylen, uint64 seed)
Definition: hashfn.c:374
#define PG_RETURN_BYTEA_P(x)
Definition: fmgr.h:360
bytea * pq_endtypsend(StringInfo buf)
Definition: pqformat.c:348
unsigned char h
Definition: inet.h:116
Datum macaddr8_ne(PG_FUNCTION_ARGS)
Definition: mac8.c:391
signed int int32
Definition: c.h:346
unsigned char f
Definition: inet.h:114
Datum macaddr8_eq(PG_FUNCTION_ARGS)
Definition: mac8.c:364
Datum macaddr8_out(PG_FUNCTION_ARGS)
Definition: mac8.c:242
unsigned char g
Definition: inet.h:115
static void pq_sendbyte(StringInfo buf, uint8 byt)
Definition: pqformat.h:161
static const FormData_pg_attribute a2
Definition: heap.c:166
Datum macaddr8_recv(PG_FUNCTION_ARGS)
Definition: mac8.c:262
#define PG_RETURN_MACADDR_P(x)
Definition: inet.h:134
unsigned char c
Definition: inet.h:98
unsigned char a
Definition: inet.h:109
#define ERROR
Definition: elog.h:43
unsigned char a
Definition: inet.h:96
Definition: inet.h:107
#define PG_GETARG_MACADDR_P(n)
Definition: inet.h:133
unsigned char d
Definition: inet.h:99
static int32 macaddr8_cmp_internal(macaddr8 *a1, macaddr8 *a2)
Definition: mac8.c:318
char * c
#define hibits(addr)
Definition: mac8.c:32
static char * buf
Definition: pg_test_fsync.c:68
unsigned char b
Definition: inet.h:110
Datum macaddr8_gt(PG_FUNCTION_ARGS)
Definition: mac8.c:382
Datum macaddr8_lt(PG_FUNCTION_ARGS)
Definition: mac8.c:346
static const signed char hexlookup[128]
Definition: mac8.c:40
Definition: zic.c:298
unsigned char e
Definition: inet.h:113
Datum macaddr8_or(PG_FUNCTION_ARGS)
Definition: mac8.c:462
#define ereport(elevel, rest)
Definition: elog.h:141
unsigned char c
Definition: inet.h:111
Datum macaddr8_trunc(PG_FUNCTION_ARGS)
Definition: mac8.c:485
void * palloc0(Size size)
Definition: mcxt.c:980
unsigned char b
Definition: inet.h:97
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:349
uintptr_t Datum
Definition: postgres.h:367
unsigned char e
Definition: inet.h:100
Datum macaddr8_ge(PG_FUNCTION_ARGS)
Definition: mac8.c:373
#define PG_GETARG_MACADDR8_P(n)
Definition: inet.h:139
int pq_getmsgbyte(StringInfo msg)
Definition: pqformat.c:401
#define PG_RETURN_CSTRING(x)
Definition: fmgr.h:352
Datum macaddr8_not(PG_FUNCTION_ARGS)
Definition: mac8.c:423
Datum macaddr8_le(PG_FUNCTION_ARGS)
Definition: mac8.c:355
Definition: inet.h:94
#define PG_RETURN_MACADDR8_P(x)
Definition: inet.h:140
e
Definition: preproc-init.c:82
Datum macaddr8_and(PG_FUNCTION_ARGS)
Definition: mac8.c:442
void * palloc(Size size)
Definition: mcxt.c:949
int errmsg(const char *fmt,...)
Definition: elog.c:784
unsigned char d
Definition: inet.h:112
Datum macaddr8tomacaddr(PG_FUNCTION_ARGS)
Definition: mac8.c:553
static unsigned char hex2_to_uchar(const unsigned char *str, const unsigned char *ptr)
Definition: mac8.c:61
#define PG_GETARG_CSTRING(n)
Definition: fmgr.h:272
#define PG_FUNCTION_ARGS
Definition: fmgr.h:188
static const FormData_pg_attribute a1
Definition: heap.c:152
#define PG_GETARG_INT64(n)
Definition: fmgr.h:277
#define snprintf
Definition: port.h:192
Datum hashmacaddr8(PG_FUNCTION_ARGS)
Definition: mac8.c:403
Datum macaddrtomacaddr8(PG_FUNCTION_ARGS)
Definition: mac8.c:532