]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/ethtool-util.c
Merge pull request #25168 from valentindavid/valentindavid/umount-move-recursive...
[thirdparty/systemd.git] / src / shared / ethtool-util.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <net/if.h>
4 #include <sys/ioctl.h>
5 #include <linux/ethtool.h>
6 #include <linux/netdevice.h>
7 #include <linux/sockios.h>
8
9 #include "conf-parser.h"
10 #include "ethtool-util.h"
11 #include "extract-word.h"
12 #include "fd-util.h"
13 #include "log.h"
14 #include "memory-util.h"
15 #include "socket-util.h"
16 #include "string-table.h"
17 #include "strv.h"
18 #include "strxcpyx.h"
19
20 static const char* const duplex_table[_DUP_MAX] = {
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
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", },
39 };
40
41 int wol_options_to_string_alloc(uint32_t opts, char **ret) {
42 _cleanup_free_ char *str = NULL;
43
44 assert(ret);
45
46 if (opts == UINT32_MAX) {
47 *ret = NULL;
48 return 0;
49 }
50
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);
63 return 1;
64 }
65
66 static const char* const port_table[] = {
67 [NET_DEV_PORT_TP] = "tp",
68 [NET_DEV_PORT_AUI] = "aui",
69 [NET_DEV_PORT_MII] = "mii",
70 [NET_DEV_PORT_FIBRE] = "fibre",
71 [NET_DEV_PORT_BNC] = "bnc",
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
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
86 static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = {
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",
149
150 [NET_DEV_FEAT_TXCSUM] = "tx-checksum-", /* The suffix "-" means any feature beginning with "tx-checksum-" */
151 };
152
153 static const char* const ethtool_link_mode_bit_table[] = {
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",
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",
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",
244 [ETHTOOL_LINK_MODE_100baseFX_Half_BIT] = "100basefx-half",
245 [ETHTOOL_LINK_MODE_100baseFX_Full_BIT] = "100basefx-full",
246 };
247 /* Make sure the array is large enough to fit all bits */
248 assert_cc((ELEMENTSOF(ethtool_link_mode_bit_table)-1) / 32 < N_ADVERTISE);
249
250 DEFINE_STRING_TABLE_LOOKUP(ethtool_link_mode_bit, enum ethtool_link_mode_bit_indices);
251
252 static int ethtool_connect(int *ethtool_fd) {
253 int fd;
254
255 assert(ethtool_fd);
256
257 /* This does nothing if already connected. */
258 if (*ethtool_fd >= 0)
259 return 0;
260
261 fd = socket_ioctl_fd();
262 if (fd < 0)
263 return log_debug_errno(fd, "ethtool: could not create control socket: %m");
264
265 *ethtool_fd = fd;
266 return 0;
267 }
268
269 int ethtool_get_driver(int *ethtool_fd, const char *ifname, char **ret) {
270 struct ethtool_drvinfo ecmd = {
271 .cmd = ETHTOOL_GDRVINFO,
272 };
273 struct ifreq ifr = {
274 .ifr_data = (void*) &ecmd,
275 };
276 char *d;
277 int r;
278
279 assert(ethtool_fd);
280 assert(ifname);
281 assert(ret);
282
283 r = ethtool_connect(ethtool_fd);
284 if (r < 0)
285 return r;
286
287 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
288
289 if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
290 return -errno;
291
292 if (isempty(ecmd.driver))
293 return -ENODATA;
294
295 d = strdup(ecmd.driver);
296 if (!d)
297 return -ENOMEM;
298
299 *ret = d;
300 return 0;
301 }
302
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
311 struct ethtool_cmd ecmd = {
312 .cmd = ETHTOOL_GSET,
313 };
314 struct ifreq ifr = {
315 .ifr_data = (void*) &ecmd,
316 };
317 int r;
318
319 assert(ethtool_fd);
320 assert(ifname);
321
322 r = ethtool_connect(ethtool_fd);
323 if (r < 0)
324 return r;
325
326 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
327
328 if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
329 return -errno;
330
331 if (ret_autonegotiation)
332 *ret_autonegotiation = ecmd.autoneg;
333
334 if (ret_speed) {
335 uint32_t speed;
336
337 speed = ethtool_cmd_speed(&ecmd);
338 *ret_speed = speed == (uint32_t) SPEED_UNKNOWN ?
339 UINT64_MAX : (uint64_t) speed * 1000 * 1000;
340 }
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
351 int ethtool_get_permanent_hw_addr(int *ethtool_fd, const char *ifname, struct hw_addr_data *ret) {
352 _cleanup_close_ int fd = -EBADF;
353 struct {
354 struct ethtool_perm_addr addr;
355 uint8_t space[HW_ADDR_MAX_SIZE];
356 } epaddr = {
357 .addr.cmd = ETHTOOL_GPERMADDR,
358 .addr.size = HW_ADDR_MAX_SIZE,
359 };
360 struct ifreq ifr = {
361 .ifr_data = (caddr_t) &epaddr,
362 };
363 int r;
364
365 assert(ifname);
366 assert(ret);
367
368 if (!ethtool_fd)
369 ethtool_fd = &fd;
370 r = ethtool_connect(ethtool_fd);
371 if (r < 0)
372 return r;
373
374 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
375
376 if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
377 return -errno;
378
379 if (epaddr.addr.size == 0)
380 return -ENODATA;
381
382 if (epaddr.addr.size > HW_ADDR_MAX_SIZE)
383 return -EINVAL;
384
385 ret->length = epaddr.addr.size;
386 memcpy(ret->bytes, epaddr.addr.data, epaddr.addr.size);
387 return 0;
388 }
389
390 #define UPDATE(dest, val, updated) \
391 do { \
392 typeof(val) _v = (val); \
393 if (dest != _v) \
394 updated = true; \
395 dest = _v; \
396 } while (false)
397
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; \
407 } while (false)
408
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
415 struct ethtool_wolinfo ecmd = {
416 .cmd = ETHTOOL_GWOL,
417 };
418 struct ifreq ifr = {
419 .ifr_data = (void*) &ecmd,
420 };
421 bool need_update = false;
422 int r;
423
424 assert(ethtool_fd);
425 assert(ifname);
426
427 if (wolopts == UINT32_MAX && !password)
428 /* Nothing requested. Return earlier. */
429 return 0;
430
431 r = ethtool_connect(ethtool_fd);
432 if (r < 0)
433 return r;
434
435 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
436
437 CLEANUP_ERASE(ecmd);
438
439 if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
440 return -errno;
441
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
450 if ((wolopts & ~ecmd.supported) != 0) {
451 _cleanup_free_ char *str = NULL;
452
453 (void) wol_options_to_string_alloc(wolopts & ~ecmd.supported, &str);
454 log_debug("Network interface %s does not support requested Wake on LAN options \"%s\", ignoring.",
455 ifname, strna(str));
456
457 wolopts &= ecmd.supported;
458 }
459
460 if (!FLAGS_SET(wolopts, WAKE_MAGICSECURE))
461 /* When WAKE_MAGICSECURE flag is not set, then ignore password. */
462 password = NULL;
463
464 UPDATE(ecmd.wolopts, wolopts, need_update);
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 }
470
471 if (!need_update)
472 return 0;
473
474 ecmd.cmd = ETHTOOL_SWOL;
475 return RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr));
476 }
477
478 int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netdev_ring_param *ring) {
479 struct ethtool_ringparam ecmd = {
480 .cmd = ETHTOOL_GRINGPARAM,
481 };
482 struct ifreq ifr = {
483 .ifr_data = (void*) &ecmd,
484 };
485 bool need_update = false;
486 int r;
487
488 assert(ethtool_fd);
489 assert(ifname);
490 assert(ring);
491
492 if (!ring->rx.set &&
493 !ring->rx_mini.set &&
494 !ring->rx_jumbo.set &&
495 !ring->tx.set)
496 return 0;
497
498 r = ethtool_connect(ethtool_fd);
499 if (r < 0)
500 return r;
501
502 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
503
504 if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
505 return -errno;
506
507 if (ring->rx.set)
508 UPDATE_WITH_MAX(ecmd.rx_pending, ecmd.rx_max_pending, ring->rx.value, need_update);
509
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);
512
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);
515
516 if (ring->tx.set)
517 UPDATE_WITH_MAX(ecmd.tx_pending, ecmd.tx_max_pending, ring->tx.value, need_update);
518
519 if (!need_update)
520 return 0;
521
522 ecmd.cmd = ETHTOOL_SRINGPARAM;
523 return RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr));
524 }
525
526 static int get_stringset(int ethtool_fd, const char *ifname, enum ethtool_stringset stringset_id, struct ethtool_gstrings **ret) {
527 _cleanup_free_ struct ethtool_gstrings *strings = NULL;
528 struct {
529 struct ethtool_sset_info info;
530 uint32_t space;
531 } buffer = {
532 .info.cmd = ETHTOOL_GSSET_INFO,
533 .info.sset_mask = UINT64_C(1) << stringset_id,
534 };
535 struct ifreq ifr = {
536 .ifr_data = (void*) &buffer,
537 };
538 uint32_t len;
539
540 assert(ethtool_fd >= 0);
541 assert(ifname);
542 assert(ret);
543
544 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
545
546 if (ioctl(ethtool_fd, SIOCETHTOOL, &ifr) < 0)
547 return -errno;
548
549 if (buffer.info.sset_mask == 0)
550 return -EOPNOTSUPP;
551
552 #pragma GCC diagnostic push
553 #if HAVE_ZERO_LENGTH_BOUNDS
554 # pragma GCC diagnostic ignored "-Wzero-length-bounds"
555 #endif
556 len = buffer.info.data[0];
557 #pragma GCC diagnostic pop
558 if (len == 0)
559 return -EOPNOTSUPP;
560
561 strings = malloc0(offsetof(struct ethtool_gstrings, data) + len * ETH_GSTRING_LEN);
562 if (!strings)
563 return -ENOMEM;
564
565 strings->cmd = ETHTOOL_GSTRINGS;
566 strings->string_set = stringset_id;
567 strings->len = len;
568
569 ifr.ifr_data = (void*) strings;
570
571 if (ioctl(ethtool_fd, SIOCETHTOOL, &ifr) < 0)
572 return -errno;
573
574 *ret = TAKE_PTR(strings);
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);
599
600 if (ioctl(ethtool_fd, SIOCETHTOOL, &ifr) < 0)
601 return -errno;
602
603 *ret = TAKE_PTR(gfeatures);
604 return 0;
605 }
606
607 static int set_features_bit(
608 const struct ethtool_gstrings *strings,
609 const struct ethtool_gfeatures *gfeatures,
610 struct ethtool_sfeatures *sfeatures,
611 const char *feature,
612 int flag) {
613
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
651 bool found = false;
652 int r = -ENODATA;
653
654 assert(strings);
655 assert(gfeatures);
656 assert(sfeatures);
657 assert(feature);
658
659 if (flag < 0)
660 return 0;
661
662 for (uint32_t i = 0; i < strings->len; i++) {
663 uint32_t block, mask;
664
665 if (!startswith((const char*) &strings->data[i * ETH_GSTRING_LEN], feature))
666 continue;
667
668 block = i / 32;
669 mask = UINT32_C(1) << (i % 32);
670
671 if (!FLAGS_SET(gfeatures->features[block].available, mask) ||
672 FLAGS_SET(gfeatures->features[block].never_changed, mask)) {
673 r = -EOPNOTSUPP;
674 continue;
675 }
676
677 /* The flags is explicitly set by set_features_bit() */
678 if (FLAGS_SET(sfeatures->features[block].valid, mask))
679 continue;
680
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;
688 }
689
690 int ethtool_set_features(int *ethtool_fd, const char *ifname, const int features[static _NET_DEV_FEAT_MAX]) {
691 _cleanup_free_ struct ethtool_gstrings *strings = NULL;
692 _cleanup_free_ struct ethtool_gfeatures *gfeatures = NULL;
693 _cleanup_free_ struct ethtool_sfeatures *sfeatures = NULL;
694 struct ifreq ifr;
695 bool have = false;
696 int r;
697
698 assert(ethtool_fd);
699 assert(ifname);
700 assert(features);
701
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
711 r = ethtool_connect(ethtool_fd);
712 if (r < 0)
713 return r;
714
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");
718
719 r = get_features(*ethtool_fd, ifname, strings->len, &gfeatures);
720 if (r < 0)
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();
727
728 sfeatures->cmd = ETHTOOL_SFEATURES;
729 sfeatures->size = DIV_ROUND_UP(strings->len, 32U);
730
731 for (size_t i = 0; i < _NET_DEV_FEAT_SIMPLE_MAX; i++) {
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);
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);
741 }
742
743 ifr = (struct ifreq) {
744 .ifr_data = (void*) sfeatures,
745 };
746 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
747
748 if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
749 return log_debug_errno(errno, "ethtool: could not set ethtool features for %s", ifname);
750
751 return 0;
752 }
753
754 static int get_glinksettings(int fd, struct ifreq *ifr, struct ethtool_link_usettings **ret) {
755 struct ecmd {
756 struct ethtool_link_settings req;
757 uint32_t link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
758 } ecmd = {
759 .req.cmd = ETHTOOL_GLINKSETTINGS,
760 };
761 struct ethtool_link_usettings *u;
762 unsigned offset;
763
764 assert(fd >= 0);
765 assert(ifr);
766 assert(ret);
767
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
778 if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
779 return -errno;
780
781 if (ecmd.req.link_mode_masks_nwords >= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
782 return -EOPNOTSUPP;
783
784 ecmd.req.link_mode_masks_nwords = -ecmd.req.link_mode_masks_nwords;
785
786 ifr->ifr_data = (void *) &ecmd;
787
788 if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
789 return -errno;
790
791 if (ecmd.req.link_mode_masks_nwords <= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
792 return -EOPNOTSUPP;
793
794 u = new(struct ethtool_link_usettings, 1);
795 if (!u)
796 return -ENOMEM;
797
798 *u = (struct ethtool_link_usettings) {
799 .base = ecmd.req,
800 };
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
811 *ret = u;
812
813 return 0;
814 }
815
816 static int get_gset(int fd, struct ifreq *ifr, struct ethtool_link_usettings **ret) {
817 struct ethtool_link_usettings *e;
818 struct ethtool_cmd ecmd = {
819 .cmd = ETHTOOL_GSET,
820 };
821
822 assert(fd >= 0);
823 assert(ifr);
824 assert(ret);
825
826 ifr->ifr_data = (void *) &ecmd;
827
828 if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
829 return -errno;
830
831 e = new(struct ethtool_link_usettings, 1);
832 if (!e)
833 return -ENOMEM;
834
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,
844 .base.eth_tp_mdix = ecmd.eth_tp_mdix,
845 .base.eth_tp_mdix_ctrl = ecmd.eth_tp_mdix_ctrl,
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 };
851
852 *ret = e;
853
854 return 0;
855 }
856
857 static int set_slinksettings(int fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) {
858 struct {
859 struct ethtool_link_settings req;
860 uint32_t link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
861 } ecmd = {};
862 unsigned offset;
863
864 assert(fd >= 0);
865 assert(ifr);
866 assert(u);
867
868 if (u->base.cmd != ETHTOOL_GLINKSETTINGS || u->base.link_mode_masks_nwords <= 0)
869 return -EINVAL;
870
871 ecmd.req = u->base;
872 ecmd.req.cmd = ETHTOOL_SLINKSETTINGS;
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
884 return RET_NERRNO(ioctl(fd, SIOCETHTOOL, ifr));
885 }
886
887 static int set_sset(int fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) {
888 struct ethtool_cmd ecmd = {
889 .cmd = ETHTOOL_SSET,
890 };
891
892 assert(fd >= 0);
893 assert(ifr);
894 assert(u);
895
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;
910 ecmd.eth_tp_mdix = u->base.eth_tp_mdix;
911 ecmd.eth_tp_mdix_ctrl = u->base.eth_tp_mdix_ctrl;
912
913 ifr->ifr_data = (void *) &ecmd;
914
915 return RET_NERRNO(ioctl(fd, SIOCETHTOOL, ifr));
916 }
917
918 int ethtool_set_glinksettings(
919 int *fd,
920 const char *ifname,
921 int autonegotiation,
922 const uint32_t advertise[static N_ADVERTISE],
923 uint64_t speed,
924 Duplex duplex,
925 NetDevPort port,
926 uint8_t mdi) {
927
928 _cleanup_free_ struct ethtool_link_usettings *u = NULL;
929 struct ifreq ifr = {};
930 bool changed = false;
931 int r;
932
933 assert(fd);
934 assert(ifname);
935 assert(advertise);
936
937 if (autonegotiation < 0 && memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE) &&
938 speed == 0 && duplex < 0 && port < 0 && mdi == ETH_TP_MDI_INVALID)
939 return 0;
940
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
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 }
957 }
958
959 r = ethtool_connect(fd);
960 if (r < 0)
961 return r;
962
963 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
964
965 r = get_glinksettings(*fd, &ifr, &u);
966 if (r < 0) {
967 r = get_gset(*fd, &ifr, &u);
968 if (r < 0)
969 return log_debug_errno(r, "ethtool: Cannot get device settings for %s: %m", ifname);
970 }
971
972 if (speed > 0)
973 UPDATE(u->base.speed, DIV_ROUND_UP(speed, 1000000), changed);
974
975 if (duplex >= 0)
976 UPDATE(u->base.duplex, duplex, changed);
977
978 if (port >= 0)
979 UPDATE(u->base.port, port, changed);
980
981 if (autonegotiation >= 0)
982 UPDATE(u->base.autoneg, autonegotiation, changed);
983
984 if (!memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE)) {
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);
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);
994 }
995
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
1003 if (!changed)
1004 return 0;
1005
1006 if (u->base.cmd == ETHTOOL_GLINKSETTINGS)
1007 r = set_slinksettings(*fd, &ifr, u);
1008 else
1009 r = set_sset(*fd, &ifr, u);
1010 if (r < 0)
1011 return log_debug_errno(r, "ethtool: Cannot set device settings for %s: %m", ifname);
1012
1013 return r;
1014 }
1015
1016 int ethtool_set_channels(int *fd, const char *ifname, const netdev_channels *channels) {
1017 struct ethtool_channels ecmd = {
1018 .cmd = ETHTOOL_GCHANNELS,
1019 };
1020 struct ifreq ifr = {
1021 .ifr_data = (void*) &ecmd,
1022 };
1023 bool need_update = false;
1024 int r;
1025
1026 assert(fd);
1027 assert(ifname);
1028 assert(channels);
1029
1030 if (!channels->rx.set &&
1031 !channels->tx.set &&
1032 !channels->other.set &&
1033 !channels->combined.set)
1034 return 0;
1035
1036 r = ethtool_connect(fd);
1037 if (r < 0)
1038 return r;
1039
1040 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
1041
1042 if (ioctl(*fd, SIOCETHTOOL, &ifr) < 0)
1043 return -errno;
1044
1045 if (channels->rx.set)
1046 UPDATE_WITH_MAX(ecmd.rx_count, ecmd.max_rx, channels->rx.value, need_update);
1047
1048 if (channels->tx.set)
1049 UPDATE_WITH_MAX(ecmd.tx_count, ecmd.max_tx, channels->tx.value, need_update);
1050
1051 if (channels->other.set)
1052 UPDATE_WITH_MAX(ecmd.other_count, ecmd.max_other, channels->other.value, need_update);
1053
1054 if (channels->combined.set)
1055 UPDATE_WITH_MAX(ecmd.combined_count, ecmd.max_combined, channels->combined.value, need_update);
1056
1057 if (!need_update)
1058 return 0;
1059
1060 ecmd.cmd = ETHTOOL_SCHANNELS;
1061 return RET_NERRNO(ioctl(*fd, SIOCETHTOOL, &ifr));
1062 }
1063
1064 int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int autoneg) {
1065 struct ethtool_pauseparam ecmd = {
1066 .cmd = ETHTOOL_GPAUSEPARAM,
1067 };
1068 struct ifreq ifr = {
1069 .ifr_data = (void*) &ecmd,
1070 };
1071 bool need_update = false;
1072 int r;
1073
1074 assert(fd);
1075 assert(ifname);
1076
1077 if (rx < 0 && tx < 0 && autoneg < 0)
1078 return 0;
1079
1080 r = ethtool_connect(fd);
1081 if (r < 0)
1082 return r;
1083
1084 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
1085
1086 if (ioctl(*fd, SIOCETHTOOL, &ifr) < 0)
1087 return -errno;
1088
1089 if (rx >= 0)
1090 UPDATE(ecmd.rx_pause, (uint32_t) rx, need_update);
1091
1092 if (tx >= 0)
1093 UPDATE(ecmd.tx_pause, (uint32_t) tx, need_update);
1094
1095 if (autoneg >= 0)
1096 UPDATE(ecmd.autoneg, (uint32_t) autoneg, need_update);
1097
1098 if (!need_update)
1099 return 0;
1100
1101 ecmd.cmd = ETHTOOL_SPAUSEPARAM;
1102 return RET_NERRNO(ioctl(*fd, SIOCETHTOOL, &ifr));
1103 }
1104
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
1147 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
1148
1149 if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
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;
1222 return RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr));
1223 }
1224
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
1237 uint32_t *advertise = ASSERT_PTR(data);
1238 int r;
1239
1240 assert(filename);
1241 assert(section);
1242 assert(lvalue);
1243 assert(rvalue);
1244
1245 if (isempty(rvalue)) {
1246 /* Empty string resets the value. */
1247 memzero(advertise, sizeof(uint32_t) * N_ADVERTISE);
1248 return 0;
1249 }
1250
1251 for (const char *p = rvalue;;) {
1252 _cleanup_free_ char *w = NULL;
1253 enum ethtool_link_mode_bit_indices mode;
1254
1255 r = extract_first_word(&p, &w, NULL, 0);
1256 if (r == -ENOMEM)
1257 return log_oom();
1258 if (r < 0) {
1259 log_syntax(unit, LOG_WARNING, filename, line, r,
1260 "Failed to split advertise modes '%s', ignoring assignment: %m", rvalue);
1261 return 0;
1262 }
1263 if (r == 0)
1264 return 0;
1265
1266 mode = ethtool_link_mode_bit_from_string(w);
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) {
1270 log_syntax(unit, LOG_WARNING, filename, line, mode,
1271 "Failed to parse advertise mode, ignoring: %s", w);
1272 continue;
1273 }
1274
1275 advertise[mode / 32] |= 1UL << (mode % 32);
1276 }
1277 }
1278
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
1321 int config_parse_ring_buffer_or_channel(
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
1333 u32_opt *dst = ASSERT_PTR(data);
1334 uint32_t k;
1335 int r;
1336
1337 assert(filename);
1338 assert(section);
1339 assert(lvalue);
1340 assert(rvalue);
1341
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;
1352 }
1353
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;
1364 }
1365
1366 dst->value = k;
1367 dst->set = true;
1368 return 0;
1369 }
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 }
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 }