1 /* SPDX-License-Identifier: LGPL-2.1+ */
5 #include <linux/ethtool.h>
6 #include <linux/sockios.h>
8 #include "conf-parser.h"
9 #include "ethtool-util.h"
10 #include "extract-word.h"
12 #include "memory-util.h"
14 #include "socket-util.h"
15 #include "string-table.h"
18 static const char* const duplex_table
[_DUP_MAX
] = {
23 DEFINE_STRING_TABLE_LOOKUP(duplex
, Duplex
);
24 DEFINE_CONFIG_PARSE_ENUM(config_parse_duplex
, duplex
, Duplex
, "Failed to parse duplex setting");
26 static const char* const wol_table
[_WOL_MAX
] = {
28 [WOL_UCAST
] = "unicast",
29 [WOL_MCAST
] = "multicast",
30 [WOL_BCAST
] = "broadcast",
32 [WOL_MAGIC
] = "magic",
33 [WOL_MAGICSECURE
] = "secureon",
37 DEFINE_STRING_TABLE_LOOKUP(wol
, WakeOnLan
);
38 DEFINE_CONFIG_PARSE_ENUM(config_parse_wol
, wol
, WakeOnLan
, "Failed to parse WakeOnLan setting");
40 static const char* const port_table
[] = {
41 [NET_DEV_PORT_TP
] = "tp",
42 [NET_DEV_PORT_AUI
] = "aui",
43 [NET_DEV_PORT_MII
] = "mii",
44 [NET_DEV_PORT_FIBRE
] = "fibre",
45 [NET_DEV_PORT_BNC
] = "bnc",
48 DEFINE_STRING_TABLE_LOOKUP(port
, NetDevPort
);
49 DEFINE_CONFIG_PARSE_ENUM(config_parse_port
, port
, NetDevPort
, "Failed to parse Port setting");
51 static const char* const netdev_feature_table
[_NET_DEV_FEAT_MAX
] = {
52 [NET_DEV_FEAT_GSO
] = "tx-generic-segmentation",
53 [NET_DEV_FEAT_GRO
] = "rx-gro",
54 [NET_DEV_FEAT_LRO
] = "rx-lro",
55 [NET_DEV_FEAT_TSO
] = "tx-tcp-segmentation",
56 [NET_DEV_FEAT_TSO6
] = "tx-tcp6-segmentation",
59 static const char* const ethtool_link_mode_bit_table
[] = {
60 [ETHTOOL_LINK_MODE_10baseT_Half_BIT
] = "10baset-half",
61 [ETHTOOL_LINK_MODE_10baseT_Full_BIT
] = "10baset-full",
62 [ETHTOOL_LINK_MODE_100baseT_Half_BIT
] = "100baset-half",
63 [ETHTOOL_LINK_MODE_100baseT_Full_BIT
] = "100baset-full",
64 [ETHTOOL_LINK_MODE_1000baseT_Half_BIT
] = "1000baset-half",
65 [ETHTOOL_LINK_MODE_1000baseT_Full_BIT
] = "1000baset-full",
66 [ETHTOOL_LINK_MODE_Autoneg_BIT
] = "autonegotiation",
67 [ETHTOOL_LINK_MODE_TP_BIT
] = "tp",
68 [ETHTOOL_LINK_MODE_AUI_BIT
] = "aui",
69 [ETHTOOL_LINK_MODE_MII_BIT
] = "mii",
70 [ETHTOOL_LINK_MODE_FIBRE_BIT
] = "fibre",
71 [ETHTOOL_LINK_MODE_BNC_BIT
] = "bnc",
72 [ETHTOOL_LINK_MODE_10000baseT_Full_BIT
] = "10000baset-full",
73 [ETHTOOL_LINK_MODE_Pause_BIT
] = "pause",
74 [ETHTOOL_LINK_MODE_Asym_Pause_BIT
] = "asym-pause",
75 [ETHTOOL_LINK_MODE_2500baseX_Full_BIT
] = "2500basex-full",
76 [ETHTOOL_LINK_MODE_Backplane_BIT
] = "backplane",
77 [ETHTOOL_LINK_MODE_1000baseKX_Full_BIT
] = "1000basekx-full",
78 [ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT
] = "10000basekx4-full",
79 [ETHTOOL_LINK_MODE_10000baseKR_Full_BIT
] = "10000basekr-full",
80 [ETHTOOL_LINK_MODE_10000baseR_FEC_BIT
] = "10000baser-fec",
81 [ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT
] = "20000basemld2-full",
82 [ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT
] = "20000basekr2-full",
83 [ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT
] = "40000basekr4-full",
84 [ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT
] = "40000basecr4-full",
85 [ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT
] = "40000basesr4-full",
86 [ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT
] = "40000baselr4-full",
87 [ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT
] = "56000basekr4-full",
88 [ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT
] = "56000basecr4-full",
89 [ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT
] = "56000basesr4-full",
90 [ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT
] = "56000baselr4-full",
91 [ETHTOOL_LINK_MODE_25000baseCR_Full_BIT
] = "25000basecr-full",
92 [ETHTOOL_LINK_MODE_25000baseKR_Full_BIT
] = "25000basekr-full",
93 [ETHTOOL_LINK_MODE_25000baseSR_Full_BIT
] = "25000basesr-full",
94 [ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT
] = "50000basecr2-full",
95 [ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT
] = "50000basekr2-full",
96 [ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT
] = "100000basekr4-full",
97 [ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT
] = "100000basesr4-full",
98 [ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT
] = "100000basecr4-full",
99 [ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT
] = "100000baselr4-er4-full",
100 [ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT
] = "50000basesr2-full",
101 [ETHTOOL_LINK_MODE_1000baseX_Full_BIT
] = "1000basex-full",
102 [ETHTOOL_LINK_MODE_10000baseCR_Full_BIT
] = "10000basecr-full",
103 [ETHTOOL_LINK_MODE_10000baseSR_Full_BIT
] = "10000basesr-full",
104 [ETHTOOL_LINK_MODE_10000baseLR_Full_BIT
] = "10000baselr-full",
105 [ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT
] = "10000baselrm-full",
106 [ETHTOOL_LINK_MODE_10000baseER_Full_BIT
] = "10000baseer-full",
107 [ETHTOOL_LINK_MODE_2500baseT_Full_BIT
] = "2500baset-full",
108 [ETHTOOL_LINK_MODE_5000baseT_Full_BIT
] = "5000baset-full",
109 [ETHTOOL_LINK_MODE_FEC_NONE_BIT
] = "fec-none",
110 [ETHTOOL_LINK_MODE_FEC_RS_BIT
] = "fec-rs",
111 [ETHTOOL_LINK_MODE_FEC_BASER_BIT
] = "fec-baser",
113 /* Make sure the array is large enough to fit all bits */
114 assert_cc((ELEMENTSOF(ethtool_link_mode_bit_table
)-1) / 32 < N_ADVERTISE
);
116 DEFINE_STRING_TABLE_LOOKUP(ethtool_link_mode_bit
, enum ethtool_link_mode_bit_indices
);
118 static int ethtool_connect_or_warn(int *ret
, bool warn
) {
121 assert_return(ret
, -EINVAL
);
123 fd
= socket_ioctl_fd();
125 return log_full_errno(warn
? LOG_WARNING
: LOG_DEBUG
, fd
,
126 "ethtool: could not create control socket: %m");
133 int ethtool_get_driver(int *fd
, const char *ifname
, char **ret
) {
134 struct ethtool_drvinfo ecmd
= {
135 .cmd
= ETHTOOL_GDRVINFO
138 .ifr_data
= (void*) &ecmd
144 r
= ethtool_connect_or_warn(fd
, true);
149 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
151 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
155 d
= strdup(ecmd
.driver
);
163 int ethtool_get_link_info(int *fd
, const char *ifname
,
164 int *ret_autonegotiation
, size_t *ret_speed
,
165 Duplex
*ret_duplex
, NetDevPort
*ret_port
) {
166 struct ethtool_cmd ecmd
= {
170 .ifr_data
= (void*) &ecmd
,
175 r
= ethtool_connect_or_warn(fd
, false);
180 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
182 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
186 if (ret_autonegotiation
)
187 *ret_autonegotiation
= ecmd
.autoneg
;
190 *ret_speed
= ethtool_cmd_speed(&ecmd
) * 1000 * 1000;
193 *ret_duplex
= ecmd
.duplex
;
196 *ret_port
= ecmd
.port
;
201 int ethtool_set_speed(int *fd
, const char *ifname
, unsigned speed
, Duplex duplex
) {
202 struct ethtool_cmd ecmd
= {
206 .ifr_data
= (void*) &ecmd
208 bool need_update
= false;
211 if (speed
== 0 && duplex
== _DUP_INVALID
)
215 r
= ethtool_connect_or_warn(fd
, true);
220 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
222 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
226 if (ethtool_cmd_speed(&ecmd
) != speed
) {
227 ethtool_cmd_speed_set(&ecmd
, speed
);
233 if (ecmd
.duplex
!= DUPLEX_HALF
) {
234 ecmd
.duplex
= DUPLEX_HALF
;
239 if (ecmd
.duplex
!= DUPLEX_FULL
) {
240 ecmd
.duplex
= DUPLEX_FULL
;
249 ecmd
.cmd
= ETHTOOL_SSET
;
251 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
259 int ethtool_set_wol(int *fd
, const char *ifname
, WakeOnLan wol
) {
260 struct ethtool_wolinfo ecmd
= {
264 .ifr_data
= (void*) &ecmd
266 bool need_update
= false;
269 if (wol
== _WOL_INVALID
)
273 r
= ethtool_connect_or_warn(fd
, true);
278 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
280 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
286 if (ecmd
.wolopts
!= WAKE_PHY
) {
287 ecmd
.wolopts
= WAKE_PHY
;
292 if (ecmd
.wolopts
!= WAKE_UCAST
) {
293 ecmd
.wolopts
= WAKE_UCAST
;
298 if (ecmd
.wolopts
!= WAKE_MCAST
) {
299 ecmd
.wolopts
= WAKE_MCAST
;
304 if (ecmd
.wolopts
!= WAKE_BCAST
) {
305 ecmd
.wolopts
= WAKE_BCAST
;
310 if (ecmd
.wolopts
!= WAKE_ARP
) {
311 ecmd
.wolopts
= WAKE_ARP
;
316 if (ecmd
.wolopts
!= WAKE_MAGIC
) {
317 ecmd
.wolopts
= WAKE_MAGIC
;
321 case WOL_MAGICSECURE
:
322 if (ecmd
.wolopts
!= WAKE_MAGICSECURE
) {
323 ecmd
.wolopts
= WAKE_MAGICSECURE
;
328 if (ecmd
.wolopts
!= 0) {
338 ecmd
.cmd
= ETHTOOL_SWOL
;
340 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
348 static int get_stringset(int fd
, struct ifreq
*ifr
, int stringset_id
, struct ethtool_gstrings
**gstrings
) {
349 _cleanup_free_
struct ethtool_gstrings
*strings
= NULL
;
351 struct ethtool_sset_info info
;
355 .cmd
= ETHTOOL_GSSET_INFO
,
356 .sset_mask
= UINT64_C(1) << stringset_id
,
362 ifr
->ifr_data
= (void *) &buffer
.info
;
364 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
368 if (!buffer
.info
.sset_mask
)
371 len
= buffer
.info
.data
[0];
373 strings
= malloc0(sizeof(struct ethtool_gstrings
) + len
* ETH_GSTRING_LEN
);
377 strings
->cmd
= ETHTOOL_GSTRINGS
;
378 strings
->string_set
= stringset_id
;
381 ifr
->ifr_data
= (void *) strings
;
383 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
387 *gstrings
= TAKE_PTR(strings
);
392 static int find_feature_index(struct ethtool_gstrings
*strings
, const char *feature
) {
395 for (i
= 0; i
< strings
->len
; i
++) {
396 if (streq((char *) &strings
->data
[i
* ETH_GSTRING_LEN
], feature
))
403 int ethtool_set_features(int *fd
, const char *ifname
, int *features
) {
404 _cleanup_free_
struct ethtool_gstrings
*strings
= NULL
;
405 struct ethtool_sfeatures
*sfeatures
;
406 int block
, bit
, i
, r
;
407 struct ifreq ifr
= {};
410 r
= ethtool_connect_or_warn(fd
, true);
415 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
417 r
= get_stringset(*fd
, &ifr
, ETH_SS_FEATURES
, &strings
);
419 return log_warning_errno(r
, "ethtool: could not get ethtool features for %s", ifname
);
421 sfeatures
= alloca0(sizeof(struct ethtool_sfeatures
) + DIV_ROUND_UP(strings
->len
, 32U) * sizeof(sfeatures
->features
[0]));
422 sfeatures
->cmd
= ETHTOOL_SFEATURES
;
423 sfeatures
->size
= DIV_ROUND_UP(strings
->len
, 32U);
425 for (i
= 0; i
< _NET_DEV_FEAT_MAX
; i
++) {
427 if (features
[i
] != -1) {
429 r
= find_feature_index(strings
, netdev_feature_table
[i
]);
431 log_warning_errno(r
, "ethtool: could not find feature: %s", netdev_feature_table
[i
]);
438 sfeatures
->features
[block
].valid
|= 1 << bit
;
441 sfeatures
->features
[block
].requested
|= 1 << bit
;
443 sfeatures
->features
[block
].requested
&= ~(1 << bit
);
447 ifr
.ifr_data
= (void *) sfeatures
;
449 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
451 return log_warning_errno(r
, "ethtool: could not set ethtool features for %s", ifname
);
456 static int get_glinksettings(int fd
, struct ifreq
*ifr
, struct ethtool_link_usettings
**g
) {
458 struct ethtool_link_settings req
;
459 __u32 link_mode_data
[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32
];
461 .req
.cmd
= ETHTOOL_GLINKSETTINGS
,
463 struct ethtool_link_usettings
*u
;
467 /* The interaction user/kernel via the new API requires a small ETHTOOL_GLINKSETTINGS
468 handshake first to agree on the length of the link mode bitmaps. If kernel doesn't
469 agree with user, it returns the bitmap length it is expecting from user as a negative
470 length (and cmd field is 0). When kernel and user agree, kernel returns valid info in
471 all fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS). Based on
472 https://github.com/torvalds/linux/commit/3f1ac7a700d039c61d8d8b99f28d605d489a60cf
475 ifr
->ifr_data
= (void *) &ecmd
;
477 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
481 if (ecmd
.req
.link_mode_masks_nwords
>= 0 || ecmd
.req
.cmd
!= ETHTOOL_GLINKSETTINGS
)
484 ecmd
.req
.link_mode_masks_nwords
= -ecmd
.req
.link_mode_masks_nwords
;
486 ifr
->ifr_data
= (void *) &ecmd
;
488 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
492 if (ecmd
.req
.link_mode_masks_nwords
<= 0 || ecmd
.req
.cmd
!= ETHTOOL_GLINKSETTINGS
)
495 u
= new(struct ethtool_link_usettings
, 1);
499 *u
= (struct ethtool_link_usettings
) {
504 memcpy(u
->link_modes
.supported
, &ecmd
.link_mode_data
[offset
], 4 * ecmd
.req
.link_mode_masks_nwords
);
506 offset
+= ecmd
.req
.link_mode_masks_nwords
;
507 memcpy(u
->link_modes
.advertising
, &ecmd
.link_mode_data
[offset
], 4 * ecmd
.req
.link_mode_masks_nwords
);
509 offset
+= ecmd
.req
.link_mode_masks_nwords
;
510 memcpy(u
->link_modes
.lp_advertising
, &ecmd
.link_mode_data
[offset
], 4 * ecmd
.req
.link_mode_masks_nwords
);
517 static int get_gset(int fd
, struct ifreq
*ifr
, struct ethtool_link_usettings
**u
) {
518 struct ethtool_link_usettings
*e
;
519 struct ethtool_cmd ecmd
= {
524 ifr
->ifr_data
= (void *) &ecmd
;
526 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
530 e
= new(struct ethtool_link_usettings
, 1);
534 *e
= (struct ethtool_link_usettings
) {
535 .base
.cmd
= ETHTOOL_GSET
,
536 .base
.link_mode_masks_nwords
= 1,
537 .base
.speed
= ethtool_cmd_speed(&ecmd
),
538 .base
.duplex
= ecmd
.duplex
,
539 .base
.port
= ecmd
.port
,
540 .base
.phy_address
= ecmd
.phy_address
,
541 .base
.autoneg
= ecmd
.autoneg
,
542 .base
.mdio_support
= ecmd
.mdio_support
,
544 .link_modes
.supported
[0] = ecmd
.supported
,
545 .link_modes
.advertising
[0] = ecmd
.advertising
,
546 .link_modes
.lp_advertising
[0] = ecmd
.lp_advertising
,
554 static int set_slinksettings(int fd
, struct ifreq
*ifr
, const struct ethtool_link_usettings
*u
) {
556 struct ethtool_link_settings req
;
557 __u32 link_mode_data
[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32
];
562 if (u
->base
.cmd
!= ETHTOOL_GLINKSETTINGS
|| u
->base
.link_mode_masks_nwords
<= 0)
566 ecmd
.req
.cmd
= ETHTOOL_SLINKSETTINGS
;
568 memcpy(&ecmd
.link_mode_data
[offset
], u
->link_modes
.supported
, 4 * ecmd
.req
.link_mode_masks_nwords
);
570 offset
+= ecmd
.req
.link_mode_masks_nwords
;
571 memcpy(&ecmd
.link_mode_data
[offset
], u
->link_modes
.advertising
, 4 * ecmd
.req
.link_mode_masks_nwords
);
573 offset
+= ecmd
.req
.link_mode_masks_nwords
;
574 memcpy(&ecmd
.link_mode_data
[offset
], u
->link_modes
.lp_advertising
, 4 * ecmd
.req
.link_mode_masks_nwords
);
576 ifr
->ifr_data
= (void *) &ecmd
;
578 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
585 static int set_sset(int fd
, struct ifreq
*ifr
, const struct ethtool_link_usettings
*u
) {
586 struct ethtool_cmd ecmd
= {
591 if (u
->base
.cmd
!= ETHTOOL_GSET
|| u
->base
.link_mode_masks_nwords
<= 0)
594 ecmd
.supported
= u
->link_modes
.supported
[0];
595 ecmd
.advertising
= u
->link_modes
.advertising
[0];
596 ecmd
.lp_advertising
= u
->link_modes
.lp_advertising
[0];
598 ethtool_cmd_speed_set(&ecmd
, u
->base
.speed
);
600 ecmd
.duplex
= u
->base
.duplex
;
601 ecmd
.port
= u
->base
.port
;
602 ecmd
.phy_address
= u
->base
.phy_address
;
603 ecmd
.autoneg
= u
->base
.autoneg
;
604 ecmd
.mdio_support
= u
->base
.mdio_support
;
605 ecmd
.eth_tp_mdix
= u
->base
.eth_tp_mdix
;
606 ecmd
.eth_tp_mdix_ctrl
= u
->base
.eth_tp_mdix_ctrl
;
608 ifr
->ifr_data
= (void *) &ecmd
;
610 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
617 /* If autonegotiation is disabled, the speed and duplex represent the fixed link
618 * mode and are writable if the driver supports multiple link modes. If it is
619 * enabled then they are read-only. If the link is up they represent the negotiated
620 * link mode; if the link is down, the speed is 0, %SPEED_UNKNOWN or the highest
621 * enabled speed and @duplex is %DUPLEX_UNKNOWN or the best enabled duplex mode.
623 int ethtool_set_glinksettings(
627 uint32_t advertise
[static N_ADVERTISE
],
631 _cleanup_free_
struct ethtool_link_usettings
*u
= NULL
;
632 struct ifreq ifr
= {};
635 if (autonegotiation
!= AUTONEG_DISABLE
&& memeqzero(advertise
, sizeof(uint32_t) * N_ADVERTISE
)) {
636 log_info("ethtool: autonegotiation is unset or enabled, the speed and duplex are not writable.");
641 r
= ethtool_connect_or_warn(fd
, true);
646 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
648 r
= get_glinksettings(*fd
, &ifr
, &u
);
650 r
= get_gset(*fd
, &ifr
, &u
);
652 return log_warning_errno(r
, "ethtool: Cannot get device settings for %s : %m", ifname
);
656 u
->base
.speed
= DIV_ROUND_UP(speed
, 1000000);
658 if (duplex
!= _DUP_INVALID
)
659 u
->base
.duplex
= duplex
;
661 if (port
!= _NET_DEV_PORT_INVALID
)
664 if (autonegotiation
>= 0)
665 u
->base
.autoneg
= autonegotiation
;
667 if (!memeqzero(advertise
, sizeof(uint32_t) * N_ADVERTISE
)) {
668 u
->base
.autoneg
= AUTONEG_ENABLE
;
669 memcpy(&u
->link_modes
.advertising
, advertise
, sizeof(uint32_t) * N_ADVERTISE
);
670 memzero((uint8_t*) &u
->link_modes
.advertising
+ sizeof(uint32_t) * N_ADVERTISE
,
671 ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES
- sizeof(uint32_t) * N_ADVERTISE
);
674 if (u
->base
.cmd
== ETHTOOL_GLINKSETTINGS
)
675 r
= set_slinksettings(*fd
, &ifr
, u
);
677 r
= set_sset(*fd
, &ifr
, u
);
679 return log_warning_errno(r
, "ethtool: Cannot set device settings for %s : %m", ifname
);
684 int ethtool_set_channels(int *fd
, const char *ifname
, netdev_channels
*channels
) {
685 struct ethtool_channels ecmd
= {
686 .cmd
= ETHTOOL_GCHANNELS
689 .ifr_data
= (void*) &ecmd
692 bool need_update
= false;
696 r
= ethtool_connect_or_warn(fd
, true);
701 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
703 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
707 if (channels
->rx_count_set
&& ecmd
.rx_count
!= channels
->rx_count
) {
708 ecmd
.rx_count
= channels
->rx_count
;
712 if (channels
->tx_count_set
&& ecmd
.tx_count
!= channels
->tx_count
) {
713 ecmd
.tx_count
= channels
->tx_count
;
717 if (channels
->other_count_set
&& ecmd
.other_count
!= channels
->other_count
) {
718 ecmd
.other_count
= channels
->other_count
;
722 if (channels
->combined_count_set
&& ecmd
.combined_count
!= channels
->combined_count
) {
723 ecmd
.combined_count
= channels
->combined_count
;
728 ecmd
.cmd
= ETHTOOL_SCHANNELS
;
730 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
738 int config_parse_channel(const char *unit
,
739 const char *filename
,
742 unsigned section_line
,
748 netdev_channels
*channels
= data
;
758 r
= safe_atou32(rvalue
, &k
);
760 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse channel value, ignoring: %s", rvalue
);
765 log_syntax(unit
, LOG_ERR
, filename
, line
, -EINVAL
, "Invalid %s value, ignoring: %s", lvalue
, rvalue
);
769 if (streq(lvalue
, "RxChannels")) {
770 channels
->rx_count
= k
;
771 channels
->rx_count_set
= true;
772 } else if (streq(lvalue
, "TxChannels")) {
773 channels
->tx_count
= k
;
774 channels
->tx_count_set
= true;
775 } else if (streq(lvalue
, "OtherChannels")) {
776 channels
->other_count
= k
;
777 channels
->other_count_set
= true;
778 } else if (streq(lvalue
, "CombinedChannels")) {
779 channels
->combined_count
= k
;
780 channels
->combined_count_set
= true;
786 int config_parse_advertise(const char *unit
,
787 const char *filename
,
790 unsigned section_line
,
796 uint32_t *advertise
= data
;
806 if (isempty(rvalue
)) {
807 /* Empty string resets the value. */
808 memzero(advertise
, sizeof(uint32_t) * N_ADVERTISE
);
813 _cleanup_free_
char *w
= NULL
;
814 enum ethtool_link_mode_bit_indices mode
;
816 r
= extract_first_word(&p
, &w
, NULL
, 0);
820 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to split advertise modes '%s', ignoring: %m", rvalue
);
826 mode
= ethtool_link_mode_bit_from_string(w
);
827 /* We reuse the kernel provided enum which does not contain negative value. So, the cast
828 * below is mandatory. Otherwise, the check below always passes and access an invalid address. */
829 if ((int) mode
< 0) {
830 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse advertise mode, ignoring: %s", w
);
834 advertise
[mode
/ 32] |= 1UL << (mode
% 32);