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-2025, 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
39static unsigned char hex2_to_uchar(const unsigned char *ptr, bool *badhex);
40
41static 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 */
58static inline unsigned char
59hex2_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
88invalid_input:
89 *badhex = true;
90 return 0;
91}
92
93/*
94 * MAC address (EUI-48 and EUI-64) reader. Accepts several common notations.
95 */
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
223fail:
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 */
233Datum
235{
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 */
253Datum
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
281}
282
283/*
284 * macaddr8_send - converts macaddr8(EUI-64) to binary format
285 */
286Datum
288{
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 */
309static 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
324Datum
326{
329
331}
332
333/*
334 * Boolean comparison functions.
335 */
336
337Datum
339{
342
344}
345
346Datum
348{
351
353}
354
355Datum
357{
360
362}
363
364Datum
366{
369
371}
372
373Datum
375{
378
380}
381
382Datum
384{
387
389}
390
391/*
392 * Support function for hash indexes on macaddr8.
393 */
394Datum
396{
398
399 return hash_any((unsigned char *) key, sizeof(macaddr8));
400}
401
402Datum
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 */
414Datum
416{
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
433Datum
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
453Datum
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 */
476Datum
478{
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 */
499Datum
501{
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
523Datum
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
544Datum
546{
548 macaddr *result;
549
550 result = (macaddr *) palloc0(sizeof(macaddr));
551
552 if ((addr->d != 0xFF) || (addr->e != 0xFE))
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}
int32_t int32
Definition: c.h:481
int errhint(const char *fmt,...)
Definition: elog.c:1317
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ereturn(context, dummy_value,...)
Definition: elog.h:277
#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
const char * str
static const FormData_pg_attribute a1
Definition: heap.c:143
static const FormData_pg_attribute a2
Definition: heap.c:156
int b
Definition: isn.c:69
int a
Definition: isn.c:68
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:1347
void * palloc(Size size)
Definition: mcxt.c:1317
static char * buf
Definition: pg_test_fsync.c:72
#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