]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/mac8.c
Add support for EUI-64 MAC addresses as macaddr8
[postgresql] / src / backend / utils / adt / mac8.c
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-2017, 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 "access/hash.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 char *str, int offset);
39
40 static const int 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 static inline unsigned char
52 hex2_to_uchar(const char *str, int offset)
53 {
54         unsigned char ret = 0;
55         int                     lookup;
56         const char *ptr = str + offset;
57
58         /* Handle the first character */
59         if (*ptr < 0 || *ptr >= 127)
60                 goto invalid_input;
61
62         lookup = hexlookup[(unsigned char) *ptr];
63         if (lookup < 0 || lookup > 15)
64                 goto invalid_input;
65
66         ret = lookup << 4;
67
68         /* Move to the second character */
69         ptr++;
70
71         if (*ptr < 0 || *ptr > 127)
72                 goto invalid_input;
73
74         lookup = hexlookup[(unsigned char) *ptr];
75         if (lookup < 0 || lookup > 15)
76                 goto invalid_input;
77
78         ret += lookup;
79
80         return ret;
81
82 invalid_input:
83         ereport(ERROR,
84                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
85                          errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
86                                         str)));
87
88         /* We do not actually reach here */
89         return 0;
90 }
91
92 /*
93  * MAC address (EUI-48 and EUI-64) reader. Accepts several common notations.
94  */
95 Datum
96 macaddr8_in(PG_FUNCTION_ARGS)
97 {
98         const char *str = PG_GETARG_CSTRING(0);
99         const char *ptr = str;
100         macaddr8   *result;
101         unsigned char a = 0,
102                                 b = 0,
103                                 c = 0,
104                                 d = 0,
105                                 e = 0,
106                                 f = 0,
107                                 g = 0,
108                                 h = 0;
109         int                     count = 0;
110         char            spacer = '\0';
111
112         /* skip leading spaces */
113         while (*ptr && isspace((unsigned char) *ptr))
114                 ptr++;
115
116         /* digits must always come in pairs */
117         while (*ptr && *(ptr + 1))
118         {
119                 /*
120                  * Attempt to decode each byte, which must be 2 hex digits in a row.
121                  * If either digit is not hex, hex2_to_uchar will throw ereport() for
122                  * us.  Either 6 or 8 byte MAC addresses are supported.
123                  */
124
125                 /* Attempt to collect a byte */
126                 count++;
127
128                 switch (count)
129                 {
130                         case 1:
131                                 a = hex2_to_uchar(str, ptr - str);
132                                 break;
133                         case 2:
134                                 b = hex2_to_uchar(str, ptr - str);
135                                 break;
136                         case 3:
137                                 c = hex2_to_uchar(str, ptr - str);
138                                 break;
139                         case 4:
140                                 d = hex2_to_uchar(str, ptr - str);
141                                 break;
142                         case 5:
143                                 e = hex2_to_uchar(str, ptr - str);
144                                 break;
145                         case 6:
146                                 f = hex2_to_uchar(str, ptr - str);
147                                 break;
148                         case 7:
149                                 g = hex2_to_uchar(str, ptr - str);
150                                 break;
151                         case 8:
152                                 h = hex2_to_uchar(str, ptr - str);
153                                 break;
154                         default:
155                                 /* must be trailing garbage... */
156                                 ereport(ERROR,
157                                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
158                                 errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
159                                            str)));
160                 }
161
162                 /* Move forward to where the next byte should be */
163                 ptr += 2;
164
165                 /* Check for a spacer, these are valid, anything else is not */
166                 if (*ptr == ':' || *ptr == '-' || *ptr == '.')
167                 {
168                         /* remember the spacer used, if it changes then it isn't valid */
169                         if (spacer == '\0')
170                                 spacer = *ptr;
171
172                         /* Have to use the same spacer throughout */
173                         else if (spacer != *ptr)
174                                 ereport(ERROR,
175                                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
176                                 errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
177                                            str)));
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((unsigned char) *ptr))
187                         {
188                                 while (*++ptr && isspace((unsigned char) *ptr));
189
190                                 /* If we found a space and then non-space, it's invalid */
191                                 if (*ptr)
192                                         ereport(ERROR,
193                                                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
194                                                          errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
195                                                                         str)));
196                         }
197                 }
198         }
199
200         /* Convert a 6 byte MAC address to macaddr8 */
201         if (count == 6)
202         {
203                 h = f;
204                 g = e;
205                 f = d;
206
207                 d = 0xFF;
208                 e = 0xFE;
209         }
210         else if (count != 8)
211                 ereport(ERROR,
212                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
213                            errmsg("invalid input syntax for type %s: \"%s\"", "macaddr8",
214                                           str)));
215
216         result = (macaddr8 *) palloc0(sizeof(macaddr8));
217
218         result->a = a;
219         result->b = b;
220         result->c = c;
221         result->d = d;
222         result->e = e;
223         result->f = f;
224         result->g = g;
225         result->h = h;
226
227         PG_RETURN_MACADDR8_P(result);
228 }
229
230 /*
231  * MAC8 address (EUI-64) output function. Fixed format.
232  */
233 Datum
234 macaddr8_out(PG_FUNCTION_ARGS)
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
254 macaddr8_recv(PG_FUNCTION_ARGS)
255 {
256         StringInfo      buf = (StringInfo) PG_GETARG_POINTER(0);
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
287 macaddr8_send(PG_FUNCTION_ARGS)
288 {
289         macaddr8   *addr = PG_GETARG_MACADDR8_P(0);
290         StringInfoData buf;
291
292         pq_begintypsend(&buf);
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
302         PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
303 }
304
305
306 /*
307  * macaddr8_cmp_internal - comparison function for sorting:
308  */
309 static int32
310 macaddr8_cmp_internal(macaddr8 *a1, macaddr8 *a2)
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
325 macaddr8_cmp(PG_FUNCTION_ARGS)
326 {
327         macaddr8   *a1 = PG_GETARG_MACADDR8_P(0);
328         macaddr8   *a2 = PG_GETARG_MACADDR8_P(1);
329
330         PG_RETURN_INT32(macaddr8_cmp_internal(a1, a2));
331 }
332
333 /*
334  * Boolean comparison functions.
335  */
336
337 Datum
338 macaddr8_lt(PG_FUNCTION_ARGS)
339 {
340         macaddr8   *a1 = PG_GETARG_MACADDR8_P(0);
341         macaddr8   *a2 = PG_GETARG_MACADDR8_P(1);
342
343         PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) < 0);
344 }
345
346 Datum
347 macaddr8_le(PG_FUNCTION_ARGS)
348 {
349         macaddr8   *a1 = PG_GETARG_MACADDR8_P(0);
350         macaddr8   *a2 = PG_GETARG_MACADDR8_P(1);
351
352         PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) <= 0);
353 }
354
355 Datum
356 macaddr8_eq(PG_FUNCTION_ARGS)
357 {
358         macaddr8   *a1 = PG_GETARG_MACADDR8_P(0);
359         macaddr8   *a2 = PG_GETARG_MACADDR8_P(1);
360
361         PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) == 0);
362 }
363
364 Datum
365 macaddr8_ge(PG_FUNCTION_ARGS)
366 {
367         macaddr8   *a1 = PG_GETARG_MACADDR8_P(0);
368         macaddr8   *a2 = PG_GETARG_MACADDR8_P(1);
369
370         PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) >= 0);
371 }
372
373 Datum
374 macaddr8_gt(PG_FUNCTION_ARGS)
375 {
376         macaddr8   *a1 = PG_GETARG_MACADDR8_P(0);
377         macaddr8   *a2 = PG_GETARG_MACADDR8_P(1);
378
379         PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) > 0);
380 }
381
382 Datum
383 macaddr8_ne(PG_FUNCTION_ARGS)
384 {
385         macaddr8   *a1 = PG_GETARG_MACADDR8_P(0);
386         macaddr8   *a2 = PG_GETARG_MACADDR8_P(1);
387
388         PG_RETURN_BOOL(macaddr8_cmp_internal(a1, a2) != 0);
389 }
390
391 /*
392  * Support function for hash indexes on macaddr8.
393  */
394 Datum
395 hashmacaddr8(PG_FUNCTION_ARGS)
396 {
397         macaddr8   *key = PG_GETARG_MACADDR8_P(0);
398
399         return hash_any((unsigned char *) key, sizeof(macaddr8));
400 }
401
402 /*
403  * Arithmetic functions: bitwise NOT, AND, OR.
404  */
405 Datum
406 macaddr8_not(PG_FUNCTION_ARGS)
407 {
408         macaddr8   *addr = PG_GETARG_MACADDR8_P(0);
409         macaddr8   *result;
410
411         result = (macaddr8 *) palloc0(sizeof(macaddr8));
412         result->a = ~addr->a;
413         result->b = ~addr->b;
414         result->c = ~addr->c;
415         result->d = ~addr->d;
416         result->e = ~addr->e;
417         result->f = ~addr->f;
418         result->g = ~addr->g;
419         result->h = ~addr->h;
420
421         PG_RETURN_MACADDR8_P(result);
422 }
423
424 Datum
425 macaddr8_and(PG_FUNCTION_ARGS)
426 {
427         macaddr8   *addr1 = PG_GETARG_MACADDR8_P(0);
428         macaddr8   *addr2 = PG_GETARG_MACADDR8_P(1);
429         macaddr8   *result;
430
431         result = (macaddr8 *) palloc0(sizeof(macaddr8));
432         result->a = addr1->a & addr2->a;
433         result->b = addr1->b & addr2->b;
434         result->c = addr1->c & addr2->c;
435         result->d = addr1->d & addr2->d;
436         result->e = addr1->e & addr2->e;
437         result->f = addr1->f & addr2->f;
438         result->g = addr1->g & addr2->g;
439         result->h = addr1->h & addr2->h;
440
441         PG_RETURN_MACADDR8_P(result);
442 }
443
444 Datum
445 macaddr8_or(PG_FUNCTION_ARGS)
446 {
447         macaddr8   *addr1 = PG_GETARG_MACADDR8_P(0);
448         macaddr8   *addr2 = PG_GETARG_MACADDR8_P(1);
449         macaddr8   *result;
450
451         result = (macaddr8 *) palloc0(sizeof(macaddr8));
452         result->a = addr1->a | addr2->a;
453         result->b = addr1->b | addr2->b;
454         result->c = addr1->c | addr2->c;
455         result->d = addr1->d | addr2->d;
456         result->e = addr1->e | addr2->e;
457         result->f = addr1->f | addr2->f;
458         result->g = addr1->g | addr2->g;
459         result->h = addr1->h | addr2->h;
460
461         PG_RETURN_MACADDR8_P(result);
462 }
463
464 /*
465  * Truncation function to allow comparing macaddr8 manufacturers.
466  */
467 Datum
468 macaddr8_trunc(PG_FUNCTION_ARGS)
469 {
470         macaddr8   *addr = PG_GETARG_MACADDR8_P(0);
471         macaddr8   *result;
472
473         result = (macaddr8 *) palloc0(sizeof(macaddr8));
474
475         result->a = addr->a;
476         result->b = addr->b;
477         result->c = addr->c;
478         result->d = 0;
479         result->e = 0;
480         result->f = 0;
481         result->g = 0;
482         result->h = 0;
483
484         PG_RETURN_MACADDR8_P(result);
485 }
486
487 /*
488  * Set 7th bit for modified EUI-64 as used in IPv6.
489  */
490 Datum
491 macaddr8_set7bit(PG_FUNCTION_ARGS)
492 {
493         macaddr8   *addr = PG_GETARG_MACADDR8_P(0);
494         macaddr8   *result;
495
496         result = (macaddr8 *) palloc0(sizeof(macaddr8));
497
498         result->a = addr->a | 0x02;
499         result->b = addr->b;
500         result->c = addr->c;
501         result->d = addr->d;
502         result->e = addr->e;
503         result->f = addr->f;
504         result->g = addr->g;
505         result->h = addr->h;
506
507         PG_RETURN_MACADDR8_P(result);
508 }
509
510 /*----------------------------------------------------------
511  *      Conversion operators.
512  *---------------------------------------------------------*/
513
514 Datum
515 macaddrtomacaddr8(PG_FUNCTION_ARGS)
516 {
517         macaddr    *addr6 = PG_GETARG_MACADDR_P(0);
518         macaddr8   *result;
519
520         result = (macaddr8 *) palloc0(sizeof(macaddr8));
521
522         result->a = addr6->a;
523         result->b = addr6->b;
524         result->c = addr6->c;
525         result->d = 0xFF;
526         result->e = 0xFE;
527         result->f = addr6->d;
528         result->g = addr6->e;
529         result->h = addr6->f;
530
531
532         PG_RETURN_MACADDR8_P(result);
533 }
534
535 Datum
536 macaddr8tomacaddr(PG_FUNCTION_ARGS)
537 {
538         macaddr8   *addr = PG_GETARG_MACADDR8_P(0);
539         macaddr    *result;
540
541         result = (macaddr *) palloc0(sizeof(macaddr));
542
543         if ((addr->d != 0xFF) || (addr->e != 0xFE))
544                 ereport(ERROR,
545                                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
546                                  errmsg("macaddr8 data out of range to convert to macaddr"),
547                            errhint("Only addresses that have FF and FE as values in the "
548                                            "4th and 5th bytes, from the left, for example: "
549                                          "XX-XX-XX-FF-FE-XX-XX-XX, are eligible to be converted "
550                                            "from macaddr8 to macaddr.")));
551
552         result->a = addr->a;
553         result->b = addr->b;
554         result->c = addr->c;
555         result->d = addr->f;
556         result->e = addr->g;
557         result->f = addr->h;
558
559         PG_RETURN_MACADDR_P(result);
560 }