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 16115 : return -1;
175 22720 : else if (hibits(a1) > hibits(a2))
176 14431 : 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 12903 : macaddr_lt(PG_FUNCTION_ARGS)
200 : {
201 12903 : macaddr *a1 = PG_GETARG_MACADDR_P(0);
202 12903 : macaddr *a2 = PG_GETARG_MACADDR_P(1);
203 :
204 12903 : PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) < 0);
205 : }
206 :
207 : Datum
208 3753 : macaddr_le(PG_FUNCTION_ARGS)
209 : {
210 3753 : macaddr *a1 = PG_GETARG_MACADDR_P(0);
211 3753 : macaddr *a2 = PG_GETARG_MACADDR_P(1);
212 :
213 3753 : 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 3022 : macaddr_ge(PG_FUNCTION_ARGS)
227 : {
228 3022 : macaddr *a1 = PG_GETARG_MACADDR_P(0);
229 3022 : macaddr *a2 = PG_GETARG_MACADDR_P(1);
230 :
231 3022 : PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) >= 0);
232 : }
233 :
234 : Datum
235 5673 : macaddr_gt(PG_FUNCTION_ARGS)
236 : {
237 5673 : macaddr *a1 = PG_GETARG_MACADDR_P(0);
238 5673 : macaddr *a2 = PG_GETARG_MACADDR_P(1);
239 :
240 5673 : 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 : }
|