1 /* SPDX-License-Identifier: LGPL-2.1+ */
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_GSO
] = "tx-generic-segmentation",
54 [NET_DEV_FEAT_GRO
] = "rx-gro",
55 [NET_DEV_FEAT_LRO
] = "rx-lro",
56 [NET_DEV_FEAT_TSO
] = "tx-tcp-segmentation",
57 [NET_DEV_FEAT_TSO6
] = "tx-tcp6-segmentation",
60 static const char* const ethtool_link_mode_bit_table
[] = {
61 [ETHTOOL_LINK_MODE_10baseT_Half_BIT
] = "10baset-half",
62 [ETHTOOL_LINK_MODE_10baseT_Full_BIT
] = "10baset-full",
63 [ETHTOOL_LINK_MODE_100baseT_Half_BIT
] = "100baset-half",
64 [ETHTOOL_LINK_MODE_100baseT_Full_BIT
] = "100baset-full",
65 [ETHTOOL_LINK_MODE_1000baseT_Half_BIT
] = "1000baset-half",
66 [ETHTOOL_LINK_MODE_1000baseT_Full_BIT
] = "1000baset-full",
67 [ETHTOOL_LINK_MODE_Autoneg_BIT
] = "autonegotiation",
68 [ETHTOOL_LINK_MODE_TP_BIT
] = "tp",
69 [ETHTOOL_LINK_MODE_AUI_BIT
] = "aui",
70 [ETHTOOL_LINK_MODE_MII_BIT
] = "mii",
71 [ETHTOOL_LINK_MODE_FIBRE_BIT
] = "fibre",
72 [ETHTOOL_LINK_MODE_BNC_BIT
] = "bnc",
73 [ETHTOOL_LINK_MODE_10000baseT_Full_BIT
] = "10000baset-full",
74 [ETHTOOL_LINK_MODE_Pause_BIT
] = "pause",
75 [ETHTOOL_LINK_MODE_Asym_Pause_BIT
] = "asym-pause",
76 [ETHTOOL_LINK_MODE_2500baseX_Full_BIT
] = "2500basex-full",
77 [ETHTOOL_LINK_MODE_Backplane_BIT
] = "backplane",
78 [ETHTOOL_LINK_MODE_1000baseKX_Full_BIT
] = "1000basekx-full",
79 [ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT
] = "10000basekx4-full",
80 [ETHTOOL_LINK_MODE_10000baseKR_Full_BIT
] = "10000basekr-full",
81 [ETHTOOL_LINK_MODE_10000baseR_FEC_BIT
] = "10000baser-fec",
82 [ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT
] = "20000basemld2-full",
83 [ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT
] = "20000basekr2-full",
84 [ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT
] = "40000basekr4-full",
85 [ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT
] = "40000basecr4-full",
86 [ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT
] = "40000basesr4-full",
87 [ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT
] = "40000baselr4-full",
88 [ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT
] = "56000basekr4-full",
89 [ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT
] = "56000basecr4-full",
90 [ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT
] = "56000basesr4-full",
91 [ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT
] = "56000baselr4-full",
92 [ETHTOOL_LINK_MODE_25000baseCR_Full_BIT
] = "25000basecr-full",
93 [ETHTOOL_LINK_MODE_25000baseKR_Full_BIT
] = "25000basekr-full",
94 [ETHTOOL_LINK_MODE_25000baseSR_Full_BIT
] = "25000basesr-full",
95 [ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT
] = "50000basecr2-full",
96 [ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT
] = "50000basekr2-full",
97 [ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT
] = "100000basekr4-full",
98 [ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT
] = "100000basesr4-full",
99 [ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT
] = "100000basecr4-full",
100 [ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT
] = "100000baselr4-er4-full",
101 [ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT
] = "50000basesr2-full",
102 [ETHTOOL_LINK_MODE_1000baseX_Full_BIT
] = "1000basex-full",
103 [ETHTOOL_LINK_MODE_10000baseCR_Full_BIT
] = "10000basecr-full",
104 [ETHTOOL_LINK_MODE_10000baseSR_Full_BIT
] = "10000basesr-full",
105 [ETHTOOL_LINK_MODE_10000baseLR_Full_BIT
] = "10000baselr-full",
106 [ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT
] = "10000baselrm-full",
107 [ETHTOOL_LINK_MODE_10000baseER_Full_BIT
] = "10000baseer-full",
108 [ETHTOOL_LINK_MODE_2500baseT_Full_BIT
] = "2500baset-full",
109 [ETHTOOL_LINK_MODE_5000baseT_Full_BIT
] = "5000baset-full",
110 [ETHTOOL_LINK_MODE_FEC_NONE_BIT
] = "fec-none",
111 [ETHTOOL_LINK_MODE_FEC_RS_BIT
] = "fec-rs",
112 [ETHTOOL_LINK_MODE_FEC_BASER_BIT
] = "fec-baser",
113 [ETHTOOL_LINK_MODE_50000baseKR_Full_BIT
] = "50000basekr_full",
114 [ETHTOOL_LINK_MODE_50000baseSR_Full_BIT
] = "50000basesr_full",
115 [ETHTOOL_LINK_MODE_50000baseCR_Full_BIT
] = "50000basecr_full",
116 [ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT
] = "50000baselr_er_fr_full",
117 [ETHTOOL_LINK_MODE_50000baseDR_Full_BIT
] = "50000basedr_full",
118 [ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT
] = "100000basekr2_full",
119 [ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT
] = "100000basesr2_full",
120 [ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT
] = "100000basecr2_full",
121 [ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT
] = "100000baselr2_er2_fr2_full",
122 [ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT
] = "100000basedr2_full",
123 [ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT
] = "200000basekr4_full",
124 [ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT
] = "200000basesr4_full",
125 [ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT
] = "200000basecr4_full",
126 [ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT
] = "200000baselr4_er4_fr4_full",
127 [ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT
] = "200000basedr4_full",
129 /* Make sure the array is large enough to fit all bits */
130 assert_cc((ELEMENTSOF(ethtool_link_mode_bit_table
)-1) / 32 < N_ADVERTISE
);
132 DEFINE_STRING_TABLE_LOOKUP(ethtool_link_mode_bit
, enum ethtool_link_mode_bit_indices
);
134 static int ethtool_connect_or_warn(int *ret
, bool warn
) {
137 assert_return(ret
, -EINVAL
);
139 fd
= socket_ioctl_fd();
141 return log_full_errno(warn
? LOG_WARNING
: LOG_DEBUG
, fd
,
142 "ethtool: could not create control socket: %m");
149 int ethtool_get_driver(int *ethtool_fd
, const char *ifname
, char **ret
) {
150 struct ethtool_drvinfo ecmd
= {
151 .cmd
= ETHTOOL_GDRVINFO
154 .ifr_data
= (void*) &ecmd
159 if (*ethtool_fd
< 0) {
160 r
= ethtool_connect_or_warn(ethtool_fd
, true);
165 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
167 r
= ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
);
171 d
= strdup(ecmd
.driver
);
179 int ethtool_get_link_info(int *ethtool_fd
, const char *ifname
,
180 int *ret_autonegotiation
, size_t *ret_speed
,
181 Duplex
*ret_duplex
, NetDevPort
*ret_port
) {
182 struct ethtool_cmd ecmd
= {
186 .ifr_data
= (void*) &ecmd
,
190 if (*ethtool_fd
< 0) {
191 r
= ethtool_connect_or_warn(ethtool_fd
, false);
196 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
198 r
= ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
);
202 if (ret_autonegotiation
)
203 *ret_autonegotiation
= ecmd
.autoneg
;
208 speed
= ethtool_cmd_speed(&ecmd
);
209 *ret_speed
= speed
== (uint32_t) SPEED_UNKNOWN
?
210 SIZE_MAX
: (size_t) speed
* 1000 * 1000;
214 *ret_duplex
= ecmd
.duplex
;
217 *ret_port
= ecmd
.port
;
222 int ethtool_get_permanent_macaddr(int *ethtool_fd
, const char *ifname
, struct ether_addr
*ret
) {
223 _cleanup_close_
int fd
= -1;
225 struct ethtool_perm_addr addr
;
226 uint8_t space
[MAX_ADDR_LEN
];
228 .addr
.cmd
= ETHTOOL_GPERMADDR
,
229 .addr
.size
= MAX_ADDR_LEN
,
232 .ifr_data
= (caddr_t
) &epaddr
,
242 if (*ethtool_fd
< 0) {
243 r
= ethtool_connect_or_warn(ethtool_fd
, false);
248 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
250 r
= ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
);
254 if (epaddr
.addr
.size
!= 6)
257 for (size_t i
= 0; i
< epaddr
.addr
.size
; i
++)
258 ret
->ether_addr_octet
[i
] = epaddr
.addr
.data
[i
];
263 int ethtool_set_speed(int *ethtool_fd
, const char *ifname
, unsigned speed
, Duplex duplex
) {
264 struct ethtool_cmd ecmd
= {
268 .ifr_data
= (void*) &ecmd
270 bool need_update
= false;
273 if (speed
== 0 && duplex
== _DUP_INVALID
)
276 if (*ethtool_fd
< 0) {
277 r
= ethtool_connect_or_warn(ethtool_fd
, true);
282 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
284 r
= ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
);
288 if (ethtool_cmd_speed(&ecmd
) != speed
) {
289 ethtool_cmd_speed_set(&ecmd
, speed
);
295 if (ecmd
.duplex
!= DUPLEX_HALF
) {
296 ecmd
.duplex
= DUPLEX_HALF
;
301 if (ecmd
.duplex
!= DUPLEX_FULL
) {
302 ecmd
.duplex
= DUPLEX_FULL
;
311 ecmd
.cmd
= ETHTOOL_SSET
;
313 r
= ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
);
321 int ethtool_set_wol(int *ethtool_fd
, const char *ifname
, WakeOnLan wol
) {
322 struct ethtool_wolinfo ecmd
= {
326 .ifr_data
= (void*) &ecmd
328 bool need_update
= false;
331 if (wol
== _WOL_INVALID
)
334 if (*ethtool_fd
< 0) {
335 r
= ethtool_connect_or_warn(ethtool_fd
, true);
340 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
342 r
= ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
);
348 if (ecmd
.wolopts
!= WAKE_PHY
) {
349 ecmd
.wolopts
= WAKE_PHY
;
354 if (ecmd
.wolopts
!= WAKE_UCAST
) {
355 ecmd
.wolopts
= WAKE_UCAST
;
360 if (ecmd
.wolopts
!= WAKE_MCAST
) {
361 ecmd
.wolopts
= WAKE_MCAST
;
366 if (ecmd
.wolopts
!= WAKE_BCAST
) {
367 ecmd
.wolopts
= WAKE_BCAST
;
372 if (ecmd
.wolopts
!= WAKE_ARP
) {
373 ecmd
.wolopts
= WAKE_ARP
;
378 if (ecmd
.wolopts
!= WAKE_MAGIC
) {
379 ecmd
.wolopts
= WAKE_MAGIC
;
383 case WOL_MAGICSECURE
:
384 if (ecmd
.wolopts
!= WAKE_MAGICSECURE
) {
385 ecmd
.wolopts
= WAKE_MAGICSECURE
;
390 if (ecmd
.wolopts
!= 0) {
400 ecmd
.cmd
= ETHTOOL_SWOL
;
402 r
= ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
);
410 int ethtool_set_nic_buffer_size(int *ethtool_fd
, const char *ifname
, netdev_ring_param
*ring
) {
411 struct ethtool_ringparam ecmd
= {
412 .cmd
= ETHTOOL_GRINGPARAM
415 .ifr_data
= (void*) &ecmd
417 bool need_update
= false;
420 if (*ethtool_fd
< 0) {
421 r
= ethtool_connect_or_warn(ethtool_fd
, true);
426 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
428 r
= ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
);
432 if (ring
->rx_pending_set
) {
433 if (ecmd
.rx_pending
!= ring
->rx_pending
) {
434 ecmd
.rx_pending
= ring
->rx_pending
;
439 if (ring
->tx_pending_set
) {
440 if (ecmd
.tx_pending
!= ring
->tx_pending
) {
441 ecmd
.tx_pending
= ring
->tx_pending
;
447 ecmd
.cmd
= ETHTOOL_SRINGPARAM
;
449 r
= ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
);
457 static int get_stringset(int ethtool_fd
, struct ifreq
*ifr
, int stringset_id
, struct ethtool_gstrings
**gstrings
) {
458 _cleanup_free_
struct ethtool_gstrings
*strings
= NULL
;
460 struct ethtool_sset_info info
;
464 .cmd
= ETHTOOL_GSSET_INFO
,
465 .sset_mask
= UINT64_C(1) << stringset_id
,
471 ifr
->ifr_data
= (void *) &buffer
.info
;
473 r
= ioctl(ethtool_fd
, SIOCETHTOOL
, ifr
);
477 if (!buffer
.info
.sset_mask
)
480 len
= buffer
.info
.data
[0];
482 strings
= malloc0(sizeof(struct ethtool_gstrings
) + len
* ETH_GSTRING_LEN
);
486 strings
->cmd
= ETHTOOL_GSTRINGS
;
487 strings
->string_set
= stringset_id
;
490 ifr
->ifr_data
= (void *) strings
;
492 r
= ioctl(ethtool_fd
, SIOCETHTOOL
, ifr
);
496 *gstrings
= TAKE_PTR(strings
);
501 static int find_feature_index(struct ethtool_gstrings
*strings
, const char *feature
) {
504 for (i
= 0; i
< strings
->len
; i
++) {
505 if (streq((char *) &strings
->data
[i
* ETH_GSTRING_LEN
], feature
))
512 int ethtool_set_features(int *ethtool_fd
, const char *ifname
, int *features
) {
513 _cleanup_free_
struct ethtool_gstrings
*strings
= NULL
;
514 struct ethtool_sfeatures
*sfeatures
;
515 int block
, bit
, i
, r
;
516 struct ifreq ifr
= {};
518 if (*ethtool_fd
< 0) {
519 r
= ethtool_connect_or_warn(ethtool_fd
, true);
524 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
526 r
= get_stringset(*ethtool_fd
, &ifr
, ETH_SS_FEATURES
, &strings
);
528 return log_warning_errno(r
, "ethtool: could not get ethtool features for %s", ifname
);
530 sfeatures
= alloca0(sizeof(struct ethtool_sfeatures
) + DIV_ROUND_UP(strings
->len
, 32U) * sizeof(sfeatures
->features
[0]));
531 sfeatures
->cmd
= ETHTOOL_SFEATURES
;
532 sfeatures
->size
= DIV_ROUND_UP(strings
->len
, 32U);
534 for (i
= 0; i
< _NET_DEV_FEAT_MAX
; i
++) {
536 if (features
[i
] != -1) {
538 r
= find_feature_index(strings
, netdev_feature_table
[i
]);
540 log_warning_errno(r
, "ethtool: could not find feature: %s", netdev_feature_table
[i
]);
547 sfeatures
->features
[block
].valid
|= 1 << bit
;
550 sfeatures
->features
[block
].requested
|= 1 << bit
;
552 sfeatures
->features
[block
].requested
&= ~(1 << bit
);
556 ifr
.ifr_data
= (void *) sfeatures
;
558 r
= ioctl(*ethtool_fd
, SIOCETHTOOL
, &ifr
);
560 return log_warning_errno(r
, "ethtool: could not set ethtool features for %s", ifname
);
565 static int get_glinksettings(int fd
, struct ifreq
*ifr
, struct ethtool_link_usettings
**g
) {
567 struct ethtool_link_settings req
;
568 __u32 link_mode_data
[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32
];
570 .req
.cmd
= ETHTOOL_GLINKSETTINGS
,
572 struct ethtool_link_usettings
*u
;
576 /* The interaction user/kernel via the new API requires a small ETHTOOL_GLINKSETTINGS
577 handshake first to agree on the length of the link mode bitmaps. If kernel doesn't
578 agree with user, it returns the bitmap length it is expecting from user as a negative
579 length (and cmd field is 0). When kernel and user agree, kernel returns valid info in
580 all fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS). Based on
581 https://github.com/torvalds/linux/commit/3f1ac7a700d039c61d8d8b99f28d605d489a60cf
584 ifr
->ifr_data
= (void *) &ecmd
;
586 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
590 if (ecmd
.req
.link_mode_masks_nwords
>= 0 || ecmd
.req
.cmd
!= ETHTOOL_GLINKSETTINGS
)
593 ecmd
.req
.link_mode_masks_nwords
= -ecmd
.req
.link_mode_masks_nwords
;
595 ifr
->ifr_data
= (void *) &ecmd
;
597 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
601 if (ecmd
.req
.link_mode_masks_nwords
<= 0 || ecmd
.req
.cmd
!= ETHTOOL_GLINKSETTINGS
)
604 u
= new(struct ethtool_link_usettings
, 1);
608 *u
= (struct ethtool_link_usettings
) {
613 memcpy(u
->link_modes
.supported
, &ecmd
.link_mode_data
[offset
], 4 * ecmd
.req
.link_mode_masks_nwords
);
615 offset
+= ecmd
.req
.link_mode_masks_nwords
;
616 memcpy(u
->link_modes
.advertising
, &ecmd
.link_mode_data
[offset
], 4 * ecmd
.req
.link_mode_masks_nwords
);
618 offset
+= ecmd
.req
.link_mode_masks_nwords
;
619 memcpy(u
->link_modes
.lp_advertising
, &ecmd
.link_mode_data
[offset
], 4 * ecmd
.req
.link_mode_masks_nwords
);
626 static int get_gset(int fd
, struct ifreq
*ifr
, struct ethtool_link_usettings
**u
) {
627 struct ethtool_link_usettings
*e
;
628 struct ethtool_cmd ecmd
= {
633 ifr
->ifr_data
= (void *) &ecmd
;
635 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
639 e
= new(struct ethtool_link_usettings
, 1);
643 *e
= (struct ethtool_link_usettings
) {
644 .base
.cmd
= ETHTOOL_GSET
,
645 .base
.link_mode_masks_nwords
= 1,
646 .base
.speed
= ethtool_cmd_speed(&ecmd
),
647 .base
.duplex
= ecmd
.duplex
,
648 .base
.port
= ecmd
.port
,
649 .base
.phy_address
= ecmd
.phy_address
,
650 .base
.autoneg
= ecmd
.autoneg
,
651 .base
.mdio_support
= ecmd
.mdio_support
,
653 .link_modes
.supported
[0] = ecmd
.supported
,
654 .link_modes
.advertising
[0] = ecmd
.advertising
,
655 .link_modes
.lp_advertising
[0] = ecmd
.lp_advertising
,
663 static int set_slinksettings(int fd
, struct ifreq
*ifr
, const struct ethtool_link_usettings
*u
) {
665 struct ethtool_link_settings req
;
666 __u32 link_mode_data
[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32
];
671 if (u
->base
.cmd
!= ETHTOOL_GLINKSETTINGS
|| u
->base
.link_mode_masks_nwords
<= 0)
675 ecmd
.req
.cmd
= ETHTOOL_SLINKSETTINGS
;
677 memcpy(&ecmd
.link_mode_data
[offset
], u
->link_modes
.supported
, 4 * ecmd
.req
.link_mode_masks_nwords
);
679 offset
+= ecmd
.req
.link_mode_masks_nwords
;
680 memcpy(&ecmd
.link_mode_data
[offset
], u
->link_modes
.advertising
, 4 * ecmd
.req
.link_mode_masks_nwords
);
682 offset
+= ecmd
.req
.link_mode_masks_nwords
;
683 memcpy(&ecmd
.link_mode_data
[offset
], u
->link_modes
.lp_advertising
, 4 * ecmd
.req
.link_mode_masks_nwords
);
685 ifr
->ifr_data
= (void *) &ecmd
;
687 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
694 static int set_sset(int fd
, struct ifreq
*ifr
, const struct ethtool_link_usettings
*u
) {
695 struct ethtool_cmd ecmd
= {
700 if (u
->base
.cmd
!= ETHTOOL_GSET
|| u
->base
.link_mode_masks_nwords
<= 0)
703 ecmd
.supported
= u
->link_modes
.supported
[0];
704 ecmd
.advertising
= u
->link_modes
.advertising
[0];
705 ecmd
.lp_advertising
= u
->link_modes
.lp_advertising
[0];
707 ethtool_cmd_speed_set(&ecmd
, u
->base
.speed
);
709 ecmd
.duplex
= u
->base
.duplex
;
710 ecmd
.port
= u
->base
.port
;
711 ecmd
.phy_address
= u
->base
.phy_address
;
712 ecmd
.autoneg
= u
->base
.autoneg
;
713 ecmd
.mdio_support
= u
->base
.mdio_support
;
714 ecmd
.eth_tp_mdix
= u
->base
.eth_tp_mdix
;
715 ecmd
.eth_tp_mdix_ctrl
= u
->base
.eth_tp_mdix_ctrl
;
717 ifr
->ifr_data
= (void *) &ecmd
;
719 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
726 /* If autonegotiation is disabled, the speed and duplex represent the fixed link
727 * mode and are writable if the driver supports multiple link modes. If it is
728 * enabled then they are read-only. If the link is up they represent the negotiated
729 * link mode; if the link is down, the speed is 0, %SPEED_UNKNOWN or the highest
730 * enabled speed and @duplex is %DUPLEX_UNKNOWN or the best enabled duplex mode.
732 int ethtool_set_glinksettings(
736 uint32_t advertise
[static N_ADVERTISE
],
740 _cleanup_free_
struct ethtool_link_usettings
*u
= NULL
;
741 struct ifreq ifr
= {};
746 if (autonegotiation
!= AUTONEG_DISABLE
&& memeqzero(advertise
, sizeof(uint32_t) * N_ADVERTISE
)) {
747 log_info("ethtool: autonegotiation is unset or enabled, the speed and duplex are not writable.");
752 r
= ethtool_connect_or_warn(fd
, true);
757 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
759 r
= get_glinksettings(*fd
, &ifr
, &u
);
761 r
= get_gset(*fd
, &ifr
, &u
);
763 return log_warning_errno(r
, "ethtool: Cannot get device settings for %s : %m", ifname
);
767 u
->base
.speed
= DIV_ROUND_UP(speed
, 1000000);
769 if (duplex
!= _DUP_INVALID
)
770 u
->base
.duplex
= duplex
;
772 if (port
!= _NET_DEV_PORT_INVALID
)
775 if (autonegotiation
>= 0)
776 u
->base
.autoneg
= autonegotiation
;
778 if (!memeqzero(advertise
, sizeof(uint32_t) * N_ADVERTISE
)) {
779 u
->base
.autoneg
= AUTONEG_ENABLE
;
780 memcpy(&u
->link_modes
.advertising
, advertise
, sizeof(uint32_t) * N_ADVERTISE
);
781 memzero((uint8_t*) &u
->link_modes
.advertising
+ sizeof(uint32_t) * N_ADVERTISE
,
782 ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES
- sizeof(uint32_t) * N_ADVERTISE
);
785 if (u
->base
.cmd
== ETHTOOL_GLINKSETTINGS
)
786 r
= set_slinksettings(*fd
, &ifr
, u
);
788 r
= set_sset(*fd
, &ifr
, u
);
790 return log_warning_errno(r
, "ethtool: Cannot set device settings for %s: %m", ifname
);
795 int ethtool_set_channels(int *fd
, const char *ifname
, netdev_channels
*channels
) {
796 struct ethtool_channels ecmd
= {
797 .cmd
= ETHTOOL_GCHANNELS
800 .ifr_data
= (void*) &ecmd
803 bool need_update
= false;
807 r
= ethtool_connect_or_warn(fd
, true);
812 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
814 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
818 if (channels
->rx_count_set
&& ecmd
.rx_count
!= channels
->rx_count
) {
819 ecmd
.rx_count
= channels
->rx_count
;
823 if (channels
->tx_count_set
&& ecmd
.tx_count
!= channels
->tx_count
) {
824 ecmd
.tx_count
= channels
->tx_count
;
828 if (channels
->other_count_set
&& ecmd
.other_count
!= channels
->other_count
) {
829 ecmd
.other_count
= channels
->other_count
;
833 if (channels
->combined_count_set
&& ecmd
.combined_count
!= channels
->combined_count
) {
834 ecmd
.combined_count
= channels
->combined_count
;
839 ecmd
.cmd
= ETHTOOL_SCHANNELS
;
841 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
849 int config_parse_channel(const char *unit
,
850 const char *filename
,
853 unsigned section_line
,
859 netdev_channels
*channels
= data
;
869 r
= safe_atou32(rvalue
, &k
);
871 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse channel value, ignoring: %s", rvalue
);
876 log_syntax(unit
, LOG_ERR
, filename
, line
, -EINVAL
, "Invalid %s value, ignoring: %s", lvalue
, rvalue
);
880 if (streq(lvalue
, "RxChannels")) {
881 channels
->rx_count
= k
;
882 channels
->rx_count_set
= true;
883 } else if (streq(lvalue
, "TxChannels")) {
884 channels
->tx_count
= k
;
885 channels
->tx_count_set
= true;
886 } else if (streq(lvalue
, "OtherChannels")) {
887 channels
->other_count
= k
;
888 channels
->other_count_set
= true;
889 } else if (streq(lvalue
, "CombinedChannels")) {
890 channels
->combined_count
= k
;
891 channels
->combined_count_set
= true;
897 int config_parse_advertise(const char *unit
,
898 const char *filename
,
901 unsigned section_line
,
907 uint32_t *advertise
= data
;
917 if (isempty(rvalue
)) {
918 /* Empty string resets the value. */
919 memzero(advertise
, sizeof(uint32_t) * N_ADVERTISE
);
924 _cleanup_free_
char *w
= NULL
;
925 enum ethtool_link_mode_bit_indices mode
;
927 r
= extract_first_word(&p
, &w
, NULL
, 0);
931 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to split advertise modes '%s', ignoring: %m", rvalue
);
937 mode
= ethtool_link_mode_bit_from_string(w
);
938 /* We reuse the kernel provided enum which does not contain negative value. So, the cast
939 * below is mandatory. Otherwise, the check below always passes and access an invalid address. */
940 if ((int) mode
< 0) {
941 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse advertise mode, ignoring: %s", w
);
945 advertise
[mode
/ 32] |= 1UL << (mode
% 32);
951 int config_parse_nic_buffer_size(const char *unit
,
952 const char *filename
,
955 unsigned section_line
,
961 netdev_ring_param
*ring
= data
;
971 r
= safe_atou32(rvalue
, &k
);
973 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse interface buffer value, ignoring: %s", rvalue
);
978 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Invalid %s value, ignoring: %s", lvalue
, rvalue
);
982 if (streq(lvalue
, "RxBufferSize")) {
983 ring
->rx_pending
= k
;
984 ring
->rx_pending_set
= true;
985 } else if (streq(lvalue
, "TxBufferSize")) {
986 ring
->tx_pending
= k
;
987 ring
->tx_pending_set
= true;