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