]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/ether-addr-util.c
siphash24: introduce siphash24_compress_typesafe() macro
[thirdparty/systemd.git] / src / basic / ether-addr-util.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
81a56d6f 2
dccca82b 3#include <errno.h>
4fc8a29a 4#include <inttypes.h>
11c3a366 5#include <net/ethernet.h>
81a56d6f 6#include <stdio.h>
11c3a366 7#include <sys/types.h>
81a56d6f
LP
8
9#include "ether-addr-util.h"
914ac555 10#include "hexdecoct.h"
81a56d6f 11#include "macro.h"
dd4d201a 12#include "string-util.h"
81a56d6f 13
e44cd437
YW
14char *hw_addr_to_string_full(
15 const struct hw_addr_data *addr,
16 HardwareAddressToStringFlags flags,
17 char buffer[static HW_ADDR_TO_STRING_MAX]) {
18
4fc8a29a
TR
19 assert(addr);
20 assert(buffer);
21 assert(addr->length <= HW_ADDR_MAX_SIZE);
22
914ac555
YW
23 for (size_t i = 0, j = 0; i < addr->length; i++) {
24 buffer[j++] = hexchar(addr->bytes[i] >> 4);
25 buffer[j++] = hexchar(addr->bytes[i] & 0x0f);
e44cd437
YW
26 if (!FLAGS_SET(flags, HW_ADDR_TO_STRING_NO_COLON))
27 buffer[j++] = ':';
4fc8a29a
TR
28 }
29
e44cd437
YW
30 buffer[addr->length == 0 || FLAGS_SET(flags, HW_ADDR_TO_STRING_NO_COLON) ?
31 addr->length * 2 :
32 addr->length * 3 - 1] = '\0';
4fc8a29a
TR
33 return buffer;
34}
35
069f5df0
YW
36struct hw_addr_data *hw_addr_set(struct hw_addr_data *addr, const uint8_t *bytes, size_t length) {
37 assert(addr);
38 assert(length <= HW_ADDR_MAX_SIZE);
39
40 addr->length = length;
41 memcpy_safe(addr->bytes, bytes, length);
42 return addr;
43}
44
30b97725
YW
45int hw_addr_compare(const struct hw_addr_data *a, const struct hw_addr_data *b) {
46 int r;
47
48 assert(a);
49 assert(b);
50
51 r = CMP(a->length, b->length);
52 if (r != 0)
53 return r;
54
55 return memcmp(a->bytes, b->bytes, a->length);
56}
57
ca7d2083 58void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state) {
907d5ce4
YW
59 assert(p);
60 assert(state);
61
c01a5c05
YW
62 siphash24_compress_typesafe(p->length, state);
63 siphash24_compress_safe(p->bytes, p->length, state);
907d5ce4
YW
64}
65
66DEFINE_HASH_OPS(hw_addr_hash_ops, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare);
c6df73ca 67DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(hw_addr_hash_ops_free, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare, free);
907d5ce4 68
81a56d6f
LP
69char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) {
70 assert(addr);
71 assert(buffer);
72
73 /* Like ether_ntoa() but uses %02x instead of %x to print
74 * ethernet addresses, which makes them look less funny. Also,
75 * doesn't use a static buffer. */
76
77 sprintf(buffer, "%02x:%02x:%02x:%02x:%02x:%02x",
78 addr->ether_addr_octet[0],
79 addr->ether_addr_octet[1],
80 addr->ether_addr_octet[2],
81 addr->ether_addr_octet[3],
82 addr->ether_addr_octet[4],
83 addr->ether_addr_octet[5]);
84
85 return buffer;
86}
b553a6b1 87
ae8e3c2b
YW
88int ether_addr_to_string_alloc(const struct ether_addr *addr, char **ret) {
89 char *buf;
90
91 assert(addr);
92 assert(ret);
93
94 buf = new(char, ETHER_ADDR_TO_STRING_MAX);
95 if (!buf)
96 return -ENOMEM;
97
98 ether_addr_to_string(addr, buf);
99
100 *ret = buf;
101 return 0;
102}
103
7a08d314 104int ether_addr_compare(const struct ether_addr *a, const struct ether_addr *b) {
583706ab 105 return memcmp(a, b, ETH_ALEN);
b553a6b1 106}
dd4d201a 107
7a08d314 108static void ether_addr_hash_func(const struct ether_addr *p, struct siphash *state) {
c01a5c05 109 siphash24_compress_typesafe(*p, state);
583706ab
YW
110}
111
7a08d314 112DEFINE_HASH_OPS(ether_addr_hash_ops, struct ether_addr, ether_addr_hash_func, ether_addr_compare);
c6df73ca 113DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(ether_addr_hash_ops_free, struct ether_addr, ether_addr_hash_func, ether_addr_compare, free);
583706ab 114
76a5d3de
YW
115static int parse_hw_addr_one_field(const char **s, char sep, size_t len, uint8_t *buf) {
116 const char *hex = HEXDIGITS, *p;
117 uint16_t data = 0;
118 bool cont;
119
120 assert(s);
121 assert(*s);
122 assert(IN_SET(len, 1, 2));
123 assert(buf);
124
125 p = *s;
126
127 for (size_t i = 0; i < len * 2; i++) {
128 const char *hexoff;
129 size_t x;
130
131 if (*p == '\0' || *p == sep) {
132 if (i == 0)
133 return -EINVAL;
134 break;
135 }
136
137 hexoff = strchr(hex, *p);
138 if (!hexoff)
139 return -EINVAL;
140
141 assert(hexoff >= hex);
142 x = hexoff - hex;
143 if (x >= 16)
144 x -= 6; /* A-F */
145
146 assert(x < 16);
147 data <<= 4;
148 data += x;
149
150 p++;
151 }
152
153 if (*p != '\0' && *p != sep)
154 return -EINVAL;
155
156 switch (len) {
157 case 1:
158 buf[0] = data;
159 break;
160 case 2:
161 buf[0] = (data & 0xff00) >> 8;
162 buf[1] = data & 0xff;
163 break;
164 default:
165 assert_not_reached();
166 }
167
168 cont = *p == sep;
169 *s = p + cont;
170 return cont;
171}
172
173int parse_hw_addr_full(const char *s, size_t expected_len, struct hw_addr_data *ret) {
174 size_t field_size, max_len, len = 0;
175 uint8_t bytes[HW_ADDR_MAX_SIZE];
176 char sep;
177 int r;
178
179 assert(s);
180 assert(expected_len <= HW_ADDR_MAX_SIZE || expected_len == SIZE_MAX);
181 assert(ret);
182
183 /* This accepts the following formats:
184 *
185 * Dot separated 2 bytes format: xxyy.zzaa.bbcc
186 * Colon separated 1 bytes format: xx:yy:zz:aa:bb:cc
187 * Hyphen separated 1 bytes format: xx-yy-zz-aa-bb-cc
188 *
189 * Moreover, if expected_len == 0, 4, or 16, this also accepts:
190 *
191 * IPv4 format: used by IPv4 tunnel, e.g. ipgre
192 * IPv6 format: used by IPv6 tunnel, e.g. ip6gre
193 *
194 * The expected_len argument controls the length of acceptable addresses:
195 *
196 * 0: accepts 4 (AF_INET), 16 (AF_INET6), 6 (ETH_ALEN), or 20 (INFINIBAND_ALEN).
197 * SIZE_MAX: accepts arbitrary length, but at least one separator must be included.
198 * Otherwise: accepts addresses with matching length.
199 */
200
201 if (IN_SET(expected_len, 0, sizeof(struct in_addr), sizeof(struct in6_addr))) {
202 union in_addr_union a;
203 int family;
204
205 if (expected_len == 0)
206 r = in_addr_from_string_auto(s, &family, &a);
207 else {
208 family = expected_len == sizeof(struct in_addr) ? AF_INET : AF_INET6;
209 r = in_addr_from_string(family, s, &a);
210 }
211 if (r >= 0) {
212 ret->length = FAMILY_ADDRESS_SIZE(family);
213 memcpy(ret->bytes, a.bytes, ret->length);
214 return 0;
215 }
216 }
217
218 max_len =
219 expected_len == 0 ? INFINIBAND_ALEN :
220 expected_len == SIZE_MAX ? HW_ADDR_MAX_SIZE : expected_len;
221 sep = s[strspn(s, HEXDIGITS)];
222
223 if (sep == '.')
224 field_size = 2;
225 else if (IN_SET(sep, ':', '-'))
226 field_size = 1;
227 else
228 return -EINVAL;
229
230 if (max_len % field_size != 0)
231 return -EINVAL;
232
233 for (size_t i = 0; i < max_len / field_size; i++) {
234 r = parse_hw_addr_one_field(&s, sep, field_size, bytes + i * field_size);
235 if (r < 0)
236 return r;
237 if (r == 0) {
238 len = (i + 1) * field_size;
239 break;
240 }
241 }
242
243 if (len == 0)
244 return -EINVAL;
245
246 if (expected_len == 0) {
247 if (!IN_SET(len, 4, 16, ETH_ALEN, INFINIBAND_ALEN))
248 return -EINVAL;
249 } else if (expected_len != SIZE_MAX) {
250 if (len != expected_len)
251 return -EINVAL;
252 }
253
254 ret->length = len;
255 memcpy(ret->bytes, bytes, ret->length);
256 return 0;
257}
02160bc9
YW
258
259int parse_ether_addr(const char *s, struct ether_addr *ret) {
260 struct hw_addr_data a;
261 int r;
262
263 assert(s);
264 assert(ret);
265
266 r = parse_hw_addr_full(s, ETH_ALEN, &a);
267 if (r < 0)
268 return r;
269
270 *ret = a.ether;
271 return 0;
272}
e22ca700
LP
273
274void ether_addr_mark_random(struct ether_addr *addr) {
275 assert(addr);
276
277 /* see eth_random_addr in the kernel */
278 addr->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */
279 addr->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */
280}