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
[_NET_DEV_PORT_MAX
] = {
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
!= 0) {
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 u
->base
.autoneg
= link
->autonegotiation
;
617 if (!eqzero(link
->advertise
)) {
618 memcpy(&u
->link_modes
.advertising
, link
->advertise
, sizeof(link
->advertise
));
619 memzero((uint8_t*) &u
->link_modes
.advertising
+ sizeof(link
->advertise
),
620 ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES
- sizeof(link
->advertise
));
623 if (u
->base
.cmd
== ETHTOOL_GLINKSETTINGS
)
624 r
= set_slinksettings(*fd
, &ifr
, u
);
626 r
= set_sset(*fd
, &ifr
, u
);
628 return log_warning_errno(r
, "link_config: Cannot set device settings for %s : %m", ifname
);
633 int config_parse_channel(const char *unit
,
634 const char *filename
,
637 unsigned section_line
,
643 link_config
*config
= data
;
653 r
= safe_atou32(rvalue
, &k
);
655 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse channel value, ignoring: %s", rvalue
);
660 log_syntax(unit
, LOG_ERR
, filename
, line
, -EINVAL
, "Invalid %s value, ignoring: %s", lvalue
, rvalue
);
664 if (streq(lvalue
, "RxChannels")) {
665 config
->channels
.rx_count
= k
;
666 config
->channels
.rx_count_set
= true;
667 } else if (streq(lvalue
, "TxChannels")) {
668 config
->channels
.tx_count
= k
;
669 config
->channels
.tx_count_set
= true;
670 } else if (streq(lvalue
, "OtherChannels")) {
671 config
->channels
.other_count
= k
;
672 config
->channels
.other_count_set
= true;
673 } else if (streq(lvalue
, "CombinedChannels")) {
674 config
->channels
.combined_count
= k
;
675 config
->channels
.combined_count_set
= true;
681 int ethtool_set_channels(int *fd
, const char *ifname
, netdev_channels
*channels
) {
682 struct ethtool_channels ecmd
= {
683 .cmd
= ETHTOOL_GCHANNELS
686 .ifr_data
= (void*) &ecmd
689 bool need_update
= false;
693 r
= ethtool_connect(fd
);
695 return log_warning_errno(r
, "link_config: could not connect to ethtool: %m");
698 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
700 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
704 if (channels
->rx_count_set
&& ecmd
.rx_count
!= channels
->rx_count
) {
705 ecmd
.rx_count
= channels
->rx_count
;
709 if (channels
->tx_count_set
&& ecmd
.tx_count
!= channels
->tx_count
) {
710 ecmd
.tx_count
= channels
->tx_count
;
714 if (channels
->other_count_set
&& ecmd
.other_count
!= channels
->other_count
) {
715 ecmd
.other_count
= channels
->other_count
;
719 if (channels
->combined_count_set
&& ecmd
.combined_count
!= channels
->combined_count
) {
720 ecmd
.combined_count
= channels
->combined_count
;
725 ecmd
.cmd
= ETHTOOL_SCHANNELS
;
727 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
735 int config_parse_advertise(const char *unit
,
736 const char *filename
,
739 unsigned section_line
,
745 link_config
*config
= data
;
755 if (isempty(rvalue
)) {
756 /* Empty string resets the value. */
757 zero(config
->advertise
);
762 _cleanup_free_
char *w
= NULL
;
763 enum ethtool_link_mode_bit_indices mode
;
765 r
= extract_first_word(&p
, &w
, NULL
, 0);
769 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to split advertise modes '%s', ignoring: %m", rvalue
);
775 mode
= ethtool_link_mode_bit_from_string(w
);
777 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse advertise mode, ignoring: %s", w
);
781 config
->advertise
[mode
/ 32] |= 1UL << (mode
% 32);