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

systemd / systemd / 15232239991

24 May 2025 08:01PM UTC coverage: 72.053% (-0.02%) from 72.07%
15232239991

push

github

web-flow
docs: add man pages for sd_device_enumerator_[new,ref,unref,unrefp] (#37586)

For #20929.

299160 of 415197 relevant lines covered (72.05%)

703671.29 hits per line

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

24.96
/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);
467✔
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);
417✔
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) {
8,946✔
168
        int fd;
8,946✔
169

170
        assert(ethtool_fd);
8,946✔
171

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

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

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

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

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

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

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

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

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

209
        return strdup_to(ret, ecmd.driver);
2,875✔
210
}
211

212
int ethtool_get_link_info(
1,218✔
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,218✔
221
                .cmd = ETHTOOL_GSET,
222
        };
223
        struct ifreq ifr = {
1,218✔
224
                .ifr_data = (void*) &ecmd,
225
        };
226
        int r;
1,218✔
227

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

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

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

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

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

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

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

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

254
        if (ret_port)
417✔
255
                *ret_port = ecmd.port;
417✔
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,201✔
261
        _cleanup_close_ int fd = -EBADF;
4,201✔
262
        union {
4,201✔
263
                struct ethtool_perm_addr addr;
264
                uint8_t buf[offsetof(struct ethtool_perm_addr, data) + HW_ADDR_MAX_SIZE];
265
        } epaddr = {
4,201✔
266
                .addr.cmd = ETHTOOL_GPERMADDR,
267
                .addr.size = HW_ADDR_MAX_SIZE,
268
        };
269
        struct ifreq ifr = {
4,201✔
270
                .ifr_data = (caddr_t) &epaddr,
271
        };
272
        int r;
4,201✔
273

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

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

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

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

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

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

294
        ret->length = epaddr.addr.size;
4,152✔
295
        memcpy(ret->bytes, epaddr.addr.data, epaddr.addr.size);
4,152✔
296
        return 0;
4,152✔
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(
877✔
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 = {
877✔
325
                .cmd = ETHTOOL_GWOL,
326
        };
327
        struct ifreq ifr = {
877✔
328
                .ifr_data = (void*) &ecmd,
329
        };
330
        bool need_update = false;
877✔
331
        int r;
877✔
332

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

336
        if (wolopts == UINT32_MAX && !password)
877✔
337
                /* Nothing requested. Return earlier. */
338
                return 0;
877✔
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) {
877✔
388
        struct ethtool_ringparam ecmd = {
877✔
389
                .cmd = ETHTOOL_GRINGPARAM,
390
        };
391
        struct ifreq ifr = {
877✔
392
                .ifr_data = (void*) &ecmd,
393
        };
394
        bool need_update = false;
877✔
395
        int r;
877✔
396

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

401
        if (!ring->rx.set &&
877✔
402
            !ring->rx_mini.set &&
877✔
403
            !ring->rx_jumbo.set &&
877✔
404
            !ring->tx.set)
877✔
405
                return 0;
877✔
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]) {
877✔
592
        _cleanup_free_ struct ethtool_gstrings *strings = NULL;
1,754✔
593
        _cleanup_free_ struct ethtool_gfeatures *gfeatures = NULL;
×
594
        _cleanup_free_ struct ethtool_sfeatures *sfeatures = NULL;
877✔
595
        struct ifreq ifr;
877✔
596
        bool have = false;
877✔
597
        int r;
877✔
598

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

603
        for (size_t i = 0; i < _NET_DEV_FEAT_MAX; i++)
56,128✔
604
                if (features[i] >= 0) {
55,251✔
605
                        have = true;
606
                        break;
607
                }
608

609
        if (!have)
877✔
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, union ethtool_link_usettings **ret) {
×
656
        union ethtool_link_usettings ecmd = {
×
657
                .base.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
665
           handshake first to agree on the length of the link mode bitmaps. If kernel doesn't
666
           agree with user, it returns the bitmap length it is expecting from user as a negative
667
           length (and cmd field is 0). When kernel and user agree, kernel returns valid info in
668
           all fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS). Based on
669
           https://github.com/torvalds/linux/commit/3f1ac7a700d039c61d8d8b99f28d605d489a60cf
670
        */
671

672
        ifr->ifr_data = (void*) &ecmd;
×
673

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

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

680
        ecmd.base.link_mode_masks_nwords = -ecmd.base.link_mode_masks_nwords;
×
681

682
        ifr->ifr_data = (void *) &ecmd;
×
683

684
        if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
×
685
                return -errno;
×
686

687
        if (ecmd.base.link_mode_masks_nwords <= 0 || ecmd.base.cmd != ETHTOOL_GLINKSETTINGS)
×
688
                return -EOPNOTSUPP;
689

690
        union ethtool_link_usettings *u = newdup(union ethtool_link_usettings, &ecmd, 1);
×
691
        if (!u)
×
692
                return -ENOMEM;
693

694
        *ret = u;
×
695
        return 0;
×
696
}
697

