]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
a5010333 | 2 | |
a5010333 | 3 | #include <net/if.h> |
8b43440b | 4 | #include <sys/ioctl.h> |
a5010333 | 5 | #include <linux/ethtool.h> |
79b4428a | 6 | #include <linux/netdevice.h> |
a5010333 TG |
7 | #include <linux/sockios.h> |
8 | ||
8b43440b | 9 | #include "conf-parser.h" |
a5010333 | 10 | #include "ethtool-util.h" |
5c2316c6 | 11 | #include "extract-word.h" |
6666c4fa | 12 | #include "fd-util.h" |
ab1263d7 | 13 | #include "log.h" |
0a970718 | 14 | #include "memory-util.h" |
429b4350 | 15 | #include "socket-util.h" |
8b43440b | 16 | #include "string-table.h" |
6c35ea5e | 17 | #include "strv.h" |
a5010333 | 18 | #include "strxcpyx.h" |
5fde13d7 | 19 | |
2c5859af | 20 | static const char* const duplex_table[_DUP_MAX] = { |
5fde13d7 TG |
21 | [DUP_FULL] = "full", |
22 | [DUP_HALF] = "half" | |
23 | }; | |
24 | ||
25 | DEFINE_STRING_TABLE_LOOKUP(duplex, Duplex); | |
26 | DEFINE_CONFIG_PARSE_ENUM(config_parse_duplex, duplex, Duplex, "Failed to parse duplex setting"); | |
27 | ||
c50404ae YW |
28 | static const struct { |
29 | uint32_t opt; | |
30 | const char *name; | |
31 | } wol_option_map[] = { | |
32 | { WAKE_PHY, "phy" }, | |
33 | { WAKE_UCAST, "unicast", }, | |
34 | { WAKE_MCAST, "multicast", }, | |
35 | { WAKE_BCAST, "broadcast", }, | |
36 | { WAKE_ARP, "arp", }, | |
37 | { WAKE_MAGIC, "magic", }, | |
38 | { WAKE_MAGICSECURE, "secureon", }, | |
5fde13d7 TG |
39 | }; |
40 | ||
c50404ae YW |
41 | int wol_options_to_string_alloc(uint32_t opts, char **ret) { |
42 | _cleanup_free_ char *str = NULL; | |
43 | ||
44 | assert(ret); | |
45 | ||
b4b2a492 YW |
46 | if (opts == UINT32_MAX) { |
47 | *ret = NULL; | |
48 | return 0; | |
49 | } | |
50 | ||
c50404ae YW |
51 | for (size_t i = 0; i < ELEMENTSOF(wol_option_map); i++) |
52 | if (opts & wol_option_map[i].opt && | |
53 | !strextend_with_separator(&str, ",", wol_option_map[i].name)) | |
54 | return -ENOMEM; | |
55 | ||
56 | if (!str) { | |
57 | str = strdup("off"); | |
58 | if (!str) | |
59 | return -ENOMEM; | |
60 | } | |
61 | ||
62 | *ret = TAKE_PTR(str); | |
b4b2a492 | 63 | return 1; |
c50404ae | 64 | } |
a5010333 | 65 | |
44909f1c | 66 | static const char* const port_table[] = { |
593022fa SS |
67 | [NET_DEV_PORT_TP] = "tp", |
68 | [NET_DEV_PORT_AUI] = "aui", | |
69 | [NET_DEV_PORT_MII] = "mii", | |
70 | [NET_DEV_PORT_FIBRE] = "fibre", | |
44909f1c | 71 | [NET_DEV_PORT_BNC] = "bnc", |
593022fa SS |
72 | }; |
73 | ||
74 | DEFINE_STRING_TABLE_LOOKUP(port, NetDevPort); | |
75 | DEFINE_CONFIG_PARSE_ENUM(config_parse_port, port, NetDevPort, "Failed to parse Port setting"); | |
76 | ||
18f84f8a YW |
77 | static const char* const mdi_table[] = { |
78 | [ETH_TP_MDI_INVALID] = "unknown", | |
79 | [ETH_TP_MDI] = "mdi", | |
80 | [ETH_TP_MDI_X] = "mdi-x", | |
81 | [ETH_TP_MDI_AUTO] = "auto", | |
82 | }; | |
83 | ||
84 | DEFINE_STRING_TABLE_LOOKUP_TO_STRING(mdi, int); | |
85 | ||
50725d10 | 86 | static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = { |
77bf5c31 YW |
87 | [NET_DEV_FEAT_SG] = "tx-scatter-gather", |
88 | [NET_DEV_FEAT_IP_CSUM] = "tx-checksum-ipv4", | |
89 | [NET_DEV_FEAT_HW_CSUM] = "tx-checksum-ip-generic", | |
90 | [NET_DEV_FEAT_IPV6_CSUM] = "tx-checksum-ipv6", | |
91 | [NET_DEV_FEAT_HIGHDMA] = "highdma", | |
92 | [NET_DEV_FEAT_FRAGLIST] = "tx-scatter-gather-fraglist", | |
93 | [NET_DEV_FEAT_HW_VLAN_CTAG_TX] = "tx-vlan-hw-insert", | |
94 | [NET_DEV_FEAT_HW_VLAN_CTAG_RX] = "rx-vlan-hw-parse", | |
95 | [NET_DEV_FEAT_HW_VLAN_CTAG_FILTER] = "rx-vlan-filter", | |
96 | [NET_DEV_FEAT_HW_VLAN_STAG_TX] = "tx-vlan-stag-hw-insert", | |
97 | [NET_DEV_FEAT_HW_VLAN_STAG_RX] = "rx-vlan-stag-hw-parse", | |
98 | [NET_DEV_FEAT_HW_VLAN_STAG_FILTER] = "rx-vlan-stag-filter", | |
99 | [NET_DEV_FEAT_VLAN_CHALLENGED] = "vlan-challenged", | |
100 | [NET_DEV_FEAT_GSO] = "tx-generic-segmentation", | |
101 | [NET_DEV_FEAT_LLTX] = "tx-lockless", | |
102 | [NET_DEV_FEAT_NETNS_LOCAL] = "netns-local", | |
103 | [NET_DEV_FEAT_GRO] = "rx-gro", | |
104 | [NET_DEV_FEAT_GRO_HW] = "rx-gro-hw", | |
105 | [NET_DEV_FEAT_LRO] = "rx-lro", | |
106 | [NET_DEV_FEAT_TSO] = "tx-tcp-segmentation", | |
107 | [NET_DEV_FEAT_GSO_ROBUST] = "tx-gso-robust", | |
108 | [NET_DEV_FEAT_TSO_ECN] = "tx-tcp-ecn-segmentation", | |
109 | [NET_DEV_FEAT_TSO_MANGLEID] = "tx-tcp-mangleid-segmentation", | |
110 | [NET_DEV_FEAT_TSO6] = "tx-tcp6-segmentation", | |
111 | [NET_DEV_FEAT_FSO] = "tx-fcoe-segmentation", | |
112 | [NET_DEV_FEAT_GSO_GRE] = "tx-gre-segmentation", | |
113 | [NET_DEV_FEAT_GSO_GRE_CSUM] = "tx-gre-csum-segmentation", | |
114 | [NET_DEV_FEAT_GSO_IPXIP4] = "tx-ipxip4-segmentation", | |
115 | [NET_DEV_FEAT_GSO_IPXIP6] = "tx-ipxip6-segmentation", | |
116 | [NET_DEV_FEAT_GSO_UDP_TUNNEL] = "tx-udp_tnl-segmentation", | |
117 | [NET_DEV_FEAT_GSO_UDP_TUNNEL_CSUM] = "tx-udp_tnl-csum-segmentation", | |
118 | [NET_DEV_FEAT_GSO_PARTIAL] = "tx-gso-partial", | |
119 | [NET_DEV_FEAT_GSO_TUNNEL_REMCSUM] = "tx-tunnel-remcsum-segmentation", | |
120 | [NET_DEV_FEAT_GSO_SCTP] = "tx-sctp-segmentation", | |
121 | [NET_DEV_FEAT_GSO_ESP] = "tx-esp-segmentation", | |
122 | [NET_DEV_FEAT_GSO_UDP_L4] = "tx-udp-segmentation", | |
123 | [NET_DEV_FEAT_GSO_FRAGLIST] = "tx-gso-list", | |
124 | [NET_DEV_FEAT_FCOE_CRC] = "tx-checksum-fcoe-crc", | |
125 | [NET_DEV_FEAT_SCTP_CRC] = "tx-checksum-sctp", | |
126 | [NET_DEV_FEAT_FCOE_MTU] = "fcoe-mtu", | |
127 | [NET_DEV_FEAT_NTUPLE] = "rx-ntuple-filter", | |
128 | [NET_DEV_FEAT_RXHASH] = "rx-hashing", | |
129 | [NET_DEV_FEAT_RXCSUM] = "rx-checksum", | |
130 | [NET_DEV_FEAT_NOCACHE_COPY] = "tx-nocache-copy", | |
131 | [NET_DEV_FEAT_LOOPBACK] = "loopback", | |
132 | [NET_DEV_FEAT_RXFCS] = "rx-fcs", | |
133 | [NET_DEV_FEAT_RXALL] = "rx-all", | |
134 | [NET_DEV_FEAT_HW_L2FW_DOFFLOAD] = "l2-fwd-offload", | |
135 | [NET_DEV_FEAT_HW_TC] = "hw-tc-offload", | |
136 | [NET_DEV_FEAT_HW_ESP] = "esp-hw-offload", | |
137 | [NET_DEV_FEAT_HW_ESP_TX_CSUM] = "esp-tx-csum-hw-offload", | |
138 | [NET_DEV_FEAT_RX_UDP_TUNNEL_PORT] = "rx-udp_tunnel-port-offload", | |
139 | [NET_DEV_FEAT_HW_TLS_RECORD] = "tls-hw-record", | |
140 | [NET_DEV_FEAT_HW_TLS_TX] = "tls-hw-tx-offload", | |
141 | [NET_DEV_FEAT_HW_TLS_RX] = "tls-hw-rx-offload", | |
142 | [NET_DEV_FEAT_GRO_FRAGLIST] = "rx-gro-list", | |
143 | [NET_DEV_FEAT_HW_MACSEC] = "macsec-hw-offload", | |
144 | [NET_DEV_FEAT_GRO_UDP_FWD] = "rx-udp-gro-forwarding", | |
145 | [NET_DEV_FEAT_HW_HSR_TAG_INS] = "hsr-tag-ins-offload", | |
146 | [NET_DEV_FEAT_HW_HSR_TAG_RM] = "hsr-tag-rm-offload", | |
147 | [NET_DEV_FEAT_HW_HSR_FWD] = "hsr-fwd-offload", | |
148 | [NET_DEV_FEAT_HW_HSR_DUP] = "hsr-dup-offload", | |
7a4f2035 | 149 | |
77bf5c31 | 150 | [NET_DEV_FEAT_TXCSUM] = "tx-checksum-", /* The suffix "-" means any feature beginning with "tx-checksum-" */ |
50725d10 SS |
151 | }; |
152 | ||
64d9f756 | 153 | static const char* const ethtool_link_mode_bit_table[] = { |
6d028889 YW |
154 | [ETHTOOL_LINK_MODE_10baseT_Half_BIT] = "10baset-half", |
155 | [ETHTOOL_LINK_MODE_10baseT_Full_BIT] = "10baset-full", | |
156 | [ETHTOOL_LINK_MODE_100baseT_Half_BIT] = "100baset-half", | |
157 | [ETHTOOL_LINK_MODE_100baseT_Full_BIT] = "100baset-full", | |
158 | [ETHTOOL_LINK_MODE_1000baseT_Half_BIT] = "1000baset-half", | |
159 | [ETHTOOL_LINK_MODE_1000baseT_Full_BIT] = "1000baset-full", | |
160 | [ETHTOOL_LINK_MODE_Autoneg_BIT] = "autonegotiation", | |
161 | [ETHTOOL_LINK_MODE_TP_BIT] = "tp", | |
162 | [ETHTOOL_LINK_MODE_AUI_BIT] = "aui", | |
163 | [ETHTOOL_LINK_MODE_MII_BIT] = "mii", | |
164 | [ETHTOOL_LINK_MODE_FIBRE_BIT] = "fibre", | |
165 | [ETHTOOL_LINK_MODE_BNC_BIT] = "bnc", | |
166 | [ETHTOOL_LINK_MODE_10000baseT_Full_BIT] = "10000baset-full", | |
167 | [ETHTOOL_LINK_MODE_Pause_BIT] = "pause", | |
168 | [ETHTOOL_LINK_MODE_Asym_Pause_BIT] = "asym-pause", | |
169 | [ETHTOOL_LINK_MODE_2500baseX_Full_BIT] = "2500basex-full", | |
170 | [ETHTOOL_LINK_MODE_Backplane_BIT] = "backplane", | |
171 | [ETHTOOL_LINK_MODE_1000baseKX_Full_BIT] = "1000basekx-full", | |
172 | [ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT] = "10000basekx4-full", | |
173 | [ETHTOOL_LINK_MODE_10000baseKR_Full_BIT] = "10000basekr-full", | |
174 | [ETHTOOL_LINK_MODE_10000baseR_FEC_BIT] = "10000baser-fec", | |
175 | [ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT] = "20000basemld2-full", | |
176 | [ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT] = "20000basekr2-full", | |
177 | [ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT] = "40000basekr4-full", | |
178 | [ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT] = "40000basecr4-full", | |
179 | [ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT] = "40000basesr4-full", | |
180 | [ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT] = "40000baselr4-full", | |
181 | [ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT] = "56000basekr4-full", | |
182 | [ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT] = "56000basecr4-full", | |
183 | [ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT] = "56000basesr4-full", | |
184 | [ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT] = "56000baselr4-full", | |
185 | [ETHTOOL_LINK_MODE_25000baseCR_Full_BIT] = "25000basecr-full", | |
186 | [ETHTOOL_LINK_MODE_25000baseKR_Full_BIT] = "25000basekr-full", | |
187 | [ETHTOOL_LINK_MODE_25000baseSR_Full_BIT] = "25000basesr-full", | |
188 | [ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT] = "50000basecr2-full", | |
189 | [ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT] = "50000basekr2-full", | |
190 | [ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT] = "100000basekr4-full", | |
191 | [ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT] = "100000basesr4-full", | |
192 | [ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT] = "100000basecr4-full", | |
193 | [ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT] = "100000baselr4-er4-full", | |
194 | [ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT] = "50000basesr2-full", | |
195 | [ETHTOOL_LINK_MODE_1000baseX_Full_BIT] = "1000basex-full", | |
196 | [ETHTOOL_LINK_MODE_10000baseCR_Full_BIT] = "10000basecr-full", | |
197 | [ETHTOOL_LINK_MODE_10000baseSR_Full_BIT] = "10000basesr-full", | |
198 | [ETHTOOL_LINK_MODE_10000baseLR_Full_BIT] = "10000baselr-full", | |
199 | [ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT] = "10000baselrm-full", | |
200 | [ETHTOOL_LINK_MODE_10000baseER_Full_BIT] = "10000baseer-full", | |
201 | [ETHTOOL_LINK_MODE_2500baseT_Full_BIT] = "2500baset-full", | |
202 | [ETHTOOL_LINK_MODE_5000baseT_Full_BIT] = "5000baset-full", | |
203 | [ETHTOOL_LINK_MODE_FEC_NONE_BIT] = "fec-none", | |
204 | [ETHTOOL_LINK_MODE_FEC_RS_BIT] = "fec-rs", | |
205 | [ETHTOOL_LINK_MODE_FEC_BASER_BIT] = "fec-baser", | |
41f42696 YW |
206 | [ETHTOOL_LINK_MODE_50000baseKR_Full_BIT] = "50000basekr-full", |
207 | [ETHTOOL_LINK_MODE_50000baseSR_Full_BIT] = "50000basesr-full", | |
208 | [ETHTOOL_LINK_MODE_50000baseCR_Full_BIT] = "50000basecr-full", | |
209 | [ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT] = "50000baselr-er-fr-full", | |
210 | [ETHTOOL_LINK_MODE_50000baseDR_Full_BIT] = "50000basedr-full", | |
211 | [ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT] = "100000basekr2-full", | |
212 | [ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT] = "100000basesr2-full", | |
213 | [ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT] = "100000basecr2-full", | |
214 | [ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT] = "100000baselr2-er2-fr2-full", | |
215 | [ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT] = "100000basedr2-full", | |
216 | [ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT] = "200000basekr4-full", | |
217 | [ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT] = "200000basesr4-full", | |
218 | [ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT] = "200000baselr4-er4-fr4-full", | |
219 | [ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT] = "200000basedr4-full", | |
220 | [ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT] = "200000basecr4-full", | |
fe2aeef8 YW |
221 | [ETHTOOL_LINK_MODE_100baseT1_Full_BIT] = "100baset1-full", |
222 | [ETHTOOL_LINK_MODE_1000baseT1_Full_BIT] = "1000baset1-full", | |
223 | [ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT] = "400000basekr8-full", | |
224 | [ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT] = "400000basesr8-full", | |
225 | [ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT] = "400000baselr8-er8-fr8-full", | |
226 | [ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT] = "400000basedr8-full", | |
227 | [ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT] = "400000basecr8-full", | |
228 | [ETHTOOL_LINK_MODE_FEC_LLRS_BIT] = "fec-llrs", | |
229 | [ETHTOOL_LINK_MODE_100000baseKR_Full_BIT] = "100000basekr-full", | |
230 | [ETHTOOL_LINK_MODE_100000baseSR_Full_BIT] = "100000basesr-full", | |
231 | [ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT] = "100000baselr-er-fr-full", | |
232 | [ETHTOOL_LINK_MODE_100000baseCR_Full_BIT] = "100000basecr-full", | |
233 | [ETHTOOL_LINK_MODE_100000baseDR_Full_BIT] = "100000basedr-full", | |
234 | [ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT] = "200000basekr2-full", | |
235 | [ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT] = "200000basesr2-full", | |
236 | [ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT] = "200000baselr2-er2-fr2-full", | |
237 | [ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT] = "200000basedr2-full", | |
238 | [ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT] = "200000basecr2-full", | |
239 | [ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT] = "400000basekr4-full", | |
240 | [ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT] = "400000basesr4-full", | |
241 | [ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT] = "400000baselr4-er4-fr4-full", | |
242 | [ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT] = "400000basedr4-full", | |
243 | [ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT] = "400000basecr4-full", | |
131e4dea YW |
244 | [ETHTOOL_LINK_MODE_100baseFX_Half_BIT] = "100basefx-half", |
245 | [ETHTOOL_LINK_MODE_100baseFX_Full_BIT] = "100basefx-full", | |
6cf0a204 | 246 | }; |
5dd10118 | 247 | /* Make sure the array is large enough to fit all bits */ |
5c2316c6 | 248 | assert_cc((ELEMENTSOF(ethtool_link_mode_bit_table)-1) / 32 < N_ADVERTISE); |
6cf0a204 | 249 | |
2d18ac44 | 250 | DEFINE_STRING_TABLE_LOOKUP(ethtool_link_mode_bit, enum ethtool_link_mode_bit_indices); |
6cf0a204 | 251 | |
4f504031 | 252 | static int ethtool_connect(int *ethtool_fd) { |
a5010333 TG |
253 | int fd; |
254 | ||
4f504031 YW |
255 | assert(ethtool_fd); |
256 | ||
257 | /* This does nothing if already connected. */ | |
258 | if (*ethtool_fd >= 0) | |
259 | return 0; | |
a5010333 | 260 | |
429b4350 | 261 | fd = socket_ioctl_fd(); |
ece174c5 | 262 | if (fd < 0) |
11a288e8 | 263 | return log_debug_errno(fd, "ethtool: could not create control socket: %m"); |
2b44daaa | 264 | |
4f504031 | 265 | *ethtool_fd = fd; |
a5010333 TG |
266 | return 0; |
267 | } | |
268 | ||
64be35ab | 269 | int ethtool_get_driver(int *ethtool_fd, const char *ifname, char **ret) { |
61f3af4f | 270 | struct ethtool_drvinfo ecmd = { |
a93187ce | 271 | .cmd = ETHTOOL_GDRVINFO, |
61f3af4f LP |
272 | }; |
273 | struct ifreq ifr = { | |
a93187ce | 274 | .ifr_data = (void*) &ecmd, |
61f3af4f LP |
275 | }; |
276 | char *d; | |
847a8a5f TG |
277 | int r; |
278 | ||
a93187ce YW |
279 | assert(ethtool_fd); |
280 | assert(ifname); | |
281 | assert(ret); | |
282 | ||
4f504031 YW |
283 | r = ethtool_connect(ethtool_fd); |
284 | if (r < 0) | |
285 | return r; | |
aedca892 | 286 | |
6d9a72f3 | 287 | strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname); |
847a8a5f | 288 | |
4253dab5 | 289 | if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0) |
847a8a5f TG |
290 | return -errno; |
291 | ||
861de64e YW |
292 | if (isempty(ecmd.driver)) |
293 | return -ENODATA; | |
294 | ||
61f3af4f LP |
295 | d = strdup(ecmd.driver); |
296 | if (!d) | |
847a8a5f TG |
297 | return -ENOMEM; |
298 | ||
61f3af4f | 299 | *ret = d; |
847a8a5f TG |
300 | return 0; |
301 | } | |
302 | ||
a93187ce YW |
303 | int ethtool_get_link_info( |
304 | int *ethtool_fd, | |
305 | const char *ifname, | |
306 | int *ret_autonegotiation, | |
307 | uint64_t *ret_speed, | |
308 | Duplex *ret_duplex, | |
309 | NetDevPort *ret_port) { | |
310 | ||
33a8695f YW |
311 | struct ethtool_cmd ecmd = { |
312 | .cmd = ETHTOOL_GSET, | |
313 | }; | |
314 | struct ifreq ifr = { | |
315 | .ifr_data = (void*) &ecmd, | |
316 | }; | |
317 | int r; | |
318 | ||
a93187ce YW |
319 | assert(ethtool_fd); |
320 | assert(ifname); | |
321 | ||
4f504031 YW |
322 | r = ethtool_connect(ethtool_fd); |
323 | if (r < 0) | |
324 | return r; | |
33a8695f | 325 | |
6d9a72f3 | 326 | strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname); |
33a8695f | 327 | |
4253dab5 | 328 | if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0) |
33a8695f YW |
329 | return -errno; |
330 | ||
331 | if (ret_autonegotiation) | |
332 | *ret_autonegotiation = ecmd.autoneg; | |
333 | ||
d16c2728 YW |
334 | if (ret_speed) { |
335 | uint32_t speed; | |
336 | ||
337 | speed = ethtool_cmd_speed(&ecmd); | |
338 | *ret_speed = speed == (uint32_t) SPEED_UNKNOWN ? | |
2f665f24 | 339 | UINT64_MAX : (uint64_t) speed * 1000 * 1000; |
d16c2728 | 340 | } |
33a8695f YW |
341 | |
342 | if (ret_duplex) | |
343 | *ret_duplex = ecmd.duplex; | |
344 | ||
345 | if (ret_port) | |
346 | *ret_port = ecmd.port; | |
347 | ||
348 | return 0; | |
349 | } | |
350 | ||
ed9fa69f | 351 | int ethtool_get_permanent_hw_addr(int *ethtool_fd, const char *ifname, struct hw_addr_data *ret) { |
254d1313 | 352 | _cleanup_close_ int fd = -EBADF; |
0475919b ZJS |
353 | struct { |
354 | struct ethtool_perm_addr addr; | |
ed9fa69f | 355 | uint8_t space[HW_ADDR_MAX_SIZE]; |
0475919b ZJS |
356 | } epaddr = { |
357 | .addr.cmd = ETHTOOL_GPERMADDR, | |
ed9fa69f | 358 | .addr.size = HW_ADDR_MAX_SIZE, |
0475919b ZJS |
359 | }; |
360 | struct ifreq ifr = { | |
361 | .ifr_data = (caddr_t) &epaddr, | |
362 | }; | |
79b4428a YW |
363 | int r; |
364 | ||
79b4428a YW |
365 | assert(ifname); |
366 | assert(ret); | |
367 | ||
6666c4fa ZJS |
368 | if (!ethtool_fd) |
369 | ethtool_fd = &fd; | |
4f504031 YW |
370 | r = ethtool_connect(ethtool_fd); |
371 | if (r < 0) | |
372 | return r; | |
79b4428a | 373 | |
6d9a72f3 | 374 | strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname); |
79b4428a | 375 | |
4253dab5 | 376 | if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0) |
79b4428a YW |
377 | return -errno; |
378 | ||
ed9fa69f YW |
379 | if (epaddr.addr.size == 0) |
380 | return -ENODATA; | |
79b4428a | 381 | |
ed9fa69f YW |
382 | if (epaddr.addr.size > HW_ADDR_MAX_SIZE) |
383 | return -EINVAL; | |
79b4428a | 384 | |
ed9fa69f YW |
385 | ret->length = epaddr.addr.size; |
386 | memcpy(ret->bytes, epaddr.addr.data, epaddr.addr.size); | |
79b4428a YW |
387 | return 0; |
388 | } | |
389 | ||
ba103059 YW |
390 | #define UPDATE(dest, val, updated) \ |
391 | do { \ | |
392 | typeof(val) _v = (val); \ | |
393 | if (dest != _v) \ | |
394 | updated = true; \ | |
395 | dest = _v; \ | |
79893116 | 396 | } while (false) |
ba103059 | 397 | |
0d341ecc YW |
398 | #define UPDATE_WITH_MAX(dest, max, val, updated) \ |
399 | do { \ | |
400 | typeof(dest) _v = (val); \ | |
401 | typeof(dest) _max = (max); \ | |
402 | if (_v == 0 || _v > _max) \ | |
403 | _v = _max; \ | |
404 | if (dest != _v) \ | |
405 | updated = true; \ | |
406 | dest = _v; \ | |
79893116 | 407 | } while (false) |
0d341ecc | 408 | |
9bd3ecdd YW |
409 | int ethtool_set_wol( |
410 | int *ethtool_fd, | |
411 | const char *ifname, | |
412 | uint32_t wolopts, | |
413 | const uint8_t password[SOPASS_MAX]) { | |
414 | ||
6c0519c0 | 415 | struct ethtool_wolinfo ecmd = { |
a93187ce | 416 | .cmd = ETHTOOL_GWOL, |
6c0519c0 TG |
417 | }; |
418 | struct ifreq ifr = { | |
a93187ce | 419 | .ifr_data = (void*) &ecmd, |
6c0519c0 | 420 | }; |
0a2c2294 | 421 | bool need_update = false; |
a5010333 TG |
422 | int r; |
423 | ||
a93187ce YW |
424 | assert(ethtool_fd); |
425 | assert(ifname); | |
426 | ||
9bd3ecdd YW |
427 | if (wolopts == UINT32_MAX && !password) |
428 | /* Nothing requested. Return earlier. */ | |
a5010333 TG |
429 | return 0; |
430 | ||
4f504031 YW |
431 | r = ethtool_connect(ethtool_fd); |
432 | if (r < 0) | |
433 | return r; | |
aedca892 | 434 | |
6d9a72f3 | 435 | strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname); |
a5010333 | 436 | |
692597c8 LP |
437 | CLEANUP_ERASE(ecmd); |
438 | ||
4253dab5 | 439 | if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0) |
a5010333 TG |
440 | return -errno; |
441 | ||
9bd3ecdd YW |
442 | if (wolopts == UINT32_MAX) { |
443 | /* When password is specified without valid WoL options specified, then enable | |
444 | * WAKE_MAGICSECURE flag if supported. */ | |
445 | wolopts = ecmd.wolopts; | |
446 | if (password && FLAGS_SET(ecmd.supported, WAKE_MAGICSECURE)) | |
447 | wolopts |= WAKE_MAGICSECURE; | |
448 | } | |
449 | ||
20274ab8 YW |
450 | if ((wolopts & ~ecmd.supported) != 0) { |
451 | _cleanup_free_ char *str = NULL; | |
452 | ||
453 | (void) wol_options_to_string_alloc(wolopts & ~ecmd.supported, &str); | |
0923b425 | 454 | log_debug("Network interface %s does not support requested Wake on LAN options \"%s\", ignoring.", |
20274ab8 YW |
455 | ifname, strna(str)); |
456 | ||
457 | wolopts &= ecmd.supported; | |
458 | } | |
459 | ||
9bd3ecdd YW |
460 | if (!FLAGS_SET(wolopts, WAKE_MAGICSECURE)) |
461 | /* When WAKE_MAGICSECURE flag is not set, then ignore password. */ | |
462 | password = NULL; | |
463 | ||
c50404ae | 464 | UPDATE(ecmd.wolopts, wolopts, need_update); |
9bd3ecdd YW |
465 | if (password && |
466 | memcmp(ecmd.sopass, password, sizeof(ecmd.sopass)) != 0) { | |
467 | memcpy(ecmd.sopass, password, sizeof(ecmd.sopass)); | |
468 | need_update = true; | |
469 | } | |
a5010333 | 470 | |
692597c8 | 471 | if (!need_update) |
c50404ae | 472 | return 0; |
a5010333 | 473 | |
c50404ae | 474 | ecmd.cmd = ETHTOOL_SWOL; |
692597c8 | 475 | return RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr)); |
a5010333 | 476 | } |
50725d10 | 477 | |
cadc7ed2 | 478 | int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netdev_ring_param *ring) { |
224ded67 | 479 | struct ethtool_ringparam ecmd = { |
a93187ce | 480 | .cmd = ETHTOOL_GRINGPARAM, |
224ded67 SS |
481 | }; |
482 | struct ifreq ifr = { | |
a93187ce | 483 | .ifr_data = (void*) &ecmd, |
224ded67 SS |
484 | }; |
485 | bool need_update = false; | |
486 | int r; | |
487 | ||
a93187ce YW |
488 | assert(ethtool_fd); |
489 | assert(ifname); | |
490 | assert(ring); | |
491 | ||
0d341ecc YW |
492 | if (!ring->rx.set && |
493 | !ring->rx_mini.set && | |
494 | !ring->rx_jumbo.set && | |
495 | !ring->tx.set) | |
ba103059 YW |
496 | return 0; |
497 | ||
4f504031 YW |
498 | r = ethtool_connect(ethtool_fd); |
499 | if (r < 0) | |
500 | return r; | |
224ded67 | 501 | |
6d9a72f3 | 502 | strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname); |
224ded67 | 503 | |
4253dab5 | 504 | if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0) |
224ded67 SS |
505 | return -errno; |
506 | ||
0d341ecc YW |
507 | if (ring->rx.set) |
508 | UPDATE_WITH_MAX(ecmd.rx_pending, ecmd.rx_max_pending, ring->rx.value, need_update); | |
224ded67 | 509 | |
0d341ecc YW |
510 | if (ring->rx_mini.set) |
511 | UPDATE_WITH_MAX(ecmd.rx_mini_pending, ecmd.rx_mini_max_pending, ring->rx_mini.value, need_update); | |
e81f5fc4 | 512 | |
0d341ecc YW |
513 | if (ring->rx_jumbo.set) |
514 | UPDATE_WITH_MAX(ecmd.rx_jumbo_pending, ecmd.rx_jumbo_max_pending, ring->rx_jumbo.value, need_update); | |
e81f5fc4 | 515 | |
0d341ecc YW |
516 | if (ring->tx.set) |
517 | UPDATE_WITH_MAX(ecmd.tx_pending, ecmd.tx_max_pending, ring->tx.value, need_update); | |
224ded67 | 518 | |
ba103059 YW |
519 | if (!need_update) |
520 | return 0; | |
224ded67 | 521 | |
ba103059 | 522 | ecmd.cmd = ETHTOOL_SRINGPARAM; |
7c248223 | 523 | return RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr)); |
224ded67 SS |
524 | } |
525 | ||
008d3a37 | 526 | static int get_stringset(int ethtool_fd, const char *ifname, enum ethtool_stringset stringset_id, struct ethtool_gstrings **ret) { |
50725d10 | 527 | _cleanup_free_ struct ethtool_gstrings *strings = NULL; |
a9dee27f SS |
528 | struct { |
529 | struct ethtool_sset_info info; | |
530 | uint32_t space; | |
531 | } buffer = { | |
008d3a37 YW |
532 | .info.cmd = ETHTOOL_GSSET_INFO, |
533 | .info.sset_mask = UINT64_C(1) << stringset_id, | |
50725d10 | 534 | }; |
008d3a37 YW |
535 | struct ifreq ifr = { |
536 | .ifr_data = (void*) &buffer, | |
537 | }; | |
538 | uint32_t len; | |
50725d10 | 539 | |
a93187ce | 540 | assert(ethtool_fd >= 0); |
008d3a37 | 541 | assert(ifname); |
a93187ce YW |
542 | assert(ret); |
543 | ||
008d3a37 | 544 | strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname); |
50725d10 | 545 | |
008d3a37 | 546 | if (ioctl(ethtool_fd, SIOCETHTOOL, &ifr) < 0) |
50725d10 SS |
547 | return -errno; |
548 | ||
008d3a37 YW |
549 | if (buffer.info.sset_mask == 0) |
550 | return -EOPNOTSUPP; | |
50725d10 | 551 | |
94c0c5b7 ZJS |
552 | #pragma GCC diagnostic push |
553 | #if HAVE_ZERO_LENGTH_BOUNDS | |
554 | # pragma GCC diagnostic ignored "-Wzero-length-bounds" | |
555 | #endif | |
a9dee27f | 556 | len = buffer.info.data[0]; |
94c0c5b7 | 557 | #pragma GCC diagnostic pop |
008d3a37 YW |
558 | if (len == 0) |
559 | return -EOPNOTSUPP; | |
50725d10 | 560 | |
008d3a37 | 561 | strings = malloc0(offsetof(struct ethtool_gstrings, data) + len * ETH_GSTRING_LEN); |
50725d10 SS |
562 | if (!strings) |
563 | return -ENOMEM; | |
564 | ||
565 | strings->cmd = ETHTOOL_GSTRINGS; | |
566 | strings->string_set = stringset_id; | |
567 | strings->len = len; | |
568 | ||
008d3a37 | 569 | ifr.ifr_data = (void*) strings; |
50725d10 | 570 | |
008d3a37 | 571 | if (ioctl(ethtool_fd, SIOCETHTOOL, &ifr) < 0) |
50725d10 SS |
572 | return -errno; |
573 | ||
a93187ce | 574 | *ret = TAKE_PTR(strings); |
008d3a37 YW |
575 | return 0; |
576 | } | |
577 | ||
578 | static int get_features(int ethtool_fd, const char *ifname, uint32_t n_features, struct ethtool_gfeatures **ret) { | |
579 | _cleanup_free_ struct ethtool_gfeatures *gfeatures = NULL; | |
580 | struct ifreq ifr; | |
581 | ||
582 | assert(ethtool_fd >= 0); | |
583 | assert(ifname); | |
584 | assert(ret); | |
585 | assert(n_features > 0); | |
586 | ||
587 | gfeatures = malloc0(offsetof(struct ethtool_gfeatures, features) + | |
588 | DIV_ROUND_UP(n_features, 32U) * sizeof(gfeatures->features[0])); | |
589 | if (!gfeatures) | |
590 | return -ENOMEM; | |
591 | ||
592 | gfeatures->cmd = ETHTOOL_GFEATURES; | |
593 | gfeatures->size = DIV_ROUND_UP(n_features, 32U); | |
594 | ||
595 | ifr = (struct ifreq) { | |
596 | .ifr_data = (void*) gfeatures, | |
597 | }; | |
598 | strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname); | |
50725d10 | 599 | |
008d3a37 YW |
600 | if (ioctl(ethtool_fd, SIOCETHTOOL, &ifr) < 0) |
601 | return -errno; | |
602 | ||
603 | *ret = TAKE_PTR(gfeatures); | |
50725d10 SS |
604 | return 0; |
605 | } | |
606 | ||
bf2334c0 YW |
607 | static int set_features_bit( |
608 | const struct ethtool_gstrings *strings, | |
008d3a37 YW |
609 | const struct ethtool_gfeatures *gfeatures, |
610 | struct ethtool_sfeatures *sfeatures, | |
bf2334c0 | 611 | const char *feature, |
008d3a37 YW |
612 | int flag) { |
613 | ||
7a4f2035 YW |
614 | assert(strings); |
615 | assert(gfeatures); | |
616 | assert(sfeatures); | |
617 | assert(feature); | |
618 | ||
619 | if (flag < 0) | |
620 | return 0; | |
621 | ||
622 | for (uint32_t i = 0; i < strings->len; i++) { | |
623 | uint32_t block, mask; | |
624 | ||
625 | if (!strneq((const char*) &strings->data[i * ETH_GSTRING_LEN], feature, ETH_GSTRING_LEN)) | |
626 | continue; | |
627 | ||
628 | block = i / 32; | |
629 | mask = UINT32_C(1) << (i % 32); | |
630 | ||
631 | if (!FLAGS_SET(gfeatures->features[block].available, mask) || | |
632 | FLAGS_SET(gfeatures->features[block].never_changed, mask)) | |
633 | return -EOPNOTSUPP; | |
634 | ||
635 | sfeatures->features[block].valid |= mask; | |
636 | SET_FLAG(sfeatures->features[block].requested, mask, flag); | |
637 | ||
638 | return 0; | |
639 | } | |
640 | ||
641 | return -ENODATA; | |
642 | } | |
643 | ||
644 | static int set_features_multiple_bit( | |
645 | const struct ethtool_gstrings *strings, | |
646 | const struct ethtool_gfeatures *gfeatures, | |
647 | struct ethtool_sfeatures *sfeatures, | |
648 | const char *feature, | |
649 | int flag) { | |
650 | ||
bf2334c0 | 651 | bool found = false; |
008d3a37 | 652 | int r = -ENODATA; |
50725d10 | 653 | |
bf2334c0 | 654 | assert(strings); |
008d3a37 | 655 | assert(gfeatures); |
bf2334c0 | 656 | assert(sfeatures); |
008d3a37 | 657 | assert(feature); |
bf2334c0 | 658 | |
008d3a37 YW |
659 | if (flag < 0) |
660 | return 0; | |
661 | ||
662 | for (uint32_t i = 0; i < strings->len; i++) { | |
663 | uint32_t block, mask; | |
664 | ||
7a4f2035 | 665 | if (!startswith((const char*) &strings->data[i * ETH_GSTRING_LEN], feature)) |
008d3a37 | 666 | continue; |
50725d10 | 667 | |
008d3a37 YW |
668 | block = i / 32; |
669 | mask = UINT32_C(1) << (i % 32); | |
bf2334c0 | 670 | |
008d3a37 YW |
671 | if (!FLAGS_SET(gfeatures->features[block].available, mask) || |
672 | FLAGS_SET(gfeatures->features[block].never_changed, mask)) { | |
673 | r = -EOPNOTSUPP; | |
674 | continue; | |
bf2334c0 YW |
675 | } |
676 | ||
7a4f2035 YW |
677 | /* The flags is explicitly set by set_features_bit() */ |
678 | if (FLAGS_SET(sfeatures->features[block].valid, mask)) | |
679 | continue; | |
680 | ||
008d3a37 YW |
681 | sfeatures->features[block].valid |= mask; |
682 | SET_FLAG(sfeatures->features[block].requested, mask, flag); | |
683 | ||
684 | found = true; | |
685 | } | |
686 | ||
687 | return found ? 0 : r; | |
50725d10 SS |
688 | } |
689 | ||
0db68800 | 690 | int ethtool_set_features(int *ethtool_fd, const char *ifname, const int features[static _NET_DEV_FEAT_MAX]) { |
50725d10 | 691 | _cleanup_free_ struct ethtool_gstrings *strings = NULL; |
008d3a37 YW |
692 | _cleanup_free_ struct ethtool_gfeatures *gfeatures = NULL; |
693 | _cleanup_free_ struct ethtool_sfeatures *sfeatures = NULL; | |
694 | struct ifreq ifr; | |
c2f2250e YW |
695 | bool have = false; |
696 | int r; | |
50725d10 | 697 | |
a93187ce YW |
698 | assert(ethtool_fd); |
699 | assert(ifname); | |
700 | assert(features); | |
701 | ||
c2f2250e YW |
702 | for (size_t i = 0; i < _NET_DEV_FEAT_MAX; i++) |
703 | if (features[i] >= 0) { | |
704 | have = true; | |
705 | break; | |
706 | } | |
707 | ||
708 | if (!have) | |
709 | return 0; | |
710 | ||
4f504031 YW |
711 | r = ethtool_connect(ethtool_fd); |
712 | if (r < 0) | |
713 | return r; | |
50725d10 | 714 | |
008d3a37 YW |
715 | r = get_stringset(*ethtool_fd, ifname, ETH_SS_FEATURES, &strings); |
716 | if (r < 0) | |
717 | return log_debug_errno(r, "ethtool: could not get ethtool feature strings: %m"); | |
50725d10 | 718 | |
008d3a37 | 719 | r = get_features(*ethtool_fd, ifname, strings->len, &gfeatures); |
50725d10 | 720 | if (r < 0) |
008d3a37 YW |
721 | return log_debug_errno(r, "ethtool: could not get ethtool features for %s: %m", ifname); |
722 | ||
723 | sfeatures = malloc0(offsetof(struct ethtool_sfeatures, features) + | |
724 | DIV_ROUND_UP(strings->len, 32U) * sizeof(sfeatures->features[0])); | |
725 | if (!sfeatures) | |
726 | return log_oom_debug(); | |
50725d10 | 727 | |
50725d10 SS |
728 | sfeatures->cmd = ETHTOOL_SFEATURES; |
729 | sfeatures->size = DIV_ROUND_UP(strings->len, 32U); | |
730 | ||
7a4f2035 | 731 | for (size_t i = 0; i < _NET_DEV_FEAT_SIMPLE_MAX; i++) { |
008d3a37 YW |
732 | r = set_features_bit(strings, gfeatures, sfeatures, netdev_feature_table[i], features[i]); |
733 | if (r < 0) | |
734 | log_debug_errno(r, "ethtool: could not set feature %s for %s, ignoring: %m", netdev_feature_table[i], ifname); | |
7a4f2035 YW |
735 | } |
736 | ||
737 | for (size_t i = _NET_DEV_FEAT_SIMPLE_MAX; i < _NET_DEV_FEAT_MAX; i++) { | |
738 | r = set_features_multiple_bit(strings, gfeatures, sfeatures, netdev_feature_table[i], features[i]); | |
739 | if (r < 0) | |
740 | log_debug_errno(r, "ethtool: could not set feature %s for %s, ignoring: %m", netdev_feature_table[i], ifname); | |
008d3a37 | 741 | } |
50725d10 | 742 | |
008d3a37 YW |
743 | ifr = (struct ifreq) { |
744 | .ifr_data = (void*) sfeatures, | |
745 | }; | |
746 | strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname); | |
50725d10 | 747 | |
4253dab5 YW |
748 | if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0) |
749 | return log_debug_errno(errno, "ethtool: could not set ethtool features for %s", ifname); | |
50725d10 SS |
750 | |
751 | return 0; | |
752 | } | |
a39f92d3 | 753 | |
a93187ce | 754 | static int get_glinksettings(int fd, struct ifreq *ifr, struct ethtool_link_usettings **ret) { |
a39f92d3 SS |
755 | struct ecmd { |
756 | struct ethtool_link_settings req; | |
95fe7b28 | 757 | uint32_t link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32]; |
a39f92d3 SS |
758 | } ecmd = { |
759 | .req.cmd = ETHTOOL_GLINKSETTINGS, | |
760 | }; | |
761 | struct ethtool_link_usettings *u; | |
762 | unsigned offset; | |
a39f92d3 | 763 | |
a93187ce YW |
764 | assert(fd >= 0); |
765 | assert(ifr); | |
766 | assert(ret); | |
767 | ||
a39f92d3 SS |
768 | /* The interaction user/kernel via the new API requires a small ETHTOOL_GLINKSETTINGS |
769 | handshake first to agree on the length of the link mode bitmaps. If kernel doesn't | |
770 | agree with user, it returns the bitmap length it is expecting from user as a negative | |
771 | length (and cmd field is 0). When kernel and user agree, kernel returns valid info in | |
772 | all fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS). Based on | |
773 | https://github.com/torvalds/linux/commit/3f1ac7a700d039c61d8d8b99f28d605d489a60cf | |
774 | */ | |
775 | ||
776 | ifr->ifr_data = (void *) &ecmd; | |
777 | ||
4253dab5 | 778 | if (ioctl(fd, SIOCETHTOOL, ifr) < 0) |
a39f92d3 SS |
779 | return -errno; |
780 | ||
781 | if (ecmd.req.link_mode_masks_nwords >= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS) | |
6b44a121 | 782 | return -EOPNOTSUPP; |
a39f92d3 SS |
783 | |
784 | ecmd.req.link_mode_masks_nwords = -ecmd.req.link_mode_masks_nwords; | |
785 | ||
786 | ifr->ifr_data = (void *) &ecmd; | |
787 | ||
4253dab5 | 788 | if (ioctl(fd, SIOCETHTOOL, ifr) < 0) |
a39f92d3 SS |
789 | return -errno; |
790 | ||
791 | if (ecmd.req.link_mode_masks_nwords <= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS) | |
6b44a121 | 792 | return -EOPNOTSUPP; |
a39f92d3 | 793 | |
b9bc7d42 | 794 | u = new(struct ethtool_link_usettings, 1); |
a39f92d3 SS |
795 | if (!u) |
796 | return -ENOMEM; | |
797 | ||
b9bc7d42 YW |
798 | *u = (struct ethtool_link_usettings) { |
799 | .base = ecmd.req, | |
800 | }; | |
a39f92d3 SS |
801 | |
802 | offset = 0; | |
803 | memcpy(u->link_modes.supported, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords); | |
804 | ||
805 | offset += ecmd.req.link_mode_masks_nwords; | |
806 | memcpy(u->link_modes.advertising, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords); | |
807 | ||
808 | offset += ecmd.req.link_mode_masks_nwords; | |
809 | memcpy(u->link_modes.lp_advertising, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords); | |
810 | ||
a93187ce | 811 | *ret = u; |
a39f92d3 SS |
812 | |
813 | return 0; | |
814 | } | |
815 | ||
a93187ce | 816 | static int get_gset(int fd, struct ifreq *ifr, struct ethtool_link_usettings **ret) { |
a39f92d3 SS |
817 | struct ethtool_link_usettings *e; |
818 | struct ethtool_cmd ecmd = { | |
819 | .cmd = ETHTOOL_GSET, | |
820 | }; | |
a39f92d3 | 821 | |
a93187ce YW |
822 | assert(fd >= 0); |
823 | assert(ifr); | |
824 | assert(ret); | |
825 | ||
a39f92d3 SS |
826 | ifr->ifr_data = (void *) &ecmd; |
827 | ||
4253dab5 | 828 | if (ioctl(fd, SIOCETHTOOL, ifr) < 0) |
a39f92d3 SS |
829 | return -errno; |
830 | ||
b9bc7d42 | 831 | e = new(struct ethtool_link_usettings, 1); |
a39f92d3 SS |
832 | if (!e) |
833 | return -ENOMEM; | |
834 | ||
b9bc7d42 YW |
835 | *e = (struct ethtool_link_usettings) { |
836 | .base.cmd = ETHTOOL_GSET, | |
837 | .base.link_mode_masks_nwords = 1, | |
838 | .base.speed = ethtool_cmd_speed(&ecmd), | |
839 | .base.duplex = ecmd.duplex, | |
840 | .base.port = ecmd.port, | |
841 | .base.phy_address = ecmd.phy_address, | |
842 | .base.autoneg = ecmd.autoneg, | |
843 | .base.mdio_support = ecmd.mdio_support, | |
18f84f8a YW |
844 | .base.eth_tp_mdix = ecmd.eth_tp_mdix, |
845 | .base.eth_tp_mdix_ctrl = ecmd.eth_tp_mdix_ctrl, | |
b9bc7d42 YW |
846 | |
847 | .link_modes.supported[0] = ecmd.supported, | |
848 | .link_modes.advertising[0] = ecmd.advertising, | |
849 | .link_modes.lp_advertising[0] = ecmd.lp_advertising, | |
850 | }; | |
a39f92d3 | 851 | |
a93187ce | 852 | *ret = e; |
a39f92d3 SS |
853 | |
854 | return 0; | |
855 | } | |
856 | ||
2b44daaa | 857 | static int set_slinksettings(int fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) { |
a39f92d3 SS |
858 | struct { |
859 | struct ethtool_link_settings req; | |
95fe7b28 | 860 | uint32_t link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32]; |
94d4acbe | 861 | } ecmd = {}; |
14cb109d | 862 | unsigned offset; |
a39f92d3 | 863 | |
a93187ce YW |
864 | assert(fd >= 0); |
865 | assert(ifr); | |
866 | assert(u); | |
867 | ||
a39f92d3 SS |
868 | if (u->base.cmd != ETHTOOL_GLINKSETTINGS || u->base.link_mode_masks_nwords <= 0) |
869 | return -EINVAL; | |
870 | ||
89e1ba0a | 871 | ecmd.req = u->base; |
94d4acbe | 872 | ecmd.req.cmd = ETHTOOL_SLINKSETTINGS; |
a39f92d3 SS |
873 | offset = 0; |
874 | memcpy(&ecmd.link_mode_data[offset], u->link_modes.supported, 4 * ecmd.req.link_mode_masks_nwords); | |
875 | ||
876 | offset += ecmd.req.link_mode_masks_nwords; | |
877 | memcpy(&ecmd.link_mode_data[offset], u->link_modes.advertising, 4 * ecmd.req.link_mode_masks_nwords); | |
878 | ||
879 | offset += ecmd.req.link_mode_masks_nwords; | |
880 | memcpy(&ecmd.link_mode_data[offset], u->link_modes.lp_advertising, 4 * ecmd.req.link_mode_masks_nwords); | |
881 | ||
882 | ifr->ifr_data = (void *) &ecmd; | |
883 | ||
7c248223 | 884 | return RET_NERRNO(ioctl(fd, SIOCETHTOOL, ifr)); |
a39f92d3 SS |
885 | } |
886 | ||
2b44daaa | 887 | static int set_sset(int fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) { |
a39f92d3 SS |
888 | struct ethtool_cmd ecmd = { |
889 | .cmd = ETHTOOL_SSET, | |
890 | }; | |
a39f92d3 | 891 | |
a93187ce YW |
892 | assert(fd >= 0); |
893 | assert(ifr); | |
894 | assert(u); | |
895 | ||
a39f92d3 SS |
896 | if (u->base.cmd != ETHTOOL_GSET || u->base.link_mode_masks_nwords <= 0) |
897 | return -EINVAL; | |
898 | ||
899 | ecmd.supported = u->link_modes.supported[0]; | |
900 | ecmd.advertising = u->link_modes.advertising[0]; | |
901 | ecmd.lp_advertising = u->link_modes.lp_advertising[0]; | |
902 | ||
903 | ethtool_cmd_speed_set(&ecmd, u->base.speed); | |
904 | ||
905 | ecmd.duplex = u->base.duplex; | |
906 | ecmd.port = u->base.port; | |
907 | ecmd.phy_address = u->base.phy_address; | |
908 | ecmd.autoneg = u->base.autoneg; | |
909 | ecmd.mdio_support = u->base.mdio_support; | |
6cf0a204 SS |
910 | ecmd.eth_tp_mdix = u->base.eth_tp_mdix; |
911 | ecmd.eth_tp_mdix_ctrl = u->base.eth_tp_mdix_ctrl; | |
a39f92d3 SS |
912 | |
913 | ifr->ifr_data = (void *) &ecmd; | |
914 | ||
7c248223 | 915 | return RET_NERRNO(ioctl(fd, SIOCETHTOOL, ifr)); |
a39f92d3 SS |
916 | } |
917 | ||
5c2316c6 YW |
918 | int ethtool_set_glinksettings( |
919 | int *fd, | |
920 | const char *ifname, | |
921 | int autonegotiation, | |
cadc7ed2 | 922 | const uint32_t advertise[static N_ADVERTISE], |
50299121 | 923 | uint64_t speed, |
5c2316c6 | 924 | Duplex duplex, |
18f84f8a YW |
925 | NetDevPort port, |
926 | uint8_t mdi) { | |
a93187ce | 927 | |
a39f92d3 SS |
928 | _cleanup_free_ struct ethtool_link_usettings *u = NULL; |
929 | struct ifreq ifr = {}; | |
ba103059 | 930 | bool changed = false; |
a39f92d3 SS |
931 | int r; |
932 | ||
a93187ce YW |
933 | assert(fd); |
934 | assert(ifname); | |
2caa38e9 LP |
935 | assert(advertise); |
936 | ||
ba103059 | 937 | if (autonegotiation < 0 && memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE) && |
18f84f8a | 938 | speed == 0 && duplex < 0 && port < 0 && mdi == ETH_TP_MDI_INVALID) |
ba103059 YW |
939 | return 0; |
940 | ||
4323046c YW |
941 | /* If autonegotiation is disabled, the speed and duplex represent the fixed link mode and are |
942 | * writable if the driver supports multiple link modes. If it is enabled then they are | |
943 | * read-only. If the link is up they represent the negotiated link mode; if the link is down, | |
944 | * the speed is 0, %SPEED_UNKNOWN or the highest enabled speed and @duplex is %DUPLEX_UNKNOWN | |
945 | * or the best enabled duplex mode. */ | |
946 | ||
c8e644b1 YW |
947 | if (speed > 0 || duplex >= 0 || port >= 0) { |
948 | if (autonegotiation == AUTONEG_ENABLE || !memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE)) { | |
949 | log_debug("ethtool: autonegotiation is enabled, ignoring speed, duplex, or port settings."); | |
950 | speed = 0; | |
951 | duplex = _DUP_INVALID; | |
952 | port = _NET_DEV_PORT_INVALID; | |
953 | } else { | |
954 | log_debug("ethtool: setting speed, duplex, or port, disabling autonegotiation."); | |
955 | autonegotiation = AUTONEG_DISABLE; | |
956 | } | |
a39f92d3 SS |
957 | } |
958 | ||
4f504031 YW |
959 | r = ethtool_connect(fd); |
960 | if (r < 0) | |
961 | return r; | |
a39f92d3 | 962 | |
6d9a72f3 | 963 | strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname); |
a39f92d3 | 964 | |
2b44daaa | 965 | r = get_glinksettings(*fd, &ifr, &u); |
a39f92d3 | 966 | if (r < 0) { |
2b44daaa | 967 | r = get_gset(*fd, &ifr, &u); |
a39f92d3 | 968 | if (r < 0) |
18f84f8a | 969 | return log_debug_errno(r, "ethtool: Cannot get device settings for %s: %m", ifname); |
a39f92d3 SS |
970 | } |
971 | ||
5c2316c6 | 972 | if (speed > 0) |
ba103059 | 973 | UPDATE(u->base.speed, DIV_ROUND_UP(speed, 1000000), changed); |
593022fa | 974 | |
ba103059 YW |
975 | if (duplex >= 0) |
976 | UPDATE(u->base.duplex, duplex, changed); | |
a39f92d3 | 977 | |
ba103059 YW |
978 | if (port >= 0) |
979 | UPDATE(u->base.port, port, changed); | |
a39f92d3 | 980 | |
5c2316c6 | 981 | if (autonegotiation >= 0) |
ba103059 | 982 | UPDATE(u->base.autoneg, autonegotiation, changed); |
a39f92d3 | 983 | |
5c2316c6 | 984 | if (!memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE)) { |
ba103059 YW |
985 | UPDATE(u->base.autoneg, AUTONEG_ENABLE, changed); |
986 | ||
987 | changed = changed || | |
988 | memcmp(&u->link_modes.advertising, advertise, sizeof(uint32_t) * N_ADVERTISE) != 0 || | |
989 | !memeqzero((uint8_t*) &u->link_modes.advertising + sizeof(uint32_t) * N_ADVERTISE, | |
990 | ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES - sizeof(uint32_t) * N_ADVERTISE); | |
5c2316c6 YW |
991 | memcpy(&u->link_modes.advertising, advertise, sizeof(uint32_t) * N_ADVERTISE); |
992 | memzero((uint8_t*) &u->link_modes.advertising + sizeof(uint32_t) * N_ADVERTISE, | |
993 | ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES - sizeof(uint32_t) * N_ADVERTISE); | |
5dd10118 | 994 | } |
6cf0a204 | 995 | |
18f84f8a YW |
996 | if (mdi != ETH_TP_MDI_INVALID) { |
997 | if (u->base.eth_tp_mdix_ctrl == ETH_TP_MDI_INVALID) | |
998 | log_debug("ethtool: setting MDI not supported for %s, ignoring.", ifname); | |
999 | else | |
1000 | UPDATE(u->base.eth_tp_mdix_ctrl, mdi, changed); | |
1001 | } | |
1002 | ||
ba103059 YW |
1003 | if (!changed) |
1004 | return 0; | |
1005 | ||
a39f92d3 | 1006 | if (u->base.cmd == ETHTOOL_GLINKSETTINGS) |
2b44daaa | 1007 | r = set_slinksettings(*fd, &ifr, u); |
a39f92d3 | 1008 | else |
2b44daaa | 1009 | r = set_sset(*fd, &ifr, u); |
a39f92d3 | 1010 | if (r < 0) |
11a288e8 | 1011 | return log_debug_errno(r, "ethtool: Cannot set device settings for %s: %m", ifname); |
a39f92d3 SS |
1012 | |
1013 | return r; | |
1014 | } | |
5f945202 | 1015 | |
cadc7ed2 | 1016 | int ethtool_set_channels(int *fd, const char *ifname, const netdev_channels *channels) { |
5f945202 | 1017 | struct ethtool_channels ecmd = { |
a93187ce | 1018 | .cmd = ETHTOOL_GCHANNELS, |
5f945202 SS |
1019 | }; |
1020 | struct ifreq ifr = { | |
a93187ce | 1021 | .ifr_data = (void*) &ecmd, |
5f945202 | 1022 | }; |
5f945202 SS |
1023 | bool need_update = false; |
1024 | int r; | |
1025 | ||
a93187ce YW |
1026 | assert(fd); |
1027 | assert(ifname); | |
1028 | assert(channels); | |
1029 | ||
0d341ecc YW |
1030 | if (!channels->rx.set && |
1031 | !channels->tx.set && | |
1032 | !channels->other.set && | |
1033 | !channels->combined.set) | |
ba103059 YW |
1034 | return 0; |
1035 | ||
4f504031 YW |
1036 | r = ethtool_connect(fd); |
1037 | if (r < 0) | |
1038 | return r; | |
5f945202 | 1039 | |
6d9a72f3 | 1040 | strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname); |
5f945202 | 1041 | |
4253dab5 | 1042 | if (ioctl(*fd, SIOCETHTOOL, &ifr) < 0) |
5f945202 SS |
1043 | return -errno; |
1044 | ||
0d341ecc YW |
1045 | if (channels->rx.set) |
1046 | UPDATE_WITH_MAX(ecmd.rx_count, ecmd.max_rx, channels->rx.value, need_update); | |
5f945202 | 1047 | |
0d341ecc YW |
1048 | if (channels->tx.set) |
1049 | UPDATE_WITH_MAX(ecmd.tx_count, ecmd.max_tx, channels->tx.value, need_update); | |
5f945202 | 1050 | |
0d341ecc YW |
1051 | if (channels->other.set) |
1052 | UPDATE_WITH_MAX(ecmd.other_count, ecmd.max_other, channels->other.value, need_update); | |
5f945202 | 1053 | |
0d341ecc YW |
1054 | if (channels->combined.set) |
1055 | UPDATE_WITH_MAX(ecmd.combined_count, ecmd.max_combined, channels->combined.value, need_update); | |
5f945202 | 1056 | |
ba103059 YW |
1057 | if (!need_update) |
1058 | return 0; | |
a34811e4 | 1059 | |
ba103059 | 1060 | ecmd.cmd = ETHTOOL_SCHANNELS; |
7c248223 | 1061 | return RET_NERRNO(ioctl(*fd, SIOCETHTOOL, &ifr)); |
a34811e4 YW |
1062 | } |
1063 | ||
1064 | int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int autoneg) { | |
1065 | struct ethtool_pauseparam ecmd = { | |
a93187ce | 1066 | .cmd = ETHTOOL_GPAUSEPARAM, |
a34811e4 YW |
1067 | }; |
1068 | struct ifreq ifr = { | |
a93187ce | 1069 | .ifr_data = (void*) &ecmd, |
a34811e4 | 1070 | }; |
a34811e4 YW |
1071 | bool need_update = false; |
1072 | int r; | |
1073 | ||
a93187ce YW |
1074 | assert(fd); |
1075 | assert(ifname); | |
1076 | ||
ba103059 YW |
1077 | if (rx < 0 && tx < 0 && autoneg < 0) |
1078 | return 0; | |
1079 | ||
4f504031 YW |
1080 | r = ethtool_connect(fd); |
1081 | if (r < 0) | |
1082 | return r; | |
a34811e4 | 1083 | |
6d9a72f3 | 1084 | strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname); |
a34811e4 | 1085 | |
4253dab5 | 1086 | if (ioctl(*fd, SIOCETHTOOL, &ifr) < 0) |
a34811e4 YW |
1087 | return -errno; |
1088 | ||
ba103059 YW |
1089 | if (rx >= 0) |
1090 | UPDATE(ecmd.rx_pause, (uint32_t) rx, need_update); | |
a34811e4 | 1091 | |
ba103059 YW |
1092 | if (tx >= 0) |
1093 | UPDATE(ecmd.tx_pause, (uint32_t) tx, need_update); | |
a34811e4 | 1094 | |
ba103059 YW |
1095 | if (autoneg >= 0) |
1096 | UPDATE(ecmd.autoneg, (uint32_t) autoneg, need_update); | |
a34811e4 | 1097 | |
ba103059 YW |
1098 | if (!need_update) |
1099 | return 0; | |
5f945202 | 1100 | |
ba103059 | 1101 | ecmd.cmd = ETHTOOL_SPAUSEPARAM; |
7c248223 | 1102 | return RET_NERRNO(ioctl(*fd, SIOCETHTOOL, &ifr)); |
5f945202 | 1103 | } |
6cf0a204 | 1104 | |
72328a59 YW |
1105 | int ethtool_set_nic_coalesce_settings(int *ethtool_fd, const char *ifname, const netdev_coalesce_param *coalesce) { |
1106 | struct ethtool_coalesce ecmd = { | |
1107 | .cmd = ETHTOOL_GCOALESCE, | |
1108 | }; | |
1109 | struct ifreq ifr = { | |
1110 | .ifr_data = (void*) &ecmd, | |
1111 | }; | |
1112 | bool need_update = false; | |
1113 | int r; | |
1114 | ||
1115 | assert(ethtool_fd); | |
1116 | assert(ifname); | |
1117 | assert(coalesce); | |
1118 | ||
1119 | if (coalesce->use_adaptive_rx_coalesce < 0 && | |
1120 | coalesce->use_adaptive_tx_coalesce < 0 && | |
1121 | !coalesce->rx_coalesce_usecs.set && | |
1122 | !coalesce->rx_max_coalesced_frames.set && | |
1123 | !coalesce->rx_coalesce_usecs_irq.set && | |
1124 | !coalesce->rx_max_coalesced_frames_irq.set && | |
1125 | !coalesce->tx_coalesce_usecs.set && | |
1126 | !coalesce->tx_max_coalesced_frames.set && | |
1127 | !coalesce->tx_coalesce_usecs_irq.set && | |
1128 | !coalesce->tx_max_coalesced_frames_irq.set && | |
1129 | !coalesce->stats_block_coalesce_usecs.set && | |
1130 | !coalesce->pkt_rate_low.set && | |
1131 | !coalesce->rx_coalesce_usecs_low.set && | |
1132 | !coalesce->rx_max_coalesced_frames_low.set && | |
1133 | !coalesce->tx_coalesce_usecs_low.set && | |
1134 | !coalesce->tx_max_coalesced_frames_low.set && | |
1135 | !coalesce->pkt_rate_high.set && | |
1136 | !coalesce->rx_coalesce_usecs_high.set && | |
1137 | !coalesce->rx_max_coalesced_frames_high.set && | |
1138 | !coalesce->tx_coalesce_usecs_high.set && | |
1139 | !coalesce->tx_max_coalesced_frames_high.set && | |
1140 | !coalesce->rate_sample_interval.set) | |
1141 | return 0; | |
1142 | ||
1143 | r = ethtool_connect(ethtool_fd); | |
1144 | if (r < 0) | |
1145 | return r; | |
1146 | ||
6d9a72f3 | 1147 | strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname); |
72328a59 | 1148 | |
4253dab5 | 1149 | if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0) |
72328a59 YW |
1150 | return -errno; |
1151 | ||
1152 | if (coalesce->use_adaptive_rx_coalesce >= 0) | |
1153 | UPDATE(ecmd.use_adaptive_rx_coalesce, (uint32_t) coalesce->use_adaptive_rx_coalesce, need_update); | |
1154 | ||
1155 | if (coalesce->use_adaptive_tx_coalesce >= 0) | |
1156 | UPDATE(ecmd.use_adaptive_tx_coalesce, (uint32_t) coalesce->use_adaptive_tx_coalesce, need_update); | |
1157 | ||
1158 | if (coalesce->rx_coalesce_usecs.set) | |
1159 | UPDATE(ecmd.rx_coalesce_usecs, coalesce->rx_coalesce_usecs.value, need_update); | |
1160 | ||
1161 | if (coalesce->rx_max_coalesced_frames.set) | |
1162 | UPDATE(ecmd.rx_max_coalesced_frames, coalesce->rx_max_coalesced_frames.value, need_update); | |
1163 | ||
1164 | if (coalesce->rx_coalesce_usecs_irq.set) | |
1165 | UPDATE(ecmd.rx_coalesce_usecs_irq, coalesce->rx_coalesce_usecs_irq.value, need_update); | |
1166 | ||
1167 | if (coalesce->rx_max_coalesced_frames_irq.set) | |
1168 | UPDATE(ecmd.rx_max_coalesced_frames_irq, coalesce->rx_max_coalesced_frames_irq.value, need_update); | |
1169 | ||
1170 | if (coalesce->tx_coalesce_usecs.set) | |
1171 | UPDATE(ecmd.tx_coalesce_usecs, coalesce->tx_coalesce_usecs.value, need_update); | |
1172 | ||
1173 | if (coalesce->tx_max_coalesced_frames.set) | |
1174 | UPDATE(ecmd.tx_max_coalesced_frames, coalesce->tx_max_coalesced_frames.value, need_update); | |
1175 | ||
1176 | if (coalesce->tx_coalesce_usecs_irq.set) | |
1177 | UPDATE(ecmd.tx_coalesce_usecs_irq, coalesce->tx_coalesce_usecs_irq.value, need_update); | |
1178 | ||
1179 | if (coalesce->tx_max_coalesced_frames_irq.set) | |
1180 | UPDATE(ecmd.tx_max_coalesced_frames_irq, coalesce->tx_max_coalesced_frames_irq.value, need_update); | |
1181 | ||
1182 | if (coalesce->stats_block_coalesce_usecs.set) | |
1183 | UPDATE(ecmd.stats_block_coalesce_usecs, coalesce->stats_block_coalesce_usecs.value, need_update); | |
1184 | ||
1185 | if (coalesce->pkt_rate_low.set) | |
1186 | UPDATE(ecmd.pkt_rate_low, coalesce->pkt_rate_low.value, need_update); | |
1187 | ||
1188 | if (coalesce->rx_coalesce_usecs_low.set) | |
1189 | UPDATE(ecmd.rx_coalesce_usecs_low, coalesce->rx_coalesce_usecs_low.value, need_update); | |
1190 | ||
1191 | if (coalesce->rx_max_coalesced_frames_low.set) | |
1192 | UPDATE(ecmd.rx_max_coalesced_frames_low, coalesce->rx_max_coalesced_frames_low.value, need_update); | |
1193 | ||
1194 | if (coalesce->tx_coalesce_usecs_low.set) | |
1195 | UPDATE(ecmd.tx_coalesce_usecs_low, coalesce->tx_coalesce_usecs_low.value, need_update); | |
1196 | ||
1197 | if (coalesce->tx_max_coalesced_frames_low.set) | |
1198 | UPDATE(ecmd.tx_max_coalesced_frames_low, coalesce->tx_max_coalesced_frames_low.value, need_update); | |
1199 | ||
1200 | if (coalesce->pkt_rate_high.set) | |
1201 | UPDATE(ecmd.pkt_rate_high, coalesce->pkt_rate_high.value, need_update); | |
1202 | ||
1203 | if (coalesce->rx_coalesce_usecs_high.set) | |
1204 | UPDATE(ecmd.rx_coalesce_usecs_high, coalesce->rx_coalesce_usecs_high.value, need_update); | |
1205 | ||
1206 | if (coalesce->rx_max_coalesced_frames_high.set) | |
1207 | UPDATE(ecmd.rx_max_coalesced_frames_high, coalesce->rx_max_coalesced_frames_high.value, need_update); | |
1208 | ||
1209 | if (coalesce->tx_coalesce_usecs_high.set) | |
1210 | UPDATE(ecmd.tx_coalesce_usecs_high, coalesce->tx_coalesce_usecs_high.value, need_update); | |
1211 | ||
1212 | if (coalesce->tx_max_coalesced_frames_high.set) | |
1213 | UPDATE(ecmd.tx_max_coalesced_frames_high, coalesce->tx_max_coalesced_frames_high.value, need_update); | |
1214 | ||
1215 | if (coalesce->rate_sample_interval.set) | |
1216 | UPDATE(ecmd.rate_sample_interval, DIV_ROUND_UP(coalesce->rate_sample_interval.value, USEC_PER_SEC), need_update); | |
1217 | ||
1218 | if (!need_update) | |
1219 | return 0; | |
1220 | ||
1221 | ecmd.cmd = ETHTOOL_SCOALESCE; | |
7c248223 | 1222 | return RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr)); |
72328a59 YW |
1223 | } |
1224 | ||
ba103059 YW |
1225 | int config_parse_advertise( |
1226 | const char *unit, | |
1227 | const char *filename, | |
1228 | unsigned line, | |
1229 | const char *section, | |
1230 | unsigned section_line, | |
1231 | const char *lvalue, | |
1232 | int ltype, | |
1233 | const char *rvalue, | |
1234 | void *data, | |
1235 | void *userdata) { | |
1236 | ||
99534007 | 1237 | uint32_t *advertise = ASSERT_PTR(data); |
6cf0a204 SS |
1238 | int r; |
1239 | ||
1240 | assert(filename); | |
1241 | assert(section); | |
1242 | assert(lvalue); | |
1243 | assert(rvalue); | |
6cf0a204 SS |
1244 | |
1245 | if (isempty(rvalue)) { | |
1246 | /* Empty string resets the value. */ | |
5c2316c6 | 1247 | memzero(advertise, sizeof(uint32_t) * N_ADVERTISE); |
6cf0a204 SS |
1248 | return 0; |
1249 | } | |
1250 | ||
c50404ae | 1251 | for (const char *p = rvalue;;) { |
6cf0a204 | 1252 | _cleanup_free_ char *w = NULL; |
5dd10118 | 1253 | enum ethtool_link_mode_bit_indices mode; |
6cf0a204 SS |
1254 | |
1255 | r = extract_first_word(&p, &w, NULL, 0); | |
1256 | if (r == -ENOMEM) | |
1257 | return log_oom(); | |
1258 | if (r < 0) { | |
4c382a87 YW |
1259 | log_syntax(unit, LOG_WARNING, filename, line, r, |
1260 | "Failed to split advertise modes '%s', ignoring assignment: %m", rvalue); | |
1261 | return 0; | |
6cf0a204 SS |
1262 | } |
1263 | if (r == 0) | |
4c382a87 | 1264 | return 0; |
6cf0a204 | 1265 | |
2d18ac44 | 1266 | mode = ethtool_link_mode_bit_from_string(w); |
84fb56d3 YW |
1267 | /* We reuse the kernel provided enum which does not contain negative value. So, the cast |
1268 | * below is mandatory. Otherwise, the check below always passes and access an invalid address. */ | |
1269 | if ((int) mode < 0) { | |
b98680b2 | 1270 | log_syntax(unit, LOG_WARNING, filename, line, mode, |
4c382a87 | 1271 | "Failed to parse advertise mode, ignoring: %s", w); |
6cf0a204 SS |
1272 | continue; |
1273 | } | |
2d18ac44 | 1274 | |
5c2316c6 | 1275 | advertise[mode / 32] |= 1UL << (mode % 32); |
2d18ac44 | 1276 | } |
6cf0a204 | 1277 | } |
224ded67 | 1278 | |
18f84f8a YW |
1279 | int config_parse_mdi( |
1280 | const char *unit, | |
1281 | const char *filename, | |
1282 | unsigned line, | |
1283 | const char *section, | |
1284 | unsigned section_line, | |
1285 | const char *lvalue, | |
1286 | int ltype, | |
1287 | const char *rvalue, | |
1288 | void *data, | |
1289 | void *userdata) { | |
1290 | ||
1291 | uint8_t *mdi = ASSERT_PTR(data); | |
1292 | ||
1293 | assert(filename); | |
1294 | assert(rvalue); | |
1295 | ||
1296 | if (isempty(rvalue)) { | |
1297 | *mdi = ETH_TP_MDI_INVALID; | |
1298 | return 0; | |
1299 | } | |
1300 | ||
1301 | if (STR_IN_SET(rvalue, "mdi", "straight")) { | |
1302 | *mdi = ETH_TP_MDI; | |
1303 | return 0; | |
1304 | } | |
1305 | ||
1306 | if (STR_IN_SET(rvalue, "mdi-x", "mdix", "crossover")) { | |
1307 | *mdi = ETH_TP_MDI_X; | |
1308 | return 0; | |
1309 | } | |
1310 | ||
1311 | if (streq(rvalue, "auto")) { | |
1312 | *mdi = ETH_TP_MDI_AUTO; | |
1313 | return 0; | |
1314 | } | |
1315 | ||
1316 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
1317 | "Failed to parse %s= setting, ignoring assignment: %s", lvalue, rvalue); | |
1318 | return 0; | |
1319 | } | |
1320 | ||
0d341ecc | 1321 | int config_parse_ring_buffer_or_channel( |
ba103059 YW |
1322 | const char *unit, |
1323 | const char *filename, | |
1324 | unsigned line, | |
1325 | const char *section, | |
1326 | unsigned section_line, | |
1327 | const char *lvalue, | |
1328 | int ltype, | |
1329 | const char *rvalue, | |
1330 | void *data, | |
1331 | void *userdata) { | |
1332 | ||
99534007 | 1333 | u32_opt *dst = ASSERT_PTR(data); |
224ded67 SS |
1334 | uint32_t k; |
1335 | int r; | |
1336 | ||
1337 | assert(filename); | |
1338 | assert(section); | |
1339 | assert(lvalue); | |
1340 | assert(rvalue); | |
224ded67 | 1341 | |
0d341ecc YW |
1342 | if (isempty(rvalue)) { |
1343 | dst->value = 0; | |
1344 | dst->set = false; | |
1345 | return 0; | |
1346 | } | |
1347 | ||
1348 | if (streq(rvalue, "max")) { | |
1349 | dst->value = 0; | |
1350 | dst->set = true; | |
1351 | return 0; | |
224ded67 SS |
1352 | } |
1353 | ||
0d341ecc YW |
1354 | r = safe_atou32(rvalue, &k); |
1355 | if (r < 0) { | |
1356 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
1357 | "Failed to parse %s=, ignoring: %s", lvalue, rvalue); | |
1358 | return 0; | |
1359 | } | |
1360 | if (k < 1) { | |
1361 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
1362 | "Invalid %s= value, ignoring: %s", lvalue, rvalue); | |
1363 | return 0; | |
224ded67 SS |
1364 | } |
1365 | ||
0d341ecc YW |
1366 | dst->value = k; |
1367 | dst->set = true; | |
224ded67 SS |
1368 | return 0; |
1369 | } | |
c50404ae YW |
1370 | |
1371 | int config_parse_wol( | |
1372 | const char *unit, | |
1373 | const char *filename, | |
1374 | unsigned line, | |
1375 | const char *section, | |
1376 | unsigned section_line, | |
1377 | const char *lvalue, | |
1378 | int ltype, | |
1379 | const char *rvalue, | |
1380 | void *data, | |
1381 | void *userdata) { | |
1382 | ||
1383 | uint32_t new_opts = 0, *opts = data; | |
1384 | int r; | |
1385 | ||
1386 | assert(filename); | |
1387 | assert(section); | |
1388 | assert(lvalue); | |
1389 | assert(rvalue); | |
1390 | assert(data); | |
1391 | ||
1392 | if (isempty(rvalue)) { | |
1393 | *opts = UINT32_MAX; /* Do not update WOL option. */ | |
1394 | return 0; | |
1395 | } | |
1396 | ||
1397 | if (streq(rvalue, "off")) { | |
1398 | *opts = 0; /* Disable WOL. */ | |
1399 | return 0; | |
1400 | } | |
1401 | ||
1402 | for (const char *p = rvalue;;) { | |
1403 | _cleanup_free_ char *w = NULL; | |
1404 | bool found = false; | |
1405 | ||
1406 | r = extract_first_word(&p, &w, NULL, 0); | |
1407 | if (r == -ENOMEM) | |
1408 | return log_oom(); | |
1409 | if (r < 0) { | |
1410 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
1411 | "Failed to split wake-on-lan modes '%s', ignoring assignment: %m", rvalue); | |
1412 | return 0; | |
1413 | } | |
1414 | if (r == 0) | |
1415 | break; | |
1416 | ||
1417 | for (size_t i = 0; i < ELEMENTSOF(wol_option_map); i++) | |
1418 | if (streq(w, wol_option_map[i].name)) { | |
1419 | new_opts |= wol_option_map[i].opt; | |
1420 | found = true; | |
1421 | break; | |
1422 | } | |
1423 | ||
1424 | if (!found) | |
1425 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
1426 | "Unknown wake-on-lan mode '%s', ignoring.", w); | |
1427 | } | |
1428 | ||
1429 | if (*opts == UINT32_MAX) | |
1430 | *opts = new_opts; | |
1431 | else | |
1432 | *opts |= new_opts; | |
1433 | ||
1434 | return 0; | |
1435 | } | |
6c35ea5e DDM |
1436 | |
1437 | int config_parse_coalesce_u32( | |
1438 | const char *unit, | |
1439 | const char *filename, | |
1440 | unsigned line, | |
1441 | const char *section, | |
1442 | unsigned section_line, | |
1443 | const char *lvalue, | |
1444 | int ltype, | |
1445 | const char *rvalue, | |
1446 | void *data, | |
1447 | void *userdata) { | |
1448 | u32_opt *dst = data; | |
1449 | uint32_t k; | |
1450 | int r; | |
1451 | ||
1452 | if (isempty(rvalue)) { | |
1453 | dst->value = 0; | |
1454 | dst->set = false; | |
1455 | return 0; | |
1456 | } | |
1457 | ||
1458 | r = safe_atou32(rvalue, &k); | |
1459 | if (r < 0) { | |
1460 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
1461 | "Failed to parse %s=, ignoring: %s", lvalue, rvalue); | |
1462 | return 0; | |
1463 | } | |
1464 | ||
1465 | dst->value = k; | |
1466 | dst->set = true; | |
1467 | return 0; | |
1468 | } | |
1469 | ||
1470 | int config_parse_coalesce_sec( | |
1471 | const char *unit, | |
1472 | const char *filename, | |
1473 | unsigned line, | |
1474 | const char *section, | |
1475 | unsigned section_line, | |
1476 | const char *lvalue, | |
1477 | int ltype, | |
1478 | const char *rvalue, | |
1479 | void *data, | |
1480 | void *userdata) { | |
1481 | u32_opt *dst = data; | |
1482 | usec_t usec; | |
1483 | int r; | |
1484 | ||
1485 | if (isempty(rvalue)) { | |
1486 | dst->value = 0; | |
1487 | dst->set = false; | |
1488 | return 0; | |
1489 | } | |
1490 | ||
1491 | r = parse_sec(rvalue, &usec); | |
1492 | if (r < 0) { | |
1493 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
1494 | "Failed to parse coalesce setting value, ignoring: %s", rvalue); | |
1495 | return 0; | |
1496 | } | |
1497 | ||
1498 | if (usec > UINT32_MAX) { | |
1499 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
1500 | "Too large %s= value, ignoring: %s", lvalue, rvalue); | |
1501 | return 0; | |
1502 | } | |
1503 | ||
1504 | if (STR_IN_SET(lvalue, "StatisticsBlockCoalesceSec", "CoalescePacketRateSampleIntervalSec") && usec < 1) { | |
1505 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
1506 | "Invalid %s= value, ignoring: %s", lvalue, rvalue); | |
1507 | return 0; | |
1508 | } | |
1509 | ||
1510 | dst->value = (uint32_t) usec; | |
1511 | dst->set = true; | |
1512 | ||
1513 | return 0; | |
1514 | } |