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