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