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