698
static int get_gset(int fd, struct ifreq *ifr, union ethtool_link_usettings **ret) {
×
699
        struct ethtool_cmd ecmd = {
×
700
                .cmd = ETHTOOL_GSET,
701
        };
702

703
        assert(fd >= 0);
×
704
        assert(ifr);
×
705
        assert(ret);
×
706

707
        ifr->ifr_data = (void *) &ecmd;
×
708

709
        if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
×
710
                return -errno;
×
711

712
        union ethtool_link_usettings *u = new(union ethtool_link_usettings, 1);
×
713
        if (!u)
×
714
                return -ENOMEM;
715

716
        *u = (union ethtool_link_usettings) {
×
717
                .base.cmd = ETHTOOL_GSET,
718
                .base.link_mode_masks_nwords = 1,
719
                .base.speed = ethtool_cmd_speed(&ecmd),
×
720
                .base.duplex = ecmd.duplex,
×
721
                .base.port = ecmd.port,
×
722
                .base.phy_address = ecmd.phy_address,
×
723
                .base.autoneg = ecmd.autoneg,
×
724
                .base.mdio_support = ecmd.mdio_support,
×
725
                .base.eth_tp_mdix = ecmd.eth_tp_mdix,
×
726
                .base.eth_tp_mdix_ctrl = ecmd.eth_tp_mdix_ctrl,
×
727
        };
728

729
        u->link_modes.supported[0] = ecmd.supported;
×
730
        u->link_modes.advertising[0] = ecmd.advertising;
×
731
        u->link_modes.lp_advertising[0] = ecmd.lp_advertising;
×
732

733
        *ret = u;
×
734
        return 0;
×
735
}
736

737
static int set_slinksettings(int fd, struct ifreq *ifr, const union ethtool_link_usettings *u) {
×
738
        assert(fd >= 0);
×
739
        assert(ifr);
×
740
        assert(u);
×
741

742
        if (u->base.cmd != ETHTOOL_GLINKSETTINGS || u->base.link_mode_masks_nwords <= 0)
×
743
                return -EINVAL;
×
744

745
        union ethtool_link_usettings ecmd = *u;
×
746
        ecmd.base.cmd = ETHTOOL_SLINKSETTINGS;
×
747
        ifr->ifr_data = (void *) &ecmd;
×
748

749
        return RET_NERRNO(ioctl(fd, SIOCETHTOOL, ifr));
×
750
}
751

752
static int set_sset(int fd, struct ifreq *ifr, const union ethtool_link_usettings *u) {
×
753
        struct ethtool_cmd ecmd = {
×
754
                .cmd = ETHTOOL_SSET,
755
        };
756

757
        assert(fd >= 0);
×
758
        assert(ifr);
×
759
        assert(u);
×
760

761
        if (u->base.cmd != ETHTOOL_GSET || u->base.link_mode_masks_nwords <= 0)
×
762
                return -EINVAL;
×
763

764
        ecmd.supported = u->link_modes.supported[0];
×
765
        ecmd.advertising = u->link_modes.advertising[0];
×
766
        ecmd.lp_advertising = u->link_modes.lp_advertising[0];
×
767

768
        ethtool_cmd_speed_set(&ecmd, u->base.speed);
×
769

770
        ecmd.duplex = u->base.duplex;
×
771
        ecmd.port = u->base.port;
×
772
        ecmd.phy_address = u->base.phy_address;
×
773
        ecmd.autoneg = u->base.autoneg;
×
774
        ecmd.mdio_support = u->base.mdio_support;
×
775
        ecmd.eth_tp_mdix = u->base.eth_tp_mdix;
×
776
        ecmd.eth_tp_mdix_ctrl = u->base.eth_tp_mdix_ctrl;
×
777

778
        ifr->ifr_data = (void *) &ecmd;
×
779

780
        return RET_NERRNO(ioctl(fd, SIOCETHTOOL, ifr));
×
781
}
782

