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
38static unsigned char hex2_to_uchar(const unsigned char *str, const unsigned char *ptr);
39
40static 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 */
60static inline unsigned char
61hex2_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
90invalid_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 */
103Datum
104macaddr8_in(PG_FUNCTION_ARGS)
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 */
241Datum
242macaddr8_out(PG_FUNCTION_ARGS)
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 */
261Datum
262macaddr8_recv(PG_FUNCTION_ARGS)
263{
264 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
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 */
294Datum
295macaddr8_send(PG_FUNCTION_ARGS)
296{
297 macaddr8 *addr = PG_GETARG_MACADDR8_P(0);
298 StringInfoData buf;
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
310 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
311}
312
313
314/*
315 * macaddr8_cmp_internal - comparison function for sorting:
316 */
317static int32
318macaddr8_cmp_internal(macaddr8 *a1, macaddr8 *a2)
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
332Datum
333macaddr8_cmp(PG_FUNCTION_ARGS)
334{
335 macaddr8 *a1 = PG_GETARG_MACADDR8_P(0);
336 macaddr8 *a2 = PG_GETARG_MACADDR8_P(1);
337
338 PG_RETURN_INT32(macaddr8_cmp_internal(a1, a2));
339}
340
341/*
342 * Boolean comparison functions.
343 */
344
345Datum
346macaddr8_lt(PG_FUNCTION_ARGS)
347{
348 macaddr8 *a1 = PG_GETARG_MACADDR8_P(0);
349 macaddr8 *a2 = PG_GETARG_MACADDR8_P(1);
350
351 PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) < 0);
352}
353
354Datum
355macaddr8_le(PG_FUNCTION_ARGS)
356{
357 macaddr8 *a1 = PG_GETARG_MACADDR8_P(0);
358 macaddr8 *a2 = PG_GETARG_MACADDR8_P(1);
359
360 PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) <= 0);
361}
362
363Datum
364macaddr8_eq(PG_FUNCTION_ARGS)
365{
366 macaddr8 *a1 = PG_GETARG_MACADDR8_P(0);
367 macaddr8 *a2 = PG_GETARG_MACADDR8_P(1);
368
369 PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) == 0);
370}
371
372Datum
373macaddr8_ge(PG_FUNCTION_ARGS)
374{
375 macaddr8 *a1 = PG_GETARG_MACADDR8_P(0);
376 macaddr8 *a2 = PG_GETARG_MACADDR8_P(1);
377
378 PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) >= 0);
379}
380
381Datum
382macaddr8_gt(PG_FUNCTION_ARGS)
383{
384 macaddr8 *a1 = PG_GETARG_MACADDR8_P(0);
385 macaddr8 *a2 = PG_GETARG_MACADDR8_P(1);
386
387 PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) > 0);
388}
389
390Datum
391macaddr8_ne(PG_FUNCTION_ARGS)
392{
393 macaddr8 *a1 = PG_GETARG_MACADDR8_P(0);
394 macaddr8 *a2 = PG_GETARG_MACADDR8_P(1);
395
396 PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) != 0);
397}
398
399/*
400 * Support function for hash indexes on macaddr8.
401 */
402Datum
403hashmacaddr8(PG_FUNCTION_ARGS)
404{
405 macaddr8 *key = PG_GETARG_MACADDR8_P(0);
406
407 return hash_any((unsigned char *) key, sizeof(macaddr8));
408}
409
410Datum
411hashmacaddr8extended(PG_FUNCTION_ARGS)
412{
413 macaddr8 *key = PG_GETARG_MACADDR8_P(0);
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 */
422Datum
423macaddr8_not(PG_FUNCTION_ARGS)
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
441Datum
442macaddr8_and(PG_FUNCTION_ARGS)
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
461Datum
462macaddr8_or(PG_FUNCTION_ARGS)
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 */
484Datum
485macaddr8_trunc(PG_FUNCTION_ARGS)
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 */
507Datum
508macaddr8_set7bit(PG_FUNCTION_ARGS)
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
531Datum
532macaddrtomacaddr8(PG_FUNCTION_ARGS)
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
552Datum
553macaddr8tomacaddr(PG_FUNCTION_ARGS)
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}
578