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 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
, "ethtool: 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
, "ethtool: 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
, "ethtool: 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
, "ethtool: 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
, "ethtool: 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
, "ethtool: 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
, "ethtool: 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(
585 uint32_t advertise
[static N_ADVERTISE
],
589 _cleanup_free_
struct ethtool_link_usettings
*u
= NULL
;
590 struct ifreq ifr
= {};
593 if (autonegotiation
!= AUTONEG_DISABLE
&& memeqzero(advertise
, sizeof(uint32_t) * N_ADVERTISE
)) {
594 log_info("ethtool: autonegotiation is unset or enabled, the speed and duplex are not writable.");
599 r
= ethtool_connect(fd
);
601 return log_warning_errno(r
, "ethtool: could not connect to ethtool: %m");
604 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
606 r
= get_glinksettings(*fd
, &ifr
, &u
);
608 r
= get_gset(*fd
, &ifr
, &u
);
610 return log_warning_errno(r
, "ethtool: Cannot get device settings for %s : %m", ifname
);
614 u
->base
.speed
= DIV_ROUND_UP(speed
, 1000000);
616 if (duplex
!= _DUP_INVALID
)
617 u
->base
.duplex
= duplex
;
619 if (port
!= _NET_DEV_PORT_INVALID
)
622 if (autonegotiation
>= 0)
623 u
->base
.autoneg
= autonegotiation
;
625 if (!memeqzero(advertise
, sizeof(uint32_t) * N_ADVERTISE
)) {
626 u
->base
.autoneg
= AUTONEG_ENABLE
;
627 memcpy(&u
->link_modes
.advertising
, advertise
, sizeof(uint32_t) * N_ADVERTISE
);
628 memzero((uint8_t*) &u
->link_modes
.advertising
+ sizeof(uint32_t) * N_ADVERTISE
,
629 ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES
- sizeof(uint32_t) * N_ADVERTISE
);
632 if (u
->base
.cmd
== ETHTOOL_GLINKSETTINGS
)
633 r
= set_slinksettings(*fd
, &ifr
, u
);
635 r
= set_sset(*fd
, &ifr
, u
);
637 return log_warning_errno(r
, "ethtool: Cannot set device settings for %s : %m", ifname
);
642 int ethtool_set_channels(int *fd
, const char *ifname
, netdev_channels
*channels
) {
643 struct ethtool_channels ecmd
= {
644 .cmd
= ETHTOOL_GCHANNELS
647 .ifr_data
= (void*) &ecmd
650 bool need_update
= false;
654 r
= ethtool_connect(fd
);
656 return log_warning_errno(r
, "ethtool: could not connect to ethtool: %m");
659 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
661 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
665 if (channels
->rx_count_set
&& ecmd
.rx_count
!= channels
->rx_count
) {
666 ecmd
.rx_count
= channels
->rx_count
;
670 if (channels
->tx_count_set
&& ecmd
.tx_count
!= channels
->tx_count
) {
671 ecmd
.tx_count
= channels
->tx_count
;
675 if (channels
->other_count_set
&& ecmd
.other_count
!= channels
->other_count
) {
676 ecmd
.other_count
= channels
->other_count
;
680 if (channels
->combined_count_set
&& ecmd
.combined_count
!= channels
->combined_count
) {
681 ecmd
.combined_count
= channels
->combined_count
;
686 ecmd
.cmd
= ETHTOOL_SCHANNELS
;
688 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
696 int config_parse_channel(const char *unit
,
697 const char *filename
,
700 unsigned section_line
,
706 netdev_channels
*channels
= data
;
716 r
= safe_atou32(rvalue
, &k
);
718 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse channel value, ignoring: %s", rvalue
);
723 log_syntax(unit
, LOG_ERR
, filename
, line
, -EINVAL
, "Invalid %s value, ignoring: %s", lvalue
, rvalue
);
727 if (streq(lvalue
, "RxChannels")) {
728 channels
->rx_count
= k
;
729 channels
->rx_count_set
= true;
730 } else if (streq(lvalue
, "TxChannels")) {
731 channels
->tx_count
= k
;
732 channels
->tx_count_set
= true;
733 } else if (streq(lvalue
, "OtherChannels")) {
734 channels
->other_count
= k
;
735 channels
->other_count_set
= true;
736 } else if (streq(lvalue
, "CombinedChannels")) {
737 channels
->combined_count
= k
;
738 channels
->combined_count_set
= true;
744 int config_parse_advertise(const char *unit
,
745 const char *filename
,
748 unsigned section_line
,
754 uint32_t *advertise
= data
;
764 if (isempty(rvalue
)) {
765 /* Empty string resets the value. */
766 memzero(advertise
, sizeof(uint32_t) * N_ADVERTISE
);
771 _cleanup_free_
char *w
= NULL
;
772 enum ethtool_link_mode_bit_indices mode
;
774 r
= extract_first_word(&p
, &w
, NULL
, 0);
778 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to split advertise modes '%s', ignoring: %m", rvalue
);
784 mode
= ethtool_link_mode_bit_from_string(w
);
785 /* We reuse the kernel provided enum which does not contain negative value. So, the cast
786 * below is mandatory. Otherwise, the check below always passes and access an invalid address. */
787 if ((int) mode
< 0) {
788 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse advertise mode, ignoring: %s", w
);
792 advertise
[mode
/ 32] |= 1UL << (mode
% 32);