• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 23927985597

02 Apr 2026 07:45PM UTC coverage: 72.362% (+0.02%) from 72.343%
23927985597

push

github

daandemeyer
ci: Drop base64 encoding in claude review workflow

Doesn't seem to work nearly as good as the previous solution which
just told claude not to escape stuff.

319121 of 441004 relevant lines covered (72.36%)

1167673.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.84
/src/basic/ether-addr-util.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <net/ethernet.h>
4
#include <net/if_arp.h>
5
#include <stdio.h>
6

7
#include "ether-addr-util.h"
8
#include "hash-funcs.h"
9
#include "hexdecoct.h"
10
#include "in-addr-util.h"
11
#include "memory-util.h"
12
#include "siphash24.h"
13
#include "string-util.h"
14

15
char* hw_addr_to_string_full(
5,748✔
16
                const struct hw_addr_data *addr,
17
                HardwareAddressToStringFlags flags,
18
                char buffer[static HW_ADDR_TO_STRING_MAX]) {
19

20
        assert(addr);
5,748✔
21
        assert(buffer);
5,748✔
22
        assert(addr->length <= HW_ADDR_MAX_SIZE);
5,748✔
23

24
        for (size_t i = 0, j = 0; i < addr->length; i++) {
44,468✔
25
                buffer[j++] = hexchar(addr->bytes[i] >> 4);
38,720✔
26
                buffer[j++] = hexchar(addr->bytes[i] & 0x0f);
38,720✔
27
                if (!FLAGS_SET(flags, HW_ADDR_TO_STRING_NO_COLON))
38,720✔
28
                        buffer[j++] = ':';
38,690✔
29
        }
30

31
        buffer[addr->length == 0 || FLAGS_SET(flags, HW_ADDR_TO_STRING_NO_COLON) ?
5,748✔
32
               addr->length * 2 :
5,753✔
33
               addr->length * 3 - 1] = '\0';
5,743✔
34
        return buffer;
5,748✔
35
}
36

37
struct hw_addr_data *hw_addr_set(struct hw_addr_data *addr, const uint8_t *bytes, size_t length) {
303✔
38
        assert(addr);
303✔
39
        assert(length <= HW_ADDR_MAX_SIZE);
303✔
40

41
        addr->length = length;
303✔
42
        memcpy_safe(addr->bytes, bytes, length);
303✔
43
        return addr;
303✔
44
}
45

46
int hw_addr_compare(const struct hw_addr_data *a, const struct hw_addr_data *b) {
13,881✔
47
        int r;
13,881✔
48

49
        assert(a);
13,881✔
50
        assert(b);
13,881✔
51

52
        r = CMP(a->length, b->length);
13,881✔
53
        if (r != 0)
10,553✔
54
                return r;
4,274✔
55

56
        return memcmp(a->bytes, b->bytes, a->length);
9,607✔
57
}
58

59
void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state) {
2,408✔
60
        assert(p);
2,408✔
61
        assert(state);
2,408✔
62

63
        siphash24_compress_typesafe(p->length, state);
2,408✔
64
        siphash24_compress_safe(p->bytes, p->length, state);
2,408✔
65
}
2,408✔
66

67
bool hw_addr_is_null(const struct hw_addr_data *addr) {
10,681✔
68
        assert(addr);
10,681✔
69
        return addr->length == 0 || memeqzero(addr->bytes, addr->length);
10,681✔
70
}
71

72
bool hw_addr_is_valid(const struct hw_addr_data *addr, uint16_t iftype) {
198✔
73
        assert(addr);
198✔
74

75
        switch (iftype) {
198✔
76
        case ARPHRD_ETHER:
198✔
77
                /* Refuse all zero and all 0xFF. */
78
                if (addr->length != ETH_ALEN)
198✔
79
                        return false;
80

81
                return !ether_addr_is_null(&addr->ether) && !ether_addr_is_broadcast(&addr->ether);
396✔
82

83
        case ARPHRD_INFINIBAND:
×
84
                /* The last 8 bytes cannot be zero. */
85
                if (addr->length != INFINIBAND_ALEN)
×
86
                        return false;
87

88
                return !memeqzero(addr->bytes + INFINIBAND_ALEN - 8, 8);
×
89

90
        default:
91
                return false;
92
        }
93
}
94

