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

systemd / systemd / 18066640619

27 Sep 2025 03:25PM UTC coverage: 72.087% (-0.1%) from 72.224%
18066640619

push

github

yuwata
vmspawn: try to set up swtpm state for 4K RSA keys support

The next version of swtpm will support RSA4096, but it needs to be called
with a new parameter in order to do so. Try with it first, and if
execution fails, fallback to running without it.

This is especially needed for OBS builds, as the signing key is RSA4096
and cannot be changed by users, so the generated UKIs have RSA4096 signatures
for the pcrsig sections, and swtpm refuses them without the new support.

0 of 11 new or added lines in 1 file covered. (0.0%)

1438 existing lines in 37 files now uncovered.

302728 of 419948 relevant lines covered (72.09%)

1152870.99 hits per line

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

26.99
/src/shared/ethtool-util.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <linux/ethtool.h>
4
#include <linux/sockios.h>
5
#include <net/if.h>
6
#include <sys/ioctl.h>
7

8
#include "alloc-util.h"
9
#include "conf-parser.h"
10
#include "errno-util.h"
11
#include "ether-addr-util.h"
12
#include "ethtool-util.h"
13
#include "extract-word.h"
14
#include "fd-util.h"
15
#include "log.h"
16
#include "macro-fundamental.h"
17
#include "memory-util.h"
18
#include "parse-util.h"
19
#include "socket-util.h"
20
#include "string-table.h"
21
#include "string-util.h"
22
#include "strv.h"
23
#include "strxcpyx.h"
24
#include "time-util.h"
25

26
static const char* const duplex_table[_DUP_MAX] = {
27
        [DUP_FULL] = "full",
28
        [DUP_HALF] = "half"
29
};
30

31
DEFINE_STRING_TABLE_LOOKUP(duplex, Duplex);
570✔
32
DEFINE_CONFIG_PARSE_ENUM(config_parse_duplex, duplex, Duplex);
×
33

34
static const struct {
35
        uint32_t opt;
36
        const char *name;
37
} wol_option_map[] = {
38
        { WAKE_PHY,         "phy"        },
39
        { WAKE_UCAST,       "unicast",   },
40
        { WAKE_MCAST,       "multicast", },
41
        { WAKE_BCAST,       "broadcast", },
42
        { WAKE_ARP,         "arp",       },
43
        { WAKE_MAGIC,       "magic",     },
44
        { WAKE_MAGICSECURE, "secureon",  },
45
};
46

47
int wol_options_to_string_alloc(uint32_t opts, char **ret) {
×
48
        _cleanup_free_ char *str = NULL;
×
49

50
        assert(ret);
×
51

52
        if (opts == UINT32_MAX) {
×
53
                *ret = NULL;
×
54
                return 0;
×
55
        }
56

57
        FOREACH_ELEMENT(option, wol_option_map)
×
58
                if (opts & option->opt &&
×
59
                    !strextend_with_separator(&str, ",", option->name))
×
60
                        return -ENOMEM;
61

62
        if (!str) {
×
63
                str = strdup("off");
×
64
                if (!str)
×
65
                        return -ENOMEM;
66
        }
67

68
        *ret = TAKE_PTR(str);
×
69
        return 1;
×
70
}
71

72
static const char* const port_table[] = {
73
        [NET_DEV_PORT_TP]     = "tp",
74
        [NET_DEV_PORT_AUI]    = "aui",
75
        [NET_DEV_PORT_MII]    = "mii",
76
        [NET_DEV_PORT_FIBRE]  = "fibre",
77
        [NET_DEV_PORT_BNC]    = "bnc",
78
};
79

80
DEFINE_STRING_TABLE_LOOKUP(port, NetDevPort);
520✔
81
DEFINE_CONFIG_PARSE_ENUM(config_parse_port, port, NetDevPort);
×
82

83
static const char* const mdi_table[] = {
84
        [ETH_TP_MDI_INVALID]  = "unknown",
85
        [ETH_TP_MDI]          = "mdi",
86
        [ETH_TP_MDI_X]        = "mdi-x",
87
        [ETH_TP_MDI_AUTO]     = "auto",
88
};
89

90
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(mdi, int);
×
91

92
static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = {
93
        [NET_DEV_FEAT_SG]                  = "tx-scatter-gather",
94
        [NET_DEV_FEAT_IP_CSUM]             = "tx-checksum-ipv4",
95
        [NET_DEV_FEAT_HW_CSUM]             = "tx-checksum-ip-generic",
96
        [NET_DEV_FEAT_IPV6_CSUM]           = "tx-checksum-ipv6",
97
        [NET_DEV_FEAT_HIGHDMA]             = "highdma",
98
        [NET_DEV_FEAT_FRAGLIST]            = "tx-scatter-gather-fraglist",
99
        [NET_DEV_FEAT_HW_VLAN_CTAG_TX]     = "tx-vlan-hw-insert",
100
        [NET_DEV_FEAT_HW_VLAN_CTAG_RX]     = "rx-vlan-hw-parse",
101
        [NET_DEV_FEAT_HW_VLAN_CTAG_FILTER] = "rx-vlan-filter",
102
        [NET_DEV_FEAT_HW_VLAN_STAG_TX]     = "tx-vlan-stag-hw-insert",
103
        [NET_DEV_FEAT_HW_VLAN_STAG_RX]     = "rx-vlan-stag-hw-parse",
104
        [NET_DEV_FEAT_HW_VLAN_STAG_FILTER] = "rx-vlan-stag-filter",
105
        [NET_DEV_FEAT_VLAN_CHALLENGED]     = "vlan-challenged",
106
        [NET_DEV_FEAT_GSO]                 = "tx-generic-segmentation",
107
        [NET_DEV_FEAT_LLTX]                = "tx-lockless",
108
        [NET_DEV_FEAT_NETNS_LOCAL]         = "netns-local",
109
        [NET_DEV_FEAT_GRO]                 = "rx-gro",
110
        [NET_DEV_FEAT_GRO_HW]              = "rx-gro-hw",
111
        [NET_DEV_FEAT_LRO]                 = "rx-lro",
112
        [NET_DEV_FEAT_TSO]                 = "tx-tcp-segmentation",
113
        [NET_DEV_FEAT_GSO_ROBUST]          = "tx-gso-robust",
114
        [NET_DEV_FEAT_TSO_ECN]             = "tx-tcp-ecn-segmentation",
115
        [NET_DEV_FEAT_TSO_MANGLEID]        = "tx-tcp-mangleid-segmentation",
116
        [NET_DEV_FEAT_TSO6]                = "tx-tcp6-segmentation",
117
        [NET_DEV_FEAT_FSO]                 = "tx-fcoe-segmentation",
118
        [NET_DEV_FEAT_GSO_GRE]             = "tx-gre-segmentation",
119
        [NET_DEV_FEAT_GSO_GRE_CSUM]        = "tx-gre-csum-segmentation",
120
        [NET_DEV_FEAT_GSO_IPXIP4]          = "tx-ipxip4-segmentation",
121
        [NET_DEV_FEAT_GSO_IPXIP6]          = "tx-ipxip6-segmentation",
122
        [NET_DEV_FEAT_GSO_UDP_TUNNEL]      = "tx-udp_tnl-segmentation",
123
        [NET_DEV_FEAT_GSO_UDP_TUNNEL_CSUM] = "tx-udp_tnl-csum-segmentation",
124
        [NET_DEV_FEAT_GSO_PARTIAL]         = "tx-gso-partial",
125
        [NET_DEV_FEAT_GSO_TUNNEL_REMCSUM]  = "tx-tunnel-remcsum-segmentation",
126
        [NET_DEV_FEAT_GSO_SCTP]            = "tx-sctp-segmentation",
127
        [NET_DEV_FEAT_GSO_ESP]             = "tx-esp-segmentation",
128
        [NET_DEV_FEAT_GSO_UDP_L4]          = "tx-udp-segmentation",
129
        [NET_DEV_FEAT_GSO_FRAGLIST]        = "tx-gso-list",
130
        [NET_DEV_FEAT_FCOE_CRC]            = "tx-checksum-fcoe-crc",
131
        [NET_DEV_FEAT_SCTP_CRC]            = "tx-checksum-sctp",
132
        [NET_DEV_FEAT_FCOE_MTU]            = "fcoe-mtu",
133
        [NET_DEV_FEAT_NTUPLE]              = "rx-ntuple-filter",
134
        [NET_DEV_FEAT_RXHASH]              = "rx-hashing",
135
        [NET_DEV_FEAT_RXCSUM]              = "rx-checksum",
136
        [NET_DEV_FEAT_NOCACHE_COPY]        = "tx-nocache-copy",
137
        [NET_DEV_FEAT_LOOPBACK]            = "loopback",
138
        [NET_DEV_FEAT_RXFCS]               = "rx-fcs",
139
        [NET_DEV_FEAT_RXALL]               = "rx-all",
140
        [NET_DEV_FEAT_HW_L2FW_DOFFLOAD]    = "l2-fwd-offload",
141
        [NET_DEV_FEAT_HW_TC]               = "hw-tc-offload",
142
        [NET_DEV_FEAT_HW_ESP]              = "esp-hw-offload",
143
        [NET_DEV_FEAT_HW_ESP_TX_CSUM]      = "esp-tx-csum-hw-offload",
144
        [NET_DEV_FEAT_RX_UDP_TUNNEL_PORT]  = "rx-udp_tunnel-port-offload",
145
        [NET_DEV_FEAT_HW_TLS_RECORD]       = "tls-hw-record",
146
        [NET_DEV_FEAT_HW_TLS_TX]           = "tls-hw-tx-offload",
147
        [NET_DEV_FEAT_HW_TLS_RX]           = "tls-hw-rx-offload",
148
        [NET_DEV_FEAT_GRO_FRAGLIST]        = "rx-gro-list",
149
        [NET_DEV_FEAT_HW_MACSEC]           = "macsec-hw-offload",
150
        [NET_DEV_FEAT_GRO_UDP_FWD]         = "rx-udp-gro-forwarding",
151
        [NET_DEV_FEAT_HW_HSR_TAG_INS]      = "hsr-tag-ins-offload",
152
        [NET_DEV_FEAT_HW_HSR_TAG_RM]       = "hsr-tag-rm-offload",
153
        [NET_DEV_FEAT_HW_HSR_FWD]          = "hsr-fwd-offload",
154
        [NET_DEV_FEAT_HW_HSR_DUP]          = "hsr-dup-offload",
155

156
        [NET_DEV_FEAT_TXCSUM]              = "tx-checksum-", /* The suffix "-" means any feature beginning with "tx-checksum-" */
157
};
158

