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 "link-config.h"
13 #include "socket-util.h"
14 #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 < ELEMENTSOF(((struct link_config
){}).advertise
));
116 DEFINE_STRING_TABLE_LOOKUP(ethtool_link_mode_bit
, enum ethtool_link_mode_bit_indices
);
118 int ethtool_connect(int *ret
) {
121 assert_return(ret
, -EINVAL
);
123 fd
= socket_ioctl_fd();
132 int ethtool_get_driver(int *fd
, const char *ifname
, char **ret
) {
133 struct ethtool_drvinfo ecmd
= {
134 .cmd
= ETHTOOL_GDRVINFO
137 .ifr_data
= (void*) &ecmd
143 r
= ethtool_connect(fd
);
145 return log_warning_errno(r
, "link_config: could not connect to ethtool: %m");
148 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
150 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
154 d
= strdup(ecmd
.driver
);
162 int ethtool_set_speed(int *fd
, const char *ifname
, unsigned speed
, Duplex duplex
) {
163 struct ethtool_cmd ecmd
= {
167 .ifr_data
= (void*) &ecmd
169 bool need_update
= false;
172 if (speed
== 0 && duplex
== _DUP_INVALID
)
176 r
= ethtool_connect(fd
);
178 return log_warning_errno(r
, "link_config: could not connect to ethtool: %m");
181 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
183 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
187 if (ethtool_cmd_speed(&ecmd
) != speed
) {
188 ethtool_cmd_speed_set(&ecmd
, speed
);
194 if (ecmd
.duplex
!= DUPLEX_HALF
) {
195 ecmd
.duplex
= DUPLEX_HALF
;
200 if (ecmd
.duplex
!= DUPLEX_FULL
) {
201 ecmd
.duplex
= DUPLEX_FULL
;
210 ecmd
.cmd
= ETHTOOL_SSET
;
212 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
220 int ethtool_set_wol(int *fd
, const char *ifname
, WakeOnLan wol
) {
221 struct ethtool_wolinfo ecmd
= {
225 .ifr_data
= (void*) &ecmd
227 bool need_update
= false;
230 if (wol
== _WOL_INVALID
)
234 r
= ethtool_connect(fd
);
236 return log_warning_errno(r
, "link_config: could not connect to ethtool: %m");
239 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
241 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
247 if (ecmd
.wolopts
!= WAKE_PHY
) {
248 ecmd
.wolopts
= WAKE_PHY
;
253 if (ecmd
.wolopts
!= WAKE_UCAST
) {
254 ecmd
.wolopts
= WAKE_UCAST
;
259 if (ecmd
.wolopts
!= WAKE_MCAST
) {
260 ecmd
.wolopts
= WAKE_MCAST
;
265 if (ecmd
.wolopts
!= WAKE_BCAST
) {
266 ecmd
.wolopts
= WAKE_BCAST
;
271 if (ecmd
.wolopts
!= WAKE_ARP
) {
272 ecmd
.wolopts
= WAKE_ARP
;
277 if (ecmd
.wolopts
!= WAKE_MAGIC
) {
278 ecmd
.wolopts
= WAKE_MAGIC
;
282 case WOL_MAGICSECURE
:
283 if (ecmd
.wolopts
!= WAKE_MAGICSECURE
) {
284 ecmd
.wolopts
= WAKE_MAGICSECURE
;
289 if (ecmd
.wolopts
!= 0) {
299 ecmd
.cmd
= ETHTOOL_SWOL
;
301 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
309 static int get_stringset(int fd
, struct ifreq
*ifr
, int stringset_id
, struct ethtool_gstrings
**gstrings
) {
310 _cleanup_free_
struct ethtool_gstrings
*strings
= NULL
;
312 struct ethtool_sset_info info
;
316 .cmd
= ETHTOOL_GSSET_INFO
,
317 .sset_mask
= UINT64_C(1) << stringset_id
,
323 ifr
->ifr_data
= (void *) &buffer
.info
;
325 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
329 if (!buffer
.info
.sset_mask
)
332 len
= buffer
.info
.data
[0];
334 strings
= malloc0(sizeof(struct ethtool_gstrings
) + len
* ETH_GSTRING_LEN
);
338 strings
->cmd
= ETHTOOL_GSTRINGS
;
339 strings
->string_set
= stringset_id
;
342 ifr
->ifr_data
= (void *) strings
;
344 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
348 *gstrings
= TAKE_PTR(strings
);
353 static int find_feature_index(struct ethtool_gstrings
*strings
, const char *feature
) {
356 for (i
= 0; i
< strings
->len
; i
++) {
357 if (streq((char *) &strings
->data
[i
* ETH_GSTRING_LEN
], feature
))
364 int ethtool_set_features(int *fd
, const char *ifname
, int *features
) {
365 _cleanup_free_
struct ethtool_gstrings
*strings
= NULL
;
366 struct ethtool_sfeatures
*sfeatures
;
367 int block
, bit
, i
, r
;
368 struct ifreq ifr
= {};
371 r
= ethtool_connect(fd
);
373 return log_warning_errno(r
, "link_config: could not connect to ethtool: %m");
376 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
378 r
= get_stringset(*fd
, &ifr
, ETH_SS_FEATURES
, &strings
);
380 return log_warning_errno(r
, "link_config: could not get ethtool features for %s", ifname
);
382 sfeatures
= alloca0(sizeof(struct ethtool_sfeatures
) + DIV_ROUND_UP(strings
->len
, 32U) * sizeof(sfeatures
->features
[0]));
383 sfeatures
->cmd
= ETHTOOL_SFEATURES
;
384 sfeatures
->size
= DIV_ROUND_UP(strings
->len
, 32U);
386 for (i
= 0; i
< _NET_DEV_FEAT_MAX
; i
++) {
388 if (features
[i
] != -1) {
390 r
= find_feature_index(strings
, netdev_feature_table
[i
]);
392 log_warning_errno(r
, "link_config: could not find feature: %s", netdev_feature_table
[i
]);
399 sfeatures
->features
[block
].valid
|= 1 << bit
;
402 sfeatures
->features
[block
].requested
|= 1 << bit
;
404 sfeatures
->features
[block
].requested
&= ~(1 << bit
);
408 ifr
.ifr_data
= (void *) sfeatures
;
410 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
412 return log_warning_errno(r
, "link_config: could not set ethtool features for %s", ifname
);
417 static int get_glinksettings(int fd
, struct ifreq
*ifr
, struct ethtool_link_usettings
**g
) {
419 struct ethtool_link_settings req
;
420 __u32 link_mode_data
[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32
];
422 .req
.cmd
= ETHTOOL_GLINKSETTINGS
,
424 struct ethtool_link_usettings
*u
;
428 /* The interaction user/kernel via the new API requires a small ETHTOOL_GLINKSETTINGS
429 handshake first to agree on the length of the link mode bitmaps. If kernel doesn't
430 agree with user, it returns the bitmap length it is expecting from user as a negative
431 length (and cmd field is 0). When kernel and user agree, kernel returns valid info in
432 all fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS). Based on
433 https://github.com/torvalds/linux/commit/3f1ac7a700d039c61d8d8b99f28d605d489a60cf
436 ifr
->ifr_data
= (void *) &ecmd
;
438 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
442 if (ecmd
.req
.link_mode_masks_nwords
>= 0 || ecmd
.req
.cmd
!= ETHTOOL_GLINKSETTINGS
)
445 ecmd
.req
.link_mode_masks_nwords
= -ecmd
.req
.link_mode_masks_nwords
;
447 ifr
->ifr_data
= (void *) &ecmd
;
449 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
453 if (ecmd
.req
.link_mode_masks_nwords
<= 0 || ecmd
.req
.cmd
!= ETHTOOL_GLINKSETTINGS
)
456 u
= new0(struct ethtool_link_usettings
, 1);
463 memcpy(u
->link_modes
.supported
, &ecmd
.link_mode_data
[offset
], 4 * ecmd
.req
.link_mode_masks_nwords
);
465 offset
+= ecmd
.req
.link_mode_masks_nwords
;
466 memcpy(u
->link_modes
.advertising
, &ecmd
.link_mode_data
[offset
], 4 * ecmd
.req
.link_mode_masks_nwords
);
468 offset
+= ecmd
.req
.link_mode_masks_nwords
;
469 memcpy(u
->link_modes
.lp_advertising
, &ecmd
.link_mode_data
[offset
], 4 * ecmd
.req
.link_mode_masks_nwords
);
476 static int get_gset(int fd
, struct ifreq
*ifr
, struct ethtool_link_usettings
**u
) {
477 struct ethtool_link_usettings
*e
;
478 struct ethtool_cmd ecmd
= {
483 ifr
->ifr_data
= (void *) &ecmd
;
485 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
489 e
= new0(struct ethtool_link_usettings
, 1);
493 e
->base
.cmd
= ETHTOOL_GSET
;
495 e
->base
.link_mode_masks_nwords
= 1;
496 e
->base
.speed
= ethtool_cmd_speed(&ecmd
);
497 e
->base
.duplex
= ecmd
.duplex
;
498 e
->base
.port
= ecmd
.port
;
499 e
->base
.phy_address
= ecmd
.phy_address
;
500 e
->base
.autoneg
= ecmd
.autoneg
;
501 e
->base
.mdio_support
= ecmd
.mdio_support
;
503 e
->link_modes
.supported
[0] = ecmd
.supported
;
504 e
->link_modes
.advertising
[0] = ecmd
.advertising
;
505 e
->link_modes
.lp_advertising
[0] = ecmd
.lp_advertising
;
512 static int set_slinksettings(int fd
, struct ifreq
*ifr
, const struct ethtool_link_usettings
*u
) {
514 struct ethtool_link_settings req
;
515 __u32 link_mode_data
[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32
];
520 if (u
->base
.cmd
!= ETHTOOL_GLINKSETTINGS
|| u
->base
.link_mode_masks_nwords
<= 0)
524 ecmd
.req
.cmd
= ETHTOOL_SLINKSETTINGS
;
526 memcpy(&ecmd
.link_mode_data
[offset
], u
->link_modes
.supported
, 4 * ecmd
.req
.link_mode_masks_nwords
);
528 offset
+= ecmd
.req
.link_mode_masks_nwords
;
529 memcpy(&ecmd
.link_mode_data
[offset
], u
->link_modes
.advertising
, 4 * ecmd
.req
.link_mode_masks_nwords
);
531 offset
+= ecmd
.req
.link_mode_masks_nwords
;
532 memcpy(&ecmd
.link_mode_data
[offset
], u
->link_modes
.lp_advertising
, 4 * ecmd
.req
.link_mode_masks_nwords
);
534 ifr
->ifr_data
= (void *) &ecmd
;
536 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
543 static int set_sset(int fd
, struct ifreq
*ifr
, const struct ethtool_link_usettings
*u
) {
544 struct ethtool_cmd ecmd
= {
549 if (u
->base
.cmd
!= ETHTOOL_GSET
|| u
->base
.link_mode_masks_nwords
<= 0)
552 ecmd
.supported
= u
->link_modes
.supported
[0];
553 ecmd
.advertising
= u
->link_modes
.advertising
[0];
554 ecmd
.lp_advertising
= u
->link_modes
.lp_advertising
[0];
556 ethtool_cmd_speed_set(&ecmd
, u
->base
.speed
);
558 ecmd
.duplex
= u
->base
.duplex
;
559 ecmd
.port
= u
->base
.port
;
560 ecmd
.phy_address
= u
->base
.phy_address
;
561 ecmd
.autoneg
= u
->base
.autoneg
;
562 ecmd
.mdio_support
= u
->base
.mdio_support
;
563 ecmd
.eth_tp_mdix
= u
->base
.eth_tp_mdix
;
564 ecmd
.eth_tp_mdix_ctrl
= u
->base
.eth_tp_mdix_ctrl
;
566 ifr
->ifr_data
= (void *) &ecmd
;
568 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
575 /* If autonegotiation is disabled, the speed and duplex represent the fixed link
576 * mode and are writable if the driver supports multiple link modes. If it is
577 * enabled then they are read-only. If the link is up they represent the negotiated
578 * link mode; if the link is down, the speed is 0, %SPEED_UNKNOWN or the highest
579 * enabled speed and @duplex is %DUPLEX_UNKNOWN or the best enabled duplex mode.
581 int ethtool_set_glinksettings(int *fd
, const char *ifname
, struct link_config
*link
) {
582 _cleanup_free_
struct ethtool_link_usettings
*u
= NULL
;
583 struct ifreq ifr
= {};
586 if (link
->autonegotiation
!= AUTONEG_DISABLE
&& eqzero(link
->advertise
)) {
587 log_info("link_config: autonegotiation is unset or enabled, the speed and duplex are not writable.");
592 r
= ethtool_connect(fd
);
594 return log_warning_errno(r
, "link_config: could not connect to ethtool: %m");
597 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
599 r
= get_glinksettings(*fd
, &ifr
, &u
);
601 r
= get_gset(*fd
, &ifr
, &u
);
603 return log_warning_errno(r
, "link_config: Cannot get device settings for %s : %m", ifname
);
607 u
->base
.speed
= DIV_ROUND_UP(link
->speed
, 1000000);
609 if (link
->duplex
!= _DUP_INVALID
)
610 u
->base
.duplex
= link
->duplex
;
612 if (link
->port
!= _NET_DEV_PORT_INVALID
)
613 u
->base
.port
= link
->port
;
615 if (link
->autonegotiation
>= 0)
616 u
->base
.autoneg
= link
->autonegotiation
;
618 if (!eqzero(link
->advertise
)) {
619 u
->base
.autoneg
= AUTONEG_ENABLE
;
620 memcpy(&u
->link_modes
.advertising
, link
->advertise
, sizeof(link
->advertise
));
621 memzero((uint8_t*) &u
->link_modes
.advertising
+ sizeof(link
->advertise
),
622 ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES
- sizeof(link
->advertise
));
625 if (u
->base
.cmd
== ETHTOOL_GLINKSETTINGS
)
626 r
= set_slinksettings(*fd
, &ifr
, u
);
628 r
= set_sset(*fd
, &ifr
, u
);
630 return log_warning_errno(r
, "link_config: Cannot set device settings for %s : %m", ifname
);
635 int config_parse_channel(const char *unit
,
636 const char *filename
,
639 unsigned section_line
,
645 link_config
*config
= data
;
655 r
= safe_atou32(rvalue
, &k
);
657 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse channel value, ignoring: %s", rvalue
);
662 log_syntax(unit
, LOG_ERR
, filename
, line
, -EINVAL
, "Invalid %s value, ignoring: %s", lvalue
, rvalue
);
666 if (streq(lvalue
, "RxChannels")) {
667 config
->channels
.rx_count
= k
;
668 config
->channels
.rx_count_set
= true;
669 } else if (streq(lvalue
, "TxChannels")) {
670 config
->channels
.tx_count
= k
;
671 config
->channels
.tx_count_set
= true;
672 } else if (streq(lvalue
, "OtherChannels")) {
673 config
->channels
.other_count
= k
;
674 config
->channels
.other_count_set
= true;
675 } else if (streq(lvalue
, "CombinedChannels")) {
676 config
->channels
.combined_count
= k
;
677 config
->channels
.combined_count_set
= true;
683 int ethtool_set_channels(int *fd
, const char *ifname
, netdev_channels
*channels
) {
684 struct ethtool_channels ecmd
= {
685 .cmd
= ETHTOOL_GCHANNELS
688 .ifr_data
= (void*) &ecmd
691 bool need_update
= false;
695 r
= ethtool_connect(fd
);
697 return log_warning_errno(r
, "link_config: could not connect to ethtool: %m");
700 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
702 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
706 if (channels
->rx_count_set
&& ecmd
.rx_count
!= channels
->rx_count
) {
707 ecmd
.rx_count
= channels
->rx_count
;
711 if (channels
->tx_count_set
&& ecmd
.tx_count
!= channels
->tx_count
) {
712 ecmd
.tx_count
= channels
->tx_count
;
716 if (channels
->other_count_set
&& ecmd
.other_count
!= channels
->other_count
) {
717 ecmd
.other_count
= channels
->other_count
;
721 if (channels
->combined_count_set
&& ecmd
.combined_count
!= channels
->combined_count
) {
722 ecmd
.combined_count
= channels
->combined_count
;
727 ecmd
.cmd
= ETHTOOL_SCHANNELS
;
729 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
737 int config_parse_advertise(const char *unit
,
738 const char *filename
,
741 unsigned section_line
,
747 link_config
*config
= data
;
757 if (isempty(rvalue
)) {
758 /* Empty string resets the value. */
759 zero(config
->advertise
);
764 _cleanup_free_
char *w
= NULL
;
765 enum ethtool_link_mode_bit_indices mode
;
767 r
= extract_first_word(&p
, &w
, NULL
, 0);
771 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to split advertise modes '%s', ignoring: %m", rvalue
);
777 mode
= ethtool_link_mode_bit_from_string(w
);
778 /* We reuse the kernel provided enum which does not contain negative value. So, the cast
779 * below is mandatory. Otherwise, the check below always passes and access an invalid address. */
780 if ((int) mode
< 0) {
781 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse advertise mode, ignoring: %s", w
);
785 config
->advertise
[mode
/ 32] |= 1UL << (mode
% 32);