Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * mac.c
4 : : * PostgreSQL type definitions for 6 byte, EUI-48, MAC addresses.
5 : : *
6 : : * Portions Copyright (c) 1998-2026, PostgreSQL Global Development Group
7 : : *
8 : : * IDENTIFICATION
9 : : * src/backend/utils/adt/mac.c
10 : : *
11 : : *-------------------------------------------------------------------------
12 : : */
13 : :
14 : : #include "postgres.h"
15 : :
16 : : #include "common/hashfn.h"
17 : : #include "libpq/pqformat.h"
18 : : #include "port/pg_bswap.h"
19 : : #include "utils/fmgrprotos.h"
20 : : #include "utils/inet.h"
21 : : #include "utils/sortsupport.h"
22 : :
23 : :
24 : : /*
25 : : * Utility macros used for sorting and comparing:
26 : : */
27 : :
28 : : #define hibits(addr) \
29 : : ((unsigned long)(((addr)->a<<16)|((addr)->b<<8)|((addr)->c)))
30 : :
31 : : #define lobits(addr) \
32 : : ((unsigned long)(((addr)->d<<16)|((addr)->e<<8)|((addr)->f)))
33 : :
34 : : static int macaddr_cmp_internal(macaddr *a1, macaddr *a2);
35 : : static int macaddr_fast_cmp(Datum x, Datum y, SortSupport ssup);
36 : : static bool macaddr_abbrev_abort(int memtupcount, SortSupport ssup);
37 : : static Datum macaddr_abbrev_convert(Datum original, SortSupport ssup);
38 : :
39 : : /*
40 : : * MAC address reader. Accepts several common notations.
41 : : */
42 : :
43 : : Datum
44 : 2276 : macaddr_in(PG_FUNCTION_ARGS)
45 : : {
46 : 2276 : char *str = PG_GETARG_CSTRING(0);
47 : 2276 : Node *escontext = fcinfo->context;
48 : : macaddr *result;
49 : : int a,
50 : : b,
51 : : c,
52 : : d,
53 : : e,
54 : : f;
55 : : char junk[2];
56 : : int count;
57 : :
58 : : /* %1s matches iff there is trailing non-whitespace garbage */
59 : :
60 : 2276 : count = sscanf(str, "%x:%x:%x:%x:%x:%x%1s",
61 : : &a, &b, &c, &d, &e, &f, junk);
62 [ + + ]: 2276 : if (count != 6)
63 : 48 : count = sscanf(str, "%x-%x-%x-%x-%x-%x%1s",
64 : : &a, &b, &c, &d, &e, &f, junk);
65 [ + + ]: 2276 : if (count != 6)
66 : 44 : count = sscanf(str, "%2x%2x%2x:%2x%2x%2x%1s",
67 : : &a, &b, &c, &d, &e, &f, junk);
68 [ + + ]: 2276 : if (count != 6)
69 : 40 : count = sscanf(str, "%2x%2x%2x-%2x%2x%2x%1s",
70 : : &a, &b, &c, &d, &e, &f, junk);
71 [ + + ]: 2276 : if (count != 6)
72 : 36 : count = sscanf(str, "%2x%2x.%2x%2x.%2x%2x%1s",
73 : : &a, &b, &c, &d, &e, &f, junk);
74 [ + + ]: 2276 : if (count != 6)
75 : 32 : count = sscanf(str, "%2x%2x-%2x%2x-%2x%2x%1s",
76 : : &a, &b, &c, &d, &e, &f, junk);
77 [ + + ]: 2276 : if (count != 6)
78 : 28 : count = sscanf(str, "%2x%2x%2x%2x%2x%2x%1s",
79 : : &a, &b, &c, &d, &e, &f, junk);
80 [ + + ]: 2276 : if (count != 6)
81 [ + + ]: 24 : ereturn(escontext, (Datum) 0,
82 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
83 : : errmsg("invalid input syntax for type %s: \"%s\"", "macaddr",
84 : : str)));
85 : :
86 [ + - + - : 2252 : if ((a < 0) || (a > 255) || (b < 0) || (b > 255) ||
+ - + - ]
87 [ + - + - : 2252 : (c < 0) || (c > 255) || (d < 0) || (d > 255) ||
+ - + - ]
88 [ + - + - : 2252 : (e < 0) || (e > 255) || (f < 0) || (f > 255))
+ - - + ]
89 [ # # ]: 0 : ereturn(escontext, (Datum) 0,
90 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
91 : : errmsg("invalid octet value in \"macaddr\" value: \"%s\"", str)));
92 : :
93 : 2252 : result = palloc_object(macaddr);
94 : :
95 : 2252 : result->a = a;
96 : 2252 : result->b = b;
97 : 2252 : result->c = c;
98 : 2252 : result->d = d;
99 : 2252 : result->e = e;
100 : 2252 : result->f = f;
101 : :
102 : 2252 : PG_RETURN_MACADDR_P(result);
103 : : }
104 : :
105 : : /*
106 : : * MAC address output function. Fixed format.
107 : : */
108 : :
109 : : Datum
110 : 1949 : macaddr_out(PG_FUNCTION_ARGS)
111 : : {
112 : 1949 : macaddr *addr = PG_GETARG_MACADDR_P(0);
113 : : char *result;
114 : :
115 : 1949 : result = (char *) palloc(32);
116 : :
117 : 1949 : snprintf(result, 32, "%02x:%02x:%02x:%02x:%02x:%02x",
118 : 1949 : addr->a, addr->b, addr->c, addr->d, addr->e, addr->f);
119 : :
120 : 1949 : PG_RETURN_CSTRING(result);
121 : : }
122 : :
123 : : /*
124 : : * macaddr_recv - converts external binary format to macaddr
125 : : *
126 : : * The external representation is just the six bytes, MSB first.
127 : : */
128 : : Datum
129 : 0 : macaddr_recv(PG_FUNCTION_ARGS)
130 : : {
131 : 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
132 : : macaddr *addr;
133 : :
134 : 0 : addr = palloc_object(macaddr);
135 : :
136 : 0 : addr->a = pq_getmsgbyte(buf);
137 : 0 : addr->b = pq_getmsgbyte(buf);
138 : 0 : addr->c = pq_getmsgbyte(buf);
139 : 0 : addr->d = pq_getmsgbyte(buf);
140 : 0 : addr->e = pq_getmsgbyte(buf);
141 : 0 : addr->f = pq_getmsgbyte(buf);
142 : :
143 : 0 : PG_RETURN_MACADDR_P(addr);
144 : : }
145 : :
146 : : /*
147 : : * macaddr_send - converts macaddr to binary format
148 : : */
149 : : Datum
150 : 0 : macaddr_send(PG_FUNCTION_ARGS)
151 : : {
152 : 0 : macaddr *addr = PG_GETARG_MACADDR_P(0);
153 : : StringInfoData buf;
154 : :
155 : 0 : pq_begintypsend(&buf);
156 : 0 : pq_sendbyte(&buf, addr->a);
157 : 0 : pq_sendbyte(&buf, addr->b);
158 : 0 : pq_sendbyte(&buf, addr->c);
159 : 0 : pq_sendbyte(&buf, addr->d);
160 : 0 : pq_sendbyte(&buf, addr->e);
161 : 0 : pq_sendbyte(&buf, addr->f);
162 : 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
163 : : }
164 : :
165 : :
166 : : /*
167 : : * Comparison function for sorting:
168 : : */
169 : :
170 : : static int
171 : 38835 : macaddr_cmp_internal(macaddr *a1, macaddr *a2)
172 : : {
173 [ + + ]: 38835 : if (hibits(a1) < hibits(a2))
174 : 14896 : return -1;
175 [ + + ]: 23939 : else if (hibits(a1) > hibits(a2))
176 : 15650 : return 1;
177 [ + + ]: 8289 : else if (lobits(a1) < lobits(a2))
178 : 37 : return -1;
179 [ + + ]: 8252 : else if (lobits(a1) > lobits(a2))
180 : 25 : return 1;
181 : : else
182 : 8227 : return 0;
183 : : }
184 : :
185 : : Datum
186 : 7929 : macaddr_cmp(PG_FUNCTION_ARGS)
187 : : {
188 : 7929 : macaddr *a1 = PG_GETARG_MACADDR_P(0);
189 : 7929 : macaddr *a2 = PG_GETARG_MACADDR_P(1);
190 : :
191 : 7929 : PG_RETURN_INT32(macaddr_cmp_internal(a1, a2));
192 : : }
193 : :
194 : : /*
195 : : * Boolean comparisons.
196 : : */
197 : :
198 : : Datum
199 : 12603 : macaddr_lt(PG_FUNCTION_ARGS)
200 : : {
201 : 12603 : macaddr *a1 = PG_GETARG_MACADDR_P(0);
202 : 12603 : macaddr *a2 = PG_GETARG_MACADDR_P(1);
203 : :
204 : 12603 : PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) < 0);
205 : : }
206 : :
207 : : Datum
208 : 3306 : macaddr_le(PG_FUNCTION_ARGS)
209 : : {
210 : 3306 : macaddr *a1 = PG_GETARG_MACADDR_P(0);
211 : 3306 : macaddr *a2 = PG_GETARG_MACADDR_P(1);
212 : :
213 : 3306 : PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) <= 0);
214 : : }
215 : :
216 : : Datum
217 : 3921 : macaddr_eq(PG_FUNCTION_ARGS)
218 : : {
219 : 3921 : macaddr *a1 = PG_GETARG_MACADDR_P(0);
220 : 3921 : macaddr *a2 = PG_GETARG_MACADDR_P(1);
221 : :
222 : 3921 : PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) == 0);
223 : : }
224 : :
225 : : Datum
226 : 3469 : macaddr_ge(PG_FUNCTION_ARGS)
227 : : {
228 : 3469 : macaddr *a1 = PG_GETARG_MACADDR_P(0);
229 : 3469 : macaddr *a2 = PG_GETARG_MACADDR_P(1);
230 : :
231 : 3469 : PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) >= 0);
232 : : }
233 : :
234 : : Datum
235 : 5973 : macaddr_gt(PG_FUNCTION_ARGS)
236 : : {
237 : 5973 : macaddr *a1 = PG_GETARG_MACADDR_P(0);
238 : 5973 : macaddr *a2 = PG_GETARG_MACADDR_P(1);
239 : :
240 : 5973 : PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) > 0);
241 : : }
242 : :
243 : : Datum
244 : 16 : macaddr_ne(PG_FUNCTION_ARGS)
245 : : {
246 : 16 : macaddr *a1 = PG_GETARG_MACADDR_P(0);
247 : 16 : macaddr *a2 = PG_GETARG_MACADDR_P(1);
248 : :
249 : 16 : PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) != 0);
250 : : }
251 : :
252 : : /*
253 : : * Support function for hash indexes on macaddr.
254 : : */
255 : : Datum
256 : 1558 : hashmacaddr(PG_FUNCTION_ARGS)
257 : : {
258 : 1558 : macaddr *key = PG_GETARG_MACADDR_P(0);
259 : :
260 : 1558 : return hash_any((unsigned char *) key, sizeof(macaddr));
261 : : }
262 : :
263 : : Datum
264 : 40 : hashmacaddrextended(PG_FUNCTION_ARGS)
265 : : {
266 : 40 : macaddr *key = PG_GETARG_MACADDR_P(0);
267 : :
268 : 40 : return hash_any_extended((unsigned char *) key, sizeof(macaddr),
269 : 40 : PG_GETARG_INT64(1));
270 : : }
271 : :
272 : : /*
273 : : * Arithmetic functions: bitwise NOT, AND, OR.
274 : : */
275 : : Datum
276 : 48 : macaddr_not(PG_FUNCTION_ARGS)
277 : : {
278 : 48 : macaddr *addr = PG_GETARG_MACADDR_P(0);
279 : : macaddr *result;
280 : :
281 : 48 : result = palloc_object(macaddr);
282 : 48 : result->a = ~addr->a;
283 : 48 : result->b = ~addr->b;
284 : 48 : result->c = ~addr->c;
285 : 48 : result->d = ~addr->d;
286 : 48 : result->e = ~addr->e;
287 : 48 : result->f = ~addr->f;
288 : 48 : PG_RETURN_MACADDR_P(result);
289 : : }
290 : :
291 : : Datum
292 : 48 : macaddr_and(PG_FUNCTION_ARGS)
293 : : {
294 : 48 : macaddr *addr1 = PG_GETARG_MACADDR_P(0);
295 : 48 : macaddr *addr2 = PG_GETARG_MACADDR_P(1);
296 : : macaddr *result;
297 : :
298 : 48 : result = palloc_object(macaddr);
299 : 48 : result->a = addr1->a & addr2->a;
300 : 48 : result->b = addr1->b & addr2->b;
301 : 48 : result->c = addr1->c & addr2->c;
302 : 48 : result->d = addr1->d & addr2->d;
303 : 48 : result->e = addr1->e & addr2->e;
304 : 48 : result->f = addr1->f & addr2->f;
305 : 48 : PG_RETURN_MACADDR_P(result);
306 : : }
307 : :
308 : : Datum
309 : 48 : macaddr_or(PG_FUNCTION_ARGS)
310 : : {
311 : 48 : macaddr *addr1 = PG_GETARG_MACADDR_P(0);
312 : 48 : macaddr *addr2 = PG_GETARG_MACADDR_P(1);
313 : : macaddr *result;
314 : :
315 : 48 : result = palloc_object(macaddr);
316 : 48 : result->a = addr1->a | addr2->a;
317 : 48 : result->b = addr1->b | addr2->b;
318 : 48 : result->c = addr1->c | addr2->c;
319 : 48 : result->d = addr1->d | addr2->d;
320 : 48 : result->e = addr1->e | addr2->e;
321 : 48 : result->f = addr1->f | addr2->f;
322 : 48 : PG_RETURN_MACADDR_P(result);
323 : : }
324 : :
325 : : /*
326 : : * Truncation function to allow comparing mac manufacturers.
327 : : * From suggestion by Alex Pilosov <alex@pilosoft.com>
328 : : */
329 : : Datum
330 : 48 : macaddr_trunc(PG_FUNCTION_ARGS)
331 : : {
332 : 48 : macaddr *addr = PG_GETARG_MACADDR_P(0);
333 : : macaddr *result;
334 : :
335 : 48 : result = palloc_object(macaddr);
336 : :
337 : 48 : result->a = addr->a;
338 : 48 : result->b = addr->b;
339 : 48 : result->c = addr->c;
340 : 48 : result->d = 0;
341 : 48 : result->e = 0;
342 : 48 : result->f = 0;
343 : :
344 : 48 : PG_RETURN_MACADDR_P(result);
345 : : }
346 : :
347 : : /*
348 : : * SortSupport strategy function. Populates a SortSupport struct with the
349 : : * information necessary to use comparison by abbreviated keys.
350 : : */
351 : : Datum
352 : 14 : macaddr_sortsupport(PG_FUNCTION_ARGS)
353 : : {
354 : 14 : SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
355 : :
356 : 14 : ssup->comparator = macaddr_fast_cmp;
357 : 14 : ssup->ssup_extra = NULL;
358 : :
359 [ + + ]: 14 : if (ssup->abbreviate)
360 : : {
361 : 12 : ssup->comparator = ssup_datum_unsigned_cmp;
362 : 12 : ssup->abbrev_converter = macaddr_abbrev_convert;
363 : 12 : ssup->abbrev_abort = macaddr_abbrev_abort;
364 : 12 : ssup->abbrev_full_comparator = macaddr_fast_cmp;
365 : : }
366 : :
367 : 14 : PG_RETURN_VOID();
368 : : }
369 : :
370 : : /*
371 : : * SortSupport "traditional" comparison function. Pulls two MAC addresses from
372 : : * the heap and runs a standard comparison on them.
373 : : */
374 : : static int
375 : 1618 : macaddr_fast_cmp(Datum x, Datum y, SortSupport ssup)
376 : : {
377 : 1618 : macaddr *arg1 = DatumGetMacaddrP(x);
378 : 1618 : macaddr *arg2 = DatumGetMacaddrP(y);
379 : :
380 : 1618 : return macaddr_cmp_internal(arg1, arg2);
381 : : }
382 : :
383 : : /*
384 : : * Abbreviation is never aborted for macaddr because the 6-byte MAC address
385 : : * fits entirely within a 64-bit Datum, making the abbreviated key
386 : : * authoritative.
387 : : */
388 : : static bool
389 : 8 : macaddr_abbrev_abort(int memtupcount, SortSupport ssup)
390 : : {
391 : 8 : return false;
392 : : }
393 : :
394 : : /*
395 : : * SortSupport conversion routine. Converts original macaddr representation
396 : : * to abbreviated key representation.
397 : : *
398 : : * Packs the bytes of a 6-byte MAC address into a Datum and treats it as an
399 : : * unsigned integer for purposes of comparison. There will be two zeroed bytes
400 : : * of padding. The integer is converted to native endianness to facilitate
401 : : * easy comparison.
402 : : */
403 : : static Datum
404 : 108 : macaddr_abbrev_convert(Datum original, SortSupport ssup)
405 : : {
406 : 108 : macaddr *authoritative = DatumGetMacaddrP(original);
407 : : Datum res;
408 : :
409 : : /*
410 : : * Zero out the 8-byte Datum and copy in the 6 bytes of the MAC address.
411 : : * There will be two bytes of zero padding on the end of the least
412 : : * significant bits.
413 : : */
414 : : StaticAssertDecl(sizeof(res) >= sizeof(macaddr),
415 : : "Datum is too small for macaddr");
416 : 108 : memset(&res, 0, sizeof(res));
417 : 108 : memcpy(&res, authoritative, sizeof(macaddr));
418 : :
419 : : /*
420 : : * Byteswap on little-endian machines.
421 : : *
422 : : * This is needed so that ssup_datum_unsigned_cmp() (an unsigned integer
423 : : * 3-way comparator) works correctly on all platforms. Without this, the
424 : : * comparator would have to call memcmp() with a pair of pointers to the
425 : : * first byte of each abbreviated key, which is slower.
426 : : */
427 : 108 : res = DatumBigEndianToNative(res);
428 : :
429 : 108 : return res;
430 : : }
|