159
static const char* const ethtool_link_mode_bit_table[] = {
160
#  include "ethtool-link-mode.inc"
161
};
162
/* Make sure the array is large enough to fit all bits */
163
assert_cc((ELEMENTSOF(ethtool_link_mode_bit_table)-1) / 32 < N_ADVERTISE);
164

165
DEFINE_STRING_TABLE_LOOKUP(ethtool_link_mode_bit, enum ethtool_link_mode_bit_indices);
×
166

167
static int ethtool_connect(int *ethtool_fd) {
9,701✔
168
        int fd;
9,701✔
169

170
        assert(ethtool_fd);
9,701✔
171

172
        /* This does nothing if already connected. */
173
        if (*ethtool_fd >= 0)
9,701✔
174
                return 0;
175

176
        fd = socket_ioctl_fd();
4,789✔
177
        if (fd < 0)
4,789✔
178
                return log_debug_errno(fd, "ethtool: could not create control socket: %m");
×
179

180
        *ethtool_fd = fd;
4,789✔
181
        return 0;
4,789✔
182
}
183

184
int ethtool_get_driver(int *ethtool_fd, const char *ifname, char **ret) {
3,739✔
185
        struct ethtool_drvinfo ecmd = {
3,739✔
186
                .cmd = ETHTOOL_GDRVINFO,
187
        };
188
        struct ifreq ifr = {
3,739✔
189
                .ifr_data = (void*) &ecmd,
190
        };
191
        int r;
3,739✔
192

193
        assert(ethtool_fd);
3,739✔
194
        assert(ifname);
3,739✔
195
        assert(ret);
3,739✔
196

197
        r = ethtool_connect(ethtool_fd);
3,739✔
198
        if (r < 0)
3,739✔
199
                return r;
3,739✔
200

201
        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
3,739✔
202

203
        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
3,739✔
204
                return -errno;
670✔
205

206
        if (isempty(ecmd.driver))
3,069✔
207
                return -ENODATA;
208

209
        return strdup_to(ret, ecmd.driver);
3,069✔
210
}
211

212
int ethtool_get_link_info(
1,453✔
213
                int *ethtool_fd,
214
                const char *ifname,
215
                int *ret_autonegotiation,
216
                uint64_t *ret_speed,
217
                Duplex *ret_duplex,
218
                NetDevPort *ret_port) {
219

220
        struct ethtool_cmd ecmd = {
1,453✔
221
                .cmd = ETHTOOL_GSET,
222
        };
223
        struct ifreq ifr = {
1,453✔
224
                .ifr_data = (void*) &ecmd,
225
        };
226
        int r;
1,453✔
227

228
        assert(ethtool_fd);
1,453✔
229
        assert(ifname);
1,453✔
230

231
        r = ethtool_connect(ethtool_fd);
1,453✔
232
        if (r < 0)
1,453✔
233
                return r;
1,453✔
234

235
        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
1,453✔
236

237
        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
1,453✔
238
                return -errno;
933✔
239

240
        if (ret_autonegotiation)
520✔
241
                *ret_autonegotiation = ecmd.autoneg;
520✔
242

243
        if (ret_speed) {
520✔
244
                uint32_t speed;
520✔
245

246
                speed = ethtool_cmd_speed(&ecmd);
520✔
247
                *ret_speed = speed == (uint32_t) SPEED_UNKNOWN ?
520✔
248
                        UINT64_MAX : (uint64_t) speed * 1000 * 1000;
520✔
249
        }
250

251
        if (ret_duplex)
520✔
252
                *ret_duplex = ecmd.duplex;
520✔
253

254
        if (ret_port)
520✔
255
                *ret_port = ecmd.port;
520✔
256

257
        return 0;
258
}
259

260
int ethtool_get_permanent_hw_addr(int *ethtool_fd, const char *ifname, struct hw_addr_data *ret) {
4,509✔
261
        _cleanup_close_ int fd = -EBADF;
4,509✔
262
        union {
4,509✔
263
                struct ethtool_perm_addr addr;
264
                uint8_t buf[offsetof(struct ethtool_perm_addr, data) + HW_ADDR_MAX_SIZE];
265
        } epaddr = {
4,509✔
266
                .addr.cmd = ETHTOOL_GPERMADDR,
267
                .addr.size = HW_ADDR_MAX_SIZE,
268
        };
269
        struct ifreq ifr = {
4,509✔
270
                .ifr_data = (caddr_t) &epaddr,
271
        };
272
        int r;
4,509✔
273

274
        assert(ifname);
4,509✔
275
        assert(ret);
4,509✔
276

277
        if (!ethtool_fd)
4,509✔
278
                ethtool_fd = &fd;
1,308✔
279
        r = ethtool_connect(ethtool_fd);
4,509✔
280
        if (r < 0)
4,509✔
281
                return r;
282

283
        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
4,509✔
284

285
        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
4,509✔
286
                return -errno;
1✔
287

288
        if (epaddr.addr.size == 0)
4,508✔
289
                return -ENODATA;
290

291
        if (epaddr.addr.size > HW_ADDR_MAX_SIZE)
4,460✔
292
                return -EINVAL;
293

294
        ret->length = epaddr.addr.size;
4,460✔
295
        memcpy(ret->bytes, epaddr.addr.data, epaddr.addr.size);
4,460✔
296
        return 0;
4,460✔
297
}
298

