]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
81a56d6f | 2 | |
11c3a366 | 3 | #include <net/ethernet.h> |
81a56d6f LP |
4 | #include <stdio.h> |
5 | ||
6 | #include "ether-addr-util.h" | |
0c15577a | 7 | #include "hash-funcs.h" |
914ac555 | 8 | #include "hexdecoct.h" |
0c15577a DDM |
9 | #include "in-addr-util.h" |
10 | #include "memory-util.h" | |
11 | #include "siphash24.h" | |
dd4d201a | 12 | #include "string-util.h" |
81a56d6f | 13 | |
ff3f2953 | 14 | char* hw_addr_to_string_full( |
e44cd437 YW |
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 | ||
0c15577a DDM |
66 | bool hw_addr_is_null(const struct hw_addr_data *addr) { |
67 | assert(addr); | |
68 | return addr->length == 0 || memeqzero(addr->bytes, addr->length); | |
69 | } | |
70 | ||
907d5ce4 | 71 | DEFINE_HASH_OPS(hw_addr_hash_ops, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare); |
c6df73ca | 72 | DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(hw_addr_hash_ops_free, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare, free); |
907d5ce4 | 73 | |
81a56d6f LP |
74 | char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) { |
75 | assert(addr); | |
76 | 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 | sprintf(buffer, "%02x:%02x:%02x:%02x:%02x:%02x", | |
83 | addr->ether_addr_octet[0], | |
84 | addr->ether_addr_octet[1], | |
85 | addr->ether_addr_octet[2], | |
86 | addr->ether_addr_octet[3], | |
87 | addr->ether_addr_octet[4], | |
88 | addr->ether_addr_octet[5]); | |
89 | ||
90 | return buffer; | |
91 | } | |
b553a6b1 | 92 | |
7a08d314 | 93 | int ether_addr_compare(const struct ether_addr *a, const struct ether_addr *b) { |
583706ab | 94 | return memcmp(a, b, ETH_ALEN); |
b553a6b1 | 95 | } |
dd4d201a | 96 | |
7a08d314 | 97 | static void ether_addr_hash_func(const struct ether_addr *p, struct siphash *state) { |
c01a5c05 | 98 | siphash24_compress_typesafe(*p, state); |
583706ab YW |
99 | } |
100 | ||
0c15577a DDM |
101 | bool ether_addr_is_broadcast(const struct ether_addr *addr) { |
102 | assert(addr); | |
103 | return memeqbyte(0xff, addr->ether_addr_octet, ETH_ALEN); | |
104 | } | |
105 | ||
7a08d314 | 106 | DEFINE_HASH_OPS(ether_addr_hash_ops, struct ether_addr, ether_addr_hash_func, ether_addr_compare); |
c6df73ca | 107 | DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(ether_addr_hash_ops_free, struct ether_addr, ether_addr_hash_func, ether_addr_compare, free); |
583706ab | 108 | |
76a5d3de YW |
109 | static int parse_hw_addr_one_field(const char **s, char sep, size_t len, uint8_t *buf) { |
110 | const char *hex = HEXDIGITS, *p; | |
111 | uint16_t data = 0; | |
112 | bool cont; | |
113 | ||
114 | assert(s); | |
115 | assert(*s); | |
116 | assert(IN_SET(len, 1, 2)); | |
117 | assert(buf); | |
118 | ||
119 | p = *s; | |
120 | ||
121 | for (size_t i = 0; i < len * 2; i++) { | |
122 | const char *hexoff; | |
123 | size_t x; | |
124 | ||
125 | if (*p == '\0' || *p == sep) { | |
126 | if (i == 0) | |
127 | return -EINVAL; | |
128 | break; | |
129 | } | |
130 | ||
131 | hexoff = strchr(hex, *p); | |
132 | if (!hexoff) | |
133 | return -EINVAL; | |
134 | ||
135 | assert(hexoff >= hex); | |
136 | x = hexoff - hex; | |
137 | if (x >= 16) | |
138 | x -= 6; /* A-F */ | |
139 | ||
140 | assert(x < 16); | |
141 | data <<= 4; | |
142 | data += x; | |
143 | ||
144 | p++; | |
145 | } | |
146 | ||
147 | if (*p != '\0' && *p != sep) | |
148 | return -EINVAL; | |
149 | ||
150 | switch (len) { | |
151 | case 1: | |
152 | buf[0] = data; | |
153 | break; | |
154 | case 2: | |
155 | buf[0] = (data & 0xff00) >> 8; | |
156 | buf[1] = data & 0xff; | |
157 | break; | |
158 | default: | |
159 | assert_not_reached(); | |
160 | } | |
161 | ||
162 | cont = *p == sep; | |
163 | *s = p + cont; | |
164 | return cont; | |
165 | } | |
166 | ||
167 | int parse_hw_addr_full(const char *s, size_t expected_len, struct hw_addr_data *ret) { | |
168 | size_t field_size, max_len, len = 0; | |
169 | uint8_t bytes[HW_ADDR_MAX_SIZE]; | |
170 | char sep; | |
171 | int r; | |
172 | ||
173 | assert(s); | |
174 | assert(expected_len <= HW_ADDR_MAX_SIZE || expected_len == SIZE_MAX); | |
175 | 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 | if (IN_SET(expected_len, 0, sizeof(struct in_addr), sizeof(struct in6_addr))) { | |
196 | union in_addr_union a; | |
197 | int family; | |
198 | ||
199 | if (expected_len == 0) | |
200 | r = in_addr_from_string_auto(s, &family, &a); | |
201 | else { | |
202 | family = expected_len == sizeof(struct in_addr) ? AF_INET : AF_INET6; | |
203 | r = in_addr_from_string(family, s, &a); | |
204 | } | |
205 | if (r >= 0) { | |
206 | ret->length = FAMILY_ADDRESS_SIZE(family); | |
207 | memcpy(ret->bytes, a.bytes, ret->length); | |
208 | return 0; | |
209 | } | |
210 | } | |
211 | ||
212 | max_len = | |
213 | expected_len == 0 ? INFINIBAND_ALEN : | |
214 | expected_len == SIZE_MAX ? HW_ADDR_MAX_SIZE : expected_len; | |
215 | sep = s[strspn(s, HEXDIGITS)]; | |
216 | ||
217 | if (sep == '.') | |
218 | field_size = 2; | |
219 | else if (IN_SET(sep, ':', '-')) | |
220 | field_size = 1; | |
221 | else | |
222 | return -EINVAL; | |
223 | ||
224 | if (max_len % field_size != 0) | |
225 | return -EINVAL; | |
226 | ||
227 | for (size_t i = 0; i < max_len / field_size; i++) { | |
228 | r = parse_hw_addr_one_field(&s, sep, field_size, bytes + i * field_size); | |
229 | if (r < 0) | |
230 | return r; | |
231 | if (r == 0) { | |
232 | len = (i + 1) * field_size; | |
233 | break; | |
234 | } | |
235 | } | |
236 | ||
237 | if (len == 0) | |
238 | return -EINVAL; | |
239 | ||
240 | if (expected_len == 0) { | |
241 | if (!IN_SET(len, 4, 16, ETH_ALEN, INFINIBAND_ALEN)) | |
242 | return -EINVAL; | |
243 | } else if (expected_len != SIZE_MAX) { | |
244 | if (len != expected_len) | |
245 | return -EINVAL; | |
246 | } | |
247 | ||
248 | ret->length = len; | |
249 | memcpy(ret->bytes, bytes, ret->length); | |
250 | return 0; | |
251 | } | |
02160bc9 YW |
252 | |
253 | int parse_ether_addr(const char *s, struct ether_addr *ret) { | |
254 | struct hw_addr_data a; | |
255 | int r; | |
256 | ||
257 | assert(s); | |
258 | assert(ret); | |
259 | ||
260 | r = parse_hw_addr_full(s, ETH_ALEN, &a); | |
261 | if (r < 0) | |
262 | return r; | |
263 | ||
264 | *ret = a.ether; | |
265 | return 0; | |
266 | } | |
e22ca700 LP |
267 | |
268 | void ether_addr_mark_random(struct ether_addr *addr) { | |
269 | assert(addr); | |
270 | ||
271 | /* see eth_random_addr in the kernel */ | |
272 | addr->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */ | |
273 | addr->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */ | |
274 | } |