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