299
#define UPDATE(dest, val, updated)                     \
300
        do {                                           \
301
                typeof(val) _v = (val);                \
302
                if (dest != _v)                        \
303
                        updated = true;                \
304
                dest = _v;                             \
305
        } while (false)
306

307
#define UPDATE_WITH_MAX(dest, max, val, updated)       \
308
        do {                                           \
309
                typeof(dest) _v = (val);               \
310
                typeof(dest) _max = (max);             \
311
                if (_v == 0 || _v > _max)              \
312
                        _v = _max;                     \
313
                if (dest != _v)                        \
314
                        updated = true;                \
315
                dest = _v;                             \
316
        } while (false)
317

318
int ethtool_set_wol(
915✔
319
                int *ethtool_fd,
320
                const char *ifname,
321
                uint32_t wolopts,
322
                const uint8_t password[SOPASS_MAX]) {
323

324
        struct ethtool_wolinfo ecmd = {
915✔
325
                .cmd = ETHTOOL_GWOL,
326
        };
327
        struct ifreq ifr = {
915✔
328
                .ifr_data = (void*) &ecmd,
329
        };
330
        bool need_update = false;
915✔
331
        int r;
915✔
332

333
        assert(ethtool_fd);
915✔
334
        assert(ifname);
915✔
335

336
        if (wolopts == UINT32_MAX && !password)
915✔
337
                /* Nothing requested. Return earlier. */
338
                return 0;
915✔
339

340
        r = ethtool_connect(ethtool_fd);
×
341
        if (r < 0)
×
342
                return r;
343

344
        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
×
345

346
        CLEANUP_ERASE(ecmd);
×
347

348
        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
×
349
                return -errno;
×
350

351
        if (wolopts == UINT32_MAX) {
×
352
                /* When password is specified without valid WoL options specified, then enable
353
                 * WAKE_MAGICSECURE flag if supported. */
354
                wolopts = ecmd.wolopts;
×
355
                if (password && FLAGS_SET(ecmd.supported, WAKE_MAGICSECURE))
×
356
                        wolopts |= WAKE_MAGICSECURE;
×
357
        }
358

359
        if ((wolopts & ~ecmd.supported) != 0) {
×
360
                _cleanup_free_ char *str = NULL;
×
361

362
                (void) wol_options_to_string_alloc(wolopts & ~ecmd.supported, &str);
×
363
                log_debug("Network interface %s does not support requested Wake on LAN options \"%s\", ignoring.",
×
364
                          ifname, strna(str));
365

366
                wolopts &= ecmd.supported;
×
367
        }
368

369
        if (!FLAGS_SET(wolopts, WAKE_MAGICSECURE))
×
370
                /* When WAKE_MAGICSECURE flag is not set, then ignore password. */
371
                password = NULL;
×
372

373
        UPDATE(ecmd.wolopts, wolopts, need_update);
×
374
        if (password &&
×
375
            memcmp(ecmd.sopass, password, sizeof(ecmd.sopass)) != 0) {
×
376
                memcpy(ecmd.sopass, password, sizeof(ecmd.sopass));
×
377
                need_update = true;
×
378
        }
379

380
        if (!need_update)
×
381
                return 0;
382

383
        ecmd.cmd = ETHTOOL_SWOL;
×
384
        return RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr));
×
385
}
386

387
int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netdev_ring_param *ring) {
915✔
388
        struct ethtool_ringparam ecmd = {
915✔
389
                .cmd = ETHTOOL_GRINGPARAM,
390
        };
391
        struct ifreq ifr = {
915✔
392
                .ifr_data = (void*) &ecmd,
393
        };
394
        bool need_update = false;
915✔
395
        int r;
915✔
396

397
        assert(ethtool_fd);
915✔
398
        assert(ifname);
915✔
399
        assert(ring);
915✔
400

401
        if (!ring->rx.set &&
915✔
402
            !ring->rx_mini.set &&
915✔
403
            !ring->rx_jumbo.set &&
915✔
404
            !ring->tx.set)
915✔
405
                return 0;
915✔
406

407
        r = ethtool_connect(ethtool_fd);
×
408
        if (r < 0)
×
409
                return r;
410

411
        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
×
412

413
        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
×
414
                return -errno;
×
415

416
        if (ring->rx.set)
×
417
                UPDATE_WITH_MAX(ecmd.rx_pending, ecmd.rx_max_pending, ring->rx.value, need_update);
×
418

419
        if (ring->rx_mini.set)
×
420
                UPDATE_WITH_MAX(ecmd.rx_mini_pending, ecmd.rx_mini_max_pending, ring->rx_mini.value, need_update);
×
421

422
        if (ring->rx_jumbo.set)
×
423
                UPDATE_WITH_MAX(ecmd.rx_jumbo_pending, ecmd.rx_jumbo_max_pending, ring->rx_jumbo.value, need_update);
×
424

425
        if (ring->tx.set)
×
426
                UPDATE_WITH_MAX(ecmd.tx_pending, ecmd.tx_max_pending, ring->tx.value, need_update);
×
427

428
        if (!need_update)
×
429
                return 0;
430

431
        ecmd.cmd = ETHTOOL_SRINGPARAM;
×
432
        return RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr));
×
433
}
434

435
static int get_stringset(int ethtool_fd, const char *ifname, enum ethtool_stringset stringset_id, struct ethtool_gstrings **ret) {
×
436
        _cleanup_free_ struct ethtool_gstrings *strings = NULL;
×
437
        uint8_t buffer[offsetof(struct ethtool_sset_info, data) + sizeof(uint32_t)] = {};
×
438
        struct ifreq ifr = {
×
439
                .ifr_data = (void*) buffer,
440
        };
441
        uint32_t len;
×
442

443
        assert(ethtool_fd >= 0);
×
444
        assert(ifname);
×
445
        assert(ret);
×
446

447
        struct ethtool_sset_info *info = (struct ethtool_sset_info*) buffer;
×
448
        info->cmd = ETHTOOL_GSSET_INFO;
×
449
        info->sset_mask = UINT64_C(1) << stringset_id;
×
450
        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
×
451

452
        if (ioctl(ethtool_fd, SIOCETHTOOL, &ifr) < 0)
×
453
                return -errno;
×
454

455
        if (info->sset_mask == 0)
×
456
                return -EOPNOTSUPP;
457

458
        len = *info->data;
×
459
        if (len == 0)
×
460
                return -EOPNOTSUPP;
461

462
        strings = malloc0(offsetof(struct ethtool_gstrings, data) + len * ETH_GSTRING_LEN);
×
463
        if (!strings)
×
464
                return -ENOMEM;
465

466
        strings->cmd = ETHTOOL_GSTRINGS;
×
467
        strings->string_set = stringset_id;
×
468
        strings->len = len;
×
469

470
        ifr.ifr_data = (void*) strings;
×
471

472
        if (ioctl(ethtool_fd, SIOCETHTOOL, &ifr) < 0)
×
473
                return -errno;
×
474

475
        *ret = TAKE_PTR(strings);
×
476
        return 0;
×
477
}
478