95
DEFINE_HASH_OPS(hw_addr_hash_ops, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare);
96
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(hw_addr_hash_ops_free, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare, free);
17✔
97

98
char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) {
38✔
99
        assert(addr);
38✔
100
        assert(buffer);
38✔
101

102
        /* Like ether_ntoa() but uses %02x instead of %x to print
103
         * ethernet addresses, which makes them look less funny. Also,
104
         * doesn't use a static buffer. */
105

106
        sprintf(buffer, "%02x:%02x:%02x:%02x:%02x:%02x",
38✔
107
                addr->ether_addr_octet[0],
38✔
108
                addr->ether_addr_octet[1],
38✔
109
                addr->ether_addr_octet[2],
38✔
110
                addr->ether_addr_octet[3],
38✔
111
                addr->ether_addr_octet[4],
38✔
112
                addr->ether_addr_octet[5]);
38✔
113

114
        return buffer;
38✔
115
}
116

117
int ether_addr_compare(const struct ether_addr *a, const struct ether_addr *b) {
15,027✔
118
        return memcmp(a, b, ETH_ALEN);
15,027✔
119
}
120

121
static void ether_addr_hash_func(const struct ether_addr *p, struct siphash *state) {
37✔
122
        siphash24_compress_typesafe(*p, state);
37✔
123
}
37✔
124

125
bool ether_addr_is_broadcast(const struct ether_addr *addr) {
1,300✔
126
        assert(addr);
1,300✔
127
        return memeqbyte(0xff, addr->ether_addr_octet, ETH_ALEN);
1,300✔
128
}
129

130
DEFINE_HASH_OPS(ether_addr_hash_ops, struct ether_addr, ether_addr_hash_func, ether_addr_compare);
131
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(ether_addr_hash_ops_free, struct ether_addr, ether_addr_hash_func, ether_addr_compare, free);
3✔
132

133
static int parse_hw_addr_one_field(const char **s, char sep, size_t len, uint8_t *buf) {
4,308✔
134
        const char *hex = HEXDIGITS, *p;
4,308✔
135
        uint16_t data = 0;
4,308✔
136
        bool cont;
4,308✔
137

138
        assert(s);
4,308✔
139
        assert(*s);
4,308✔
140
        assert(IN_SET(len, 1, 2));
4,308✔
141
        assert(buf);
4,308✔
142

143
        p = *s;
144

145
        for (size_t i = 0; i < len * 2; i++) {
12,982✔
146
                const char *hexoff;
8,750✔
147
                size_t x;
8,750✔
148

149
                if (*p == '\0' || *p == sep) {
8,750✔
150
                        if (i == 0)
69✔
151
                                return -EINVAL;
152
                        break;
153
                }
154

155
                hexoff = strchr(hex, *p);
8,681✔
156
                if (!hexoff)
8,681✔
157
                        return -EINVAL;
158

159
                assert(hexoff >= hex);
8,674✔
160
                x = hexoff - hex;
8,674✔
161
                if (x >= 16)
8,674✔
162
                        x -= 6; /* A-F */
70✔
163

164
                assert(x < 16);
70✔
165
                data <<= 4;
8,674✔
166
                data += x;
8,674✔
167

168
                p++;
8,674✔
169
        }
170

171
        if (*p != '\0' && *p != sep)
4,292✔
172
                return -EINVAL;
173

174
        switch (len) {
4,276✔
175
        case 1:
4,183✔
176
                buf[0] = data;
4,183✔
177
                break;
4,183✔
178
        case 2:
93✔
179
                buf[0] = (data & 0xff00) >> 8;
93✔
180
                buf[1] = data & 0xff;
93✔
181
                break;
93✔
182
        default:
×
183
                assert_not_reached();
×
184
        }
185

186
        cont = *p == sep;
4,276✔
187
        *s = p + cont;
4,276✔
188
        return cont;
4,276✔
189
}
190

