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 | |
38 | static unsigned char hex2_to_uchar(const unsigned char *str, const unsigned char *ptr); |
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 | * 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 | */ |
60 | static inline unsigned char |
61 | hex2_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 | |
90 | invalid_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 | */ |
103 | Datum |
104 | macaddr8_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 | */ |
241 | Datum |
242 | macaddr8_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 | */ |
261 | Datum |
262 | macaddr8_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 | */ |
294 | Datum |
295 | macaddr8_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 | */ |
317 | static int32 |
318 | macaddr8_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 | |
332 | Datum |
333 | macaddr8_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 | |
345 | Datum |
346 | macaddr8_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 | |
354 | Datum |
355 | macaddr8_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 | |
363 | Datum |
364 | macaddr8_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 | |
372 | Datum |
373 | macaddr8_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 | |
381 | Datum |
382 | macaddr8_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 | |
390 | Datum |
391 | macaddr8_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 | */ |
402 | Datum |
403 | hashmacaddr8(PG_FUNCTION_ARGS) |
404 | { |
405 | macaddr8 *key = PG_GETARG_MACADDR8_P(0); |
406 | |
407 | return hash_any((unsigned char *) key, sizeof(macaddr8)); |
408 | } |
409 | |
410 | Datum |
411 | hashmacaddr8extended(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 | */ |
422 | Datum |
423 | macaddr8_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 | |
441 | Datum |
442 | macaddr8_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 | |
461 | Datum |
462 | macaddr8_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 | */ |
484 | Datum |
485 | macaddr8_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 | */ |
507 | Datum |
508 | macaddr8_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 | |
531 | Datum |
532 | macaddrtomacaddr8(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 | |
552 | Datum |
553 | macaddr8tomacaddr(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 | |