479
static int get_features(int ethtool_fd, const char *ifname, uint32_t n_features, struct ethtool_gfeatures **ret) {
×
480
        _cleanup_free_ struct ethtool_gfeatures *gfeatures = NULL;
×
481
        struct ifreq ifr;
×
482

483
        assert(ethtool_fd >= 0);
×
484
        assert(ifname);
×
485
        assert(ret);
×
486
        assert(n_features > 0);
×
487

488
        gfeatures = malloc0(offsetof(struct ethtool_gfeatures, features) +
×
489
                            DIV_ROUND_UP(n_features, 32U) * sizeof(gfeatures->features[0]));
490
        if (!gfeatures)
×
491
                return -ENOMEM;
492

493
        gfeatures->cmd = ETHTOOL_GFEATURES;
×
494
        gfeatures->size = DIV_ROUND_UP(n_features, 32U);
×
495

496
        ifr = (struct ifreq) {
×
497
                .ifr_data = (void*) gfeatures,
498
        };
499
        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
×
500

501
        if (ioctl(ethtool_fd, SIOCETHTOOL, &ifr) < 0)
×
502
                return -errno;
×
503

504
        *ret = TAKE_PTR(gfeatures);
×
505
        return 0;
×
506
}
507

508
static int set_features_bit(
×
509
                const struct ethtool_gstrings *strings,
510
                const struct ethtool_gfeatures *gfeatures,
511
                struct ethtool_sfeatures *sfeatures,
512
                const char *feature,
513
                int flag) {
514

515
        assert(strings);
×
516
        assert(gfeatures);
×
517
        assert(sfeatures);
×
518
        assert(feature);
×
519

520
        if (flag < 0)
×
521
                return 0;
522

523
        for (uint32_t i = 0; i < strings->len; i++) {
×
524
                uint32_t block, mask;
×
525

526
                if (!strneq((const char*) &strings->data[i * ETH_GSTRING_LEN], feature, ETH_GSTRING_LEN))
×
527
                        continue;
×
528

529
                block = i / 32;
×
530
                mask = UINT32_C(1) << (i % 32);
×
531

532
                if (!FLAGS_SET(gfeatures->features[block].available, mask) ||
×
533
                    FLAGS_SET(gfeatures->features[block].never_changed, mask))
×
534
                        return -EOPNOTSUPP;
535

536
                sfeatures->features[block].valid |= mask;
×
537
                SET_FLAG(sfeatures->features[block].requested, mask, flag);
×
538

539
                return 0;
×
540
        }
541

542
        return -ENODATA;
543
}
544

545
static int set_features_multiple_bit(
×
546
                const struct ethtool_gstrings *strings,
547
                const struct ethtool_gfeatures *gfeatures,
548
                struct ethtool_sfeatures *sfeatures,
549
                const char *feature,
550
                int flag) {
551

552
        bool found = false;
×
553
        int r = -ENODATA;
×
554

555
        assert(strings);
×
556
        assert(gfeatures);
×
557
        assert(sfeatures);
×
558
        assert(feature);
×
559

560
        if (flag < 0)
×
561
                return 0;
562

563
        for (uint32_t i = 0; i < strings->len; i++) {
×
564
                uint32_t block, mask;
×
565

566
                if (!startswith((const char*) &strings->data[i * ETH_GSTRING_LEN], feature))
×
567
                        continue;
×
568

569
                block = i / 32;
×
570
                mask = UINT32_C(1) << (i % 32);
×
571

572
                if (!FLAGS_SET(gfeatures->features[block].available, mask) ||
×
573
                    FLAGS_SET(gfeatures->features[block].never_changed, mask)) {
×
574
                        r = -EOPNOTSUPP;
×
575
                        continue;
×
576
                }
577

578
                /* The flags is explicitly set by set_features_bit() */
579
                if (FLAGS_SET(sfeatures->features[block].valid, mask))
×
580
                        continue;
×
581

582
                sfeatures->features[block].valid |= mask;
×
583
                SET_FLAG(sfeatures->features[block].requested, mask, flag);
×
584

585
                found = true;
×
586
        }
587

588
        return found ? 0 : r;
×
589
}
590

591
int ethtool_set_features(int *ethtool_fd, const char *ifname, const int features[static _NET_DEV_FEAT_MAX]) {
915✔
592
        _cleanup_free_ struct ethtool_gstrings *strings = NULL;
1,830✔
593
        _cleanup_free_ struct ethtool_gfeatures *gfeatures = NULL;
×
594
        _cleanup_free_ struct ethtool_sfeatures *sfeatures = NULL;
915✔
595
        struct ifreq ifr;
915✔
596
        bool have = false;
915✔
597
        int r;
915✔
598

599
        assert(ethtool_fd);
915✔
600
        assert(ifname);
915✔
601
        assert(features);
602

603
        for (size_t i = 0; i < _NET_DEV_FEAT_MAX; i++)
58,560✔
604
                if (features[i] >= 0) {
57,645✔
605
                        have = true;
606
                        break;
607
                }
608

609
        if (!have)
915✔
610
                return 0;
611

612
        r = ethtool_connect(ethtool_fd);
×
613
        if (r < 0)
×
614
                return r;
615

616
        r = get_stringset(*ethtool_fd, ifname, ETH_SS_FEATURES, &strings);
×
617
        if (r < 0)
×
618
                return log_debug_errno(r, "ethtool: could not get ethtool feature strings: %m");
×
619

620
        r = get_features(*ethtool_fd, ifname, strings->len, &gfeatures);
×
621
        if (r < 0)
×
622
                return log_debug_errno(r, "ethtool: could not get ethtool features for %s: %m", ifname);
×
623

624
        sfeatures = malloc0(offsetof(struct ethtool_sfeatures, features) +
×
625
                            DIV_ROUND_UP(strings->len, 32U) * sizeof(sfeatures->features[0]));
626
        if (!sfeatures)
×
627
                return log_oom_debug();
×
628

629
        sfeatures->cmd = ETHTOOL_SFEATURES;
×
630
        sfeatures->size = DIV_ROUND_UP(strings->len, 32U);
×
631

632
        for (size_t i = 0; i < _NET_DEV_FEAT_SIMPLE_MAX; i++) {
×
633
                r = set_features_bit(strings, gfeatures, sfeatures, netdev_feature_table[i], features[i]);
×
634
                if (r < 0)
×
635
                        log_debug_errno(r, "ethtool: could not set feature %s for %s, ignoring: %m", netdev_feature_table[i], ifname);
×
636
        }
637

638
        for (size_t i = _NET_DEV_FEAT_SIMPLE_MAX; i < _NET_DEV_FEAT_MAX; i++) {
×
639
                r = set_features_multiple_bit(strings, gfeatures, sfeatures, netdev_feature_table[i], features[i]);
×
640
                if (r < 0)
×
641
                        log_debug_errno(r, "ethtool: could not set feature %s for %s, ignoring: %m", netdev_feature_table[i], ifname);
×
642
        }
643

644
        ifr = (struct ifreq) {
×
645
                .ifr_data = (void*) sfeatures,
646
        };
647
        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
×
648

649
        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
×
650
                return log_debug_errno(errno, "ethtool: could not set ethtool features for %s", ifname);
×
651

652
        return 0;
653
}
654

