Coverage Report

Created: 2025-11-16 06:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/systemd/src/basic/ether-addr-util.c
Line
Count
Source
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3
#include <net/ethernet.h>
4
#include <stdio.h>
5
6
#include "ether-addr-util.h"
7
#include "hash-funcs.h"
8
#include "hexdecoct.h"
9
#include "in-addr-util.h"
10
#include "memory-util.h"
11
#include "siphash24.h"
12
#include "string-util.h"
13
14
char* hw_addr_to_string_full(
15
                const struct hw_addr_data *addr,
16
                HardwareAddressToStringFlags flags,
17
0
                char buffer[static HW_ADDR_TO_STRING_MAX]) {
18
19
0
        assert(addr);
20
0
        assert(buffer);
21
0
        assert(addr->length <= HW_ADDR_MAX_SIZE);
22
23
0
        for (size_t i = 0, j = 0; i < addr->length; i++) {
24
0
                buffer[j++] = hexchar(addr->bytes[i] >> 4);
25
0
                buffer[j++] = hexchar(addr->bytes[i] & 0x0f);
26
0
                if (!FLAGS_SET(flags, HW_ADDR_TO_STRING_NO_COLON))
27
0
                        buffer[j++] = ':';
28
0
        }
29
30
0
        buffer[addr->length == 0 || FLAGS_SET(flags, HW_ADDR_TO_STRING_NO_COLON) ?
31
0
               addr->length * 2 :
32
0
               addr->length * 3 - 1] = '\0';
33
0
        return buffer;
34
0
}
35
36
4.23k
struct hw_addr_data *hw_addr_set(struct hw_addr_data *addr, const uint8_t *bytes, size_t length) {
37
4.23k
        assert(addr);
38
4.23k
        assert(length <= HW_ADDR_MAX_SIZE);
39
40
4.23k
        addr->length = length;
41
4.23k
        memcpy_safe(addr->bytes, bytes, length);
42
4.23k
        return addr;
43
4.23k
}
44
45
27.1k
int hw_addr_compare(const struct hw_addr_data *a, const struct hw_addr_data *b) {
46
27.1k
        int r;
47
48
27.1k
        assert(a);
49
27.1k
        assert(b);
50
51
27.1k
        r = CMP(a->length, b->length);
52
27.1k
        if (r != 0)
53
4.88k
                return r;
54
55
22.3k
        return memcmp(a->bytes, b->bytes, a->length);
56
27.1k
}
57
58
95.3k
void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state) {
59
95.3k
        assert(p);
60
95.3k
        assert(state);
61
62
95.3k
        siphash24_compress_typesafe(p->length, state);
63
95.3k
        siphash24_compress_safe(p->bytes, p->length, state);
64
95.3k
}
65
66
0
bool hw_addr_is_null(const struct hw_addr_data *addr) {
67
0
        assert(addr);
68
0
        return addr->length == 0 || memeqzero(addr->bytes, addr->length);
69
0
}
70
71
DEFINE_HASH_OPS(hw_addr_hash_ops, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare);
72
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(hw_addr_hash_ops_free, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare, free);
73
74
6
char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) {
75
6
        assert(addr);
76
6
        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
6
        sprintf(buffer, "%02x:%02x:%02x:%02x:%02x:%02x",
83
6
                addr->ether_addr_octet[0],
84
6
                addr->ether_addr_octet[1],
85
6
                addr->ether_addr_octet[2],
86
6
                addr->ether_addr_octet[3],
87
6
                addr->ether_addr_octet[4],
88
6
                addr->ether_addr_octet[5]);
89
90
6
        return buffer;
91
6
}
92
93
22.7k
int ether_addr_compare(const struct ether_addr *a, const struct ether_addr *b) {
94
22.7k
        return memcmp(a, b, ETH_ALEN);
95
22.7k
}
96
97
1.01k
static void ether_addr_hash_func(const struct ether_addr *p, struct siphash *state) {
98
1.01k
        siphash24_compress_typesafe(*p, state);
99
1.01k
}
100
101
0
bool ether_addr_is_broadcast(const struct ether_addr *addr) {
102
0
        assert(addr);
103
0
        return memeqbyte(0xff, addr->ether_addr_octet, ETH_ALEN);
104
0
}
105
106
DEFINE_HASH_OPS(ether_addr_hash_ops, struct ether_addr, ether_addr_hash_func, ether_addr_compare);
107
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(ether_addr_hash_ops_free, struct ether_addr, ether_addr_hash_func, ether_addr_compare, free);
108
109
141k
static int parse_hw_addr_one_field(const char **s, char sep, size_t len, uint8_t *buf) {
110
141k
        const char *hex = HEXDIGITS, *p;
111
141k
        uint16_t data = 0;
112
141k
        bool cont;
113
114
141k
        assert(s);
115
141k
        assert(*s);
116
141k
        assert(IN_SET(len, 1, 2));
117
141k
        assert(buf);
118
119
141k
        p = *s;
120
121
308k
        for (size_t i = 0; i < len * 2; i++) {
122
298k
                const char *hexoff;
123
298k
                size_t x;
124
125
298k
                if (*p == '\0' || *p == sep) {
126
129k
                        if (i == 0)
127
5.24k
                                return -EINVAL;
128
123k
                        break;
129
129k
                }
130
131
169k
                hexoff = strchr(hex, *p);
132
169k
                if (!hexoff)
133
2.19k
                        return -EINVAL;
134
135
167k
                assert(hexoff >= hex);
136
167k
                x = hexoff - hex;
137
167k
                if (x >= 16)
138
40.1k
                        x -= 6; /* A-F */
139
140
167k
                assert(x < 16);
141
167k
                data <<= 4;
142
167k
                data += x;
143
144
167k
                p++;
145
167k
        }
146
147
133k
        if (*p != '\0' && *p != sep)
148
3.41k
                return -EINVAL;
149
150
130k
        switch (len) {
151
10.4k
        case 1:
152
10.4k
                buf[0] = data;
153
10.4k
                break;
154
119k
        case 2:
155
119k
                buf[0] = (data & 0xff00) >> 8;
156
119k
                buf[1] = data & 0xff;
157
119k
                break;
158
0
        default:
159
0
                assert_not_reached();
160
130k
        }
161
162
130k
        cont = *p == sep;
163
130k
        *s = p + cont;
164
130k
        return cont;
165
130k
}
166
167
71.0k
int parse_hw_addr_full(const char *s, size_t expected_len, struct hw_addr_data *ret) {
168
71.0k
        size_t field_size, max_len, len = 0;
169
71.0k
        uint8_t bytes[HW_ADDR_MAX_SIZE];
170
71.0k
        char sep;
171
71.0k
        int r;
172
173
71.0k
        assert(s);
174
71.0k
        assert(expected_len <= HW_ADDR_MAX_SIZE || expected_len == SIZE_MAX);
175
71.0k
        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
71.0k
        if (IN_SET(expected_len, 0, sizeof(struct in_addr), sizeof(struct in6_addr))) {
196
54.3k
                union in_addr_union a;
197
54.3k
                int family;
198
199
54.3k
                if (expected_len == 0)
200
54.3k
                        r = in_addr_from_string_auto(s, &family, &a);
201
0
                else {
202
0
                        family = expected_len == sizeof(struct in_addr) ? AF_INET : AF_INET6;
203
0
                        r = in_addr_from_string(family, s, &a);
204
0
                }
205
54.3k
                if (r >= 0) {
206
2.88k
                        ret->length = FAMILY_ADDRESS_SIZE(family);
207
2.88k
                        memcpy(ret->bytes, a.bytes, ret->length);
208
2.88k
                        return 0;
209
2.88k
                }
210
54.3k
        }
211
212
68.1k
        max_len =
213
68.1k
                expected_len == 0 ? INFINIBAND_ALEN :
214
68.1k
                expected_len == SIZE_MAX ? HW_ADDR_MAX_SIZE : expected_len;
215
68.1k
        sep = s[strspn(s, HEXDIGITS)];
216
217
68.1k
        if (sep == '.')
218
53.4k
                field_size = 2;
219
14.7k
        else if (IN_SET(sep, ':', '-'))
220
8.10k
                field_size = 1;
221
6.67k
        else
222
6.67k
                return -EINVAL;
223
224
61.5k
        if (max_len % field_size != 0)
225
0
                return -EINVAL;
226
227
142k
        for (size_t i = 0; i < max_len / field_size; i++) {
228
141k
                r = parse_hw_addr_one_field(&s, sep, field_size, bytes + i * field_size);
229
141k
                if (r < 0)
230
10.8k
                        return r;
231
130k
                if (r == 0) {
232
49.5k
                        len = (i + 1) * field_size;
233
49.5k
                        break;
234
49.5k
                }
235
130k
        }
236
237
50.6k
        if (len == 0)
238
1.12k
                return -EINVAL;
239
240
49.5k
        if (expected_len == 0) {
241
42.0k
                if (!IN_SET(len, 4, 16, ETH_ALEN, INFINIBAND_ALEN))
242
860
                        return -EINVAL;
243
42.0k
        } else if (expected_len != SIZE_MAX) {
244
7.49k
                if (len != expected_len)
245
947
                        return -EINVAL;
246
7.49k
        }
247
248
47.7k
        ret->length = len;
249
47.7k
        memcpy(ret->bytes, bytes, ret->length);
250
47.7k
        return 0;
251
49.5k
}
252
253
15.5k
int parse_ether_addr(const char *s, struct ether_addr *ret) {
254
15.5k
        struct hw_addr_data a;
255
15.5k
        int r;
256
257
15.5k
        assert(s);
258
15.5k
        assert(ret);
259
260
15.5k
        r = parse_hw_addr_full(s, ETH_ALEN, &a);
261
15.5k
        if (r < 0)
262
9.20k
                return r;
263
264
6.31k
        *ret = a.ether;
265
6.31k
        return 0;
266
15.5k
}
267
268
0
void ether_addr_mark_random(struct ether_addr *addr) {
269
0
        assert(addr);
270
271
        /* see eth_random_addr in the kernel */
272
0
        addr->ether_addr_octet[0] &= 0xfe;        /* clear multicast bit */
273
0
        addr->ether_addr_octet[0] |= 0x02;        /* set local assignment bit (IEEE802) */
274
0
}