1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include <linux/ethtool.h>
6 #include <linux/netdevice.h>
7 #include <linux/sockios.h>
9 #include "conf-parser.h"
10 #include "ethtool-util.h"
11 #include "extract-word.h"
14 #include "memory-util.h"
15 #include "socket-util.h"
16 #include "string-table.h"
19 static const char* const duplex_table
[_DUP_MAX
] = {
24 DEFINE_STRING_TABLE_LOOKUP(duplex
, Duplex
);
25 DEFINE_CONFIG_PARSE_ENUM(config_parse_duplex
, duplex
, Duplex
, "Failed to parse duplex setting");
27 static const char* const wol_table
[_WOL_MAX
] = {
29 [WOL_UCAST
] = "unicast",
30 [WOL_MCAST
] = "multicast",
31 [WOL_BCAST
] = "broadcast",
33 [WOL_MAGIC
] = "magic",
34 [WOL_MAGICSECURE
] = "secureon",
38 DEFINE_STRING_TABLE_LOOKUP(wol
, WakeOnLan
);
39 DEFINE_CONFIG_PARSE_ENUM(config_parse_wol
, wol
, WakeOnLan
, "Failed to parse WakeOnLan setting");
41 static const char* const port_table
[] = {
42 [NET_DEV_PORT_TP
] = "tp",
43 [NET_DEV_PORT_AUI
] = "aui",
44 [NET_DEV_PORT_MII
] = "mii",
45 [NET_DEV_PORT_FIBRE
] = "fibre",
46 [NET_DEV_PORT_BNC
] = "bnc",
49 DEFINE_STRING_TABLE_LOOKUP(port
, NetDevPort
);
50 DEFINE_CONFIG_PARSE_ENUM(config_parse_port
, port
, NetDevPort
, "Failed to parse Port setting");
52 static const char* const netdev_feature_table
[_NET_DEV_FEAT_MAX
] = {
53 [NET_DEV_FEAT_RX
] = "rx-checksum",
54 [NET_DEV_FEAT_TX
] = "tx-checksum-", /* The suffix "-" means any feature beginning with "tx-checksum-" */
55 [NET_DEV_FEAT_GSO
] = "tx-generic-segmentation",
56 [NET_DEV_FEAT_GRO
] = "rx-gro",
57 [NET_DEV_FEAT_LRO
] = "rx-lro",
58 [NET_DEV_FEAT_TSO
] = "tx-tcp-segmentation",
59 [NET_DEV_FEAT_TSO6
] = "tx-tcp6-segmentation",
62 static const char* const ethtool_link_mode_bit_table
[] = {
63 [ETHTOOL_LINK_MODE_10baseT_Half_BIT
] = "10baset-half",
64 [ETHTOOL_LINK_MODE_10baseT_Full_BIT
] = "10baset-full",
65 [ETHTOOL_LINK_MODE_100baseT_Half_BIT
] = "100baset-half",
66 [ETHTOOL_LINK_MODE_100baseT_Full_BIT
] = "100baset-full",
67 [ETHTOOL_LINK_MODE_1000baseT_Half_BIT
] = "1000baset-half",
68 [ETHTOOL_LINK_MODE_1000baseT_Full_BIT
] = "1000baset-full",
69 [ETHTOOL_LINK_MODE_Autoneg_BIT
] = "autonegotiation",
70 [ETHTOOL_LINK_MODE_TP_BIT
] = "tp",
71 [ETHTOOL_LINK_MODE_AUI_BIT
] = "aui",
72 [ETHTOOL_LINK_MODE_MII_BIT
] = "mii",
73 [ETHTOOL_LINK_MODE_FIBRE_BIT
] = "fibre",
74 [ETHTOOL_LINK_MODE_BNC_BIT
] = "bnc",
75 [ETHTOOL_LINK_MODE_10000baseT_Full_BIT
] = "10000baset-full",
76 [ETHTOOL_LINK_MODE_Pause_BIT
] = "pause",
77 [ETHTOOL_LINK_MODE_Asym_Pause_BIT
] = "asym-pause",
78 [ETHTOOL_LINK_MODE_2500baseX_Full_BIT
] = "2500basex-full",
79 [ETHTOOL_LINK_MODE_Backplane_BIT
] = "backplane",
80 [ETHTOOL_LINK_MODE_1000baseKX_Full_BIT
] = "1000basekx-full",
81 [ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT
] = "10000basekx4-full",
82 [ETHTOOL_LINK_MODE_10000baseKR_Full_BIT
] = "10000basekr-full",
83 [ETHTOOL_LINK_MODE_10000baseR_FEC_BIT
] = "10000baser-fec",
84 [ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT
] = "20000basemld2-full",
85 [ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT
] = "20000basekr2-full",
86 [ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT
] = "40000basekr4-full",
87 [ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT
] = "40000basecr4-full",
88 [ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT
] = "40000basesr4-full",
89 [ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT
] = "40000baselr4-full",
90 [ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT
] = "56000basekr4-full",
91 [ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT
] = "56000basecr4-full",
92 [ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT
] = "56000basesr4-full",
93 [ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT
] = "56000baselr4-full",
94 [ETHTOOL_LINK_MODE_25000baseCR_Full_BIT
] = "25000basecr-full",
95 [ETHTOOL_LINK_MODE_25000baseKR_Full_BIT
] = "25000basekr-full",
96 [ETHTOOL_LINK_MODE_25000baseSR_Full_BIT
] = "25000basesr-full",
97 [ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT
] = "50000basecr2-full",
98 [ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT
] = "50000basekr2-full",
99 [ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT
] = "100000basekr4-full",
100 [ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT
] = "100000basesr4-full",
101 [ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT
] = "100000basecr4-full",
102 [ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT
] = "100000baselr4-er4-full",
103 [ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT
] = "50000basesr2-full",
104 [ETHTOOL_LINK_MODE_1000baseX_Full_BIT
] = "1000basex-full",
105 [ETHTOOL_LINK_MODE_10000baseCR_Full_BIT
] = "10000basecr-full",
106 [ETHTOOL_LINK_MODE_10000baseSR_Full_BIT
] = "10000basesr-full",
107 [ETHTOOL_LINK_MODE_10000baseLR_Full_BIT
] = "10000baselr-full",
108 [ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT
] = "10000baselrm-full",
109 [ETHTOOL_LINK_MODE_10000baseER_Full_BIT
] = "10000baseer-full",
110 [ETHTOOL_LINK_MODE_2500baseT_Full_BIT
] = "2500baset-full",
111 [ETHTOOL_LINK_MODE_5000baseT_Full_BIT
] = "5000baset-full",
112 [ETHTOOL_LINK_MODE_FEC_NONE_BIT
] = "fec-none",
113 [ETHTOOL_LINK_MODE_FEC_RS_BIT
] = "fec-rs",
114 [ETHTOOL_LINK_MODE_FEC_BASER_BIT
] = "fec-baser",
115 [ETHTOOL_LINK_MODE_50000baseKR_Full_BIT
] = "50000basekr-full",
116 [ETHTOOL_LINK_MODE_50000baseSR_Full_BIT
] = "50000basesr-full",
117 [ETHTOOL_LINK_MODE_50000baseCR_Full_BIT
] = "50000basecr-full",
118 [ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT
] = "50000baselr-er-fr-full",
119 [ETHTOOL_LINK_MODE_50000baseDR_Full_BIT
] = "50000basedr-full",
120 [ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT
] = "100000basekr2-full",
121 [ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT
] = "100000basesr2-full",
122 [ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT
] = "100000basecr2-full",
123 [ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT
] = "100000baselr2-er2-fr2-full",
124 [ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT
] = "100000basedr2-full",
125 [ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT
] = "200000basekr4-full",
126 [ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT
] = "200000basesr4-full",
127 [ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT
] = "200000baselr4-er4-fr4-full",
128 [ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT
] = "200000basedr4-full",
129 [ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT
] = "200000basecr4-full",
130 [ETHTOOL_LINK_MODE_100baseT1_Full_BIT
] = "100baset1-full",
131 [ETHTOOL_LINK_MODE_1000baseT1_Full_BIT
] = "1000baset1-full",
132 [ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT
] = "400000basekr8-full",
133 [ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT
] = "400000basesr8-full",
134 [ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT
] = "400000baselr8-er8-fr8-full",
135 [ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT
] = "400000basedr8-full",
136 [ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT
] = "400000basecr8-full",
137 [ETHTOOL_LINK_MODE_FEC_LLRS_BIT
] = "fec-llrs",
138 [ETHTOOL_LINK_MODE_100000baseKR_Full_BIT
] = "100000basekr-full",
139 [ETHTOOL_LINK_MODE_100000baseSR_Full_BIT
] = "100000basesr-full",
140 [ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT
] = "100000baselr-er-fr-full",
141 [ETHTOOL_LINK_MODE_100000baseCR_Full_BIT
] = "100000basecr-full",
142 [ETHTOOL_LINK_MODE_100000baseDR_Full_BIT
] = "100000basedr-full",
143 [ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT
] = "200000basekr2-full",
144 [ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT
] = "200000basesr2-full",
145 [ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT
] = "200000baselr2-er2-fr2-full",
146 [ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT
] = "200000basedr2-full",
147 [ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT
] = "200000basecr2-full",
148 [ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT
] = "400000basekr4-full",
149 [ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT
] = "400000basesr4-full",
150 [ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT
] = "400000baselr4-er4-fr4-full",
151 [ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT
] = "400000basedr4-full",
152 [ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT
] = "400000basecr4-full",
153 [ETHTOOL_LINK_MODE_100baseFX_Half_BIT
] = "100basefx-half",
154 [ETHTOOL_LINK_MODE_100baseFX_Full_BIT
] = "100basefx-full",
156 /* Make sure the array is large enough to fit all bits */
157 assert_cc((ELEMENTSOF(ethtool_link_mode_bit_table
)-1) / 32 < N_ADVERTISE
);
159 DEFINE_STRING_TABLE_LOOKUP(ethtool_link_mode_bit
, enum ethtool_link_mode_bit_indices
);
161 static int ethtool_connect_or_warn(int *ret
, bool warn
) {
164 assert_return(ret
, -EINVAL
);
166 fd
= socket_ioctl_fd();
168 return log_full_errno(warn
? LOG_WARNING
: LOG_DEBUG
, fd
,
169 "ethtool: could not create control socket: %m");
176 int ethtool_get_driver(int *ethtool_fd
, const char *ifname
, char **ret
) {
177 struct ethtool_drvinfo ecmd
= {
178 .cmd
= ETHTOOL_GDRVINFO
,
181 .ifr_data
= (void*) &ecmd
,
190 if (*ethtool_fd
< 0) {
191 r
= ethtool_connect_or_warn(ethtool_fd
, true);
196 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
198 r
= ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
);
202 if (isempty(ecmd
.driver
))
205 d
= strdup(ecmd
.driver
);
213 int ethtool_get_link_info(
216 int *ret_autonegotiation
,
219 NetDevPort
*ret_port
) {
221 struct ethtool_cmd ecmd
= {
225 .ifr_data
= (void*) &ecmd
,
232 if (*ethtool_fd
< 0) {
233 r
= ethtool_connect_or_warn(ethtool_fd
, false);
238 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
240 r
= ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
);
244 if (ret_autonegotiation
)
245 *ret_autonegotiation
= ecmd
.autoneg
;
250 speed
= ethtool_cmd_speed(&ecmd
);
251 *ret_speed
= speed
== (uint32_t) SPEED_UNKNOWN
?
252 UINT64_MAX
: (uint64_t) speed
* 1000 * 1000;
256 *ret_duplex
= ecmd
.duplex
;
259 *ret_port
= ecmd
.port
;
264 int ethtool_get_permanent_macaddr(int *ethtool_fd
, const char *ifname
, struct ether_addr
*ret
) {
265 _cleanup_close_
int fd
= -1;
267 struct ethtool_perm_addr addr
;
268 uint8_t space
[MAX_ADDR_LEN
];
270 .addr
.cmd
= ETHTOOL_GPERMADDR
,
271 .addr
.size
= MAX_ADDR_LEN
,
274 .ifr_data
= (caddr_t
) &epaddr
,
284 if (*ethtool_fd
< 0) {
285 r
= ethtool_connect_or_warn(ethtool_fd
, false);
290 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
292 r
= ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
);
296 if (epaddr
.addr
.size
!= 6)
299 #pragma GCC diagnostic push
300 #if HAVE_ZERO_LENGTH_BOUNDS
301 # pragma GCC diagnostic ignored "-Wzero-length-bounds"
303 for (size_t i
= 0; i
< epaddr
.addr
.size
; i
++)
304 ret
->ether_addr_octet
[i
] = epaddr
.addr
.data
[i
];
305 #pragma GCC diagnostic pop
310 int ethtool_set_speed(int *ethtool_fd
, const char *ifname
, unsigned speed
, Duplex duplex
) {
311 struct ethtool_cmd ecmd
= {
315 .ifr_data
= (void*) &ecmd
,
317 bool need_update
= false;
323 if (speed
== 0 && duplex
== _DUP_INVALID
)
326 if (*ethtool_fd
< 0) {
327 r
= ethtool_connect_or_warn(ethtool_fd
, true);
332 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
334 r
= ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
);
338 if (ethtool_cmd_speed(&ecmd
) != speed
) {
339 ethtool_cmd_speed_set(&ecmd
, speed
);
345 if (ecmd
.duplex
!= DUPLEX_HALF
) {
346 ecmd
.duplex
= DUPLEX_HALF
;
351 if (ecmd
.duplex
!= DUPLEX_FULL
) {
352 ecmd
.duplex
= DUPLEX_FULL
;
361 ecmd
.cmd
= ETHTOOL_SSET
;
363 r
= ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
);
371 int ethtool_set_wol(int *ethtool_fd
, const char *ifname
, WakeOnLan wol
) {
372 struct ethtool_wolinfo ecmd
= {
376 .ifr_data
= (void*) &ecmd
,
378 bool need_update
= false;
384 if (wol
== _WOL_INVALID
)
387 if (*ethtool_fd
< 0) {
388 r
= ethtool_connect_or_warn(ethtool_fd
, true);
393 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
395 r
= ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
);
401 if (ecmd
.wolopts
!= WAKE_PHY
) {
402 ecmd
.wolopts
= WAKE_PHY
;
407 if (ecmd
.wolopts
!= WAKE_UCAST
) {
408 ecmd
.wolopts
= WAKE_UCAST
;
413 if (ecmd
.wolopts
!= WAKE_MCAST
) {
414 ecmd
.wolopts
= WAKE_MCAST
;
419 if (ecmd
.wolopts
!= WAKE_BCAST
) {
420 ecmd
.wolopts
= WAKE_BCAST
;
425 if (ecmd
.wolopts
!= WAKE_ARP
) {
426 ecmd
.wolopts
= WAKE_ARP
;
431 if (ecmd
.wolopts
!= WAKE_MAGIC
) {
432 ecmd
.wolopts
= WAKE_MAGIC
;
436 case WOL_MAGICSECURE
:
437 if (ecmd
.wolopts
!= WAKE_MAGICSECURE
) {
438 ecmd
.wolopts
= WAKE_MAGICSECURE
;
443 if (ecmd
.wolopts
!= 0) {
453 ecmd
.cmd
= ETHTOOL_SWOL
;
455 r
= ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
);
463 int ethtool_set_nic_buffer_size(int *ethtool_fd
, const char *ifname
, const netdev_ring_param
*ring
) {
464 struct ethtool_ringparam ecmd
= {
465 .cmd
= ETHTOOL_GRINGPARAM
,
468 .ifr_data
= (void*) &ecmd
,
470 bool need_update
= false;
477 if (*ethtool_fd
< 0) {
478 r
= ethtool_connect_or_warn(ethtool_fd
, true);
483 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
485 r
= ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
);
489 if (ring
->rx_pending_set
&& ecmd
.rx_pending
!= ring
->rx_pending
) {
490 ecmd
.rx_pending
= ring
->rx_pending
;
494 if (ring
->rx_mini_pending_set
&& ecmd
.rx_mini_pending
!= ring
->rx_mini_pending
) {
495 ecmd
.rx_mini_pending
= ring
->rx_mini_pending
;
499 if (ring
->rx_jumbo_pending_set
&& ecmd
.rx_jumbo_pending
!= ring
->rx_jumbo_pending
) {
500 ecmd
.rx_jumbo_pending
= ring
->rx_jumbo_pending
;
504 if (ring
->tx_pending_set
&& ecmd
.tx_pending
!= ring
->tx_pending
) {
505 ecmd
.tx_pending
= ring
->tx_pending
;
510 ecmd
.cmd
= ETHTOOL_SRINGPARAM
;
512 r
= ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
);
520 static int get_stringset(int ethtool_fd
, struct ifreq
*ifr
, int stringset_id
, struct ethtool_gstrings
**ret
) {
521 _cleanup_free_
struct ethtool_gstrings
*strings
= NULL
;
523 struct ethtool_sset_info info
;
527 .cmd
= ETHTOOL_GSSET_INFO
,
528 .sset_mask
= UINT64_C(1) << stringset_id
,
534 assert(ethtool_fd
>= 0);
538 ifr
->ifr_data
= (void *) &buffer
.info
;
540 r
= ioctl(ethtool_fd
, SIOCETHTOOL
, ifr
);
544 if (!buffer
.info
.sset_mask
)
547 #pragma GCC diagnostic push
548 #if HAVE_ZERO_LENGTH_BOUNDS
549 # pragma GCC diagnostic ignored "-Wzero-length-bounds"
551 len
= buffer
.info
.data
[0];
552 #pragma GCC diagnostic pop
554 strings
= malloc0(sizeof(struct ethtool_gstrings
) + len
* ETH_GSTRING_LEN
);
558 strings
->cmd
= ETHTOOL_GSTRINGS
;
559 strings
->string_set
= stringset_id
;
562 ifr
->ifr_data
= (void *) strings
;
564 r
= ioctl(ethtool_fd
, SIOCETHTOOL
, ifr
);
568 *ret
= TAKE_PTR(strings
);
573 static int set_features_bit(
574 const struct ethtool_gstrings
*strings
,
577 struct ethtool_sfeatures
*sfeatures
) {
584 for (size_t i
= 0; i
< strings
->len
; i
++)
585 if (streq((char *) &strings
->data
[i
* ETH_GSTRING_LEN
], feature
) ||
586 (endswith(feature
, "-") && startswith((char *) &strings
->data
[i
* ETH_GSTRING_LEN
], feature
))) {
592 sfeatures
->features
[block
].valid
|= 1 << bit
;
593 SET_FLAG(sfeatures
->features
[block
].requested
, 1 << bit
, flag
);
597 return found
? 0 : -ENODATA
;
600 int ethtool_set_features(int *ethtool_fd
, const char *ifname
, const int *features
) {
601 _cleanup_free_
struct ethtool_gstrings
*strings
= NULL
;
602 struct ethtool_sfeatures
*sfeatures
;
603 struct ifreq ifr
= {};
610 if (*ethtool_fd
< 0) {
611 r
= ethtool_connect_or_warn(ethtool_fd
, true);
616 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
618 r
= get_stringset(*ethtool_fd
, &ifr
, ETH_SS_FEATURES
, &strings
);
620 return log_warning_errno(r
, "ethtool: could not get ethtool features for %s", ifname
);
622 sfeatures
= alloca0(sizeof(struct ethtool_sfeatures
) + DIV_ROUND_UP(strings
->len
, 32U) * sizeof(sfeatures
->features
[0]));
623 sfeatures
->cmd
= ETHTOOL_SFEATURES
;
624 sfeatures
->size
= DIV_ROUND_UP(strings
->len
, 32U);
626 for (i
= 0; i
< _NET_DEV_FEAT_MAX
; i
++)
627 if (features
[i
] != -1) {
628 r
= set_features_bit(strings
, netdev_feature_table
[i
], features
[i
], sfeatures
);
630 log_warning_errno(r
, "ethtool: could not find feature, ignoring: %s", netdev_feature_table
[i
]);
635 ifr
.ifr_data
= (void *) sfeatures
;
637 r
= ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
);
639 return log_warning_errno(r
, "ethtool: could not set ethtool features for %s", ifname
);
644 static int get_glinksettings(int fd
, struct ifreq
*ifr
, struct ethtool_link_usettings
**ret
) {
646 struct ethtool_link_settings req
;
647 __u32 link_mode_data
[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32
];
649 .req
.cmd
= ETHTOOL_GLINKSETTINGS
,
651 struct ethtool_link_usettings
*u
;
659 /* The interaction user/kernel via the new API requires a small ETHTOOL_GLINKSETTINGS
660 handshake first to agree on the length of the link mode bitmaps. If kernel doesn't
661 agree with user, it returns the bitmap length it is expecting from user as a negative
662 length (and cmd field is 0). When kernel and user agree, kernel returns valid info in
663 all fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS). Based on
664 https://github.com/torvalds/linux/commit/3f1ac7a700d039c61d8d8b99f28d605d489a60cf
667 ifr
->ifr_data
= (void *) &ecmd
;
669 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
673 if (ecmd
.req
.link_mode_masks_nwords
>= 0 || ecmd
.req
.cmd
!= ETHTOOL_GLINKSETTINGS
)
676 ecmd
.req
.link_mode_masks_nwords
= -ecmd
.req
.link_mode_masks_nwords
;
678 ifr
->ifr_data
= (void *) &ecmd
;
680 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
684 if (ecmd
.req
.link_mode_masks_nwords
<= 0 || ecmd
.req
.cmd
!= ETHTOOL_GLINKSETTINGS
)
687 u
= new(struct ethtool_link_usettings
, 1);
691 *u
= (struct ethtool_link_usettings
) {
696 memcpy(u
->link_modes
.supported
, &ecmd
.link_mode_data
[offset
], 4 * ecmd
.req
.link_mode_masks_nwords
);
698 offset
+= ecmd
.req
.link_mode_masks_nwords
;
699 memcpy(u
->link_modes
.advertising
, &ecmd
.link_mode_data
[offset
], 4 * ecmd
.req
.link_mode_masks_nwords
);
701 offset
+= ecmd
.req
.link_mode_masks_nwords
;
702 memcpy(u
->link_modes
.lp_advertising
, &ecmd
.link_mode_data
[offset
], 4 * ecmd
.req
.link_mode_masks_nwords
);
709 static int get_gset(int fd
, struct ifreq
*ifr
, struct ethtool_link_usettings
**ret
) {
710 struct ethtool_link_usettings
*e
;
711 struct ethtool_cmd ecmd
= {
720 ifr
->ifr_data
= (void *) &ecmd
;
722 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
726 e
= new(struct ethtool_link_usettings
, 1);
730 *e
= (struct ethtool_link_usettings
) {
731 .base
.cmd
= ETHTOOL_GSET
,
732 .base
.link_mode_masks_nwords
= 1,
733 .base
.speed
= ethtool_cmd_speed(&ecmd
),
734 .base
.duplex
= ecmd
.duplex
,
735 .base
.port
= ecmd
.port
,
736 .base
.phy_address
= ecmd
.phy_address
,
737 .base
.autoneg
= ecmd
.autoneg
,
738 .base
.mdio_support
= ecmd
.mdio_support
,
740 .link_modes
.supported
[0] = ecmd
.supported
,
741 .link_modes
.advertising
[0] = ecmd
.advertising
,
742 .link_modes
.lp_advertising
[0] = ecmd
.lp_advertising
,
750 static int set_slinksettings(int fd
, struct ifreq
*ifr
, const struct ethtool_link_usettings
*u
) {
752 struct ethtool_link_settings req
;
753 __u32 link_mode_data
[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32
];
762 if (u
->base
.cmd
!= ETHTOOL_GLINKSETTINGS
|| u
->base
.link_mode_masks_nwords
<= 0)
766 ecmd
.req
.cmd
= ETHTOOL_SLINKSETTINGS
;
768 memcpy(&ecmd
.link_mode_data
[offset
], u
->link_modes
.supported
, 4 * ecmd
.req
.link_mode_masks_nwords
);
770 offset
+= ecmd
.req
.link_mode_masks_nwords
;
771 memcpy(&ecmd
.link_mode_data
[offset
], u
->link_modes
.advertising
, 4 * ecmd
.req
.link_mode_masks_nwords
);
773 offset
+= ecmd
.req
.link_mode_masks_nwords
;
774 memcpy(&ecmd
.link_mode_data
[offset
], u
->link_modes
.lp_advertising
, 4 * ecmd
.req
.link_mode_masks_nwords
);
776 ifr
->ifr_data
= (void *) &ecmd
;
778 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
785 static int set_sset(int fd
, struct ifreq
*ifr
, const struct ethtool_link_usettings
*u
) {
786 struct ethtool_cmd ecmd
= {
795 if (u
->base
.cmd
!= ETHTOOL_GSET
|| u
->base
.link_mode_masks_nwords
<= 0)
798 ecmd
.supported
= u
->link_modes
.supported
[0];
799 ecmd
.advertising
= u
->link_modes
.advertising
[0];
800 ecmd
.lp_advertising
= u
->link_modes
.lp_advertising
[0];
802 ethtool_cmd_speed_set(&ecmd
, u
->base
.speed
);
804 ecmd
.duplex
= u
->base
.duplex
;
805 ecmd
.port
= u
->base
.port
;
806 ecmd
.phy_address
= u
->base
.phy_address
;
807 ecmd
.autoneg
= u
->base
.autoneg
;
808 ecmd
.mdio_support
= u
->base
.mdio_support
;
809 ecmd
.eth_tp_mdix
= u
->base
.eth_tp_mdix
;
810 ecmd
.eth_tp_mdix_ctrl
= u
->base
.eth_tp_mdix_ctrl
;
812 ifr
->ifr_data
= (void *) &ecmd
;
814 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
821 /* If autonegotiation is disabled, the speed and duplex represent the fixed link
822 * mode and are writable if the driver supports multiple link modes. If it is
823 * enabled then they are read-only. If the link is up they represent the negotiated
824 * link mode; if the link is down, the speed is 0, %SPEED_UNKNOWN or the highest
825 * enabled speed and @duplex is %DUPLEX_UNKNOWN or the best enabled duplex mode.
827 int ethtool_set_glinksettings(
831 const uint32_t advertise
[static N_ADVERTISE
],
836 _cleanup_free_
struct ethtool_link_usettings
*u
= NULL
;
837 struct ifreq ifr
= {};
844 if (autonegotiation
!= AUTONEG_DISABLE
&& memeqzero(advertise
, sizeof(uint32_t) * N_ADVERTISE
)) {
845 log_info("ethtool: autonegotiation is unset or enabled, the speed and duplex are not writable.");
850 r
= ethtool_connect_or_warn(fd
, true);
855 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
857 r
= get_glinksettings(*fd
, &ifr
, &u
);
859 r
= get_gset(*fd
, &ifr
, &u
);
861 return log_warning_errno(r
, "ethtool: Cannot get device settings for %s : %m", ifname
);
865 u
->base
.speed
= DIV_ROUND_UP(speed
, 1000000);
867 if (duplex
!= _DUP_INVALID
)
868 u
->base
.duplex
= duplex
;
870 if (port
!= _NET_DEV_PORT_INVALID
)
873 if (autonegotiation
>= 0)
874 u
->base
.autoneg
= autonegotiation
;
876 if (!memeqzero(advertise
, sizeof(uint32_t) * N_ADVERTISE
)) {
877 u
->base
.autoneg
= AUTONEG_ENABLE
;
878 memcpy(&u
->link_modes
.advertising
, advertise
, sizeof(uint32_t) * N_ADVERTISE
);
879 memzero((uint8_t*) &u
->link_modes
.advertising
+ sizeof(uint32_t) * N_ADVERTISE
,
880 ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES
- sizeof(uint32_t) * N_ADVERTISE
);
883 if (u
->base
.cmd
== ETHTOOL_GLINKSETTINGS
)
884 r
= set_slinksettings(*fd
, &ifr
, u
);
886 r
= set_sset(*fd
, &ifr
, u
);
888 return log_warning_errno(r
, "ethtool: Cannot set device settings for %s: %m", ifname
);
893 int ethtool_set_channels(int *fd
, const char *ifname
, const netdev_channels
*channels
) {
894 struct ethtool_channels ecmd
= {
895 .cmd
= ETHTOOL_GCHANNELS
,
898 .ifr_data
= (void*) &ecmd
,
900 bool need_update
= false;
908 r
= ethtool_connect_or_warn(fd
, true);
913 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
915 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
919 if (channels
->rx_count_set
&& ecmd
.rx_count
!= channels
->rx_count
) {
920 ecmd
.rx_count
= channels
->rx_count
;
924 if (channels
->tx_count_set
&& ecmd
.tx_count
!= channels
->tx_count
) {
925 ecmd
.tx_count
= channels
->tx_count
;
929 if (channels
->other_count_set
&& ecmd
.other_count
!= channels
->other_count
) {
930 ecmd
.other_count
= channels
->other_count
;
934 if (channels
->combined_count_set
&& ecmd
.combined_count
!= channels
->combined_count
) {
935 ecmd
.combined_count
= channels
->combined_count
;
940 ecmd
.cmd
= ETHTOOL_SCHANNELS
;
942 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
950 int ethtool_set_flow_control(int *fd
, const char *ifname
, int rx
, int tx
, int autoneg
) {
951 struct ethtool_pauseparam ecmd
= {
952 .cmd
= ETHTOOL_GPAUSEPARAM
,
955 .ifr_data
= (void*) &ecmd
,
957 bool need_update
= false;
964 r
= ethtool_connect_or_warn(fd
, true);
969 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
971 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
975 if (rx
>= 0 && ecmd
.rx_pause
!= (uint32_t) rx
) {
980 if (tx
>= 0 && ecmd
.tx_pause
!= (uint32_t) tx
) {
985 if (autoneg
>= 0 && ecmd
.autoneg
!= (uint32_t) autoneg
) {
986 ecmd
.autoneg
= autoneg
;
991 ecmd
.cmd
= ETHTOOL_SPAUSEPARAM
;
993 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
1001 int config_parse_channel(const char *unit
,
1002 const char *filename
,
1004 const char *section
,
1005 unsigned section_line
,
1011 netdev_channels
*channels
= data
;
1021 r
= safe_atou32(rvalue
, &k
);
1023 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1024 "Failed to parse channel value for %s=, ignoring: %s", lvalue
, rvalue
);
1028 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1029 "Invalid %s= value, ignoring: %s", lvalue
, rvalue
);
1033 if (streq(lvalue
, "RxChannels")) {
1034 channels
->rx_count
= k
;
1035 channels
->rx_count_set
= true;
1036 } else if (streq(lvalue
, "TxChannels")) {
1037 channels
->tx_count
= k
;
1038 channels
->tx_count_set
= true;
1039 } else if (streq(lvalue
, "OtherChannels")) {
1040 channels
->other_count
= k
;
1041 channels
->other_count_set
= true;
1042 } else if (streq(lvalue
, "CombinedChannels")) {
1043 channels
->combined_count
= k
;
1044 channels
->combined_count_set
= true;
1050 int config_parse_advertise(const char *unit
,
1051 const char *filename
,
1053 const char *section
,
1054 unsigned section_line
,
1060 uint32_t *advertise
= data
;
1070 if (isempty(rvalue
)) {
1071 /* Empty string resets the value. */
1072 memzero(advertise
, sizeof(uint32_t) * N_ADVERTISE
);
1076 for (p
= rvalue
;;) {
1077 _cleanup_free_
char *w
= NULL
;
1078 enum ethtool_link_mode_bit_indices mode
;
1080 r
= extract_first_word(&p
, &w
, NULL
, 0);
1084 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1085 "Failed to split advertise modes '%s', ignoring assignment: %m", rvalue
);
1091 mode
= ethtool_link_mode_bit_from_string(w
);
1092 /* We reuse the kernel provided enum which does not contain negative value. So, the cast
1093 * below is mandatory. Otherwise, the check below always passes and access an invalid address. */
1094 if ((int) mode
< 0) {
1095 log_syntax(unit
, LOG_WARNING
, filename
, line
, mode
,
1096 "Failed to parse advertise mode, ignoring: %s", w
);
1100 advertise
[mode
/ 32] |= 1UL << (mode
% 32);
1104 int config_parse_nic_buffer_size(const char *unit
,
1105 const char *filename
,
1107 const char *section
,
1108 unsigned section_line
,
1114 netdev_ring_param
*ring
= data
;
1124 r
= safe_atou32(rvalue
, &k
);
1126 log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1127 "Failed to parse interface buffer value, ignoring: %s", rvalue
);
1131 log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1132 "Invalid %s= value, ignoring: %s", lvalue
, rvalue
);
1136 if (streq(lvalue
, "RxBufferSize")) {
1137 ring
->rx_pending
= k
;
1138 ring
->rx_pending_set
= true;
1139 } else if (streq(lvalue
, "RxMiniBufferSize")) {
1140 ring
->rx_mini_pending
= k
;
1141 ring
->rx_mini_pending_set
= true;
1142 } else if (streq(lvalue
, "RxJumboBufferSize")) {
1143 ring
->rx_jumbo_pending
= k
;
1144 ring
->rx_jumbo_pending_set
= true;
1145 } else if (streq(lvalue
, "TxBufferSize")) {
1146 ring
->tx_pending
= k
;
1147 ring
->tx_pending_set
= true;