655
static int get_link_settings(int fd, struct ifreq *ifr, struct ethtool_link_settings **ret) {
×
656
        struct ethtool_link_settings ecmd = {
×
657
                .cmd = ETHTOOL_GLINKSETTINGS,
658
        };
659

660
        assert(fd >= 0);
×
661
        assert(ifr);
×
662
        assert(ret);
×
663

664
        /* The interaction user/kernel via the new API requires a small ETHTOOL_GLINKSETTINGS handshake first
665
         * to agree on the length of the link mode bitmaps. If kernel doesn't agree with user, it returns the
666
         * bitmap length it is expecting from user as a negative length. When kernel and user agree, kernel
667
         * returns valid info in all fields (with bitmap length > 0).
668
         * https://github.com/torvalds/linux/commit/3f1ac7a700d039c61d8d8b99f28d605d489a60cf
669
         * https://github.com/torvalds/linux/commit/793cf87de9d1a62dc9079c3ec5fcc01cfc62fafb */
670

UNCOV
671
        ifr->ifr_data = &ecmd;
×
672

UNCOV
673
        if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
×
674
                return -errno;
×
675

UNCOV
676
        if (ecmd.link_mode_masks_nwords >= 0 || ecmd.cmd != ETHTOOL_GLINKSETTINGS)
×
677
                return -EOPNOTSUPP;
678

UNCOV
679
        int8_t n = -ecmd.link_mode_masks_nwords;
×
680
        size_t sz = offsetof(struct ethtool_link_settings, link_mode_masks) + sizeof(uint32_t) * n * 3;
×
UNCOV
681
        _cleanup_free_ struct ethtool_link_settings *settings = calloc(sz, 1);
×
682
        if (!settings)
×
683
                return -ENOMEM;
684

685
        settings->cmd = ETHTOOL_GLINKSETTINGS;
×
UNCOV
686
        settings->link_mode_masks_nwords = n;
×
687

UNCOV
688
        ifr->ifr_data = settings;
×
UNCOV
689
        if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
×
690
                return -errno;
×
691

UNCOV
692
        if (settings->link_mode_masks_nwords != n || settings->cmd != ETHTOOL_GLINKSETTINGS)
×
693
                return -EOPNOTSUPP;
694

UNCOV
695
        *ret = TAKE_PTR(settings);
×
696
        return 0;
×
697
}
698

699
int ethtool_set_link_settings(
915✔
700
                int *fd,
701
                const char *ifname,
702
                int autonegotiation,
703
                const uint32_t advertise[static N_ADVERTISE],
704
                uint64_t speed,
705
                Duplex duplex,
706
                NetDevPort port,
707
                uint8_t mdi) {
708

709
        _cleanup_free_ struct ethtool_link_settings *settings = NULL;
915✔
710
        struct ifreq ifr = {};
915✔
711
        bool changed = false;
915✔
712
        int r;
915✔
713

714
        assert(fd);
915✔
715
        assert(ifname);
915✔
716
        assert(advertise);
915✔
717

718
        if (autonegotiation < 0 && memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE) &&
915✔
719
            speed == 0 && duplex < 0 && port < 0 && mdi == ETH_TP_MDI_INVALID)
915✔
720
                return 0;
721

722
        /* If autonegotiation is disabled, the speed and duplex represent the fixed link mode and are
723
         * writable if the driver supports multiple link modes. If it is enabled then they are
724
         * read-only. If the link is up they represent the negotiated link mode; if the link is down,
725
         * the speed is 0, %SPEED_UNKNOWN or the highest enabled speed and @duplex is %DUPLEX_UNKNOWN
726
         * or the best enabled duplex mode. */
727

728
        if (speed > 0 || duplex >= 0 || port >= 0) {
×
729
                if (autonegotiation == AUTONEG_ENABLE || !memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE)) {
×
730
                        log_debug("ethtool: autonegotiation is enabled, ignoring speed, duplex, or port settings.");
×
731
                        speed = 0;
732
                        duplex = _DUP_INVALID;
733
                        port = _NET_DEV_PORT_INVALID;
734
                } else {
735
                        log_debug("ethtool: setting speed, duplex, or port, disabling autonegotiation.");
×
736
                        autonegotiation = AUTONEG_DISABLE;
737
                }
738
        }
739

740
        r = ethtool_connect(fd);
×
UNCOV
741
        if (r < 0)
×
742
                return r;
743

UNCOV
744
        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
×
745

746
        r = get_link_settings(*fd, &ifr, &settings);
×
747
        if (r < 0)
×
748
                return log_debug_errno(r, "ethtool: Cannot get link settings for %s: %m", ifname);
×
749

UNCOV
750
        if (speed > 0)
×
751
                UPDATE(settings->speed, DIV_ROUND_UP(speed, 1000000), changed);
×
752

UNCOV
753
        if (duplex >= 0)
×
754
                UPDATE(settings->duplex, duplex, changed);
×
755

UNCOV
756
        if (port >= 0)
×
757
                UPDATE(settings->port, port, changed);
×
758

759
        if (autonegotiation >= 0)
×
760
                UPDATE(settings->autoneg, autonegotiation, changed);
×
761

762
        if (!memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE)) {
×
UNCOV
763
                UPDATE(settings->autoneg, AUTONEG_ENABLE, changed);
×
764

UNCOV
765
                uint32_t *a = settings->link_mode_masks + settings->link_mode_masks_nwords;
×
UNCOV
766
                size_t n = MIN(settings->link_mode_masks_nwords, N_ADVERTISE);
×
767

768
                changed = changed ||
×
UNCOV
769
                        memcmp(a, advertise, sizeof(uint32_t) * n) != 0 ||
×
UNCOV
770
                        !memeqzero(a + n, sizeof(uint32_t) * (settings->link_mode_masks_nwords - n));
×
771

772
                memcpy(a, advertise, sizeof(uint32_t) * n);
×
773
                memzero(a + n, sizeof(uint32_t) * (settings->link_mode_masks_nwords - n));
×
774
        }
775

776
        if (mdi != ETH_TP_MDI_INVALID) {
×
777
                if (settings->eth_tp_mdix_ctrl == ETH_TP_MDI_INVALID)
×
UNCOV
778
                        log_debug("ethtool: setting MDI not supported for %s, ignoring.", ifname);
×
779
                else
780
                        UPDATE(settings->eth_tp_mdix_ctrl, mdi, changed);
×
781
        }
782

783
        if (!changed)
×
784
                return 0;
785

786
        settings->cmd = ETHTOOL_SLINKSETTINGS;
×
787
        ifr.ifr_data = settings;
×
788
        if (ioctl(*fd, SIOCETHTOOL, &ifr) < 0)
×
789
                return log_debug_errno(errno, "ethtool: Cannot set link settings for %s: %m", ifname);
×
790

791
        return r;
792
}
793

794
int ethtool_set_channels(int *fd, const char *ifname, const netdev_channels *channels) {
915✔
795
        struct ethtool_channels ecmd = {
915✔
796
                .cmd = ETHTOOL_GCHANNELS,
797
        };
798
        struct ifreq ifr = {
915✔
799
                .ifr_data = (void*) &ecmd,
800
        };
801
        bool need_update = false;
915✔
802
        int r;
915✔
803

804
        assert(fd);
915✔
805
        assert(ifname);
915✔
806
        assert(channels);
915✔
807

808
        if (!channels->rx.set &&
915✔
809
            !channels->tx.set &&
915✔
810
            !channels->other.set &&
915✔
811
            !channels->combined.set)
915✔
812
                return 0;
915✔
813

UNCOV
814
        r = ethtool_connect(fd);
×
UNCOV
815
        if (r < 0)
×
816
                return r;
817

UNCOV
818
        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
×
819

UNCOV
820
        if (ioctl(*fd, SIOCETHTOOL, &ifr) < 0)
×
UNCOV
821
                return -errno;
×
822

UNCOV
823
        if (channels->rx.set)
×
UNCOV
824
                UPDATE_WITH_MAX(ecmd.rx_count, ecmd.max_rx, channels->rx.value, need_update);
×
825

UNCOV
826
        if (channels->tx.set)
×
827
                UPDATE_WITH_MAX(ecmd.tx_count, ecmd.max_tx, channels->tx.value, need_update);
×
828

829
        if (channels->other.set)
×
UNCOV
830
                UPDATE_WITH_MAX(ecmd.other_count, ecmd.max_other, channels->other.value, need_update);
×
831

UNCOV
832
        if (channels->combined.set)
×
UNCOV
833
                UPDATE_WITH_MAX(ecmd.combined_count, ecmd.max_combined, channels->combined.value, need_update);
×
834

UNCOV
835
        if (!need_update)
×
836
                return 0;
837

UNCOV
838
        ecmd.cmd = ETHTOOL_SCHANNELS;
×
839
        return RET_NERRNO(ioctl(*fd, SIOCETHTOOL, &ifr));
×
840
}
841

