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