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