842
int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int autoneg) {
915✔
843
        struct ethtool_pauseparam ecmd = {
915✔
844
                .cmd = ETHTOOL_GPAUSEPARAM,
845
        };
846
        struct ifreq ifr = {
915✔
847
                .ifr_data = (void*) &ecmd,
848
        };
849
        bool need_update = false;
915✔
850
        int r;
915✔
851

852
        assert(fd);
915✔
853
        assert(ifname);
915✔
854

855
        if (rx < 0 && tx < 0 && autoneg < 0)
915✔
856
                return 0;
915✔
857

858
        r = ethtool_connect(fd);
×
859
        if (r < 0)
×
860
                return r;
861

862
        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
×
863

864
        if (ioctl(*fd, SIOCETHTOOL, &ifr) < 0)
×
865
                return -errno;
×
866

867
        if (rx >= 0)
×
868
                UPDATE(ecmd.rx_pause, (uint32_t) rx, need_update);
×
869

UNCOV
870
        if (tx >= 0)
×
871
                UPDATE(ecmd.tx_pause, (uint32_t) tx, need_update);
×
872

UNCOV
873
        if (autoneg >= 0)
×
UNCOV
874
                UPDATE(ecmd.autoneg, (uint32_t) autoneg, need_update);
×
875

876
        if (!need_update)
×
877
                return 0;
878

UNCOV
879
        ecmd.cmd = ETHTOOL_SPAUSEPARAM;
×
880
        return RET_NERRNO(ioctl(*fd, SIOCETHTOOL, &ifr));
×
881
}
882

883
int ethtool_set_nic_coalesce_settings(int *ethtool_fd, const char *ifname, const netdev_coalesce_param *coalesce) {
915✔
884
        struct ethtool_coalesce ecmd = {
915✔
885
                .cmd = ETHTOOL_GCOALESCE,
886
        };
887
        struct ifreq ifr = {
915✔
888
                .ifr_data = (void*) &ecmd,
889
        };
890
        bool need_update = false;
915✔
891
        int r;
915✔
892

893
        assert(ethtool_fd);
915✔
894
        assert(ifname);
915✔
895
        assert(coalesce);
915✔
896

897
        if (coalesce->use_adaptive_rx_coalesce < 0 &&
915✔
898
            coalesce->use_adaptive_tx_coalesce < 0 &&
915✔
899
            !coalesce->rx_coalesce_usecs.set &&
915✔
900
            !coalesce->rx_max_coalesced_frames.set &&
915✔
901
            !coalesce->rx_coalesce_usecs_irq.set &&
915✔
902
            !coalesce->rx_max_coalesced_frames_irq.set &&
915✔
903
            !coalesce->tx_coalesce_usecs.set &&
915✔
904
            !coalesce->tx_max_coalesced_frames.set &&
915✔
905
            !coalesce->tx_coalesce_usecs_irq.set &&
915✔
906
            !coalesce->tx_max_coalesced_frames_irq.set &&
915✔
907
            !coalesce->stats_block_coalesce_usecs.set &&
915✔
908
            !coalesce->pkt_rate_low.set &&
915✔
909
            !coalesce->rx_coalesce_usecs_low.set &&
915✔
910
            !coalesce->rx_max_coalesced_frames_low.set &&
915✔
911
            !coalesce->tx_coalesce_usecs_low.set &&
915✔
912
            !coalesce->tx_max_coalesced_frames_low.set &&
915✔
913
            !coalesce->pkt_rate_high.set &&
915✔
914
            !coalesce->rx_coalesce_usecs_high.set &&
915✔
915
            !coalesce->rx_max_coalesced_frames_high.set &&
915✔
916
            !coalesce->tx_coalesce_usecs_high.set &&
915✔
917
            !coalesce->tx_max_coalesced_frames_high.set &&
915✔
918
            !coalesce->rate_sample_interval.set)
915✔
919
                return 0;
915✔
920

UNCOV
921
        r = ethtool_connect(ethtool_fd);
×
922
        if (r < 0)
×
923
                return r;
924

925
        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
×
926

UNCOV
927
        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
×
928
                return -errno;
×
929

UNCOV
930
        if (coalesce->use_adaptive_rx_coalesce >= 0)
×
931
                UPDATE(ecmd.use_adaptive_rx_coalesce, (uint32_t) coalesce->use_adaptive_rx_coalesce, need_update);
×
932

UNCOV
933
        if (coalesce->use_adaptive_tx_coalesce >= 0)
×
934
                UPDATE(ecmd.use_adaptive_tx_coalesce, (uint32_t) coalesce->use_adaptive_tx_coalesce, need_update);
×
935

UNCOV
936
        if (coalesce->rx_coalesce_usecs.set)
×
937
                UPDATE(ecmd.rx_coalesce_usecs, coalesce->rx_coalesce_usecs.value, need_update);
×
938

UNCOV
939
        if (coalesce->rx_max_coalesced_frames.set)
×
940
                UPDATE(ecmd.rx_max_coalesced_frames, coalesce->rx_max_coalesced_frames.value, need_update);
×
941

UNCOV
942
        if (coalesce->rx_coalesce_usecs_irq.set)
×
UNCOV
943
                UPDATE(ecmd.rx_coalesce_usecs_irq, coalesce->rx_coalesce_usecs_irq.value, need_update);
×
944

UNCOV
945
        if (coalesce->rx_max_coalesced_frames_irq.set)
×
UNCOV
946
                UPDATE(ecmd.rx_max_coalesced_frames_irq, coalesce->rx_max_coalesced_frames_irq.value, need_update);
×
947

UNCOV
948
        if (coalesce->tx_coalesce_usecs.set)
×
UNCOV
949
                UPDATE(ecmd.tx_coalesce_usecs, coalesce->tx_coalesce_usecs.value, need_update);
×
950

UNCOV
951
        if (coalesce->tx_max_coalesced_frames.set)
×
UNCOV
952
                UPDATE(ecmd.tx_max_coalesced_frames, coalesce->tx_max_coalesced_frames.value, need_update);
×
953

UNCOV
954
        if (coalesce->tx_coalesce_usecs_irq.set)
×
UNCOV
955
                UPDATE(ecmd.tx_coalesce_usecs_irq, coalesce->tx_coalesce_usecs_irq.value, need_update);
×
956

UNCOV
957
        if (coalesce->tx_max_coalesced_frames_irq.set)
×
UNCOV
958
                UPDATE(ecmd.tx_max_coalesced_frames_irq, coalesce->tx_max_coalesced_frames_irq.value, need_update);
×
959

960
        if (coalesce->stats_block_coalesce_usecs.set)
×
961
                UPDATE(ecmd.stats_block_coalesce_usecs, coalesce->stats_block_coalesce_usecs.value, need_update);
×
962

UNCOV
963
        if (coalesce->pkt_rate_low.set)
×
964
                UPDATE(ecmd.pkt_rate_low, coalesce->pkt_rate_low.value, need_update);
×
965

966
        if (coalesce->rx_coalesce_usecs_low.set)
×
967
                UPDATE(ecmd.rx_coalesce_usecs_low, coalesce->rx_coalesce_usecs_low.value, need_update);
×
968

969
        if (coalesce->rx_max_coalesced_frames_low.set)
×
970
                UPDATE(ecmd.rx_max_coalesced_frames_low, coalesce->rx_max_coalesced_frames_low.value, need_update);
×
971

972
        if (coalesce->tx_coalesce_usecs_low.set)
×
973
                UPDATE(ecmd.tx_coalesce_usecs_low, coalesce->tx_coalesce_usecs_low.value, need_update);
×
974

975
        if (coalesce->tx_max_coalesced_frames_low.set)
×
976
                UPDATE(ecmd.tx_max_coalesced_frames_low, coalesce->tx_max_coalesced_frames_low.value, need_update);
×
977

978
        if (coalesce->pkt_rate_high.set)
×
UNCOV
979
                UPDATE(ecmd.pkt_rate_high, coalesce->pkt_rate_high.value, need_update);
×
980

981
        if (coalesce->rx_coalesce_usecs_high.set)
×
982
                UPDATE(ecmd.rx_coalesce_usecs_high, coalesce->rx_coalesce_usecs_high.value, need_update);
×
983

UNCOV
984
        if (coalesce->rx_max_coalesced_frames_high.set)
×
UNCOV
985
                UPDATE(ecmd.rx_max_coalesced_frames_high, coalesce->rx_max_coalesced_frames_high.value, need_update);
×
986

UNCOV
987
        if (coalesce->tx_coalesce_usecs_high.set)
×
UNCOV
988
                UPDATE(ecmd.tx_coalesce_usecs_high, coalesce->tx_coalesce_usecs_high.value, need_update);
×
989

UNCOV
990
        if (coalesce->tx_max_coalesced_frames_high.set)
×
UNCOV
991
                UPDATE(ecmd.tx_max_coalesced_frames_high, coalesce->tx_max_coalesced_frames_high.value, need_update);
×
992

UNCOV
993
        if (coalesce->rate_sample_interval.set)
×
UNCOV
994
                UPDATE(ecmd.rate_sample_interval, DIV_ROUND_UP(coalesce->rate_sample_interval.value, USEC_PER_SEC), need_update);
×
995

UNCOV
996
        if (!need_update)
×
997
                return 0;
998

UNCOV
999
        ecmd.cmd = ETHTOOL_SCOALESCE;
×
UNCOV
1000
        return RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr));
×
1001
}
1002

1003
int ethtool_set_eee_settings(
915✔
1004
                int *ethtool_fd,
1005
                const char *ifname,
1006
                int enabled,
1007
                int tx_lpi_enabled,
1008
                usec_t tx_lpi_timer_usec,
1009
                uint32_t advertise) {
1010

1011
        int r;
915✔
1012

1013
        assert(ethtool_fd);
915✔
1014
        assert(ifname);
915✔
1015

1016
        if (enabled < 0 &&
915✔
1017
            tx_lpi_enabled < 0 &&
915✔
1018
            tx_lpi_timer_usec == USEC_INFINITY &&
915✔
1019
            advertise == 0)
915✔
1020
                return 0; /* Nothing requested. */
915✔
1021

UNCOV
1022
        r = ethtool_connect(ethtool_fd);
×
1023
        if (r < 0)
×
1024
                return r;
1025

UNCOV
1026
        struct ethtool_eee ecmd = {
×
1027
                .cmd = ETHTOOL_GEEE,
1028
        };
1029
        struct ifreq ifr = {
×
1030
                .ifr_data = (void*) &ecmd,
1031
        };
1032

1033
        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
×
1034

1035
        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
×
1036
                return -errno;
×
1037

1038
        if (ecmd.supported == 0)
×
1039
                return 0; /* Unsupported. */
1040

1041
        bool need_update = false;
×
1042

UNCOV
1043
        if (enabled >= 0)
×
1044
                UPDATE(ecmd.eee_enabled, (uint32_t) enabled, need_update);
×
1045
        if (tx_lpi_enabled >= 0)
×
UNCOV
1046
                UPDATE(ecmd.tx_lpi_enabled, (uint32_t) tx_lpi_enabled, need_update);
×
1047
        if (tx_lpi_timer_usec != USEC_INFINITY)
×
1048
                UPDATE(ecmd.tx_lpi_timer, (uint32_t) MIN(DIV_ROUND_UP(tx_lpi_timer_usec, USEC_PER_MSEC), (usec_t) UINT32_MAX), need_update);
×
UNCOV
1049
        if (advertise != 0)
×
1050
                UPDATE(ecmd.advertised, advertise & ecmd.supported, need_update);
×
1051

UNCOV
1052
        if (!need_update)
×
1053
                return 0; /* Nothing changed. */
1054

UNCOV
1055
        ecmd.cmd = ETHTOOL_SEEE;
×
1056
        return RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr));
×
1057
}
1058

1059
int config_parse_advertise(
×
1060
                const char *unit,
1061
                const char *filename,
1062
                unsigned line,
1063
                const char *section,
1064
                unsigned section_line,
1065
                const char *lvalue,
1066
                int ltype,
1067
                const char *rvalue,
1068
                void *data,
1069
                void *userdata) {
1070

1071
        uint32_t *advertise = ASSERT_PTR(data);
×
1072
        int r;
×
1073

1074
        assert(filename);
×
1075
        assert(section);
×
UNCOV
1076
        assert(lvalue);
×
1077
        assert(rvalue);
×
1078

UNCOV
1079
        if (isempty(rvalue)) {
×
1080
                /* Empty string resets the value. */
1081
                memzero(advertise, sizeof(uint32_t) * N_ADVERTISE);
×
UNCOV
1082
                return 0;
×
1083
        }
1084

UNCOV
1085
        for (const char *p = rvalue;;) {
×
1086
                _cleanup_free_ char *w = NULL;
×
1087
                enum ethtool_link_mode_bit_indices mode;
×
1088

1089
                r = extract_first_word(&p, &w, NULL, 0);
×
1090
                if (r == -ENOMEM)
×
UNCOV
1091
                        return log_oom();
×
1092
                if (r < 0) {
×
1093
                        log_syntax(unit, LOG_WARNING, filename, line, r,
×
1094
                                   "Failed to split advertise modes '%s', ignoring assignment: %m", rvalue);
1095
                        return 0;
×
1096
                }
UNCOV
1097
                if (r == 0)
×
1098
                        return 0;
1099

UNCOV
1100
                mode = ethtool_link_mode_bit_from_string(w);
×
1101
                /* We reuse the kernel provided enum which does not contain negative value. So, the cast
1102
                 * below is mandatory. Otherwise, the check below always passes and access an invalid address. */
UNCOV
1103
                if ((int) mode < 0) {
×
UNCOV
1104
                        log_syntax(unit, LOG_WARNING, filename, line, mode,
×
1105
                                   "Failed to parse advertise mode, ignoring: %s", w);
UNCOV
1106
                        continue;
×
1107
                }
1108

UNCOV
1109
                advertise[mode / 32] |= 1UL << (mode % 32);
×
1110
        }
1111
}
1112

UNCOV
1113
int config_parse_mdi(
×
1114
                const char *unit,
1115
                const char *filename,
1116
                unsigned line,
1117
                const char *section,
1118
                unsigned section_line,
1119
                const char *lvalue,
1120
                int ltype,
1121
                const char *rvalue,
1122
                void *data,
1123
                void *userdata) {
1124

1125
        uint8_t *mdi = ASSERT_PTR(data);
×
1126

UNCOV
1127
        assert(filename);
×
1128
        assert(rvalue);
×
1129

UNCOV
1130
        if (isempty(rvalue)) {
×
1131
                *mdi = ETH_TP_MDI_INVALID;
×
UNCOV
1132
                return 0;
×
1133
        }
1134

1135
        if (STR_IN_SET(rvalue, "mdi", "straight")) {
×
UNCOV
1136
                *mdi = ETH_TP_MDI;
×
1137
                return 0;
×
1138
        }
1139

1140
        if (STR_IN_SET(rvalue, "mdi-x", "mdix", "crossover")) {
×
UNCOV
1141
                *mdi = ETH_TP_MDI_X;
×
UNCOV
1142
                return 0;
×
1143
        }
1144

1145
        if (streq(rvalue, "auto")) {
×
1146
                *mdi = ETH_TP_MDI_AUTO;
×
1147
                return 0;
×
1148
        }
1149

1150
        log_syntax(unit, LOG_WARNING, filename, line, 0,
×
1151
                   "Failed to parse %s= setting, ignoring assignment: %s", lvalue, rvalue);
1152
        return 0;
1153
}
1154

UNCOV
1155
int config_parse_ring_buffer_or_channel(
×
1156
                const char *unit,
1157
                const char *filename,
1158
                unsigned line,
1159
                const char *section,
1160
                unsigned section_line,
1161
                const char *lvalue,
1162
                int ltype,
1163
                const char *rvalue,
1164
                void *data,
1165
                void *userdata) {
1166

UNCOV
1167
        u32_opt *dst = ASSERT_PTR(data);
×
UNCOV
1168
        uint32_t k;
×
UNCOV
1169
        int r;
×
1170

UNCOV
1171
        assert(filename);
×
UNCOV
1172
        assert(section);
×
1173
        assert(lvalue);
×
1174
        assert(rvalue);
×
1175

1176
        if (isempty(rvalue)) {
×
1177
                dst->value = 0;
×
1178
                dst->set = false;
×
1179
                return 0;
×
1180
        }
1181

UNCOV
1182
        if (streq(rvalue, "max")) {
×
1183
                dst->value = 0;
×
1184
                dst->set = true;
×
UNCOV
1185
                return 0;
×
1186
        }
1187

1188
        r = safe_atou32(rvalue, &k);
×
1189
        if (r < 0) {
×
UNCOV
1190
                log_syntax(unit, LOG_WARNING, filename, line, r,
×
1191
                           "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
1192
                return 0;
×
1193
        }
1194
        if (k < 1) {
×
1195
                log_syntax(unit, LOG_WARNING, filename, line, 0,
×
1196
                           "Invalid %s= value, ignoring: %s", lvalue, rvalue);
1197
                return 0;
×
1198
        }
1199

UNCOV
1200
        dst->value = k;
×
UNCOV
1201
        dst->set = true;
×
1202
        return 0;
×
1203
}
1204

1205
int config_parse_wol(
×
1206
                const char *unit,
1207
                const char *filename,
1208
                unsigned line,
1209
                const char *section,
1210
                unsigned section_line,
1211
                const char *lvalue,
1212
                int ltype,
1213
                const char *rvalue,
1214
                void *data,
1215
                void *userdata) {
1216

UNCOV
1217
        uint32_t new_opts = 0, *opts = data;
×
UNCOV
1218
        int r;
×
1219

UNCOV
1220
        assert(filename);
×
UNCOV
1221
        assert(section);
×
UNCOV
1222
        assert(lvalue);
×
UNCOV
1223
        assert(rvalue);
×
UNCOV
1224
        assert(data);
×
1225

UNCOV
1226
        if (isempty(rvalue)) {
×
1227
                *opts = UINT32_MAX; /* Do not update WOL option. */
×
UNCOV
1228
                return 0;
×
1229
        }
1230

UNCOV
1231
        if (streq(rvalue, "off")) {
×
1232
                *opts = 0; /* Disable WOL. */
×
1233
                return 0;
×
1234
        }
1235

UNCOV
1236
        for (const char *p = rvalue;;) {
×
1237
                _cleanup_free_ char *w = NULL;
×
1238
                bool found = false;
×
1239

UNCOV
1240
                r = extract_first_word(&p, &w, NULL, 0);
×
UNCOV
1241
                if (r == -ENOMEM)
×
1242
                        return log_oom();
×
1243
                if (r < 0) {
×
1244
                        log_syntax(unit, LOG_WARNING, filename, line, r,
×
1245
                                   "Failed to split wake-on-lan modes '%s', ignoring assignment: %m", rvalue);
UNCOV
1246
                        return 0;
×
1247
                }
1248
                if (r == 0)
×
1249
                        break;
1250

UNCOV
1251
                FOREACH_ELEMENT(option, wol_option_map)
×
1252
                        if (streq(w, option->name)) {
×
UNCOV
1253
                                new_opts |= option->opt;
×
UNCOV
1254
                                found = true;
×
UNCOV
1255
                                break;
×
1256
                        }
1257

UNCOV
1258
                if (!found)
×
UNCOV
1259
                        log_syntax(unit, LOG_WARNING, filename, line, 0,
×
1260
                                   "Unknown wake-on-lan mode '%s', ignoring.", w);
1261
        }
1262

UNCOV
1263
        if (*opts == UINT32_MAX)
×
UNCOV
1264
                *opts = new_opts;
×
1265
        else
UNCOV
1266
                *opts |= new_opts;
×
1267

1268
        return 0;
1269
}
1270

1271
int config_parse_coalesce_u32(
×
1272
                const char *unit,
1273
                const char *filename,
1274
                unsigned line,
1275
                const char *section,
1276
                unsigned section_line,
1277
                const char *lvalue,
1278
                int ltype,
1279
                const char *rvalue,
1280
                void *data,
1281
                void *userdata) {
UNCOV
1282
        u32_opt *dst = data;
×
UNCOV
1283
        uint32_t k;
×
1284
        int r;
×
1285

1286
        if (isempty(rvalue)) {
×
1287
                dst->value = 0;
×
UNCOV
1288
                dst->set = false;
×
UNCOV
1289
                return 0;
×
1290
        }
1291

1292
        r = safe_atou32(rvalue, &k);
×
UNCOV
1293
        if (r < 0) {
×
1294
                log_syntax(unit, LOG_WARNING, filename, line, r,
×
1295
                           "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
1296
                return 0;
×
1297
        }
1298

1299
        dst->value = k;
×
UNCOV
1300
        dst->set = true;
×
UNCOV
1301
        return 0;
×
1302
}
1303

1304
int config_parse_coalesce_sec(
×
1305
                const char *unit,
1306
                const char *filename,
1307
                unsigned line,
1308
                const char *section,
1309
                unsigned section_line,
1310
                const char *lvalue,
1311
                int ltype,
1312
                const char *rvalue,
1313
                void *data,
1314
                void *userdata) {
UNCOV
1315
        u32_opt *dst = data;
×
UNCOV
1316
        usec_t usec;
×
UNCOV
1317
        int r;
×
1318

1319
        if (isempty(rvalue)) {
×
1320
                dst->value = 0;
×
UNCOV
1321
                dst->set = false;
×
1322
                return 0;
×
1323
        }
1324

1325
        r = parse_sec(rvalue, &usec);
×
1326
        if (r < 0) {
×
UNCOV
1327
                log_syntax(unit, LOG_WARNING, filename, line, r,
×
1328
                           "Failed to parse coalesce setting value, ignoring: %s", rvalue);
1329
                return 0;
×
1330
        }
1331

UNCOV
1332
        if (usec > UINT32_MAX) {
×
1333
                log_syntax(unit, LOG_WARNING, filename, line, 0,
×
1334
                           "Too large %s= value, ignoring: %s", lvalue, rvalue);
1335
                return 0;
×
1336
        }
1337

1338
        if (STR_IN_SET(lvalue, "StatisticsBlockCoalesceSec", "CoalescePacketRateSampleIntervalSec") && usec < 1) {
×
1339
                log_syntax(unit, LOG_WARNING, filename, line, 0,
×
1340
                           "Invalid %s= value, ignoring: %s", lvalue, rvalue);
UNCOV
1341
                return 0;
×
1342
        }
1343

1344
        dst->value = (uint32_t) usec;
×
1345
        dst->set = true;
×
1346

UNCOV
1347
        return 0;
×
1348
}
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