783
int ethtool_set_glinksettings(
877✔
784
                int *fd,
785
                const char *ifname,
786
                int autonegotiation,
787
                const uint32_t advertise[static N_ADVERTISE],
788
                uint64_t speed,
789
                Duplex duplex,
790
                NetDevPort port,
791
                uint8_t mdi) {
792

793
        _cleanup_free_ union ethtool_link_usettings *u = NULL;
877✔
794
        struct ifreq ifr = {};
877✔
795
        bool changed = false;
877✔
796
        int r;
877✔
797

798
        assert(fd);
877✔
799
        assert(ifname);
877✔
800
        assert(advertise);
877✔
801

802
        if (autonegotiation < 0 && memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE) &&
877✔
803
            speed == 0 && duplex < 0 && port < 0 && mdi == ETH_TP_MDI_INVALID)
877✔
804
                return 0;
805

806
        /* If autonegotiation is disabled, the speed and duplex represent the fixed link mode and are
807
         * writable if the driver supports multiple link modes. If it is enabled then they are
808
         * read-only. If the link is up they represent the negotiated link mode; if the link is down,
809
         * the speed is 0, %SPEED_UNKNOWN or the highest enabled speed and @duplex is %DUPLEX_UNKNOWN
810
         * or the best enabled duplex mode. */
811

812
        if (speed > 0 || duplex >= 0 || port >= 0) {
×
813
                if (autonegotiation == AUTONEG_ENABLE || !memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE)) {
×
814
                        log_debug("ethtool: autonegotiation is enabled, ignoring speed, duplex, or port settings.");
×
815
                        speed = 0;
816
                        duplex = _DUP_INVALID;
817
                        port = _NET_DEV_PORT_INVALID;
818
                } else {
819
                        log_debug("ethtool: setting speed, duplex, or port, disabling autonegotiation.");
×
820
                        autonegotiation = AUTONEG_DISABLE;
821
                }
822
        }
823

824
        r = ethtool_connect(fd);
×
825
        if (r < 0)
×
826
                return r;
827

828
        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
×
829

830
        r = get_glinksettings(*fd, &ifr, &u);
×
831
        if (r < 0) {
×
832
                r = get_gset(*fd, &ifr, &u);
×
833
                if (r < 0)
×
834
                        return log_debug_errno(r, "ethtool: Cannot get device settings for %s: %m", ifname);
×
835
        }
836

837
        if (speed > 0)
×
838
                UPDATE(u->base.speed, DIV_ROUND_UP(speed, 1000000), changed);
×
839

840
        if (duplex >= 0)
×
841
                UPDATE(u->base.duplex, duplex, changed);
×
842

843
        if (port >= 0)
×
844
                UPDATE(u->base.port, port, changed);
×
845

846
        if (autonegotiation >= 0)
×
847
                UPDATE(u->base.autoneg, autonegotiation, changed);
×
848

849
        if (!memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE)) {
×
850
                UPDATE(u->base.autoneg, AUTONEG_ENABLE, changed);
×
851

852
                changed = changed ||
×
853
                        memcmp(&u->link_modes.advertising, advertise, sizeof(uint32_t) * N_ADVERTISE) != 0 ||
×
854
                        !memeqzero((uint8_t*) &u->link_modes.advertising + sizeof(uint32_t) * N_ADVERTISE,
×
855
                                   ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES - sizeof(uint32_t) * N_ADVERTISE);
856
                memcpy(&u->link_modes.advertising, advertise, sizeof(uint32_t) * N_ADVERTISE);
×
857
                memzero((uint8_t*) &u->link_modes.advertising + sizeof(uint32_t) * N_ADVERTISE,
×
858
                        ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES - sizeof(uint32_t) * N_ADVERTISE);
859
        }
860

861
        if (mdi != ETH_TP_MDI_INVALID) {
×
862
                if (u->base.eth_tp_mdix_ctrl == ETH_TP_MDI_INVALID)
×
863
                        log_debug("ethtool: setting MDI not supported for %s, ignoring.", ifname);
×
864
                else
865
                        UPDATE(u->base.eth_tp_mdix_ctrl, mdi, changed);
×
866
        }
867

868
        if (!changed)
×
869
                return 0;
870

871
        if (u->base.cmd == ETHTOOL_GLINKSETTINGS)
×
872
                r = set_slinksettings(*fd, &ifr, u);
×
873
        else
