Age Owner Branch data TLA 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
9406 tgl@sss.pgh.pa.us 44 :CBC 2396 : macaddr_in(PG_FUNCTION_ARGS)
45 : : {
46 : 2396 : char *str = PG_GETARG_CSTRING(0);
1238 47 : 2396 : 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 : :
8605 60 : 2396 : count = sscanf(str, "%x:%x:%x:%x:%x:%x%1s",
61 : : &a, &b, &c, &d, &e, &f, junk);
9023 62 [ + + ]: 2396 : if (count != 6)
8605 63 : 48 : count = sscanf(str, "%x-%x-%x-%x-%x-%x%1s",
64 : : &a, &b, &c, &d, &e, &f, junk);
9023 65 [ + + ]: 2396 : if (count != 6)
8605 66 : 44 : count = sscanf(str, "%2x%2x%2x:%2x%2x%2x%1s",
67 : : &a, &b, &c, &d, &e, &f, junk);
9023 68 [ + + ]: 2396 : if (count != 6)
8605 69 : 40 : count = sscanf(str, "%2x%2x%2x-%2x%2x%2x%1s",
70 : : &a, &b, &c, &d, &e, &f, junk);
9023 71 [ + + ]: 2396 : if (count != 6)
8605 72 : 36 : count = sscanf(str, "%2x%2x.%2x%2x.%2x%2x%1s",
73 : : &a, &b, &c, &d, &e, &f, junk);
4214 peter_e@gmx.net 74 [ + + ]: 2396 : if (count != 6)
75 : 32 : count = sscanf(str, "%2x%2x-%2x%2x-%2x%2x%1s",
76 : : &a, &b, &c, &d, &e, &f, junk);
8725 bruce@momjian.us 77 [ + + ]: 2396 : if (count != 6)
8605 tgl@sss.pgh.pa.us 78 : 28 : count = sscanf(str, "%2x%2x%2x%2x%2x%2x%1s",
79 : : &a, &b, &c, &d, &e, &f, junk);
9023 80 [ + + ]: 2396 : if (count != 6)
1238 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 : :
9023 86 [ + - + - : 2372 : if ((a < 0) || (a > 255) || (b < 0) || (b > 255) ||
+ - + - ]
87 [ + - + - : 2372 : (c < 0) || (c > 255) || (d < 0) || (d > 255) ||
+ - + - ]
88 [ + - + - : 2372 : (e < 0) || (e > 255) || (f < 0) || (f > 255))
+ - - + ]
1238 tgl@sss.pgh.pa.us 89 [ # # ]:UBC 0 : ereturn(escontext, (Datum) 0,
90 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
91 : : errmsg("invalid octet value in \"macaddr\" value: \"%s\"", str)));
92 : :
146 michael@paquier.xyz 93 :GNC 2372 : result = palloc_object(macaddr);
94 : :
10076 bruce@momjian.us 95 :CBC 2372 : result->a = a;
96 : 2372 : result->b = b;
97 : 2372 : result->c = c;
98 : 2372 : result->d = d;
99 : 2372 : result->e = e;
100 : 2372 : result->f = f;
101 : :
9406 tgl@sss.pgh.pa.us 102 : 2372 : PG_RETURN_MACADDR_P(result);
103 : : }
104 : :
105 : : /*
106 : : * MAC address output function. Fixed format.
107 : : */
108 : :
109 : : Datum
110 : 2069 : macaddr_out(PG_FUNCTION_ARGS)
111 : : {
9175 bruce@momjian.us 112 : 2069 : macaddr *addr = PG_GETARG_MACADDR_P(0);
113 : : char *result;
114 : :
10076 115 : 2069 : result = (char *) palloc(32);
116 : :
8651 117 : 2069 : snprintf(result, 32, "%02x:%02x:%02x:%02x:%02x:%02x",
8644 118 : 2069 : addr->a, addr->b, addr->c, addr->d, addr->e, addr->f);
119 : :
9406 tgl@sss.pgh.pa.us 120 : 2069 : 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
8393 tgl@sss.pgh.pa.us 129 :UBC 0 : macaddr_recv(PG_FUNCTION_ARGS)
130 : : {
131 : 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
132 : : macaddr *addr;
133 : :
146 michael@paquier.xyz 134 :UNC 0 : addr = palloc_object(macaddr);
135 : :
8393 tgl@sss.pgh.pa.us 136 :UBC 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 : : {
8310 bruce@momjian.us 152 : 0 : macaddr *addr = PG_GETARG_MACADDR_P(0);
153 : : StringInfoData buf;
154 : :
8393 tgl@sss.pgh.pa.us 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
9406 tgl@sss.pgh.pa.us 171 :CBC 138329 : macaddr_cmp_internal(macaddr *a1, macaddr *a2)
172 : : {
173 [ + + ]: 138329 : if (hibits(a1) < hibits(a2))
174 : 65012 : return -1;
175 [ + + ]: 73317 : else if (hibits(a1) > hibits(a2))
176 : 63794 : return 1;
177 [ + + ]: 9523 : else if (lobits(a1) < lobits(a2))
178 : 37 : return -1;
179 [ + + ]: 9486 : else if (lobits(a1) > lobits(a2))
180 : 25 : return 1;
181 : : else
182 : 9461 : return 0;
183 : : }
184 : :
185 : : Datum
186 : 7929 : macaddr_cmp(PG_FUNCTION_ARGS)
187 : : {
9175 bruce@momjian.us 188 : 7929 : macaddr *a1 = PG_GETARG_MACADDR_P(0);
189 : 7929 : macaddr *a2 = PG_GETARG_MACADDR_P(1);
190 : :
9406 tgl@sss.pgh.pa.us 191 : 7929 : PG_RETURN_INT32(macaddr_cmp_internal(a1, a2));
192 : : }
193 : :
194 : : /*
195 : : * Boolean comparisons.
196 : : */
197 : :
198 : : Datum
199 : 93709 : macaddr_lt(PG_FUNCTION_ARGS)
200 : : {
9175 bruce@momjian.us 201 : 93709 : macaddr *a1 = PG_GETARG_MACADDR_P(0);
202 : 93709 : macaddr *a2 = PG_GETARG_MACADDR_P(1);
203 : :
9406 tgl@sss.pgh.pa.us 204 : 93709 : PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) < 0);
205 : : }
206 : :
207 : : Datum
208 : 3753 : macaddr_le(PG_FUNCTION_ARGS)
209 : : {
9175 bruce@momjian.us 210 : 3753 : macaddr *a1 = PG_GETARG_MACADDR_P(0);
211 : 3753 : macaddr *a2 = PG_GETARG_MACADDR_P(1);
212 : :
9406 tgl@sss.pgh.pa.us 213 : 3753 : PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) <= 0);
214 : : }
215 : :
216 : : Datum
217 : 18403 : macaddr_eq(PG_FUNCTION_ARGS)
218 : : {
9175 bruce@momjian.us 219 : 18403 : macaddr *a1 = PG_GETARG_MACADDR_P(0);
220 : 18403 : macaddr *a2 = PG_GETARG_MACADDR_P(1);
221 : :
9406 tgl@sss.pgh.pa.us 222 : 18403 : PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) == 0);
223 : : }
224 : :
225 : : Datum
226 : 3022 : macaddr_ge(PG_FUNCTION_ARGS)
227 : : {
9175 bruce@momjian.us 228 : 3022 : macaddr *a1 = PG_GETARG_MACADDR_P(0);
229 : 3022 : macaddr *a2 = PG_GETARG_MACADDR_P(1);
230 : :
9406 tgl@sss.pgh.pa.us 231 : 3022 : PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) >= 0);
232 : : }
233 : :
234 : : Datum
235 : 5673 : macaddr_gt(PG_FUNCTION_ARGS)
236 : : {
9175 bruce@momjian.us 237 : 5673 : macaddr *a1 = PG_GETARG_MACADDR_P(0);
238 : 5673 : macaddr *a2 = PG_GETARG_MACADDR_P(1);
239 : :
9406 tgl@sss.pgh.pa.us 240 : 5673 : PG_RETURN_BOOL(macaddr_cmp_internal(a1, a2) > 0);
241 : : }
242 : :
243 : : Datum
244 : 16 : macaddr_ne(PG_FUNCTION_ARGS)
245 : : {
9175 bruce@momjian.us 246 : 16 : macaddr *a1 = PG_GETARG_MACADDR_P(0);
247 : 16 : macaddr *a2 = PG_GETARG_MACADDR_P(1);
248 : :
9406 tgl@sss.pgh.pa.us 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
9279 256 : 1564 : hashmacaddr(PG_FUNCTION_ARGS)
257 : : {
9175 bruce@momjian.us 258 : 1564 : macaddr *key = PG_GETARG_MACADDR_P(0);
259 : :
8823 tgl@sss.pgh.pa.us 260 : 1564 : return hash_any((unsigned char *) key, sizeof(macaddr));
261 : : }
262 : :
263 : : Datum
3169 rhaas@postgresql.org 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
5220 276 : 48 : macaddr_not(PG_FUNCTION_ARGS)
277 : : {
5077 bruce@momjian.us 278 : 48 : macaddr *addr = PG_GETARG_MACADDR_P(0);
279 : : macaddr *result;
280 : :
146 michael@paquier.xyz 281 :GNC 48 : result = palloc_object(macaddr);
5220 rhaas@postgresql.org 282 :CBC 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 : : {
5077 bruce@momjian.us 294 : 48 : macaddr *addr1 = PG_GETARG_MACADDR_P(0);
295 : 48 : macaddr *addr2 = PG_GETARG_MACADDR_P(1);
296 : : macaddr *result;
297 : :
146 michael@paquier.xyz 298 :GNC 48 : result = palloc_object(macaddr);
5220 rhaas@postgresql.org 299 :CBC 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 : : {
5077 bruce@momjian.us 311 : 48 : macaddr *addr1 = PG_GETARG_MACADDR_P(0);
312 : 48 : macaddr *addr2 = PG_GETARG_MACADDR_P(1);
313 : : macaddr *result;
314 : :
146 michael@paquier.xyz 315 :GNC 48 : result = palloc_object(macaddr);
5220 rhaas@postgresql.org 316 :CBC 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
9386 lockhart@fourpalms.o 330 : 48 : macaddr_trunc(PG_FUNCTION_ARGS)
331 : : {
9175 bruce@momjian.us 332 : 48 : macaddr *addr = PG_GETARG_MACADDR_P(0);
333 : : macaddr *result;
334 : :
146 michael@paquier.xyz 335 :GNC 48 : result = palloc_object(macaddr);
336 : :
9386 lockhart@fourpalms.o 337 :CBC 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
3324 teodor@sigaev.ru 352 : 20 : macaddr_sortsupport(PG_FUNCTION_ARGS)
353 : : {
354 : 20 : SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
355 : :
356 : 20 : ssup->comparator = macaddr_fast_cmp;
357 : 20 : ssup->ssup_extra = NULL;
358 : :
359 [ + + ]: 20 : if (ssup->abbreviate)
360 : : {
1494 john.naylor@postgres 361 : 12 : ssup->comparator = ssup_datum_unsigned_cmp;
3324 teodor@sigaev.ru 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 : 20 : 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 : 5824 : macaddr_fast_cmp(Datum x, Datum y, SortSupport ssup)
376 : : {
377 : 5824 : macaddr *arg1 = DatumGetMacaddrP(x);
378 : 5824 : macaddr *arg2 = DatumGetMacaddrP(y);
379 : :
380 : 5824 : 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 : : {
3324 teodor@sigaev.ru 391 :GBC 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
3324 teodor@sigaev.ru 404 :CBC 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");
265 tgl@sss.pgh.pa.us 416 :GNC 108 : memset(&res, 0, sizeof(res));
3324 teodor@sigaev.ru 417 :CBC 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 : : }
|