TLA Line data Source code
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 GNC 8179 : hex2_to_uchar(const unsigned char *ptr, bool *badhex)
59 : {
60 : unsigned char ret;
61 ECB : signed char lookup;
62 EUB :
63 : /* Handle the first character */
64 CBC 8179 : if (*ptr > 127)
65 LBC 0 : goto invalid_input;
66 ECB :
67 GIC 8179 : lookup = hexlookup[*ptr];
68 CBC 8179 : if (lookup < 0)
69 GIC 12 : goto invalid_input;
70 :
71 CBC 8167 : ret = lookup << 4;
72 :
73 ECB : /* Move to the second character */
74 GBC 8167 : ptr++;
75 :
76 CBC 8167 : if (*ptr > 127)
77 LBC 0 : goto invalid_input;
78 ECB :
79 GIC 8167 : lookup = hexlookup[*ptr];
80 CBC 8167 : if (lookup < 0)
81 GIC 9 : goto invalid_input;
82 ECB :
83 GIC 8158 : ret += lookup;
84 ECB :
85 CBC 8158 : return ret;
86 ECB :
87 GIC 21 : invalid_input:
88 GNC 21 : *badhex = true;
89 GIC 21 : return 0;
90 ECB : }
91 :
92 : /*
93 : * MAC address (EUI-48 and EUI-64) reader. Accepts several common notations.
94 : */
95 : Datum
96 CBC 1211 : macaddr8_in(PG_FUNCTION_ARGS)
97 ECB : {
98 CBC 1211 : const unsigned char *str = (unsigned char *) PG_GETARG_CSTRING(0);
99 GNC 1211 : Node *escontext = fcinfo->context;
100 CBC 1211 : const unsigned char *ptr = str;
101 GNC 1211 : bool badhex = false;
102 ECB : macaddr8 *result;
103 CBC 1211 : unsigned char a = 0,
104 1211 : b = 0,
105 1211 : c = 0,
106 1211 : d = 0,
107 GIC 1211 : e = 0,
108 1211 : f = 0,
109 CBC 1211 : g = 0,
110 1211 : h = 0;
111 GIC 1211 : int count = 0;
112 1211 : unsigned char spacer = '\0';
113 ECB :
114 : /* skip leading spaces */
115 GIC 1259 : while (*ptr && isspace(*ptr))
116 48 : ptr++;
117 :
118 : /* digits must always come in pairs */
119 9351 : while (*ptr && *(ptr + 1))
120 : {
121 : /*
122 ECB : * 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 CBC 8191 : count++;
129 ECB :
130 CBC 8191 : switch (count)
131 ECB : {
132 CBC 1211 : case 1:
133 GNC 1211 : a = hex2_to_uchar(ptr, &badhex);
134 CBC 1211 : break;
135 1205 : case 2:
136 GNC 1205 : b = hex2_to_uchar(ptr, &badhex);
137 CBC 1205 : break;
138 1193 : case 3:
139 GNC 1193 : c = hex2_to_uchar(ptr, &badhex);
140 CBC 1193 : break;
141 1193 : case 4:
142 GNC 1193 : d = hex2_to_uchar(ptr, &badhex);
143 CBC 1193 : break;
144 1187 : case 5:
145 GNC 1187 : e = hex2_to_uchar(ptr, &badhex);
146 CBC 1187 : break;
147 1187 : case 6:
148 GNC 1187 : f = hex2_to_uchar(ptr, &badhex);
149 CBC 1187 : break;
150 506 : case 7:
151 GNC 506 : g = hex2_to_uchar(ptr, &badhex);
152 CBC 506 : break;
153 GIC 497 : case 8:
154 GNC 497 : h = hex2_to_uchar(ptr, &badhex);
155 CBC 497 : break;
156 12 : default:
157 : /* must be trailing garbage... */
158 GNC 12 : goto fail;
159 ECB : }
160 :
161 GNC 8179 : if (badhex)
162 21 : goto fail;
163 :
164 : /* Move forward to where the next byte should be */
165 CBC 8158 : ptr += 2;
166 ECB :
167 : /* Check for a spacer, these are valid, anything else is not */
168 GIC 8158 : if (*ptr == ':' || *ptr == '-' || *ptr == '.')
169 ECB : {
170 : /* remember the spacer used, if it changes then it isn't valid */
171 GIC 4466 : if (spacer == '\0')
172 857 : spacer = *ptr;
173 ECB :
174 : /* Have to use the same spacer throughout */
175 GIC 3609 : else if (spacer != *ptr)
176 GNC 12 : goto fail;
177 :
178 ECB : /* move past the spacer */
179 GIC 4454 : ptr++;
180 : }
181 ECB :
182 : /* allow trailing whitespace after if we have 6 or 8 bytes */
183 GIC 8146 : if (count == 6 || count == 8)
184 : {
185 1678 : if (isspace(*ptr))
186 : {
187 72 : while (*++ptr && isspace(*ptr));
188 ECB :
189 : /* If we found a space and then non-space, it's invalid */
190 CBC 18 : if (*ptr)
191 GNC 6 : goto fail;
192 ECB : }
193 : }
194 : }
195 :
196 : /* Convert a 6 byte MAC address to macaddr8 */
197 CBC 1160 : if (count == 6)
198 : {
199 678 : h = f;
200 678 : g = e;
201 678 : f = d;
202 ECB :
203 CBC 678 : d = 0xFF;
204 678 : e = 0xFE;
205 ECB : }
206 CBC 482 : else if (count != 8)
207 GNC 6 : goto fail;
208 ECB :
209 GIC 1154 : result = (macaddr8 *) palloc0(sizeof(macaddr8));
210 :
211 1154 : result->a = a;
212 1154 : result->b = b;
213 1154 : result->c = c;
214 1154 : result->d = d;
215 1154 : result->e = e;
216 1154 : result->f = f;
217 1154 : result->g = g;
218 CBC 1154 : result->h = h;
219 :
220 1154 : PG_RETURN_MACADDR8_P(result);
221 :
222 GNC 57 : fail:
223 57 : 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 ECB : /*
230 : * MAC8 address (EUI-64) output function. Fixed format.
231 : */
232 : Datum
233 CBC 872 : macaddr8_out(PG_FUNCTION_ARGS)
234 : {
235 872 : macaddr8 *addr = PG_GETARG_MACADDR8_P(0);
236 : char *result;
237 :
238 GIC 872 : result = (char *) palloc(32);
239 :
240 872 : snprintf(result, 32, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
241 872 : addr->a, addr->b, addr->c, addr->d,
242 872 : addr->e, addr->f, addr->g, addr->h);
243 :
244 GBC 872 : PG_RETURN_CSTRING(result);
245 : }
246 EUB :
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
253 UBC 0 : macaddr8_recv(PG_FUNCTION_ARGS)
254 : {
255 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
256 : macaddr8 *addr;
257 EUB :
258 UBC 0 : addr = (macaddr8 *) palloc0(sizeof(macaddr8));
259 :
260 UIC 0 : addr->a = pq_getmsgbyte(buf);
261 0 : addr->b = pq_getmsgbyte(buf);
262 UBC 0 : addr->c = pq_getmsgbyte(buf);
263 EUB :
264 UIC 0 : if (buf->len == 6)
265 : {
266 UBC 0 : addr->d = 0xFF;
267 0 : addr->e = 0xFE;
268 EUB : }
269 : else
270 : {
271 UIC 0 : addr->d = pq_getmsgbyte(buf);
272 0 : addr->e = pq_getmsgbyte(buf);
273 : }
274 :
275 0 : addr->f = pq_getmsgbyte(buf);
276 0 : addr->g = pq_getmsgbyte(buf);
277 UBC 0 : addr->h = pq_getmsgbyte(buf);
278 :
279 0 : PG_RETURN_MACADDR8_P(addr);
280 : }
281 :
282 EUB : /*
283 : * macaddr8_send - converts macaddr8(EUI-64) to binary format
284 : */
285 : Datum
286 UBC 0 : macaddr8_send(PG_FUNCTION_ARGS)
287 EUB : {
288 UBC 0 : macaddr8 *addr = PG_GETARG_MACADDR8_P(0);
289 EUB : StringInfoData buf;
290 :
291 UIC 0 : pq_begintypsend(&buf);
292 UBC 0 : pq_sendbyte(&buf, addr->a);
293 UIC 0 : pq_sendbyte(&buf, addr->b);
294 0 : pq_sendbyte(&buf, addr->c);
295 0 : pq_sendbyte(&buf, addr->d);
296 0 : pq_sendbyte(&buf, addr->e);
297 0 : pq_sendbyte(&buf, addr->f);
298 0 : pq_sendbyte(&buf, addr->g);
299 0 : pq_sendbyte(&buf, addr->h);
300 ECB :
301 UIC 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
302 ECB : }
303 :
304 :
305 : /*
306 : * macaddr8_cmp_internal - comparison function for sorting:
307 : */
308 : static int32
309 CBC 89347 : macaddr8_cmp_internal(macaddr8 *a1, macaddr8 *a2)
310 : {
311 89347 : if (hibits(a1) < hibits(a2))
312 GIC 49408 : return -1;
313 39939 : else if (hibits(a1) > hibits(a2))
314 35846 : return 1;
315 CBC 4093 : else if (lobits(a1) < lobits(a2))
316 GIC 111 : return -1;
317 CBC 3982 : else if (lobits(a1) > lobits(a2))
318 12 : return 1;
319 : else
320 3970 : return 0;
321 : }
322 :
323 : Datum
324 GIC 7253 : macaddr8_cmp(PG_FUNCTION_ARGS)
325 : {
326 7253 : macaddr8 *a1 = PG_GETARG_MACADDR8_P(0);
327 7253 : macaddr8 *a2 = PG_GETARG_MACADDR8_P(1);
328 ECB :
329 GIC 7253 : PG_RETURN_INT32(macaddr8_cmp_internal(a1, a2));
330 ECB : }
331 :
332 : /*
333 : * Boolean comparison functions.
334 : */
335 :
336 : Datum
337 CBC 56675 : macaddr8_lt(PG_FUNCTION_ARGS)
338 : {
339 56675 : macaddr8 *a1 = PG_GETARG_MACADDR8_P(0);
340 56675 : macaddr8 *a2 = PG_GETARG_MACADDR8_P(1);
341 :
342 56675 : PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) < 0);
343 : }
344 :
345 : Datum
346 1846 : macaddr8_le(PG_FUNCTION_ARGS)
347 : {
348 1846 : macaddr8 *a1 = PG_GETARG_MACADDR8_P(0);
349 1846 : macaddr8 *a2 = PG_GETARG_MACADDR8_P(1);
350 :
351 1846 : PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) <= 0);
352 : }
353 :
354 : Datum
355 19005 : macaddr8_eq(PG_FUNCTION_ARGS)
356 : {
357 19005 : macaddr8 *a1 = PG_GETARG_MACADDR8_P(0);
358 19005 : macaddr8 *a2 = PG_GETARG_MACADDR8_P(1);
359 :
360 19005 : PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) == 0);
361 : }
362 :
363 : Datum
364 1497 : macaddr8_ge(PG_FUNCTION_ARGS)
365 : {
366 1497 : macaddr8 *a1 = PG_GETARG_MACADDR8_P(0);
367 1497 : macaddr8 *a2 = PG_GETARG_MACADDR8_P(1);
368 :
369 1497 : PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) >= 0);
370 : }
371 :
372 : Datum
373 3065 : macaddr8_gt(PG_FUNCTION_ARGS)
374 : {
375 3065 : macaddr8 *a1 = PG_GETARG_MACADDR8_P(0);
376 3065 : macaddr8 *a2 = PG_GETARG_MACADDR8_P(1);
377 :
378 3065 : PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) > 0);
379 : }
380 :
381 : Datum
382 GIC 6 : macaddr8_ne(PG_FUNCTION_ARGS)
383 : {
384 6 : macaddr8 *a1 = PG_GETARG_MACADDR8_P(0);
385 CBC 6 : macaddr8 *a2 = PG_GETARG_MACADDR8_P(1);
386 :
387 6 : PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) != 0);
388 : }
389 ECB :
390 : /*
391 : * Support function for hash indexes on macaddr8.
392 : */
393 : Datum
394 GIC 90 : hashmacaddr8(PG_FUNCTION_ARGS)
395 ECB : {
396 GIC 90 : macaddr8 *key = PG_GETARG_MACADDR8_P(0);
397 ECB :
398 CBC 90 : return hash_any((unsigned char *) key, sizeof(macaddr8));
399 : }
400 :
401 : Datum
402 GIC 30 : hashmacaddr8extended(PG_FUNCTION_ARGS)
403 : {
404 30 : macaddr8 *key = PG_GETARG_MACADDR8_P(0);
405 ECB :
406 GIC 30 : return hash_any_extended((unsigned char *) key, sizeof(macaddr8),
407 CBC 30 : PG_GETARG_INT64(1));
408 : }
409 :
410 ECB : /*
411 : * Arithmetic functions: bitwise NOT, AND, OR.
412 : */
413 : Datum
414 CBC 60 : macaddr8_not(PG_FUNCTION_ARGS)
415 ECB : {
416 CBC 60 : macaddr8 *addr = PG_GETARG_MACADDR8_P(0);
417 ECB : macaddr8 *result;
418 :
419 GIC 60 : result = (macaddr8 *) palloc0(sizeof(macaddr8));
420 CBC 60 : result->a = ~addr->a;
421 GIC 60 : result->b = ~addr->b;
422 60 : result->c = ~addr->c;
423 60 : result->d = ~addr->d;
424 CBC 60 : result->e = ~addr->e;
425 GIC 60 : result->f = ~addr->f;
426 CBC 60 : result->g = ~addr->g;
427 60 : result->h = ~addr->h;
428 :
429 GIC 60 : PG_RETURN_MACADDR8_P(result);
430 ECB : }
431 :
432 : Datum
433 CBC 60 : macaddr8_and(PG_FUNCTION_ARGS)
434 ECB : {
435 CBC 60 : macaddr8 *addr1 = PG_GETARG_MACADDR8_P(0);
436 60 : macaddr8 *addr2 = PG_GETARG_MACADDR8_P(1);
437 ECB : macaddr8 *result;
438 :
439 GIC 60 : result = (macaddr8 *) palloc0(sizeof(macaddr8));
440 CBC 60 : result->a = addr1->a & addr2->a;
441 GIC 60 : result->b = addr1->b & addr2->b;
442 60 : result->c = addr1->c & addr2->c;
443 60 : result->d = addr1->d & addr2->d;
444 CBC 60 : result->e = addr1->e & addr2->e;
445 GIC 60 : result->f = addr1->f & addr2->f;
446 CBC 60 : result->g = addr1->g & addr2->g;
447 60 : result->h = addr1->h & addr2->h;
448 :
449 GIC 60 : PG_RETURN_MACADDR8_P(result);
450 ECB : }
451 :
452 : Datum
453 CBC 60 : macaddr8_or(PG_FUNCTION_ARGS)
454 ECB : {
455 CBC 60 : macaddr8 *addr1 = PG_GETARG_MACADDR8_P(0);
456 60 : macaddr8 *addr2 = PG_GETARG_MACADDR8_P(1);
457 ECB : macaddr8 *result;
458 :
459 GIC 60 : result = (macaddr8 *) palloc0(sizeof(macaddr8));
460 CBC 60 : result->a = addr1->a | addr2->a;
461 GIC 60 : result->b = addr1->b | addr2->b;
462 60 : result->c = addr1->c | addr2->c;
463 60 : result->d = addr1->d | addr2->d;
464 60 : result->e = addr1->e | addr2->e;
465 60 : result->f = addr1->f | addr2->f;
466 60 : result->g = addr1->g | addr2->g;
467 CBC 60 : result->h = addr1->h | addr2->h;
468 :
469 60 : PG_RETURN_MACADDR8_P(result);
470 : }
471 :
472 ECB : /*
473 : * Truncation function to allow comparing macaddr8 manufacturers.
474 : */
475 : Datum
476 CBC 60 : macaddr8_trunc(PG_FUNCTION_ARGS)
477 ECB : {
478 CBC 60 : macaddr8 *addr = PG_GETARG_MACADDR8_P(0);
479 ECB : macaddr8 *result;
480 :
481 CBC 60 : result = (macaddr8 *) palloc0(sizeof(macaddr8));
482 :
483 60 : result->a = addr->a;
484 GIC 60 : result->b = addr->b;
485 60 : result->c = addr->c;
486 60 : result->d = 0;
487 60 : result->e = 0;
488 60 : result->f = 0;
489 60 : result->g = 0;
490 CBC 60 : result->h = 0;
491 :
492 60 : PG_RETURN_MACADDR8_P(result);
493 : }
494 :
495 ECB : /*
496 : * Set 7th bit for modified EUI-64 as used in IPv6.
497 : */
498 : Datum
499 CBC 3 : macaddr8_set7bit(PG_FUNCTION_ARGS)
500 ECB : {
501 CBC 3 : macaddr8 *addr = PG_GETARG_MACADDR8_P(0);
502 ECB : macaddr8 *result;
503 :
504 CBC 3 : result = (macaddr8 *) palloc0(sizeof(macaddr8));
505 :
506 3 : result->a = addr->a | 0x02;
507 GIC 3 : result->b = addr->b;
508 3 : result->c = addr->c;
509 3 : result->d = addr->d;
510 3 : result->e = addr->e;
511 3 : result->f = addr->f;
512 3 : result->g = addr->g;
513 3 : result->h = addr->h;
514 EUB :
515 GIC 3 : PG_RETURN_MACADDR8_P(result);
516 EUB : }
517 :
518 : /*----------------------------------------------------------
519 : * Conversion operators.
520 : *---------------------------------------------------------*/
521 :
522 : Datum
523 UBC 0 : macaddrtomacaddr8(PG_FUNCTION_ARGS)
524 EUB : {
525 UBC 0 : macaddr *addr6 = PG_GETARG_MACADDR_P(0);
526 EUB : macaddr8 *result;
527 :
528 UBC 0 : result = (macaddr8 *) palloc0(sizeof(macaddr8));
529 :
530 UIC 0 : result->a = addr6->a;
531 UBC 0 : result->b = addr6->b;
532 UIC 0 : result->c = addr6->c;
533 0 : result->d = 0xFF;
534 0 : result->e = 0xFE;
535 LBC 0 : result->f = addr6->d;
536 UIC 0 : result->g = addr6->e;
537 LBC 0 : result->h = addr6->f;
538 :
539 :
540 0 : PG_RETURN_MACADDR8_P(result);
541 : }
542 ECB :
543 EUB : Datum
544 GIC 12 : macaddr8tomacaddr(PG_FUNCTION_ARGS)
545 : {
546 12 : macaddr8 *addr = PG_GETARG_MACADDR8_P(0);
547 : macaddr *result;
548 :
549 12 : result = (macaddr *) palloc0(sizeof(macaddr));
550 :
551 CBC 12 : if ((addr->d != 0xFF) || (addr->e != 0xFE))
552 LBC 0 : ereport(ERROR,
553 ECB : (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 GIC 12 : result->a = addr->a;
561 12 : result->b = addr->b;
562 12 : result->c = addr->c;
563 12 : result->d = addr->f;
564 12 : result->e = addr->g;
565 12 : result->f = addr->h;
566 :
567 12 : PG_RETURN_MACADDR_P(result);
568 : }
|