]>
Commit | Line | Data |
---|---|---|
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 |
14 | char *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 |
36 | struct 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 |
45 | int 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 | 58 | void 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 | ||
66 | DEFINE_HASH_OPS(hw_addr_hash_ops, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare); | |
c6df73ca | 67 | DEFINE_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 |
69 | char* 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 |
88 | int 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 | 104 | int 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 | 108 | static 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 | 112 | DEFINE_HASH_OPS(ether_addr_hash_ops, struct ether_addr, ether_addr_hash_func, ether_addr_compare); |
c6df73ca | 113 | DEFINE_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 |
115 | static 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 | ||
173 | int 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 | |
259 | int 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 | |
274 | void 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 | } |