]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/ethtool-util.c
Merge pull request #18481 from keszybz/rpm-restart-post-trans
[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 "strxcpyx.h"
18
19 static const char* const duplex_table[_DUP_MAX] = {
20 [DUP_FULL] = "full",
21 [DUP_HALF] = "half"
22 };
23
24 DEFINE_STRING_TABLE_LOOKUP(duplex, Duplex);
25 DEFINE_CONFIG_PARSE_ENUM(config_parse_duplex, duplex, Duplex, "Failed to parse duplex setting");
26
27 static const char* const wol_table[_WOL_MAX] = {
28 [WOL_PHY] = "phy",
29 [WOL_UCAST] = "unicast",
30 [WOL_MCAST] = "multicast",
31 [WOL_BCAST] = "broadcast",
32 [WOL_ARP] = "arp",
33 [WOL_MAGIC] = "magic",
34 [WOL_MAGICSECURE] = "secureon",
35 [WOL_OFF] = "off",
36 };
37
38 DEFINE_STRING_TABLE_LOOKUP(wol, WakeOnLan);
39 DEFINE_CONFIG_PARSE_ENUM(config_parse_wol, wol, WakeOnLan, "Failed to parse WakeOnLan setting");
40
41 static const char* const port_table[] = {
42 [NET_DEV_PORT_TP] = "tp",
43 [NET_DEV_PORT_AUI] = "aui",
44 [NET_DEV_PORT_MII] = "mii",
45 [NET_DEV_PORT_FIBRE] = "fibre",
46 [NET_DEV_PORT_BNC] = "bnc",
47 };
48
49 DEFINE_STRING_TABLE_LOOKUP(port, NetDevPort);
50 DEFINE_CONFIG_PARSE_ENUM(config_parse_port, port, NetDevPort, "Failed to parse Port setting");
51
52 static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = {
53 [NET_DEV_FEAT_RX] = "rx-checksum",
54 [NET_DEV_FEAT_TX] = "tx-checksum-", /* The suffix "-" means any feature beginning with "tx-checksum-" */
55 [NET_DEV_FEAT_GSO] = "tx-generic-segmentation",
56 [NET_DEV_FEAT_GRO] = "rx-gro",
57 [NET_DEV_FEAT_LRO] = "rx-lro",
58 [NET_DEV_FEAT_TSO] = "tx-tcp-segmentation",
59 [NET_DEV_FEAT_TSO6] = "tx-tcp6-segmentation",
60 };
61
62 static const char* const ethtool_link_mode_bit_table[] = {
63 [ETHTOOL_LINK_MODE_10baseT_Half_BIT] = "10baset-half",
64 [ETHTOOL_LINK_MODE_10baseT_Full_BIT] = "10baset-full",
65 [ETHTOOL_LINK_MODE_100baseT_Half_BIT] = "100baset-half",
66 [ETHTOOL_LINK_MODE_100baseT_Full_BIT] = "100baset-full",
67 [ETHTOOL_LINK_MODE_1000baseT_Half_BIT] = "1000baset-half",
68 [ETHTOOL_LINK_MODE_1000baseT_Full_BIT] = "1000baset-full",
69 [ETHTOOL_LINK_MODE_Autoneg_BIT] = "autonegotiation",
70 [ETHTOOL_LINK_MODE_TP_BIT] = "tp",
71 [ETHTOOL_LINK_MODE_AUI_BIT] = "aui",
72 [ETHTOOL_LINK_MODE_MII_BIT] = "mii",
73 [ETHTOOL_LINK_MODE_FIBRE_BIT] = "fibre",
74 [ETHTOOL_LINK_MODE_BNC_BIT] = "bnc",
75 [ETHTOOL_LINK_MODE_10000baseT_Full_BIT] = "10000baset-full",
76 [ETHTOOL_LINK_MODE_Pause_BIT] = "pause",
77 [ETHTOOL_LINK_MODE_Asym_Pause_BIT] = "asym-pause",
78 [ETHTOOL_LINK_MODE_2500baseX_Full_BIT] = "2500basex-full",
79 [ETHTOOL_LINK_MODE_Backplane_BIT] = "backplane",
80 [ETHTOOL_LINK_MODE_1000baseKX_Full_BIT] = "1000basekx-full",
81 [ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT] = "10000basekx4-full",
82 [ETHTOOL_LINK_MODE_10000baseKR_Full_BIT] = "10000basekr-full",
83 [ETHTOOL_LINK_MODE_10000baseR_FEC_BIT] = "10000baser-fec",
84 [ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT] = "20000basemld2-full",
85 [ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT] = "20000basekr2-full",
86 [ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT] = "40000basekr4-full",
87 [ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT] = "40000basecr4-full",
88 [ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT] = "40000basesr4-full",
89 [ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT] = "40000baselr4-full",
90 [ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT] = "56000basekr4-full",
91 [ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT] = "56000basecr4-full",
92 [ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT] = "56000basesr4-full",
93 [ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT] = "56000baselr4-full",
94 [ETHTOOL_LINK_MODE_25000baseCR_Full_BIT] = "25000basecr-full",
95 [ETHTOOL_LINK_MODE_25000baseKR_Full_BIT] = "25000basekr-full",
96 [ETHTOOL_LINK_MODE_25000baseSR_Full_BIT] = "25000basesr-full",
97 [ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT] = "50000basecr2-full",
98 [ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT] = "50000basekr2-full",
99 [ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT] = "100000basekr4-full",
100 [ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT] = "100000basesr4-full",
101 [ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT] = "100000basecr4-full",
102 [ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT] = "100000baselr4-er4-full",
103 [ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT] = "50000basesr2-full",
104 [ETHTOOL_LINK_MODE_1000baseX_Full_BIT] = "1000basex-full",
105 [ETHTOOL_LINK_MODE_10000baseCR_Full_BIT] = "10000basecr-full",
106 [ETHTOOL_LINK_MODE_10000baseSR_Full_BIT] = "10000basesr-full",
107 [ETHTOOL_LINK_MODE_10000baseLR_Full_BIT] = "10000baselr-full",
108 [ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT] = "10000baselrm-full",
109 [ETHTOOL_LINK_MODE_10000baseER_Full_BIT] = "10000baseer-full",
110 [ETHTOOL_LINK_MODE_2500baseT_Full_BIT] = "2500baset-full",
111 [ETHTOOL_LINK_MODE_5000baseT_Full_BIT] = "5000baset-full",
112 [ETHTOOL_LINK_MODE_FEC_NONE_BIT] = "fec-none",
113 [ETHTOOL_LINK_MODE_FEC_RS_BIT] = "fec-rs",
114 [ETHTOOL_LINK_MODE_FEC_BASER_BIT] = "fec-baser",
115 [ETHTOOL_LINK_MODE_50000baseKR_Full_BIT] = "50000basekr-full",
116 [ETHTOOL_LINK_MODE_50000baseSR_Full_BIT] = "50000basesr-full",
117 [ETHTOOL_LINK_MODE_50000baseCR_Full_BIT] = "50000basecr-full",
118 [ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT] = "50000baselr-er-fr-full",
119 [ETHTOOL_LINK_MODE_50000baseDR_Full_BIT] = "50000basedr-full",
120 [ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT] = "100000basekr2-full",
121 [ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT] = "100000basesr2-full",
122 [ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT] = "100000basecr2-full",
123 [ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT] = "100000baselr2-er2-fr2-full",
124 [ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT] = "100000basedr2-full",
125 [ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT] = "200000basekr4-full",
126 [ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT] = "200000basesr4-full",
127 [ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT] = "200000baselr4-er4-fr4-full",
128 [ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT] = "200000basedr4-full",
129 [ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT] = "200000basecr4-full",
130 [ETHTOOL_LINK_MODE_100baseT1_Full_BIT] = "100baset1-full",
131 [ETHTOOL_LINK_MODE_1000baseT1_Full_BIT] = "1000baset1-full",
132 [ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT] = "400000basekr8-full",
133 [ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT] = "400000basesr8-full",
134 [ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT] = "400000baselr8-er8-fr8-full",
135 [ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT] = "400000basedr8-full",
136 [ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT] = "400000basecr8-full",
137 [ETHTOOL_LINK_MODE_FEC_LLRS_BIT] = "fec-llrs",
138 [ETHTOOL_LINK_MODE_100000baseKR_Full_BIT] = "100000basekr-full",
139 [ETHTOOL_LINK_MODE_100000baseSR_Full_BIT] = "100000basesr-full",
140 [ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT] = "100000baselr-er-fr-full",
141 [ETHTOOL_LINK_MODE_100000baseCR_Full_BIT] = "100000basecr-full",
142 [ETHTOOL_LINK_MODE_100000baseDR_Full_BIT] = "100000basedr-full",
143 [ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT] = "200000basekr2-full",
144 [ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT] = "200000basesr2-full",
145 [ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT] = "200000baselr2-er2-fr2-full",
146 [ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT] = "200000basedr2-full",
147 [ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT] = "200000basecr2-full",
148 [ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT] = "400000basekr4-full",
149 [ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT] = "400000basesr4-full",
150 [ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT] = "400000baselr4-er4-fr4-full",
151 [ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT] = "400000basedr4-full",
152 [ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT] = "400000basecr4-full",
153 [ETHTOOL_LINK_MODE_100baseFX_Half_BIT] = "100basefx-half",
154 [ETHTOOL_LINK_MODE_100baseFX_Full_BIT] = "100basefx-full",
155 };
156 /* Make sure the array is large enough to fit all bits */
157 assert_cc((ELEMENTSOF(ethtool_link_mode_bit_table)-1) / 32 < N_ADVERTISE);
158
159 DEFINE_STRING_TABLE_LOOKUP(ethtool_link_mode_bit, enum ethtool_link_mode_bit_indices);
160
161 static int ethtool_connect_or_warn(int *ret, bool warn) {
162 int fd;
163
164 assert_return(ret, -EINVAL);
165
166 fd = socket_ioctl_fd();
167 if (fd < 0)
168 return log_full_errno(warn ? LOG_WARNING: LOG_DEBUG, fd,
169 "ethtool: could not create control socket: %m");
170
171 *ret = fd;
172
173 return 0;
174 }
175
176 int ethtool_get_driver(int *ethtool_fd, const char *ifname, char **ret) {
177 struct ethtool_drvinfo ecmd = {
178 .cmd = ETHTOOL_GDRVINFO,
179 };
180 struct ifreq ifr = {
181 .ifr_data = (void*) &ecmd,
182 };
183 char *d;
184 int r;
185
186 assert(ethtool_fd);
187 assert(ifname);
188 assert(ret);
189
190 if (*ethtool_fd < 0) {
191 r = ethtool_connect_or_warn(ethtool_fd, true);
192 if (r < 0)
193 return r;
194 }
195
196 strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
197
198 r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
199 if (r < 0)
200 return -errno;
201
202 if (isempty(ecmd.driver))
203 return -ENODATA;
204
205 d = strdup(ecmd.driver);
206 if (!d)
207 return -ENOMEM;
208
209 *ret = d;
210 return 0;
211 }
212
213 int ethtool_get_link_info(
214 int *ethtool_fd,
215 const char *ifname,
216 int *ret_autonegotiation,
217 uint64_t *ret_speed,
218 Duplex *ret_duplex,
219 NetDevPort *ret_port) {
220
221 struct ethtool_cmd ecmd = {
222 .cmd = ETHTOOL_GSET,
223 };
224 struct ifreq ifr = {
225 .ifr_data = (void*) &ecmd,
226 };
227 int r;
228
229 assert(ethtool_fd);
230 assert(ifname);
231
232 if (*ethtool_fd < 0) {
233 r = ethtool_connect_or_warn(ethtool_fd, false);
234 if (r < 0)
235 return r;
236 }
237
238 strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
239
240 r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
241 if (r < 0)
242 return -errno;
243
244 if (ret_autonegotiation)
245 *ret_autonegotiation = ecmd.autoneg;
246
247 if (ret_speed) {
248 uint32_t speed;
249
250 speed = ethtool_cmd_speed(&ecmd);
251 *ret_speed = speed == (uint32_t) SPEED_UNKNOWN ?
252 UINT64_MAX : (uint64_t) speed * 1000 * 1000;
253 }
254
255 if (ret_duplex)
256 *ret_duplex = ecmd.duplex;
257
258 if (ret_port)
259 *ret_port = ecmd.port;
260
261 return 0;
262 }
263
264 int ethtool_get_permanent_macaddr(int *ethtool_fd, const char *ifname, struct ether_addr *ret) {
265 _cleanup_close_ int fd = -1;
266 struct {
267 struct ethtool_perm_addr addr;
268 uint8_t space[MAX_ADDR_LEN];
269 } epaddr = {
270 .addr.cmd = ETHTOOL_GPERMADDR,
271 .addr.size = MAX_ADDR_LEN,
272 };
273 struct ifreq ifr = {
274 .ifr_data = (caddr_t) &epaddr,
275 };
276 int r;
277
278 assert(ifname);
279 assert(ret);
280
281 if (!ethtool_fd)
282 ethtool_fd = &fd;
283
284 if (*ethtool_fd < 0) {
285 r = ethtool_connect_or_warn(ethtool_fd, false);
286 if (r < 0)
287 return r;
288 }
289
290 strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
291
292 r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
293 if (r < 0)
294 return -errno;
295
296 if (epaddr.addr.size != 6)
297 return -EOPNOTSUPP;
298
299 #pragma GCC diagnostic push
300 #if HAVE_ZERO_LENGTH_BOUNDS
301 # pragma GCC diagnostic ignored "-Wzero-length-bounds"
302 #endif
303 for (size_t i = 0; i < epaddr.addr.size; i++)
304 ret->ether_addr_octet[i] = epaddr.addr.data[i];
305 #pragma GCC diagnostic pop
306
307 return 0;
308 }
309
310 int ethtool_set_speed(int *ethtool_fd, const char *ifname, unsigned speed, Duplex duplex) {
311 struct ethtool_cmd ecmd = {
312 .cmd = ETHTOOL_GSET,
313 };
314 struct ifreq ifr = {
315 .ifr_data = (void*) &ecmd,
316 };
317 bool need_update = false;
318 int r;
319
320 assert(ethtool_fd);
321 assert(ifname);
322
323 if (speed == 0 && duplex == _DUP_INVALID)
324 return 0;
325
326 if (*ethtool_fd < 0) {
327 r = ethtool_connect_or_warn(ethtool_fd, true);
328 if (r < 0)
329 return r;
330 }
331
332 strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
333
334 r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
335 if (r < 0)
336 return -errno;
337
338 if (ethtool_cmd_speed(&ecmd) != speed) {
339 ethtool_cmd_speed_set(&ecmd, speed);
340 need_update = true;
341 }
342
343 switch (duplex) {
344 case DUP_HALF:
345 if (ecmd.duplex != DUPLEX_HALF) {
346 ecmd.duplex = DUPLEX_HALF;
347 need_update = true;
348 }
349 break;
350 case DUP_FULL:
351 if (ecmd.duplex != DUPLEX_FULL) {
352 ecmd.duplex = DUPLEX_FULL;
353 need_update = true;
354 }
355 break;
356 default:
357 break;
358 }
359
360 if (need_update) {
361 ecmd.cmd = ETHTOOL_SSET;
362
363 r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
364 if (r < 0)
365 return -errno;
366 }
367
368 return 0;
369 }
370
371 int ethtool_set_wol(int *ethtool_fd, const char *ifname, WakeOnLan wol) {
372 struct ethtool_wolinfo ecmd = {
373 .cmd = ETHTOOL_GWOL,
374 };
375 struct ifreq ifr = {
376 .ifr_data = (void*) &ecmd,
377 };
378 bool need_update = false;
379 int r;
380
381 assert(ethtool_fd);
382 assert(ifname);
383
384 if (wol == _WOL_INVALID)
385 return 0;
386
387 if (*ethtool_fd < 0) {
388 r = ethtool_connect_or_warn(ethtool_fd, true);
389 if (r < 0)
390 return r;
391 }
392
393 strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
394
395 r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
396 if (r < 0)
397 return -errno;
398
399 switch (wol) {
400 case WOL_PHY:
401 if (ecmd.wolopts != WAKE_PHY) {
402 ecmd.wolopts = WAKE_PHY;
403 need_update = true;
404 }
405 break;
406 case WOL_UCAST:
407 if (ecmd.wolopts != WAKE_UCAST) {
408 ecmd.wolopts = WAKE_UCAST;
409 need_update = true;
410 }
411 break;
412 case WOL_MCAST:
413 if (ecmd.wolopts != WAKE_MCAST) {
414 ecmd.wolopts = WAKE_MCAST;
415 need_update = true;
416 }
417 break;
418 case WOL_BCAST:
419 if (ecmd.wolopts != WAKE_BCAST) {
420 ecmd.wolopts = WAKE_BCAST;
421 need_update = true;
422 }
423 break;
424 case WOL_ARP:
425 if (ecmd.wolopts != WAKE_ARP) {
426 ecmd.wolopts = WAKE_ARP;
427 need_update = true;
428 }
429 break;
430 case WOL_MAGIC:
431 if (ecmd.wolopts != WAKE_MAGIC) {
432 ecmd.wolopts = WAKE_MAGIC;
433 need_update = true;
434 }
435 break;
436 case WOL_MAGICSECURE:
437 if (ecmd.wolopts != WAKE_MAGICSECURE) {
438 ecmd.wolopts = WAKE_MAGICSECURE;
439 need_update = true;
440 }
441 break;
442 case WOL_OFF:
443 if (ecmd.wolopts != 0) {
444 ecmd.wolopts = 0;
445 need_update = true;
446 }
447 break;
448 default:
449 break;
450 }
451
452 if (need_update) {
453 ecmd.cmd = ETHTOOL_SWOL;
454
455 r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
456 if (r < 0)
457 return -errno;
458 }
459
460 return 0;
461 }
462
463 int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netdev_ring_param *ring) {
464 struct ethtool_ringparam ecmd = {
465 .cmd = ETHTOOL_GRINGPARAM,
466 };
467 struct ifreq ifr = {
468 .ifr_data = (void*) &ecmd,
469 };
470 bool need_update = false;
471 int r;
472
473 assert(ethtool_fd);
474 assert(ifname);
475 assert(ring);
476
477 if (*ethtool_fd < 0) {
478 r = ethtool_connect_or_warn(ethtool_fd, true);
479 if (r < 0)
480 return r;
481 }
482
483 strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
484
485 r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
486 if (r < 0)
487 return -errno;
488
489 if (ring->rx_pending_set && ecmd.rx_pending != ring->rx_pending) {
490 ecmd.rx_pending = ring->rx_pending;
491 need_update = true;
492 }
493
494 if (ring->rx_mini_pending_set && ecmd.rx_mini_pending != ring->rx_mini_pending) {
495 ecmd.rx_mini_pending = ring->rx_mini_pending;
496 need_update = true;
497 }
498
499 if (ring->rx_jumbo_pending_set && ecmd.rx_jumbo_pending != ring->rx_jumbo_pending) {
500 ecmd.rx_jumbo_pending = ring->rx_jumbo_pending;
501 need_update = true;
502 }
503
504 if (ring->tx_pending_set && ecmd.tx_pending != ring->tx_pending) {
505 ecmd.tx_pending = ring->tx_pending;
506 need_update = true;
507 }
508
509 if (need_update) {
510 ecmd.cmd = ETHTOOL_SRINGPARAM;
511
512 r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
513 if (r < 0)
514 return -errno;
515 }
516
517 return 0;
518 }
519
520 static int get_stringset(int ethtool_fd, struct ifreq *ifr, int stringset_id, struct ethtool_gstrings **ret) {
521 _cleanup_free_ struct ethtool_gstrings *strings = NULL;
522 struct {
523 struct ethtool_sset_info info;
524 uint32_t space;
525 } buffer = {
526 .info = {
527 .cmd = ETHTOOL_GSSET_INFO,
528 .sset_mask = UINT64_C(1) << stringset_id,
529 },
530 };
531 unsigned len;
532 int r;
533
534 assert(ethtool_fd >= 0);
535 assert(ifr);
536 assert(ret);
537
538 ifr->ifr_data = (void *) &buffer.info;
539
540 r = ioctl(ethtool_fd, SIOCETHTOOL, ifr);
541 if (r < 0)
542 return -errno;
543
544 if (!buffer.info.sset_mask)
545 return -EINVAL;
546
547 #pragma GCC diagnostic push
548 #if HAVE_ZERO_LENGTH_BOUNDS
549 # pragma GCC diagnostic ignored "-Wzero-length-bounds"
550 #endif
551 len = buffer.info.data[0];
552 #pragma GCC diagnostic pop
553
554 strings = malloc0(sizeof(struct ethtool_gstrings) + len * ETH_GSTRING_LEN);
555 if (!strings)
556 return -ENOMEM;
557
558 strings->cmd = ETHTOOL_GSTRINGS;
559 strings->string_set = stringset_id;
560 strings->len = len;
561
562 ifr->ifr_data = (void *) strings;
563
564 r = ioctl(ethtool_fd, SIOCETHTOOL, ifr);
565 if (r < 0)
566 return -errno;
567
568 *ret = TAKE_PTR(strings);
569
570 return 0;
571 }
572
573 static int set_features_bit(
574 const struct ethtool_gstrings *strings,
575 const char *feature,
576 bool flag,
577 struct ethtool_sfeatures *sfeatures) {
578 bool found = false;
579
580 assert(strings);
581 assert(feature);
582 assert(sfeatures);
583
584 for (size_t i = 0; i < strings->len; i++)
585 if (streq((char *) &strings->data[i * ETH_GSTRING_LEN], feature) ||
586 (endswith(feature, "-") && startswith((char *) &strings->data[i * ETH_GSTRING_LEN], feature))) {
587 size_t block, bit;
588
589 block = i / 32;
590 bit = i % 32;
591
592 sfeatures->features[block].valid |= 1 << bit;
593 SET_FLAG(sfeatures->features[block].requested, 1 << bit, flag);
594 found = true;
595 }
596
597 return found ? 0 : -ENODATA;
598 }
599
600 int ethtool_set_features(int *ethtool_fd, const char *ifname, const int *features) {
601 _cleanup_free_ struct ethtool_gstrings *strings = NULL;
602 struct ethtool_sfeatures *sfeatures;
603 struct ifreq ifr = {};
604 int i, r;
605
606 assert(ethtool_fd);
607 assert(ifname);
608 assert(features);
609
610 if (*ethtool_fd < 0) {
611 r = ethtool_connect_or_warn(ethtool_fd, true);
612 if (r < 0)
613 return r;
614 }
615
616 strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
617
618 r = get_stringset(*ethtool_fd, &ifr, ETH_SS_FEATURES, &strings);
619 if (r < 0)
620 return log_warning_errno(r, "ethtool: could not get ethtool features for %s", ifname);
621
622 sfeatures = alloca0(sizeof(struct ethtool_sfeatures) + DIV_ROUND_UP(strings->len, 32U) * sizeof(sfeatures->features[0]));
623 sfeatures->cmd = ETHTOOL_SFEATURES;
624 sfeatures->size = DIV_ROUND_UP(strings->len, 32U);
625
626 for (i = 0; i < _NET_DEV_FEAT_MAX; i++)
627 if (features[i] != -1) {
628 r = set_features_bit(strings, netdev_feature_table[i], features[i], sfeatures);
629 if (r < 0) {
630 log_warning_errno(r, "ethtool: could not find feature, ignoring: %s", netdev_feature_table[i]);
631 continue;
632 }
633 }
634
635 ifr.ifr_data = (void *) sfeatures;
636
637 r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
638 if (r < 0)
639 return log_warning_errno(r, "ethtool: could not set ethtool features for %s", ifname);
640
641 return 0;
642 }
643
644 static int get_glinksettings(int fd, struct ifreq *ifr, struct ethtool_link_usettings **ret) {
645 struct ecmd {
646 struct ethtool_link_settings req;
647 __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
648 } ecmd = {
649 .req.cmd = ETHTOOL_GLINKSETTINGS,
650 };
651 struct ethtool_link_usettings *u;
652 unsigned offset;
653 int r;
654
655 assert(fd >= 0);
656 assert(ifr);
657 assert(ret);
658
659 /* The interaction user/kernel via the new API requires a small ETHTOOL_GLINKSETTINGS
660 handshake first to agree on the length of the link mode bitmaps. If kernel doesn't
661 agree with user, it returns the bitmap length it is expecting from user as a negative
662 length (and cmd field is 0). When kernel and user agree, kernel returns valid info in
663 all fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS). Based on
664 https://github.com/torvalds/linux/commit/3f1ac7a700d039c61d8d8b99f28d605d489a60cf
665 */
666
667 ifr->ifr_data = (void *) &ecmd;
668
669 r = ioctl(fd, SIOCETHTOOL, ifr);
670 if (r < 0)
671 return -errno;
672
673 if (ecmd.req.link_mode_masks_nwords >= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
674 return -EOPNOTSUPP;
675
676 ecmd.req.link_mode_masks_nwords = -ecmd.req.link_mode_masks_nwords;
677
678 ifr->ifr_data = (void *) &ecmd;
679
680 r = ioctl(fd, SIOCETHTOOL, ifr);
681 if (r < 0)
682 return -errno;
683
684 if (ecmd.req.link_mode_masks_nwords <= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
685 return -EOPNOTSUPP;
686
687 u = new(struct ethtool_link_usettings, 1);
688 if (!u)
689 return -ENOMEM;
690
691 *u = (struct ethtool_link_usettings) {
692 .base = ecmd.req,
693 };
694
695 offset = 0;
696 memcpy(u->link_modes.supported, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords);
697
698 offset += ecmd.req.link_mode_masks_nwords;
699 memcpy(u->link_modes.advertising, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords);
700
701 offset += ecmd.req.link_mode_masks_nwords;
702 memcpy(u->link_modes.lp_advertising, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords);
703
704 *ret = u;
705
706 return 0;
707 }
708
709 static int get_gset(int fd, struct ifreq *ifr, struct ethtool_link_usettings **ret) {
710 struct ethtool_link_usettings *e;
711 struct ethtool_cmd ecmd = {
712 .cmd = ETHTOOL_GSET,
713 };
714 int r;
715
716 assert(fd >= 0);
717 assert(ifr);
718 assert(ret);
719
720 ifr->ifr_data = (void *) &ecmd;
721
722 r = ioctl(fd, SIOCETHTOOL, ifr);
723 if (r < 0)
724 return -errno;
725
726 e = new(struct ethtool_link_usettings, 1);
727 if (!e)
728 return -ENOMEM;
729
730 *e = (struct ethtool_link_usettings) {
731 .base.cmd = ETHTOOL_GSET,
732 .base.link_mode_masks_nwords = 1,
733 .base.speed = ethtool_cmd_speed(&ecmd),
734 .base.duplex = ecmd.duplex,
735 .base.port = ecmd.port,
736 .base.phy_address = ecmd.phy_address,
737 .base.autoneg = ecmd.autoneg,
738 .base.mdio_support = ecmd.mdio_support,
739
740 .link_modes.supported[0] = ecmd.supported,
741 .link_modes.advertising[0] = ecmd.advertising,
742 .link_modes.lp_advertising[0] = ecmd.lp_advertising,
743 };
744
745 *ret = e;
746
747 return 0;
748 }
749
750 static int set_slinksettings(int fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) {
751 struct {
752 struct ethtool_link_settings req;
753 __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
754 } ecmd = {};
755 unsigned offset;
756 int r;
757
758 assert(fd >= 0);
759 assert(ifr);
760 assert(u);
761
762 if (u->base.cmd != ETHTOOL_GLINKSETTINGS || u->base.link_mode_masks_nwords <= 0)
763 return -EINVAL;
764
765 ecmd.req = u->base;
766 ecmd.req.cmd = ETHTOOL_SLINKSETTINGS;
767 offset = 0;
768 memcpy(&ecmd.link_mode_data[offset], u->link_modes.supported, 4 * ecmd.req.link_mode_masks_nwords);
769
770 offset += ecmd.req.link_mode_masks_nwords;
771 memcpy(&ecmd.link_mode_data[offset], u->link_modes.advertising, 4 * ecmd.req.link_mode_masks_nwords);
772
773 offset += ecmd.req.link_mode_masks_nwords;
774 memcpy(&ecmd.link_mode_data[offset], u->link_modes.lp_advertising, 4 * ecmd.req.link_mode_masks_nwords);
775
776 ifr->ifr_data = (void *) &ecmd;
777
778 r = ioctl(fd, SIOCETHTOOL, ifr);
779 if (r < 0)
780 return -errno;
781
782 return 0;
783 }
784
785 static int set_sset(int fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) {
786 struct ethtool_cmd ecmd = {
787 .cmd = ETHTOOL_SSET,
788 };
789 int r;
790
791 assert(fd >= 0);
792 assert(ifr);
793 assert(u);
794
795 if (u->base.cmd != ETHTOOL_GSET || u->base.link_mode_masks_nwords <= 0)
796 return -EINVAL;
797
798 ecmd.supported = u->link_modes.supported[0];
799 ecmd.advertising = u->link_modes.advertising[0];
800 ecmd.lp_advertising = u->link_modes.lp_advertising[0];
801
802 ethtool_cmd_speed_set(&ecmd, u->base.speed);
803
804 ecmd.duplex = u->base.duplex;
805 ecmd.port = u->base.port;
806 ecmd.phy_address = u->base.phy_address;
807 ecmd.autoneg = u->base.autoneg;
808 ecmd.mdio_support = u->base.mdio_support;
809 ecmd.eth_tp_mdix = u->base.eth_tp_mdix;
810 ecmd.eth_tp_mdix_ctrl = u->base.eth_tp_mdix_ctrl;
811
812 ifr->ifr_data = (void *) &ecmd;
813
814 r = ioctl(fd, SIOCETHTOOL, ifr);
815 if (r < 0)
816 return -errno;
817
818 return 0;
819 }
820
821 /* If autonegotiation is disabled, the speed and duplex represent the fixed link
822 * mode and are writable if the driver supports multiple link modes. If it is
823 * enabled then they are read-only. If the link is up they represent the negotiated
824 * link mode; if the link is down, the speed is 0, %SPEED_UNKNOWN or the highest
825 * enabled speed and @duplex is %DUPLEX_UNKNOWN or the best enabled duplex mode.
826 */
827 int ethtool_set_glinksettings(
828 int *fd,
829 const char *ifname,
830 int autonegotiation,
831 const uint32_t advertise[static N_ADVERTISE],
832 uint64_t speed,
833 Duplex duplex,
834 NetDevPort port) {
835
836 _cleanup_free_ struct ethtool_link_usettings *u = NULL;
837 struct ifreq ifr = {};
838 int r;
839
840 assert(fd);
841 assert(ifname);
842 assert(advertise);
843
844 if (autonegotiation != AUTONEG_DISABLE && memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE)) {
845 log_info("ethtool: autonegotiation is unset or enabled, the speed and duplex are not writable.");
846 return 0;
847 }
848
849 if (*fd < 0) {
850 r = ethtool_connect_or_warn(fd, true);
851 if (r < 0)
852 return r;
853 }
854
855 strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
856
857 r = get_glinksettings(*fd, &ifr, &u);
858 if (r < 0) {
859 r = get_gset(*fd, &ifr, &u);
860 if (r < 0)
861 return log_warning_errno(r, "ethtool: Cannot get device settings for %s : %m", ifname);
862 }
863
864 if (speed > 0)
865 u->base.speed = DIV_ROUND_UP(speed, 1000000);
866
867 if (duplex != _DUP_INVALID)
868 u->base.duplex = duplex;
869
870 if (port != _NET_DEV_PORT_INVALID)
871 u->base.port = port;
872
873 if (autonegotiation >= 0)
874 u->base.autoneg = autonegotiation;
875
876 if (!memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE)) {
877 u->base.autoneg = AUTONEG_ENABLE;
878 memcpy(&u->link_modes.advertising, advertise, sizeof(uint32_t) * N_ADVERTISE);
879 memzero((uint8_t*) &u->link_modes.advertising + sizeof(uint32_t) * N_ADVERTISE,
880 ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES - sizeof(uint32_t) * N_ADVERTISE);
881 }
882
883 if (u->base.cmd == ETHTOOL_GLINKSETTINGS)
884 r = set_slinksettings(*fd, &ifr, u);
885 else
886 r = set_sset(*fd, &ifr, u);
887 if (r < 0)
888 return log_warning_errno(r, "ethtool: Cannot set device settings for %s: %m", ifname);
889
890 return r;
891 }
892
893 int ethtool_set_channels(int *fd, const char *ifname, const netdev_channels *channels) {
894 struct ethtool_channels ecmd = {
895 .cmd = ETHTOOL_GCHANNELS,
896 };
897 struct ifreq ifr = {
898 .ifr_data = (void*) &ecmd,
899 };
900 bool need_update = false;
901 int r;
902
903 assert(fd);
904 assert(ifname);
905 assert(channels);
906
907 if (*fd < 0) {
908 r = ethtool_connect_or_warn(fd, true);
909 if (r < 0)
910 return r;
911 }
912
913 strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
914
915 r = ioctl(*fd, SIOCETHTOOL, &ifr);
916 if (r < 0)
917 return -errno;
918
919 if (channels->rx_count_set && ecmd.rx_count != channels->rx_count) {
920 ecmd.rx_count = channels->rx_count;
921 need_update = true;
922 }
923
924 if (channels->tx_count_set && ecmd.tx_count != channels->tx_count) {
925 ecmd.tx_count = channels->tx_count;
926 need_update = true;
927 }
928
929 if (channels->other_count_set && ecmd.other_count != channels->other_count) {
930 ecmd.other_count = channels->other_count;
931 need_update = true;
932 }
933
934 if (channels->combined_count_set && ecmd.combined_count != channels->combined_count) {
935 ecmd.combined_count = channels->combined_count;
936 need_update = true;
937 }
938
939 if (need_update) {
940 ecmd.cmd = ETHTOOL_SCHANNELS;
941
942 r = ioctl(*fd, SIOCETHTOOL, &ifr);
943 if (r < 0)
944 return -errno;
945 }
946
947 return 0;
948 }
949
950 int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int autoneg) {
951 struct ethtool_pauseparam ecmd = {
952 .cmd = ETHTOOL_GPAUSEPARAM,
953 };
954 struct ifreq ifr = {
955 .ifr_data = (void*) &ecmd,
956 };
957 bool need_update = false;
958 int r;
959
960 assert(fd);
961 assert(ifname);
962
963 if (*fd < 0) {
964 r = ethtool_connect_or_warn(fd, true);
965 if (r < 0)
966 return r;
967 }
968
969 strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
970
971 r = ioctl(*fd, SIOCETHTOOL, &ifr);
972 if (r < 0)
973 return -errno;
974
975 if (rx >= 0 && ecmd.rx_pause != (uint32_t) rx) {
976 ecmd.rx_pause = rx;
977 need_update = true;
978 }
979
980 if (tx >= 0 && ecmd.tx_pause != (uint32_t) tx) {
981 ecmd.tx_pause = tx;
982 need_update = true;
983 }
984
985 if (autoneg >= 0 && ecmd.autoneg != (uint32_t) autoneg) {
986 ecmd.autoneg = autoneg;
987 need_update = true;
988 }
989
990 if (need_update) {
991 ecmd.cmd = ETHTOOL_SPAUSEPARAM;
992
993 r = ioctl(*fd, SIOCETHTOOL, &ifr);
994 if (r < 0)
995 return -errno;
996 }
997
998 return 0;
999 }
1000
1001 int config_parse_channel(const char *unit,
1002 const char *filename,
1003 unsigned line,
1004 const char *section,
1005 unsigned section_line,
1006 const char *lvalue,
1007 int ltype,
1008 const char *rvalue,
1009 void *data,
1010 void *userdata) {
1011 netdev_channels *channels = data;
1012 uint32_t k;
1013 int r;
1014
1015 assert(filename);
1016 assert(section);
1017 assert(lvalue);
1018 assert(rvalue);
1019 assert(data);
1020
1021 r = safe_atou32(rvalue, &k);
1022 if (r < 0) {
1023 log_syntax(unit, LOG_WARNING, filename, line, r,
1024 "Failed to parse channel value for %s=, ignoring: %s", lvalue, rvalue);
1025 return 0;
1026 }
1027 if (k < 1) {
1028 log_syntax(unit, LOG_WARNING, filename, line, 0,
1029 "Invalid %s= value, ignoring: %s", lvalue, rvalue);
1030 return 0;
1031 }
1032
1033 if (streq(lvalue, "RxChannels")) {
1034 channels->rx_count = k;
1035 channels->rx_count_set = true;
1036 } else if (streq(lvalue, "TxChannels")) {
1037 channels->tx_count = k;
1038 channels->tx_count_set = true;
1039 } else if (streq(lvalue, "OtherChannels")) {
1040 channels->other_count = k;
1041 channels->other_count_set = true;
1042 } else if (streq(lvalue, "CombinedChannels")) {
1043 channels->combined_count = k;
1044 channels->combined_count_set = true;
1045 }
1046
1047 return 0;
1048 }
1049
1050 int config_parse_advertise(const char *unit,
1051 const char *filename,
1052 unsigned line,
1053 const char *section,
1054 unsigned section_line,
1055 const char *lvalue,
1056 int ltype,
1057 const char *rvalue,
1058 void *data,
1059 void *userdata) {
1060 uint32_t *advertise = data;
1061 const char *p;
1062 int r;
1063
1064 assert(filename);
1065 assert(section);
1066 assert(lvalue);
1067 assert(rvalue);
1068 assert(data);
1069
1070 if (isempty(rvalue)) {
1071 /* Empty string resets the value. */
1072 memzero(advertise, sizeof(uint32_t) * N_ADVERTISE);
1073 return 0;
1074 }
1075
1076 for (p = rvalue;;) {
1077 _cleanup_free_ char *w = NULL;
1078 enum ethtool_link_mode_bit_indices mode;
1079
1080 r = extract_first_word(&p, &w, NULL, 0);
1081 if (r == -ENOMEM)
1082 return log_oom();
1083 if (r < 0) {
1084 log_syntax(unit, LOG_WARNING, filename, line, r,
1085 "Failed to split advertise modes '%s', ignoring assignment: %m", rvalue);
1086 return 0;
1087 }
1088 if (r == 0)
1089 return 0;
1090
1091 mode = ethtool_link_mode_bit_from_string(w);
1092 /* We reuse the kernel provided enum which does not contain negative value. So, the cast
1093 * below is mandatory. Otherwise, the check below always passes and access an invalid address. */
1094 if ((int) mode < 0) {
1095 log_syntax(unit, LOG_WARNING, filename, line, mode,
1096 "Failed to parse advertise mode, ignoring: %s", w);
1097 continue;
1098 }
1099
1100 advertise[mode / 32] |= 1UL << (mode % 32);
1101 }
1102 }
1103
1104 int config_parse_nic_buffer_size(const char *unit,
1105 const char *filename,
1106 unsigned line,
1107 const char *section,
1108 unsigned section_line,
1109 const char *lvalue,
1110 int ltype,
1111 const char *rvalue,
1112 void *data,
1113 void *userdata) {
1114 netdev_ring_param *ring = data;
1115 uint32_t k;
1116 int r;
1117
1118 assert(filename);
1119 assert(section);
1120 assert(lvalue);
1121 assert(rvalue);
1122 assert(data);
1123
1124 r = safe_atou32(rvalue, &k);
1125 if (r < 0) {
1126 log_syntax(unit, LOG_WARNING, filename, line, r,
1127 "Failed to parse interface buffer value, ignoring: %s", rvalue);
1128 return 0;
1129 }
1130 if (k < 1) {
1131 log_syntax(unit, LOG_WARNING, filename, line, 0,
1132 "Invalid %s= value, ignoring: %s", lvalue, rvalue);
1133 return 0;
1134 }
1135
1136 if (streq(lvalue, "RxBufferSize")) {
1137 ring->rx_pending = k;
1138 ring->rx_pending_set = true;
1139 } else if (streq(lvalue, "RxMiniBufferSize")) {
1140 ring->rx_mini_pending = k;
1141 ring->rx_mini_pending_set = true;
1142 } else if (streq(lvalue, "RxJumboBufferSize")) {
1143 ring->rx_jumbo_pending = k;
1144 ring->rx_jumbo_pending_set = true;
1145 } else if (streq(lvalue, "TxBufferSize")) {
1146 ring->tx_pending = k;
1147 ring->tx_pending_set = true;
1148 }
1149
1150 return 0;
1151 }