874
                r = set_sset(*fd, &ifr, u);
×
875
        if (r < 0)
×
876
                return log_debug_errno(r, "ethtool: Cannot set device settings for %s: %m", ifname);
×
877

878
        return r;
879
}
880

881
int ethtool_set_channels(int *fd, const char *ifname, const netdev_channels *channels) {
877✔
882
        struct ethtool_channels ecmd = {
877✔
883
                .cmd = ETHTOOL_GCHANNELS,
884
        };
885
        struct ifreq ifr = {
877✔
886
                .ifr_data = (void*) &ecmd,
887
        };
888
        bool need_update = false;
877✔
889
        int r;
877✔
890

891
        assert(fd);
877✔
892
        assert(ifname);
877✔
893
        assert(channels);
877✔
894

895
        if (!channels->rx.set &&
877✔
896
            !channels->tx.set &&
877✔
897
            !channels->other.set &&
877✔
898
            !channels->combined.set)
877✔
899
                return 0;
877✔
900

901
        r = ethtool_connect(fd);
×
902
        if (r < 0)
×
903
                return r;
904

905
        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
×
906

907
        if (ioctl(*fd, SIOCETHTOOL, &ifr) < 0)
×
908
                return -errno;
×
909

910
        if (channels->rx.set)
×
911
                UPDATE_WITH_MAX(ecmd.rx_count, ecmd.max_rx, channels->rx.value, need_update);
×
912

913
        if (channels->tx.set)
×
914
                UPDATE_WITH_MAX(ecmd.tx_count, ecmd.max_tx, channels->tx.value, need_update);
×
915

916
        if (channels->other.set)
×
917
                UPDATE_WITH_MAX(ecmd.other_count, ecmd.max_other, channels->other.value, need_update);
×
918

919
        if (channels->combined.set)
×
920
                UPDATE_WITH_MAX(ecmd.combined_count, ecmd.max_combined, channels->combined.value, need_update);
×
921

922
        if (!need_update)
×
923
                return 0;
924

925
        ecmd.cmd = ETHTOOL_SCHANNELS;
×
926
        return RET_NERRNO(ioctl(*fd, SIOCETHTOOL, &ifr));
×
927
}
928

929
int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int autoneg) {
877✔
930
        struct ethtool_pauseparam ecmd = {
877✔
931
                .cmd = ETHTOOL_GPAUSEPARAM,
932
        };
933
        struct ifreq ifr = {
877✔
934
                .ifr_data = (void*) &ecmd,
935
        };
936
        bool need_update = false;
877✔
937
        int r;
877✔
938

939
        assert(fd);
877✔
940
        assert(ifname);
877✔
941

942
        if (rx < 0 && tx < 0 && autoneg < 0)
877✔
943
                return 0;
877✔
944

945
        r = ethtool_connect(fd);
×
946
        if (r < 0)
×
947
                return r;
948

949
        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
×
950

951
        if (ioctl(*fd, SIOCETHTOOL, &ifr) < 0)
×
952
                return -errno;
×
953

954
        if (rx >= 0)
×
955
                UPDATE(ecmd.rx_pause, (uint32_t) rx, need_update);
×
956

957
        if (tx >= 0)
×
958
                UPDATE(ecmd.tx_pause, (uint32_t) tx, need_update);
×
959

960
        if (autoneg >= 0)
×
961
                UPDATE(ecmd.autoneg, (uint32_t) autoneg, need_update);
×
962

963
        if (!need_update)
×
964
                return 0;
965

966
        ecmd.cmd = ETHTOOL_SPAUSEPARAM;
×
967
        return RET_NERRNO(ioctl(*fd, SIOCETHTOOL, &ifr));
×
968
}
969