191
int parse_hw_addr_full(const char *s, size_t expected_len, struct hw_addr_data *ret) {
795✔
192
        size_t field_size, max_len, len = 0;
795✔
193
        uint8_t bytes[HW_ADDR_MAX_SIZE];
795✔
194
        char sep;
795✔
195
        int r;
795✔
196

197
        assert(s);
795✔
198
        assert(expected_len <= HW_ADDR_MAX_SIZE || expected_len == SIZE_MAX);
795✔
199
        assert(ret);
795✔
200

201
        /* This accepts the following formats:
202
         *
203
         * Dot separated 2 bytes format: xxyy.zzaa.bbcc
204
         * Colon separated 1 bytes format: xx:yy:zz:aa:bb:cc
205
         * Hyphen separated 1 bytes format: xx-yy-zz-aa-bb-cc
206
         *
207
         * Moreover, if expected_len == 0, 4, or 16, this also accepts:
208
         *
209
         * IPv4 format: used by IPv4 tunnel, e.g. ipgre
210
         * IPv6 format: used by IPv6 tunnel, e.g. ip6gre
211
         *
212
         * The expected_len argument controls the length of acceptable addresses:
213
         *
214
         * 0: accepts 4 (AF_INET), 16 (AF_INET6), 6 (ETH_ALEN), or 20 (INFINIBAND_ALEN).
215
         * SIZE_MAX: accepts arbitrary length, but at least one separator must be included.
216
         * Otherwise: accepts addresses with matching length.
217
         */
218

219
        if (IN_SET(expected_len, 0, sizeof(struct in_addr), sizeof(struct in6_addr))) {
795✔
220
                union in_addr_union a;
103✔
221
                int family;
103✔
222

223
                if (expected_len == 0)
103✔
224
                        r = in_addr_from_string_auto(s, &family, &a);
90✔
225
                else {
226
                        family = expected_len == sizeof(struct in_addr) ? AF_INET : AF_INET6;
13✔
227
                        r = in_addr_from_string(family, s, &a);
13✔
228
                }
229
                if (r >= 0) {
103✔
230
                        ret->length = FAMILY_ADDRESS_SIZE(family);
30✔
231
                        memcpy(ret->bytes, a.bytes, ret->length);
30✔
232
                        return 0;
30✔
233
                }
234
        }
235

236
        max_len =
28✔
237
                expected_len == 0 ? INFINIBAND_ALEN :
765✔
238
                expected_len == SIZE_MAX ? HW_ADDR_MAX_SIZE : expected_len;
694✔
239
        sep = s[strspn(s, HEXDIGITS)];
765✔
240

241
        if (sep == '.')
765✔
242
                field_size = 2;
243
        else if (IN_SET(sep, ':', '-'))
727✔
244
                field_size = 1;
245
        else
246
                return -EINVAL;
247

248
        if (max_len % field_size != 0)
754✔
249
                return -EINVAL;
250

251
        for (size_t i = 0; i < max_len / field_size; i++) {
4,316✔
252
                r = parse_hw_addr_one_field(&s, sep, field_size, bytes + i * field_size);
4,308✔
253
                if (r < 0)
4,308✔
254
                        return r;
255
                if (r == 0) {
4,276✔
256
                        len = (i + 1) * field_size;
714✔
257
                        break;
714✔
258
                }
259
        }
260

261
        if (len == 0)
722✔
262
                return -EINVAL;
263

264
        if (expected_len == 0) {
714✔
265
                if (!IN_SET(len, 4, 16, ETH_ALEN, INFINIBAND_ALEN))
68✔
266
                        return -EINVAL;
267
        } else if (expected_len != SIZE_MAX) {
646✔
268
                if (len != expected_len)
633✔
269
                        return -EINVAL;
270
        }
271

272
        ret->length = len;
702✔
273
        memcpy(ret->bytes, bytes, ret->length);
702✔
274
        return 0;
702✔
275
}
276

277
int parse_ether_addr(const char *s, struct ether_addr *ret) {
130✔
278
        struct hw_addr_data a;
130✔
279
        int r;
130✔
280

281
        assert(s);
130✔
282
        assert(ret);
130✔
283

284
        r = parse_hw_addr_full(s, ETH_ALEN, &a);
130✔
285
        if (r < 0)
130✔
286
                return r;
130✔
287

288
        *ret = a.ether;
91✔
289
        return 0;
91✔
290
}
291

292
void ether_addr_mark_random(struct ether_addr *addr) {
90✔
293
        assert(addr);
90✔
294

295
        /* see eth_random_addr in the kernel */
296
        addr->ether_addr_octet[0] &= 0xfe;        /* clear multicast bit */
90✔
297
        addr->ether_addr_octet[0] |= 0x02;        /* set local assignment bit (IEEE802) */
90✔
298
}
90✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc