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

systemd / systemd / 13210122091

07 Feb 2025 10:56PM UTC coverage: 71.748% (-0.07%) from 71.813%
13210122091

push

github

web-flow
network: allow to configure routing policy rule even if requesting interface is not activated yet (#36257)

Fixes a regression caused by 4f8b153d9
(v257).
Fixes #36244.

16 of 16 new or added lines in 2 files covered. (100.0%)

3079 existing lines in 54 files now uncovered.

292917 of 408258 relevant lines covered (71.75%)

714241.77 hits per line

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

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

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

9
#include "conf-parser.h"
10
#include "ethtool-util.h"
11
#include "extract-word.h"
12
#include "fd-util.h"
13
#include "log.h"
14
#include "macro-fundamental.h"
15
#include "memory-util.h"
16
#include "socket-util.h"
17
#include "string-table.h"
18
#include "strv.h"
19
#include "strxcpyx.h"
20

21
static const char* const duplex_table[_DUP_MAX] = {
22
        [DUP_FULL] = "full",
23
        [DUP_HALF] = "half"
24
};
25

26
DEFINE_STRING_TABLE_LOOKUP(duplex, Duplex);
464✔
27
DEFINE_CONFIG_PARSE_ENUM(config_parse_duplex, duplex, Duplex);
×
28

29
static const struct {
30
        uint32_t opt;
31
        const char *name;
32
} wol_option_map[] = {
33
        { WAKE_PHY,         "phy"        },
34
        { WAKE_UCAST,       "unicast",   },
35
        { WAKE_MCAST,       "multicast", },
36
        { WAKE_BCAST,       "broadcast", },
37
        { WAKE_ARP,         "arp",       },
38
        { WAKE_MAGIC,       "magic",     },
39
        { WAKE_MAGICSECURE, "secureon",  },
40
};
41

42
int wol_options_to_string_alloc(uint32_t opts, char **ret) {
×
43
        _cleanup_free_ char *str = NULL;
×
44

45
        assert(ret);
×
46

47
        if (opts == UINT32_MAX) {
×
48
                *ret = NULL;
×
49
                return 0;
×
50
        }
51

52
        FOREACH_ELEMENT(option, wol_option_map)
×
53
                if (opts & option->opt &&
×
54
                    !strextend_with_separator(&str, ",", option->name))
×
55
                        return -ENOMEM;
56

57
        if (!str) {
×
58
                str = strdup("off");
×
59
                if (!str)
×
60
                        return -ENOMEM;
61
        }
62

63
        *ret = TAKE_PTR(str);
×
64
        return 1;
×
65
}
66

67
static const char* const port_table[] = {
68
        [NET_DEV_PORT_TP]     = "tp",
69
        [NET_DEV_PORT_AUI]    = "aui",
70
        [NET_DEV_PORT_MII]    = "mii",
71
        [NET_DEV_PORT_FIBRE]  = "fibre",
72
        [NET_DEV_PORT_BNC]    = "bnc",
73
};
74

75
DEFINE_STRING_TABLE_LOOKUP(port, NetDevPort);
414✔
76
DEFINE_CONFIG_PARSE_ENUM(config_parse_port, port, NetDevPort);
×
77

78
static const char* const mdi_table[] = {
79
        [ETH_TP_MDI_INVALID]  = "unknown",
80
        [ETH_TP_MDI]          = "mdi",
81
        [ETH_TP_MDI_X]        = "mdi-x",
82
        [ETH_TP_MDI_AUTO]     = "auto",
83
};
84

85
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(mdi, int);
×
86

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

151
        [NET_DEV_FEAT_TXCSUM]              = "tx-checksum-", /* The suffix "-" means any feature beginning with "tx-checksum-" */
152
};
153

154
static const char* const ethtool_link_mode_bit_table[] = {
155
#  include "ethtool-link-mode.h"
156
};
157
/* Make sure the array is large enough to fit all bits */
158
assert_cc((ELEMENTSOF(ethtool_link_mode_bit_table)-1) / 32 < N_ADVERTISE);
159

160
DEFINE_STRING_TABLE_LOOKUP(ethtool_link_mode_bit, enum ethtool_link_mode_bit_indices);
×
161

162
static int ethtool_connect(int *ethtool_fd) {
8,764✔
163
        int fd;
8,764✔
164

165
        assert(ethtool_fd);
8,764✔
166

167
        /* This does nothing if already connected. */
168
        if (*ethtool_fd >= 0)
8,764✔
169
                return 0;
170

171
        fd = socket_ioctl_fd();
4,288✔
172
        if (fd < 0)
4,288✔
173
                return log_debug_errno(fd, "ethtool: could not create control socket: %m");
×
174

175
        *ethtool_fd = fd;
4,288✔
176
        return 0;
4,288✔
177
}
178

179
int ethtool_get_driver(int *ethtool_fd, const char *ifname, char **ret) {
3,485✔
180
        struct ethtool_drvinfo ecmd = {
3,485✔
181
                .cmd = ETHTOOL_GDRVINFO,
182
        };
183
        struct ifreq ifr = {
3,485✔
184
                .ifr_data = (void*) &ecmd,
185
        };
186
        int r;
3,485✔
187

188
        assert(ethtool_fd);
3,485✔
189
        assert(ifname);
3,485✔
190
        assert(ret);
3,485✔
191

192
        r = ethtool_connect(ethtool_fd);
3,485✔
193
        if (r < 0)
3,485✔
194
                return r;
3,485✔
195

196
        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
3,485✔
197

198
        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
3,485✔
199
                return -errno;
691✔
200

201
        if (isempty(ecmd.driver))
2,794✔
202
                return -ENODATA;
203

204
        return strdup_to(ret, ecmd.driver);
2,794✔
205
}
206

207
int ethtool_get_link_info(
1,168✔
208
                int *ethtool_fd,
209
                const char *ifname,
210
                int *ret_autonegotiation,
211
                uint64_t *ret_speed,
212
                Duplex *ret_duplex,
213
                NetDevPort *ret_port) {
214

215
        struct ethtool_cmd ecmd = {
1,168✔
216
                .cmd = ETHTOOL_GSET,
217
        };
218
        struct ifreq ifr = {
1,168✔
219
                .ifr_data = (void*) &ecmd,
220
        };
221
        int r;
1,168✔
222

223
        assert(ethtool_fd);
1,168✔
224
        assert(ifname);
1,168✔
225

226
        r = ethtool_connect(ethtool_fd);
1,168✔
227
        if (r < 0)
1,168✔
228
                return r;
1,168✔
229

230
        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
1,168✔
231

232
        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
1,168✔
233
                return -errno;
754✔
234

235
        if (ret_autonegotiation)
414✔
236
                *ret_autonegotiation = ecmd.autoneg;
414✔
237

238
        if (ret_speed) {
414✔
239
                uint32_t speed;
414✔
240

241
                speed = ethtool_cmd_speed(&ecmd);
414✔
242
                *ret_speed = speed == (uint32_t) SPEED_UNKNOWN ?
414✔
243
                        UINT64_MAX : (uint64_t) speed * 1000 * 1000;
414✔
244
        }
245

246
        if (ret_duplex)
414✔
247
                *ret_duplex = ecmd.duplex;
414✔
248

249
        if (ret_port)
414✔
250
                *ret_port = ecmd.port;
414✔
251

252
        return 0;
253
}
254

255
int ethtool_get_permanent_hw_addr(int *ethtool_fd, const char *ifname, struct hw_addr_data *ret) {
4,111✔
256
        _cleanup_close_ int fd = -EBADF;
4,111✔
257
        struct {
4,111✔
258
                struct ethtool_perm_addr addr;
259
                uint8_t space[HW_ADDR_MAX_SIZE];
260
        } epaddr = {
4,111✔
261
                .addr.cmd = ETHTOOL_GPERMADDR,
262
                .addr.size = HW_ADDR_MAX_SIZE,
263
        };
264
        struct ifreq ifr = {
4,111✔
265
                .ifr_data = (caddr_t) &epaddr,
266
        };
267
        int r;
4,111✔
268

269
        assert(ifname);
4,111✔
270
        assert(ret);
4,111✔
271

272
        if (!ethtool_fd)
4,111✔
273
                ethtool_fd = &fd;
1,094✔
274
        r = ethtool_connect(ethtool_fd);
4,111✔
275
        if (r < 0)
4,111✔
276
                return r;
277

278
        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
4,111✔
279

280
        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
4,111✔
281
                return -errno;
3✔
282

283
        if (epaddr.addr.size == 0)
4,108✔
284
                return -ENODATA;
285

286
        if (epaddr.addr.size > HW_ADDR_MAX_SIZE)
4,060✔
287
                return -EINVAL;
288

289
        ret->length = epaddr.addr.size;
4,060✔
290
        memcpy(ret->bytes, epaddr.addr.data, epaddr.addr.size);
4,060✔
291
        return 0;
4,060✔
292
}
293

294
#define UPDATE(dest, val, updated)                     \
295
        do {                                           \
296
                typeof(val) _v = (val);                \
297
                if (dest != _v)                        \
298
                        updated = true;                \
299
                dest = _v;                             \
300
        } while (false)
301

302
#define UPDATE_WITH_MAX(dest, max, val, updated)       \
303
        do {                                           \
304
                typeof(dest) _v = (val);               \
305
                typeof(dest) _max = (max);             \
306
                if (_v == 0 || _v > _max)              \
307
                        _v = _max;                     \
308
                if (dest != _v)                        \
309
                        updated = true;                \
310
                dest = _v;                             \
311
        } while (false)
312

313
int ethtool_set_wol(
839✔
314
                int *ethtool_fd,
315
                const char *ifname,
316
                uint32_t wolopts,
317
                const uint8_t password[SOPASS_MAX]) {
318

319
        struct ethtool_wolinfo ecmd = {
839✔
320
                .cmd = ETHTOOL_GWOL,
321
        };
322
        struct ifreq ifr = {
839✔
323
                .ifr_data = (void*) &ecmd,
324
        };
325
        bool need_update = false;
839✔
326
        int r;
839✔
327

328
        assert(ethtool_fd);
839✔
329
        assert(ifname);
839✔
330

331
        if (wolopts == UINT32_MAX && !password)
839✔
332
                /* Nothing requested. Return earlier. */
333
                return 0;
839✔
334

335
        r = ethtool_connect(ethtool_fd);
×
336
        if (r < 0)
×
337
                return r;
338

339
        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
×
340

341
        CLEANUP_ERASE(ecmd);
×
342

343
        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
×
344
                return -errno;
×
345

346
        if (wolopts == UINT32_MAX) {
×
347
                /* When password is specified without valid WoL options specified, then enable
348
                 * WAKE_MAGICSECURE flag if supported. */
349
                wolopts = ecmd.wolopts;
×
350
                if (password && FLAGS_SET(ecmd.supported, WAKE_MAGICSECURE))
×
351
                        wolopts |= WAKE_MAGICSECURE;
×
352
        }
353

354
        if ((wolopts & ~ecmd.supported) != 0) {
×
355
                _cleanup_free_ char *str = NULL;
×
356

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

361
                wolopts &= ecmd.supported;
×
362
        }
363

364
        if (!FLAGS_SET(wolopts, WAKE_MAGICSECURE))
×
365
                /* When WAKE_MAGICSECURE flag is not set, then ignore password. */
366
                password = NULL;
×
367

368
        UPDATE(ecmd.wolopts, wolopts, need_update);
×
369
        if (password &&
×
370
            memcmp(ecmd.sopass, password, sizeof(ecmd.sopass)) != 0) {
×
371
                memcpy(ecmd.sopass, password, sizeof(ecmd.sopass));
×
372
                need_update = true;
×
373
        }
374

375
        if (!need_update)
×
376
                return 0;
377

378
        ecmd.cmd = ETHTOOL_SWOL;
×
379
        return RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr));
×
380
}
381

382
int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netdev_ring_param *ring) {
839✔
383
        struct ethtool_ringparam ecmd = {
839✔
384
                .cmd = ETHTOOL_GRINGPARAM,
385
        };
386
        struct ifreq ifr = {
839✔
387
                .ifr_data = (void*) &ecmd,
388
        };
389
        bool need_update = false;
839✔
390
        int r;
839✔
391

392
        assert(ethtool_fd);
839✔
393
        assert(ifname);
839✔
394
        assert(ring);
839✔
395

396
        if (!ring->rx.set &&
839✔
397
            !ring->rx_mini.set &&
839✔
398
            !ring->rx_jumbo.set &&
839✔
399
            !ring->tx.set)
839✔
400
                return 0;
839✔
401

402
        r = ethtool_connect(ethtool_fd);
×
403
        if (r < 0)
×
404
                return r;
405

406
        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
×
407

408
        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
×
409
                return -errno;
×
410

411
        if (ring->rx.set)
×
412
                UPDATE_WITH_MAX(ecmd.rx_pending, ecmd.rx_max_pending, ring->rx.value, need_update);
×
413

414
        if (ring->rx_mini.set)
×
415
                UPDATE_WITH_MAX(ecmd.rx_mini_pending, ecmd.rx_mini_max_pending, ring->rx_mini.value, need_update);
×
416

417
        if (ring->rx_jumbo.set)
×
418
                UPDATE_WITH_MAX(ecmd.rx_jumbo_pending, ecmd.rx_jumbo_max_pending, ring->rx_jumbo.value, need_update);
×
419

420
        if (ring->tx.set)
×
421
                UPDATE_WITH_MAX(ecmd.tx_pending, ecmd.tx_max_pending, ring->tx.value, need_update);
×
422

423
        if (!need_update)
×
424
                return 0;
425

426
        ecmd.cmd = ETHTOOL_SRINGPARAM;
×
427
        return RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr));
×
428
}
429

430
static int get_stringset(int ethtool_fd, const char *ifname, enum ethtool_stringset stringset_id, struct ethtool_gstrings **ret) {
×
431
        _cleanup_free_ struct ethtool_gstrings *strings = NULL;
×
432
        struct {
×
433
                struct ethtool_sset_info info;
434
                uint32_t space;
435
        } buffer = {
×
436
                .info.cmd = ETHTOOL_GSSET_INFO,
437
                .info.sset_mask = UINT64_C(1) << stringset_id,
×
438
        };
439
        struct ifreq ifr = {
×
440
                .ifr_data = (void*) &buffer,
441
        };
442
        uint32_t len;
×
443

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

448
        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
×
449

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

453
        if (buffer.info.sset_mask == 0)
×
454
                return -EOPNOTSUPP;
455

456
        DISABLE_WARNING_ZERO_LENGTH_BOUNDS;
×
457
        len = buffer.info.data[0];
×
458
        REENABLE_WARNING;
×
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]) {
839✔
592
        _cleanup_free_ struct ethtool_gstrings *strings = NULL;
1,678✔
593
        _cleanup_free_ struct ethtool_gfeatures *gfeatures = NULL;
×
594
        _cleanup_free_ struct ethtool_sfeatures *sfeatures = NULL;
839✔
595
        struct ifreq ifr;
839✔
596
        bool have = false;
839✔
597
        int r;
839✔
598

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

603
        for (size_t i = 0; i < _NET_DEV_FEAT_MAX; i++)
53,696✔
604
                if (features[i] >= 0) {
52,857✔
605
                        have = true;
606
                        break;
607
                }
608

609
        if (!have)
839✔
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_glinksettings(int fd, struct ifreq *ifr, struct ethtool_link_usettings **ret) {
×
656
        struct ecmd {
×
657
                struct ethtool_link_settings req;
658
                uint32_t link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
659
        } ecmd = {
×
660
                .req.cmd = ETHTOOL_GLINKSETTINGS,
661
        };
662
        struct ethtool_link_usettings *u;
×
663
        unsigned offset;
×
664

665
        assert(fd >= 0);
×
666
        assert(ifr);
×
667
        assert(ret);
×
668

669
        /* The interaction user/kernel via the new API requires a small ETHTOOL_GLINKSETTINGS
670
           handshake first to agree on the length of the link mode bitmaps. If kernel doesn't
671
           agree with user, it returns the bitmap length it is expecting from user as a negative
672
           length (and cmd field is 0). When kernel and user agree, kernel returns valid info in
673
           all fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS). Based on
674
           https://github.com/torvalds/linux/commit/3f1ac7a700d039c61d8d8b99f28d605d489a60cf
675
        */
676

677
        ifr->ifr_data = (void *) &ecmd;
×
678

679
        if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
×
680
                return -errno;
×
681

682
        if (ecmd.req.link_mode_masks_nwords >= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
×
683
                return -EOPNOTSUPP;
684

685
        ecmd.req.link_mode_masks_nwords = -ecmd.req.link_mode_masks_nwords;
×
686

687
        ifr->ifr_data = (void *) &ecmd;
×
688

689
        if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
×
690
                return -errno;
×
691

692
        if (ecmd.req.link_mode_masks_nwords <= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
×
693
                return -EOPNOTSUPP;
694

695
        u = new(struct ethtool_link_usettings, 1);
×
696
        if (!u)
×
697
                return -ENOMEM;
698

699
        *u = (struct ethtool_link_usettings) {
×
700
                .base = ecmd.req,
701
        };
702

703
        offset = 0;
×
704
        memcpy(u->link_modes.supported, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords);
×
705

706
        offset += ecmd.req.link_mode_masks_nwords;
×
707
        memcpy(u->link_modes.advertising, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords);
×
708

709
        offset += ecmd.req.link_mode_masks_nwords;
×
710
        memcpy(u->link_modes.lp_advertising, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords);
×
711

712
        *ret = u;
×
713

714
        return 0;
×
715
}
716

717
static int get_gset(int fd, struct ifreq *ifr, struct ethtool_link_usettings **ret) {
×
718
        struct ethtool_link_usettings *e;
×
719
        struct ethtool_cmd ecmd = {
×
720
                .cmd = ETHTOOL_GSET,
721
        };
722

723
        assert(fd >= 0);
×
724
        assert(ifr);
×
725
        assert(ret);
×
726

727
        ifr->ifr_data = (void *) &ecmd;
×
728

729
        if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
×
730
                return -errno;
×
731

732
        e = new(struct ethtool_link_usettings, 1);
×
733
        if (!e)
×
734
                return -ENOMEM;
735

736
        *e = (struct ethtool_link_usettings) {
×
737
                .base.cmd = ETHTOOL_GSET,
738
                .base.link_mode_masks_nwords = 1,
739
                .base.speed = ethtool_cmd_speed(&ecmd),
×
740
                .base.duplex = ecmd.duplex,
×
741
                .base.port = ecmd.port,
×
742
                .base.phy_address = ecmd.phy_address,
×
743
                .base.autoneg = ecmd.autoneg,
×
744
                .base.mdio_support = ecmd.mdio_support,
×
745
                .base.eth_tp_mdix = ecmd.eth_tp_mdix,
×
746
                .base.eth_tp_mdix_ctrl = ecmd.eth_tp_mdix_ctrl,
×
747

748
                .link_modes.supported[0] = ecmd.supported,
×
749
                .link_modes.advertising[0] = ecmd.advertising,
×
750
                .link_modes.lp_advertising[0] = ecmd.lp_advertising,
×
751
        };
752

753
        *ret = e;
×
754

755
        return 0;
×
756
}
757

758
static int set_slinksettings(int fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) {
×
759
        struct {
×
760
                struct ethtool_link_settings req;
761
                uint32_t link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
762
        } ecmd = {};
×
763
        unsigned offset;
×
764

765
        assert(fd >= 0);
×
766
        assert(ifr);
×
767
        assert(u);
×
768

769
        if (u->base.cmd != ETHTOOL_GLINKSETTINGS || u->base.link_mode_masks_nwords <= 0)
×
770
                return -EINVAL;
×
771

772
        ecmd.req = u->base;
×
773
        ecmd.req.cmd = ETHTOOL_SLINKSETTINGS;
×
774
        offset = 0;
×
775
        memcpy(&ecmd.link_mode_data[offset], u->link_modes.supported, 4 * ecmd.req.link_mode_masks_nwords);
×
776

777
        offset += ecmd.req.link_mode_masks_nwords;
×
778
        memcpy(&ecmd.link_mode_data[offset], u->link_modes.advertising, 4 * ecmd.req.link_mode_masks_nwords);
×
779

780
        offset += ecmd.req.link_mode_masks_nwords;
×
781
        memcpy(&ecmd.link_mode_data[offset], u->link_modes.lp_advertising, 4 * ecmd.req.link_mode_masks_nwords);
×
782

783
        ifr->ifr_data = (void *) &ecmd;
×
784

785
        return RET_NERRNO(ioctl(fd, SIOCETHTOOL, ifr));
×
786
}
787

788
static int set_sset(int fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) {
×
789
        struct ethtool_cmd ecmd = {
×
790
                .cmd = ETHTOOL_SSET,
791
        };
792

793
        assert(fd >= 0);
×
794
        assert(ifr);
×
795
        assert(u);
×
796

797
        if (u->base.cmd != ETHTOOL_GSET || u->base.link_mode_masks_nwords <= 0)
×
798
                return -EINVAL;
×
799

800
        ecmd.supported = u->link_modes.supported[0];
×
801
        ecmd.advertising = u->link_modes.advertising[0];
×
802
        ecmd.lp_advertising = u->link_modes.lp_advertising[0];
×
803

804
        ethtool_cmd_speed_set(&ecmd, u->base.speed);
×
805

806
        ecmd.duplex = u->base.duplex;
×
807
        ecmd.port = u->base.port;
×
808
        ecmd.phy_address = u->base.phy_address;
×
809
        ecmd.autoneg = u->base.autoneg;
×
810
        ecmd.mdio_support = u->base.mdio_support;
×
811
        ecmd.eth_tp_mdix = u->base.eth_tp_mdix;
×
812
        ecmd.eth_tp_mdix_ctrl = u->base.eth_tp_mdix_ctrl;
×
813

814
        ifr->ifr_data = (void *) &ecmd;
×
815

816
        return RET_NERRNO(ioctl(fd, SIOCETHTOOL, ifr));
×
817
}
818

819
int ethtool_set_glinksettings(
839✔
820
                int *fd,
821
                const char *ifname,
822
                int autonegotiation,
823
                const uint32_t advertise[static N_ADVERTISE],
824
                uint64_t speed,
825
                Duplex duplex,
826
                NetDevPort port,
827
                uint8_t mdi) {
828

829
        _cleanup_free_ struct ethtool_link_usettings *u = NULL;
839✔
830
        struct ifreq ifr = {};
839✔
831
        bool changed = false;
839✔
832
        int r;
839✔
833

834
        assert(fd);
839✔
835
        assert(ifname);
839✔
836
        assert(advertise);
839✔
837

838
        if (autonegotiation < 0 && memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE) &&
839✔
839
            speed == 0 && duplex < 0 && port < 0 && mdi == ETH_TP_MDI_INVALID)
839✔
840
                return 0;
841

842
        /* If autonegotiation is disabled, the speed and duplex represent the fixed link mode and are
843
         * writable if the driver supports multiple link modes. If it is enabled then they are
844
         * read-only. If the link is up they represent the negotiated link mode; if the link is down,
845
         * the speed is 0, %SPEED_UNKNOWN or the highest enabled speed and @duplex is %DUPLEX_UNKNOWN
846
         * or the best enabled duplex mode. */
847

848
        if (speed > 0 || duplex >= 0 || port >= 0) {
×
849
                if (autonegotiation == AUTONEG_ENABLE || !memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE)) {
×
850
                        log_debug("ethtool: autonegotiation is enabled, ignoring speed, duplex, or port settings.");
×
851
                        speed = 0;
852
                        duplex = _DUP_INVALID;
853
                        port = _NET_DEV_PORT_INVALID;
854
                } else {
855
                        log_debug("ethtool: setting speed, duplex, or port, disabling autonegotiation.");
×
856
                        autonegotiation = AUTONEG_DISABLE;
857
                }
858
        }
859

860
        r = ethtool_connect(fd);
×
861
        if (r < 0)
×
862
                return r;
863

864
        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
×
865

866
        r = get_glinksettings(*fd, &ifr, &u);
×
867
        if (r < 0) {
×
868
                r = get_gset(*fd, &ifr, &u);
×
869
                if (r < 0)
×
870
                        return log_debug_errno(r, "ethtool: Cannot get device settings for %s: %m", ifname);
×
871
        }
872

873
        if (speed > 0)
×
874
                UPDATE(u->base.speed, DIV_ROUND_UP(speed, 1000000), changed);
×
875

876
        if (duplex >= 0)
×
877
                UPDATE(u->base.duplex, duplex, changed);
×
878

879
        if (port >= 0)
×
880
                UPDATE(u->base.port, port, changed);
×
881

882
        if (autonegotiation >= 0)
×
883
                UPDATE(u->base.autoneg, autonegotiation, changed);
×
884

885
        if (!memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE)) {
×
886
                UPDATE(u->base.autoneg, AUTONEG_ENABLE, changed);
×
887

888
                changed = changed ||
×
889
                        memcmp(&u->link_modes.advertising, advertise, sizeof(uint32_t) * N_ADVERTISE) != 0 ||
×
890
                        !memeqzero((uint8_t*) &u->link_modes.advertising + sizeof(uint32_t) * N_ADVERTISE,
×
891
                                   ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES - sizeof(uint32_t) * N_ADVERTISE);
892
                memcpy(&u->link_modes.advertising, advertise, sizeof(uint32_t) * N_ADVERTISE);
×
893
                memzero((uint8_t*) &u->link_modes.advertising + sizeof(uint32_t) * N_ADVERTISE,
×
894
                        ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES - sizeof(uint32_t) * N_ADVERTISE);
895
        }
896

897
        if (mdi != ETH_TP_MDI_INVALID) {
×
898
                if (u->base.eth_tp_mdix_ctrl == ETH_TP_MDI_INVALID)
×
899
                        log_debug("ethtool: setting MDI not supported for %s, ignoring.", ifname);
×
900
                else
901
                        UPDATE(u->base.eth_tp_mdix_ctrl, mdi, changed);
×
902
        }
903

904
        if (!changed)
×
905
                return 0;
906

907
        if (u->base.cmd == ETHTOOL_GLINKSETTINGS)
×
908
                r = set_slinksettings(*fd, &ifr, u);
×
909
        else
910
                r = set_sset(*fd, &ifr, u);
×
911
        if (r < 0)
×
912
                return log_debug_errno(r, "ethtool: Cannot set device settings for %s: %m", ifname);
×
913

914
        return r;
915
}
916

917
int ethtool_set_channels(int *fd, const char *ifname, const netdev_channels *channels) {
839✔
918
        struct ethtool_channels ecmd = {
839✔
919
                .cmd = ETHTOOL_GCHANNELS,
920
        };
921
        struct ifreq ifr = {
839✔
922
                .ifr_data = (void*) &ecmd,
923
        };
924
        bool need_update = false;
839✔
925
        int r;
839✔
926

927
        assert(fd);
839✔
928
        assert(ifname);
839✔
929
        assert(channels);
839✔
930

931
        if (!channels->rx.set &&
839✔
932
            !channels->tx.set &&
839✔
933
            !channels->other.set &&
839✔
934
            !channels->combined.set)
839✔
935
                return 0;
839✔
936

937
        r = ethtool_connect(fd);
×
938
        if (r < 0)
×
939
                return r;
940

941
        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
×
942

943
        if (ioctl(*fd, SIOCETHTOOL, &ifr) < 0)
×
944
                return -errno;
×
945

946
        if (channels->rx.set)
×
947
                UPDATE_WITH_MAX(ecmd.rx_count, ecmd.max_rx, channels->rx.value, need_update);
×
948

949
        if (channels->tx.set)
×
950
                UPDATE_WITH_MAX(ecmd.tx_count, ecmd.max_tx, channels->tx.value, need_update);
×
951

952
        if (channels->other.set)
×
953
                UPDATE_WITH_MAX(ecmd.other_count, ecmd.max_other, channels->other.value, need_update);
×
954

955
        if (channels->combined.set)
×
956
                UPDATE_WITH_MAX(ecmd.combined_count, ecmd.max_combined, channels->combined.value, need_update);
×
957

958
        if (!need_update)
×
959
                return 0;
960

961
        ecmd.cmd = ETHTOOL_SCHANNELS;
×
962
        return RET_NERRNO(ioctl(*fd, SIOCETHTOOL, &ifr));
×
963
}
964

965
int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int autoneg) {
839✔
966
        struct ethtool_pauseparam ecmd = {
839✔
967
                .cmd = ETHTOOL_GPAUSEPARAM,
968
        };
969
        struct ifreq ifr = {
839✔
970
                .ifr_data = (void*) &ecmd,
971
        };
972
        bool need_update = false;
839✔
973
        int r;
839✔
974

975
        assert(fd);
839✔
976
        assert(ifname);
839✔
977

978
        if (rx < 0 && tx < 0 && autoneg < 0)
839✔
979
                return 0;
839✔
980

981
        r = ethtool_connect(fd);
×
982
        if (r < 0)
×
983
                return r;
984

985
        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
×
986

987
        if (ioctl(*fd, SIOCETHTOOL, &ifr) < 0)
×
988
                return -errno;
×
989

990
        if (rx >= 0)
×
991
                UPDATE(ecmd.rx_pause, (uint32_t) rx, need_update);
×
992

993
        if (tx >= 0)
×
994
                UPDATE(ecmd.tx_pause, (uint32_t) tx, need_update);
×
995

996
        if (autoneg >= 0)
×
997
                UPDATE(ecmd.autoneg, (uint32_t) autoneg, need_update);
×
998

999
        if (!need_update)
×
1000
                return 0;
1001

1002
        ecmd.cmd = ETHTOOL_SPAUSEPARAM;
×
1003
        return RET_NERRNO(ioctl(*fd, SIOCETHTOOL, &ifr));
×
1004
}
1005

1006
int ethtool_set_nic_coalesce_settings(int *ethtool_fd, const char *ifname, const netdev_coalesce_param *coalesce) {
839✔
1007
        struct ethtool_coalesce ecmd = {
839✔
1008
                .cmd = ETHTOOL_GCOALESCE,
1009
        };
1010
        struct ifreq ifr = {
839✔
1011
                .ifr_data = (void*) &ecmd,
1012
        };
1013
        bool need_update = false;
839✔
1014
        int r;
839✔
1015

1016
        assert(ethtool_fd);
839✔
1017
        assert(ifname);
839✔
1018
        assert(coalesce);
839✔
1019

1020
        if (coalesce->use_adaptive_rx_coalesce < 0 &&
839✔
1021
            coalesce->use_adaptive_tx_coalesce < 0 &&
839✔
1022
            !coalesce->rx_coalesce_usecs.set &&
839✔
1023
            !coalesce->rx_max_coalesced_frames.set &&
839✔
1024
            !coalesce->rx_coalesce_usecs_irq.set &&
839✔
1025
            !coalesce->rx_max_coalesced_frames_irq.set &&
839✔
1026
            !coalesce->tx_coalesce_usecs.set &&
839✔
1027
            !coalesce->tx_max_coalesced_frames.set &&
839✔
1028
            !coalesce->tx_coalesce_usecs_irq.set &&
839✔
1029
            !coalesce->tx_max_coalesced_frames_irq.set &&
839✔
1030
            !coalesce->stats_block_coalesce_usecs.set &&
839✔
1031
            !coalesce->pkt_rate_low.set &&
839✔
1032
            !coalesce->rx_coalesce_usecs_low.set &&
839✔
1033
            !coalesce->rx_max_coalesced_frames_low.set &&
839✔
1034
            !coalesce->tx_coalesce_usecs_low.set &&
839✔
1035
            !coalesce->tx_max_coalesced_frames_low.set &&
839✔
1036
            !coalesce->pkt_rate_high.set &&
839✔
1037
            !coalesce->rx_coalesce_usecs_high.set &&
839✔
1038
            !coalesce->rx_max_coalesced_frames_high.set &&
839✔
1039
            !coalesce->tx_coalesce_usecs_high.set &&
839✔
1040
            !coalesce->tx_max_coalesced_frames_high.set &&
839✔
1041
            !coalesce->rate_sample_interval.set)
839✔
1042
                return 0;
839✔
1043

1044
        r = ethtool_connect(ethtool_fd);
×
1045
        if (r < 0)
×
1046
                return r;
1047

1048
        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
×
1049

1050
        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
×
1051
                return -errno;
×
1052

1053
        if (coalesce->use_adaptive_rx_coalesce >= 0)
×
1054
                UPDATE(ecmd.use_adaptive_rx_coalesce, (uint32_t) coalesce->use_adaptive_rx_coalesce, need_update);
×
1055

1056
        if (coalesce->use_adaptive_tx_coalesce >= 0)
×
1057
                UPDATE(ecmd.use_adaptive_tx_coalesce, (uint32_t) coalesce->use_adaptive_tx_coalesce, need_update);
×
1058

1059
        if (coalesce->rx_coalesce_usecs.set)
×
1060
                UPDATE(ecmd.rx_coalesce_usecs, coalesce->rx_coalesce_usecs.value, need_update);
×
1061

1062
        if (coalesce->rx_max_coalesced_frames.set)
×
1063
                UPDATE(ecmd.rx_max_coalesced_frames, coalesce->rx_max_coalesced_frames.value, need_update);
×
1064

1065
        if (coalesce->rx_coalesce_usecs_irq.set)
×
1066
                UPDATE(ecmd.rx_coalesce_usecs_irq, coalesce->rx_coalesce_usecs_irq.value, need_update);
×
1067

1068
        if (coalesce->rx_max_coalesced_frames_irq.set)
×
1069
                UPDATE(ecmd.rx_max_coalesced_frames_irq, coalesce->rx_max_coalesced_frames_irq.value, need_update);
×
1070

1071
        if (coalesce->tx_coalesce_usecs.set)
×
1072
                UPDATE(ecmd.tx_coalesce_usecs, coalesce->tx_coalesce_usecs.value, need_update);
×
1073

1074
        if (coalesce->tx_max_coalesced_frames.set)
×
1075
                UPDATE(ecmd.tx_max_coalesced_frames, coalesce->tx_max_coalesced_frames.value, need_update);
×
1076

1077
        if (coalesce->tx_coalesce_usecs_irq.set)
×
1078
                UPDATE(ecmd.tx_coalesce_usecs_irq, coalesce->tx_coalesce_usecs_irq.value, need_update);
×
1079

1080
        if (coalesce->tx_max_coalesced_frames_irq.set)
×
1081
                UPDATE(ecmd.tx_max_coalesced_frames_irq, coalesce->tx_max_coalesced_frames_irq.value, need_update);
×
1082

1083
        if (coalesce->stats_block_coalesce_usecs.set)
×
1084
                UPDATE(ecmd.stats_block_coalesce_usecs, coalesce->stats_block_coalesce_usecs.value, need_update);
×
1085

1086
        if (coalesce->pkt_rate_low.set)
×
1087
                UPDATE(ecmd.pkt_rate_low, coalesce->pkt_rate_low.value, need_update);
×
1088

1089
        if (coalesce->rx_coalesce_usecs_low.set)
×
1090
                UPDATE(ecmd.rx_coalesce_usecs_low, coalesce->rx_coalesce_usecs_low.value, need_update);
×
1091

1092
        if (coalesce->rx_max_coalesced_frames_low.set)
×
1093
                UPDATE(ecmd.rx_max_coalesced_frames_low, coalesce->rx_max_coalesced_frames_low.value, need_update);
×
1094

1095
        if (coalesce->tx_coalesce_usecs_low.set)
×
1096
                UPDATE(ecmd.tx_coalesce_usecs_low, coalesce->tx_coalesce_usecs_low.value, need_update);
×
1097

1098
        if (coalesce->tx_max_coalesced_frames_low.set)
×
1099
                UPDATE(ecmd.tx_max_coalesced_frames_low, coalesce->tx_max_coalesced_frames_low.value, need_update);
×
1100

1101
        if (coalesce->pkt_rate_high.set)
×
1102
                UPDATE(ecmd.pkt_rate_high, coalesce->pkt_rate_high.value, need_update);
×
1103

1104
        if (coalesce->rx_coalesce_usecs_high.set)
×
1105
                UPDATE(ecmd.rx_coalesce_usecs_high, coalesce->rx_coalesce_usecs_high.value, need_update);
×
1106

1107
        if (coalesce->rx_max_coalesced_frames_high.set)
×
1108
                UPDATE(ecmd.rx_max_coalesced_frames_high, coalesce->rx_max_coalesced_frames_high.value, need_update);
×
1109

1110
        if (coalesce->tx_coalesce_usecs_high.set)
×
1111
                UPDATE(ecmd.tx_coalesce_usecs_high, coalesce->tx_coalesce_usecs_high.value, need_update);
×
1112

1113
        if (coalesce->tx_max_coalesced_frames_high.set)
×
1114
                UPDATE(ecmd.tx_max_coalesced_frames_high, coalesce->tx_max_coalesced_frames_high.value, need_update);
×
1115

1116
        if (coalesce->rate_sample_interval.set)
×
1117
                UPDATE(ecmd.rate_sample_interval, DIV_ROUND_UP(coalesce->rate_sample_interval.value, USEC_PER_SEC), need_update);
×
1118

1119
        if (!need_update)
×
1120
                return 0;
1121

1122
        ecmd.cmd = ETHTOOL_SCOALESCE;
×
1123
        return RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr));
×
1124
}
1125

1126
int ethtool_set_eee_settings(
839✔
1127
                int *ethtool_fd,
1128
                const char *ifname,
1129
                int enabled,
1130
                int tx_lpi_enabled,
1131
                usec_t tx_lpi_timer_usec,
1132
                uint32_t advertise) {
1133

1134
        int r;
839✔
1135

1136
        assert(ethtool_fd);
839✔
1137
        assert(ifname);
839✔
1138

1139
        if (enabled < 0 &&
839✔
1140
            tx_lpi_enabled < 0 &&
839✔
1141
            tx_lpi_timer_usec == USEC_INFINITY &&
839✔
1142
            advertise == 0)
839✔
1143
                return 0; /* Nothing requested. */
839✔
1144

UNCOV
1145
        r = ethtool_connect(ethtool_fd);
×
1146
        if (r < 0)
×
1147
                return r;
1148

1149
        struct ethtool_eee ecmd = {
×
1150
                .cmd = ETHTOOL_GEEE,
1151
        };
1152
        struct ifreq ifr = {
×
1153
                .ifr_data = (void*) &ecmd,
1154
        };
1155

1156
        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
×
1157

1158
        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
×
1159
                return -errno;
×
1160

UNCOV
1161
        if (ecmd.supported == 0)
×
1162
                return 0; /* Unsupported. */
1163

1164
        bool need_update = false;
×
1165

UNCOV
1166
        if (enabled >= 0)
×
1167
                UPDATE(ecmd.eee_enabled, (uint32_t) enabled, need_update);
×
UNCOV
1168
        if (tx_lpi_enabled >= 0)
×
UNCOV
1169
                UPDATE(ecmd.tx_lpi_enabled, (uint32_t) tx_lpi_enabled, need_update);
×
1170
        if (tx_lpi_timer_usec != USEC_INFINITY)
×
1171
                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
1172
        if (advertise != 0)
×
1173
                UPDATE(ecmd.advertised, advertise & ecmd.supported, need_update);
×
1174

UNCOV
1175
        if (!need_update)
×
1176
                return 0; /* Nothing changed. */
1177

UNCOV
1178
        ecmd.cmd = ETHTOOL_SEEE;
×
UNCOV
1179
        return RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr));
×
1180
}
1181

UNCOV
1182
int config_parse_advertise(
×
1183
                const char *unit,
1184
                const char *filename,
1185
                unsigned line,
1186
                const char *section,
1187
                unsigned section_line,
1188
                const char *lvalue,
1189
                int ltype,
1190
                const char *rvalue,
1191
                void *data,
1192
                void *userdata) {
1193

1194
        uint32_t *advertise = ASSERT_PTR(data);
×
1195
        int r;
×
1196

1197
        assert(filename);
×
1198
        assert(section);
×
1199
        assert(lvalue);
×
UNCOV
1200
        assert(rvalue);
×
1201

1202
        if (isempty(rvalue)) {
×
1203
                /* Empty string resets the value. */
1204
                memzero(advertise, sizeof(uint32_t) * N_ADVERTISE);
×
UNCOV
1205
                return 0;
×
1206
        }
1207

1208
        for (const char *p = rvalue;;) {
×
1209
                _cleanup_free_ char *w = NULL;
×
UNCOV
1210
                enum ethtool_link_mode_bit_indices mode;
×
1211

1212
                r = extract_first_word(&p, &w, NULL, 0);
×
1213
                if (r == -ENOMEM)
×
1214
                        return log_oom();
×
UNCOV
1215
                if (r < 0) {
×
UNCOV
1216
                        log_syntax(unit, LOG_WARNING, filename, line, r,
×
1217
                                   "Failed to split advertise modes '%s', ignoring assignment: %m", rvalue);
UNCOV
1218
                        return 0;
×
1219
                }
UNCOV
1220
                if (r == 0)
×
1221
                        return 0;
1222

UNCOV
1223
                mode = ethtool_link_mode_bit_from_string(w);
×
1224
                /* We reuse the kernel provided enum which does not contain negative value. So, the cast
1225
                 * below is mandatory. Otherwise, the check below always passes and access an invalid address. */
UNCOV
1226
                if ((int) mode < 0) {
×
UNCOV
1227
                        log_syntax(unit, LOG_WARNING, filename, line, mode,
×
1228
                                   "Failed to parse advertise mode, ignoring: %s", w);
UNCOV
1229
                        continue;
×
1230
                }
1231

UNCOV
1232
                advertise[mode / 32] |= 1UL << (mode % 32);
×
1233
        }
1234
}
1235

1236
int config_parse_mdi(
×
1237
                const char *unit,
1238
                const char *filename,
1239
                unsigned line,
1240
                const char *section,
1241
                unsigned section_line,
1242
                const char *lvalue,
1243
                int ltype,
1244
                const char *rvalue,
1245
                void *data,
1246
                void *userdata) {
1247

UNCOV
1248
        uint8_t *mdi = ASSERT_PTR(data);
×
1249

1250
        assert(filename);
×
1251
        assert(rvalue);
×
1252

UNCOV
1253
        if (isempty(rvalue)) {
×
UNCOV
1254
                *mdi = ETH_TP_MDI_INVALID;
×
1255
                return 0;
×
1256
        }
1257

UNCOV
1258
        if (STR_IN_SET(rvalue, "mdi", "straight")) {
×
1259
                *mdi = ETH_TP_MDI;
×
UNCOV
1260
                return 0;
×
1261
        }
1262

UNCOV
1263
        if (STR_IN_SET(rvalue, "mdi-x", "mdix", "crossover")) {
×
1264
                *mdi = ETH_TP_MDI_X;
×
UNCOV
1265
                return 0;
×
1266
        }
1267

1268
        if (streq(rvalue, "auto")) {
×
1269
                *mdi = ETH_TP_MDI_AUTO;
×
UNCOV
1270
                return 0;
×
1271
        }
1272

UNCOV
1273
        log_syntax(unit, LOG_WARNING, filename, line, 0,
×
1274
                   "Failed to parse %s= setting, ignoring assignment: %s", lvalue, rvalue);
1275
        return 0;
1276
}
1277

UNCOV
1278
int config_parse_ring_buffer_or_channel(
×
1279
                const char *unit,
1280
                const char *filename,
1281
                unsigned line,
1282
                const char *section,
1283
                unsigned section_line,
1284
                const char *lvalue,
1285
                int ltype,
1286
                const char *rvalue,
1287
                void *data,
1288
                void *userdata) {
1289

1290
        u32_opt *dst = ASSERT_PTR(data);
×
1291
        uint32_t k;
×
UNCOV
1292
        int r;
×
1293

1294
        assert(filename);
×
1295
        assert(section);
×
UNCOV
1296
        assert(lvalue);
×
UNCOV
1297
        assert(rvalue);
×
1298

1299
        if (isempty(rvalue)) {
×
1300
                dst->value = 0;
×
UNCOV
1301
                dst->set = false;
×
UNCOV
1302
                return 0;
×
1303
        }
1304

1305
        if (streq(rvalue, "max")) {
×
UNCOV
1306
                dst->value = 0;
×
1307
                dst->set = true;
×
1308
                return 0;
×
1309
        }
1310

1311
        r = safe_atou32(rvalue, &k);
×
UNCOV
1312
        if (r < 0) {
×
1313
                log_syntax(unit, LOG_WARNING, filename, line, r,
×
1314
                           "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
1315
                return 0;
×
1316
        }
UNCOV
1317
        if (k < 1) {
×
1318
                log_syntax(unit, LOG_WARNING, filename, line, 0,
×
1319
                           "Invalid %s= value, ignoring: %s", lvalue, rvalue);
1320
                return 0;
×
1321
        }
1322

UNCOV
1323
        dst->value = k;
×
UNCOV
1324
        dst->set = true;
×
1325
        return 0;
×
1326
}
1327

UNCOV
1328
int config_parse_wol(
×
1329
                const char *unit,
1330
                const char *filename,
1331
                unsigned line,
1332
                const char *section,
1333
                unsigned section_line,
1334
                const char *lvalue,
1335
                int ltype,
1336
                const char *rvalue,
1337
                void *data,
1338
                void *userdata) {
1339

UNCOV
1340
        uint32_t new_opts = 0, *opts = data;
×
UNCOV
1341
        int r;
×
1342

UNCOV
1343
        assert(filename);
×
UNCOV
1344
        assert(section);
×
UNCOV
1345
        assert(lvalue);
×
UNCOV
1346
        assert(rvalue);
×
UNCOV
1347
        assert(data);
×
1348

1349
        if (isempty(rvalue)) {
×
1350
                *opts = UINT32_MAX; /* Do not update WOL option. */
×
1351
                return 0;
×
1352
        }
1353

1354
        if (streq(rvalue, "off")) {
×
1355
                *opts = 0; /* Disable WOL. */
×
1356
                return 0;
×
1357
        }
1358

1359
        for (const char *p = rvalue;;) {
×
1360
                _cleanup_free_ char *w = NULL;
×
1361
                bool found = false;
×
1362

1363
                r = extract_first_word(&p, &w, NULL, 0);
×
UNCOV
1364
                if (r == -ENOMEM)
×
UNCOV
1365
                        return log_oom();
×
1366
                if (r < 0) {
×
1367
                        log_syntax(unit, LOG_WARNING, filename, line, r,
×
1368
                                   "Failed to split wake-on-lan modes '%s', ignoring assignment: %m", rvalue);
UNCOV
1369
                        return 0;
×
1370
                }
1371
                if (r == 0)
×
1372
                        break;
1373

UNCOV
1374
                FOREACH_ELEMENT(option, wol_option_map)
×
UNCOV
1375
                        if (streq(w, option->name)) {
×
UNCOV
1376
                                new_opts |= option->opt;
×
UNCOV
1377
                                found = true;
×
UNCOV
1378
                                break;
×
1379
                        }
1380

UNCOV
1381
                if (!found)
×
1382
                        log_syntax(unit, LOG_WARNING, filename, line, 0,
×
1383
                                   "Unknown wake-on-lan mode '%s', ignoring.", w);
1384
        }
1385

1386
        if (*opts == UINT32_MAX)
×
1387
                *opts = new_opts;
×
1388
        else
1389
                *opts |= new_opts;
×
1390

1391
        return 0;
1392
}
1393

1394
int config_parse_coalesce_u32(
×
1395
                const char *unit,
1396
                const char *filename,
1397
                unsigned line,
1398
                const char *section,
1399
                unsigned section_line,
1400
                const char *lvalue,
1401
                int ltype,
1402
                const char *rvalue,
1403
                void *data,
1404
                void *userdata) {
1405
        u32_opt *dst = data;
×
1406
        uint32_t k;
×
UNCOV
1407
        int r;
×
1408

UNCOV
1409
        if (isempty(rvalue)) {
×
UNCOV
1410
                dst->value = 0;
×
1411
                dst->set = false;
×
1412
                return 0;
×
1413
        }
1414

UNCOV
1415
        r = safe_atou32(rvalue, &k);
×
UNCOV
1416
        if (r < 0) {
×
UNCOV
1417
                log_syntax(unit, LOG_WARNING, filename, line, r,
×
1418
                           "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
UNCOV
1419
                return 0;
×
1420
        }
1421

UNCOV
1422
        dst->value = k;
×
UNCOV
1423
        dst->set = true;
×
UNCOV
1424
        return 0;
×
1425
}
1426

UNCOV
1427
int config_parse_coalesce_sec(
×
1428
                const char *unit,
1429
                const char *filename,
1430
                unsigned line,
1431
                const char *section,
1432
                unsigned section_line,
1433
                const char *lvalue,
1434
                int ltype,
1435
                const char *rvalue,
1436
                void *data,
1437
                void *userdata) {
UNCOV
1438
        u32_opt *dst = data;
×
UNCOV
1439
        usec_t usec;
×
UNCOV
1440
        int r;
×
1441

UNCOV
1442
        if (isempty(rvalue)) {
×
UNCOV
1443
                dst->value = 0;
×
UNCOV
1444
                dst->set = false;
×
UNCOV
1445
                return 0;
×
1446
        }
1447

UNCOV
1448
        r = parse_sec(rvalue, &usec);
×
UNCOV
1449
        if (r < 0) {
×
UNCOV
1450
                log_syntax(unit, LOG_WARNING, filename, line, r,
×
1451
                           "Failed to parse coalesce setting value, ignoring: %s", rvalue);
UNCOV
1452
                return 0;
×
1453
        }
1454

UNCOV
1455
        if (usec > UINT32_MAX) {
×
UNCOV
1456
                log_syntax(unit, LOG_WARNING, filename, line, 0,
×
1457
                           "Too large %s= value, ignoring: %s", lvalue, rvalue);
UNCOV
1458
                return 0;
×
1459
        }
1460

UNCOV
1461
        if (STR_IN_SET(lvalue, "StatisticsBlockCoalesceSec", "CoalescePacketRateSampleIntervalSec") && usec < 1) {
×
UNCOV
1462
                log_syntax(unit, LOG_WARNING, filename, line, 0,
×
1463
                           "Invalid %s= value, ignoring: %s", lvalue, rvalue);
UNCOV
1464
                return 0;
×
1465
        }
1466

UNCOV
1467
        dst->value = (uint32_t) usec;
×
UNCOV
1468
        dst->set = true;
×
1469

UNCOV
1470
        return 0;
×
1471
}
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