970
int ethtool_set_nic_coalesce_settings(int *ethtool_fd, const char *ifname, const netdev_coalesce_param *coalesce) {
877✔
971
        struct ethtool_coalesce ecmd = {
877✔
972
                .cmd = ETHTOOL_GCOALESCE,
973
        };
974
        struct ifreq ifr = {
877✔
975
                .ifr_data = (void*) &ecmd,
976
        };
977
        bool need_update = false;
877✔
978
        int r;
877✔
979

980
        assert(ethtool_fd);
877✔
981
        assert(ifname);
877✔
982
        assert(coalesce);
877✔
983

984
        if (coalesce->use_adaptive_rx_coalesce < 0 &&
877✔
985
            coalesce->use_adaptive_tx_coalesce < 0 &&
877✔
986
            !coalesce->rx_coalesce_usecs.set &&
877✔
987
            !coalesce->rx_max_coalesced_frames.set &&
877✔
988
            !coalesce->rx_coalesce_usecs_irq.set &&
877✔
989
            !coalesce->rx_max_coalesced_frames_irq.set &&
877✔
990
            !coalesce->tx_coalesce_usecs.set &&
877✔
991
            !coalesce->tx_max_coalesced_frames.set &&
877✔
992
            !coalesce->tx_coalesce_usecs_irq.set &&
877✔
993
            !coalesce->tx_max_coalesced_frames_irq.set &&
877✔
994
            !coalesce->stats_block_coalesce_usecs.set &&
877✔
995
            !coalesce->pkt_rate_low.set &&
877✔
996
            !coalesce->rx_coalesce_usecs_low.set &&
877✔
997
            !coalesce->rx_max_coalesced_frames_low.set &&
877✔
998
            !coalesce->tx_coalesce_usecs_low.set &&
877✔
999
            !coalesce->tx_max_coalesced_frames_low.set &&
877✔
1000
            !coalesce->pkt_rate_high.set &&
877✔
1001
            !coalesce->rx_coalesce_usecs_high.set &&
877✔
1002
            !coalesce->rx_max_coalesced_frames_high.set &&
877✔
1003
            !coalesce->tx_coalesce_usecs_high.set &&
877✔
1004
            !coalesce->tx_max_coalesced_frames_high.set &&
877✔
1005
            !coalesce->rate_sample_interval.set)
877✔
1006
                return 0;
877✔
1007

1008
        r = ethtool_connect(ethtool_fd);
×
1009
        if (r < 0)
×
1010
                return r;
1011

1012
        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
×
1013

1014
        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
×
1015
                return -errno;
×
1016

1017
        if (coalesce->use_adaptive_rx_coalesce >= 0)
×
1018
                UPDATE(ecmd.use_adaptive_rx_coalesce, (uint32_t) coalesce->use_adaptive_rx_coalesce, need_update);
×
1019

1020
        if (coalesce->use_adaptive_tx_coalesce >= 0)
×
1021
                UPDATE(ecmd.use_adaptive_tx_coalesce, (uint32_t) coalesce->use_adaptive_tx_coalesce, need_update);
×
1022

1023
        if (coalesce->rx_coalesce_usecs.set)
×
1024
                UPDATE(ecmd.rx_coalesce_usecs, coalesce->rx_coalesce_usecs.value, need_update);
×
1025

1026
        if (coalesce->rx_max_coalesced_frames.set)
×
1027
                UPDATE(ecmd.rx_max_coalesced_frames, coalesce->rx_max_coalesced_frames.value, need_update);
×
1028

1029
        if (coalesce->rx_coalesce_usecs_irq.set)
×
1030
                UPDATE(ecmd.rx_coalesce_usecs_irq, coalesce->rx_coalesce_usecs_irq.value, need_update);
×
1031

1032
        if (coalesce->rx_max_coalesced_frames_irq.set)
×
1033
                UPDATE(ecmd.rx_max_coalesced_frames_irq, coalesce->rx_max_coalesced_frames_irq.value, need_update);
×
1034

1035
        if (coalesce->tx_coalesce_usecs.set)
×
1036
                UPDATE(ecmd.tx_coalesce_usecs, coalesce->tx_coalesce_usecs.value, need_update);
×
1037

1038
        if (coalesce->tx_max_coalesced_frames.set)
×
1039
                UPDATE(ecmd.tx_max_coalesced_frames, coalesce->tx_max_coalesced_frames.value, need_update);
×
1040

1041
        if (coalesce->tx_coalesce_usecs_irq.set)
×
1042
                UPDATE(ecmd.tx_coalesce_usecs_irq, coalesce->tx_coalesce_usecs_irq.value, need_update);
×
1043

1044
        if (coalesce->tx_max_coalesced_frames_irq.set)
×
1045
                UPDATE(ecmd.tx_max_coalesced_frames_irq, coalesce->tx_max_coalesced_frames_irq.value, need_update);
×
1046

1047
        if (coalesce->stats_block_coalesce_usecs.set)
×
1048
                UPDATE(ecmd.stats_block_coalesce_usecs, coalesce->stats_block_coalesce_usecs.value, need_update);
×
1049

1050
        if (coalesce->pkt_rate_low.set)
×
1051
                UPDATE(ecmd.pkt_rate_low, coalesce->pkt_rate_low.value, need_update);
×
1052

1053
        if (coalesce->rx_coalesce_usecs_low.set)
×
1054
                UPDATE(ecmd.rx_coalesce_usecs_low, coalesce->rx_coalesce_usecs_low.value, need_update);
×
1055

1056
        if (coalesce->rx_max_coalesced_frames_low.set)
×
1057
                UPDATE(ecmd.rx_max_coalesced_frames_low, coalesce->rx_max_coalesced_frames_low.value, need_update);
×
1058

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

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

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

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

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

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

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

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

1083
        if (!need_update)
×
1084
                return 0;
1085

1086
        ecmd.cmd = ETHTOOL_SCOALESCE;
×
1087
        return RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr));
×
1088
}
1089

1090
int ethtool_set_eee_settings(
877✔
1091
                int *ethtool_fd,
1092
                const char *ifname,
1093
                int enabled,
1094
                int tx_lpi_enabled,
1095
                usec_t tx_lpi_timer_usec,
1096
                uint32_t advertise) {
1097

1098
        int r;
877✔
1099

1100
        assert(ethtool_fd);
877✔
1101
        assert(ifname);
877✔
1102

1103
        if (enabled < 0 &&
877✔
1104
            tx_lpi_enabled < 0 &&
877✔
1105
            tx_lpi_timer_usec == USEC_INFINITY &&
877✔
1106
            advertise == 0)
877✔
1107
                return 0; /* Nothing requested. */
877✔
1108

1109
        r = ethtool_connect(ethtool_fd);
×
1110
        if (r < 0)
×
1111
                return r;
1112

1113
        struct ethtool_eee ecmd = {
×
1114
                .cmd = ETHTOOL_GEEE,
1115
        };
1116
        struct ifreq ifr = {
×
1117
                .ifr_data = (void*) &ecmd,
1118
        };
1119

1120
        strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
×
1121

1122
        if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
×
1123
                return -errno;
×
1124

1125
        if (ecmd.supported == 0)
×
1126
                return 0; /* Unsupported. */
1127

1128
        bool need_update = false;
×
1129

1130
        if (enabled >= 0)
×
1131
                UPDATE(ecmd.eee_enabled, (uint32_t) enabled, need_update);
×
1132
        if (tx_lpi_enabled >= 0)
×
1133
                UPDATE(ecmd.tx_lpi_enabled, (uint32_t) tx_lpi_enabled, need_update);
×
1134
        if (tx_lpi_timer_usec != USEC_INFINITY)
×
1135
                UPDATE(ecmd.tx_lpi_timer, (uint32_t) MIN(DIV_ROUND_UP(tx_lpi_timer_usec, USEC_PER_MSEC), (usec_t) UINT32_MAX), need_update);
×
1136
        if (advertise != 0)
×
1137
                UPDATE(ecmd.advertised, advertise & ecmd.supported, need_update);
×
1138

1139
        if (!need_update)
×
1140
                return 0; /* Nothing changed. */
1141

1142
        ecmd.cmd = ETHTOOL_SEEE;
×
1143
        return RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr));
×
1144
}
1145

1146
int config_parse_advertise(
×
1147
                const char *unit,
1148
                const char *filename,
1149
                unsigned line,
1150
                const char *section,
1151
                unsigned section_line,
1152
                const char *lvalue,
1153
                int ltype,
1154
                const char *rvalue,
1155
                void *data,
1156
                void *userdata) {
1157

1158
        uint32_t *advertise = ASSERT_PTR(data);
×
1159
        int r;
×
1160

1161
        assert(filename);
×
1162
        assert(section);
×
1163
        assert(lvalue);
×
1164
        assert(rvalue);
×
1165

1166
        if (isempty(rvalue)) {
×
1167
                /* Empty string resets the value. */
1168
                memzero(advertise, sizeof(uint32_t) * N_ADVERTISE);
×
1169
                return 0;
×
1170
        }
1171

1172
        for (const char *p = rvalue;;) {
×
1173
                _cleanup_free_ char *w = NULL;
×
1174
                enum ethtool_link_mode_bit_indices mode;
×
1175

1176
                r = extract_first_word(&p, &w, NULL, 0);
×
1177
                if (r == -ENOMEM)
×
1178
                        return log_oom();
×
1179
                if (r < 0) {
×
1180
                        log_syntax(unit, LOG_WARNING, filename, line, r,
×
1181
                                   "Failed to split advertise modes '%s', ignoring assignment: %m", rvalue);
1182
                        return 0;
×
1183
                }
1184
                if (r == 0)
×
1185
                        return 0;
1186

1187
                mode = ethtool_link_mode_bit_from_string(w);
×
1188
                /* We reuse the kernel provided enum which does not contain negative value. So, the cast
1189
                 * below is mandatory. Otherwise, the check below always passes and access an invalid address. */
1190
                if ((int) mode < 0) {
×
1191
                        log_syntax(unit, LOG_WARNING, filename, line, mode,
×
1192
                                   "Failed to parse advertise mode, ignoring: %s", w);
1193
                        continue;
×
1194
                }
1195

1196
                advertise[mode / 32] |= 1UL << (mode % 32);
×
1197
        }
1198
}
1199

1200
int config_parse_mdi(
×
1201
                const char *unit,
1202
                const char *filename,
1203
                unsigned line,
1204
                const char *section,
1205
                unsigned section_line,
1206
                const char *lvalue,
1207
                int ltype,
1208
                const char *rvalue,
1209
                void *data,
1210
                void *userdata) {
1211

1212
        uint8_t *mdi = ASSERT_PTR(data);
×
1213

1214
        assert(filename);
×
1215
        assert(rvalue);
×
1216

1217
        if (isempty(rvalue)) {
×
1218
                *mdi = ETH_TP_MDI_INVALID;
×
1219
                return 0;
×
1220
        }
1221

1222
        if (STR_IN_SET(rvalue, "mdi", "straight")) {
×
1223
                *mdi = ETH_TP_MDI;
×
1224
                return 0;
×
1225
        }
1226

1227
        if (STR_IN_SET(rvalue, "mdi-x", "mdix", "crossover")) {
×
1228
                *mdi = ETH_TP_MDI_X;
×
1229
                return 0;
×
1230
        }
1231

1232
        if (streq(rvalue, "auto")) {
×
1233
                *mdi = ETH_TP_MDI_AUTO;
×
1234
                return 0;
×
1235
        }
1236

1237
        log_syntax(unit, LOG_WARNING, filename, line, 0,
×
1238
                   "Failed to parse %s= setting, ignoring assignment: %s", lvalue, rvalue);
1239
        return 0;
1240
}
1241

1242
int config_parse_ring_buffer_or_channel(
×
1243
                const char *unit,
1244
                const char *filename,
1245
                unsigned line,
1246
                const char *section,
1247
                unsigned section_line,
1248
                const char *lvalue,
1249
                int ltype,
1250
                const char *rvalue,
1251
                void *data,
1252
                void *userdata) {
1253

1254
        u32_opt *dst = ASSERT_PTR(data);
×
1255
        uint32_t k;
×
1256
        int r;
×
1257

1258
        assert(filename);
×
1259
        assert(section);
×
1260
        assert(lvalue);
×
1261
        assert(rvalue);
×
1262

1263
        if (isempty(rvalue)) {
×
1264
                dst->value = 0;
×
1265
                dst->set = false;
×
1266
                return 0;
×
1267
        }
1268

1269
        if (streq(rvalue, "max")) {
×
1270
                dst->value = 0;
×
1271
                dst->set = true;
×
1272
                return 0;
×
1273
        }
1274

1275
        r = safe_atou32(rvalue, &k);
×
1276
        if (r < 0) {
×
1277
                log_syntax(unit, LOG_WARNING, filename, line, r,
×
1278
                           "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
1279
                return 0;
×
1280
        }
1281
        if (k < 1) {
×
1282
                log_syntax(unit, LOG_WARNING, filename, line, 0,
×
1283
                           "Invalid %s= value, ignoring: %s", lvalue, rvalue);
1284
                return 0;
×
1285
        }
1286

1287
        dst->value = k;
×
1288
        dst->set = true;
×
1289
        return 0;
×
1290
}
1291

1292
int config_parse_wol(
×
1293
                const char *unit,
1294
                const char *filename,
1295
                unsigned line,
1296
                const char *section,
1297
                unsigned section_line,
1298
                const char *lvalue,
1299
                int ltype,
1300
                const char *rvalue,
1301
                void *data,
1302
                void *userdata) {
1303

1304
        uint32_t new_opts = 0, *opts = data;
×
1305
        int r;
×
1306

1307
        assert(filename);
×
1308
        assert(section);
×
1309
        assert(lvalue);
×
1310
        assert(rvalue);
×
1311
        assert(data);
×
1312

1313
        if (isempty(rvalue)) {
×
1314
                *opts = UINT32_MAX; /* Do not update WOL option. */
×
1315
                return 0;
×
1316
        }
1317

1318
        if (streq(rvalue, "off")) {
×
1319
                *opts = 0; /* Disable WOL. */
×
1320
                return 0;
×
1321
        }
1322

1323
        for (const char *p = rvalue;;) {
×
1324
                _cleanup_free_ char *w = NULL;
×
1325
                bool found = false;
×
1326

1327
                r = extract_first_word(&p, &w, NULL, 0);
×
1328
                if (r == -ENOMEM)
×
1329
                        return log_oom();
×
1330
                if (r < 0) {
×
1331
                        log_syntax(unit, LOG_WARNING, filename, line, r,
×
1332
                                   "Failed to split wake-on-lan modes '%s', ignoring assignment: %m", rvalue);
1333
                        return 0;
×
1334
                }
1335
                if (r == 0)
×
1336
                        break;
1337

1338
                FOREACH_ELEMENT(option, wol_option_map)
×
1339
                        if (streq(w, option->name)) {
×
1340
                                new_opts |= option->opt;
×
1341
                                found = true;
×
1342
                                break;
×
1343
                        }
1344

1345
                if (!found)
×
1346
                        log_syntax(unit, LOG_WARNING, filename, line, 0,
×
1347
                                   "Unknown wake-on-lan mode '%s', ignoring.", w);
1348
        }
1349

1350
        if (*opts == UINT32_MAX)
×
1351
                *opts = new_opts;
×
1352
        else
1353
                *opts |= new_opts;
×
1354

1355
        return 0;
1356
}
1357

1358
int config_parse_coalesce_u32(
×
1359
                const char *unit,
1360
                const char *filename,
1361
                unsigned line,
1362
                const char *section,
1363
                unsigned section_line,
1364
                const char *lvalue,
1365
                int ltype,
1366
                const char *rvalue,
1367
                void *data,
1368
                void *userdata) {
1369
        u32_opt *dst = data;
×
1370
        uint32_t k;
×
1371
        int r;
×
1372

1373
        if (isempty(rvalue)) {
×
1374
                dst->value = 0;
×
1375
                dst->set = false;
×
1376
                return 0;
×
1377
        }
1378

1379
        r = safe_atou32(rvalue, &k);
×
1380
        if (r < 0) {
×
1381
                log_syntax(unit, LOG_WARNING, filename, line, r,
×
1382
                           "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
1383
                return 0;
×
1384
        }
1385

1386
        dst->value = k;
×
1387
        dst->set = true;
×
1388
        return 0;
×
1389
}
1390

1391
int config_parse_coalesce_sec(
×
1392
                const char *unit,
1393
                const char *filename,
1394
                unsigned line,
1395
                const char *section,
1396
                unsigned section_line,
1397
                const char *lvalue,
1398
                int ltype,
1399
                const char *rvalue,
1400
                void *data,
1401
                void *userdata) {
1402
        u32_opt *dst = data;
×
1403
        usec_t usec;
×
1404
        int r;
×
1405

1406
        if (isempty(rvalue)) {
×
1407
                dst->value = 0;
×
1408
                dst->set = false;
×
1409
                return 0;
×
1410
        }
1411

1412
        r = parse_sec(rvalue, &usec);
×
1413
        if (r < 0) {
×
1414
                log_syntax(unit, LOG_WARNING, filename, line, r,
×
1415
                           "Failed to parse coalesce setting value, ignoring: %s", rvalue);
1416
                return 0;
×
1417
        }
1418

1419
        if (usec > UINT32_MAX) {
×
1420
                log_syntax(unit, LOG_WARNING, filename, line, 0,
×
1421
                           "Too large %s= value, ignoring: %s", lvalue, rvalue);
1422
                return 0;
×
1423
        }
1424

1425
        if (STR_IN_SET(lvalue, "StatisticsBlockCoalesceSec", "CoalescePacketRateSampleIntervalSec") && usec < 1) {
×
1426
                log_syntax(unit, LOG_WARNING, filename, line, 0,
×
1427
                           "Invalid %s= value, ignoring: %s", lvalue, rvalue);
1428
                return 0;
×
1429
        }
1430

1431
        dst->value = (uint32_t) usec;
×
1432
        dst->set = true;
×
1433

1434
        return 0;
×
1435
}
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