]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/ethtool-util.c
tree-wide: use -EBADF for fd initialization
[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 if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
438 return -errno;
439
440 if (wolopts == UINT32_MAX) {
441 /* When password is specified without valid WoL options specified, then enable
442 * WAKE_MAGICSECURE flag if supported. */
443 wolopts = ecmd.wolopts;
444 if (password && FLAGS_SET(ecmd.supported, WAKE_MAGICSECURE))
445 wolopts |= WAKE_MAGICSECURE;
446 }
447
448 if ((wolopts & ~ecmd.supported) != 0) {
449 _cleanup_free_ char *str = NULL;
450
451 (void) wol_options_to_string_alloc(wolopts & ~ecmd.supported, &str);
452 log_debug("Network interface %s does not support requested Wake on LAN options \"%s\", ignoring.",
453 ifname, strna(str));
454
455 wolopts &= ecmd.supported;
456 }
457
458 if (!FLAGS_SET(wolopts, WAKE_MAGICSECURE))
459 /* When WAKE_MAGICSECURE flag is not set, then ignore password. */
460 password = NULL;
461
462 UPDATE(ecmd.wolopts, wolopts, need_update);
463 if (password &&
464 memcmp(ecmd.sopass, password, sizeof(ecmd.sopass)) != 0) {
465 memcpy(ecmd.sopass, password, sizeof(ecmd.sopass));
466 need_update = true;
467 }
468
469 if (!need_update) {
470 explicit_bzero_safe(&ecmd, sizeof(ecmd));
471 return 0;
472 }
473
474 ecmd.cmd = ETHTOOL_SWOL;
475 r = RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr));
476
477 explicit_bzero_safe(&ecmd, sizeof(ecmd));
478 return r;
479 }
480
481 int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netdev_ring_param *ring) {
482 struct ethtool_ringparam ecmd = {
483 .cmd = ETHTOOL_GRINGPARAM,
484 };
485 struct ifreq ifr = {
486 .ifr_data = (void*) &ecmd,
487 };
488 bool need_update = false;
489 int r;
490
491 assert(ethtool_fd);
492 assert(ifname);
493 assert(ring);
494
495 if (!ring->rx.set &&
496 !ring->rx_mini.set &&
497 !ring->rx_jumbo.set &&
498 !ring->tx.set)
499 return 0;
500
501 r = ethtool_connect(ethtool_fd);
502 if (r < 0)
503 return r;
504
505 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
506
507 if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
508 return -errno;
509
510 if (ring->rx.set)
511 UPDATE_WITH_MAX(ecmd.rx_pending, ecmd.rx_max_pending, ring->rx.value, need_update);
512
513 if (ring->rx_mini.set)
514 UPDATE_WITH_MAX(ecmd.rx_mini_pending, ecmd.rx_mini_max_pending, ring->rx_mini.value, need_update);
515
516 if (ring->rx_jumbo.set)
517 UPDATE_WITH_MAX(ecmd.rx_jumbo_pending, ecmd.rx_jumbo_max_pending, ring->rx_jumbo.value, need_update);
518
519 if (ring->tx.set)
520 UPDATE_WITH_MAX(ecmd.tx_pending, ecmd.tx_max_pending, ring->tx.value, need_update);
521
522 if (!need_update)
523 return 0;
524
525 ecmd.cmd = ETHTOOL_SRINGPARAM;
526 return RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr));
527 }
528
529 static int get_stringset(int ethtool_fd, const char *ifname, enum ethtool_stringset stringset_id, struct ethtool_gstrings **ret) {
530 _cleanup_free_ struct ethtool_gstrings *strings = NULL;
531 struct {
532 struct ethtool_sset_info info;
533 uint32_t space;
534 } buffer = {
535 .info.cmd = ETHTOOL_GSSET_INFO,
536 .info.sset_mask = UINT64_C(1) << stringset_id,
537 };
538 struct ifreq ifr = {
539 .ifr_data = (void*) &buffer,
540 };
541 uint32_t len;
542
543 assert(ethtool_fd >= 0);
544 assert(ifname);
545 assert(ret);
546
547 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
548
549 if (ioctl(ethtool_fd, SIOCETHTOOL, &ifr) < 0)
550 return -errno;
551
552 if (buffer.info.sset_mask == 0)
553 return -EOPNOTSUPP;
554
555 #pragma GCC diagnostic push
556 #if HAVE_ZERO_LENGTH_BOUNDS
557 # pragma GCC diagnostic ignored "-Wzero-length-bounds"
558 #endif
559 len = buffer.info.data[0];
560 #pragma GCC diagnostic pop
561 if (len == 0)
562 return -EOPNOTSUPP;
563
564 strings = malloc0(offsetof(struct ethtool_gstrings, data) + len * ETH_GSTRING_LEN);
565 if (!strings)
566 return -ENOMEM;
567
568 strings->cmd = ETHTOOL_GSTRINGS;
569 strings->string_set = stringset_id;
570 strings->len = len;
571
572 ifr.ifr_data = (void*) strings;
573
574 if (ioctl(ethtool_fd, SIOCETHTOOL, &ifr) < 0)
575 return -errno;
576
577 *ret = TAKE_PTR(strings);
578 return 0;
579 }
580
581 static int get_features(int ethtool_fd, const char *ifname, uint32_t n_features, struct ethtool_gfeatures **ret) {
582 _cleanup_free_ struct ethtool_gfeatures *gfeatures = NULL;
583 struct ifreq ifr;
584
585 assert(ethtool_fd >= 0);
586 assert(ifname);
587 assert(ret);
588 assert(n_features > 0);
589
590 gfeatures = malloc0(offsetof(struct ethtool_gfeatures, features) +
591 DIV_ROUND_UP(n_features, 32U) * sizeof(gfeatures->features[0]));
592 if (!gfeatures)
593 return -ENOMEM;
594
595 gfeatures->cmd = ETHTOOL_GFEATURES;
596 gfeatures->size = DIV_ROUND_UP(n_features, 32U);
597
598 ifr = (struct ifreq) {
599 .ifr_data = (void*) gfeatures,
600 };
601 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
602
603 if (ioctl(ethtool_fd, SIOCETHTOOL, &ifr) < 0)
604 return -errno;
605
606 *ret = TAKE_PTR(gfeatures);
607 return 0;
608 }
609
610 static int set_features_bit(
611 const struct ethtool_gstrings *strings,
612 const struct ethtool_gfeatures *gfeatures,
613 struct ethtool_sfeatures *sfeatures,
614 const char *feature,
615 int flag) {
616
617 assert(strings);
618 assert(gfeatures);
619 assert(sfeatures);
620 assert(feature);
621
622 if (flag < 0)
623 return 0;
624
625 for (uint32_t i = 0; i < strings->len; i++) {
626 uint32_t block, mask;
627
628 if (!strneq((const char*) &strings->data[i * ETH_GSTRING_LEN], feature, ETH_GSTRING_LEN))
629 continue;
630
631 block = i / 32;
632 mask = UINT32_C(1) << (i % 32);
633
634 if (!FLAGS_SET(gfeatures->features[block].available, mask) ||
635 FLAGS_SET(gfeatures->features[block].never_changed, mask))
636 return -EOPNOTSUPP;
637
638 sfeatures->features[block].valid |= mask;
639 SET_FLAG(sfeatures->features[block].requested, mask, flag);
640
641 return 0;
642 }
643
644 return -ENODATA;
645 }
646
647 static int set_features_multiple_bit(
648 const struct ethtool_gstrings *strings,
649 const struct ethtool_gfeatures *gfeatures,
650 struct ethtool_sfeatures *sfeatures,
651 const char *feature,
652 int flag) {
653
654 bool found = false;
655 int r = -ENODATA;
656
657 assert(strings);
658 assert(gfeatures);
659 assert(sfeatures);
660 assert(feature);
661
662 if (flag < 0)
663 return 0;
664
665 for (uint32_t i = 0; i < strings->len; i++) {
666 uint32_t block, mask;
667
668 if (!startswith((const char*) &strings->data[i * ETH_GSTRING_LEN], feature))
669 continue;
670
671 block = i / 32;
672 mask = UINT32_C(1) << (i % 32);
673
674 if (!FLAGS_SET(gfeatures->features[block].available, mask) ||
675 FLAGS_SET(gfeatures->features[block].never_changed, mask)) {
676 r = -EOPNOTSUPP;
677 continue;
678 }
679
680 /* The flags is explicitly set by set_features_bit() */
681 if (FLAGS_SET(sfeatures->features[block].valid, mask))
682 continue;
683
684 sfeatures->features[block].valid |= mask;
685 SET_FLAG(sfeatures->features[block].requested, mask, flag);
686
687 found = true;
688 }
689
690 return found ? 0 : r;
691 }
692
693 int ethtool_set_features(int *ethtool_fd, const char *ifname, const int features[static _NET_DEV_FEAT_MAX]) {
694 _cleanup_free_ struct ethtool_gstrings *strings = NULL;
695 _cleanup_free_ struct ethtool_gfeatures *gfeatures = NULL;
696 _cleanup_free_ struct ethtool_sfeatures *sfeatures = NULL;
697 struct ifreq ifr;
698 bool have = false;
699 int r;
700
701 assert(ethtool_fd);
702 assert(ifname);
703 assert(features);
704
705 for (size_t i = 0; i < _NET_DEV_FEAT_MAX; i++)
706 if (features[i] >= 0) {
707 have = true;
708 break;
709 }
710
711 if (!have)
712 return 0;
713
714 r = ethtool_connect(ethtool_fd);
715 if (r < 0)
716 return r;
717
718 r = get_stringset(*ethtool_fd, ifname, ETH_SS_FEATURES, &strings);
719 if (r < 0)
720 return log_debug_errno(r, "ethtool: could not get ethtool feature strings: %m");
721
722 r = get_features(*ethtool_fd, ifname, strings->len, &gfeatures);
723 if (r < 0)
724 return log_debug_errno(r, "ethtool: could not get ethtool features for %s: %m", ifname);
725
726 sfeatures = malloc0(offsetof(struct ethtool_sfeatures, features) +
727 DIV_ROUND_UP(strings->len, 32U) * sizeof(sfeatures->features[0]));
728 if (!sfeatures)
729 return log_oom_debug();
730
731 sfeatures->cmd = ETHTOOL_SFEATURES;
732 sfeatures->size = DIV_ROUND_UP(strings->len, 32U);
733
734 for (size_t i = 0; i < _NET_DEV_FEAT_SIMPLE_MAX; i++) {
735 r = set_features_bit(strings, gfeatures, sfeatures, netdev_feature_table[i], features[i]);
736 if (r < 0)
737 log_debug_errno(r, "ethtool: could not set feature %s for %s, ignoring: %m", netdev_feature_table[i], ifname);
738 }
739
740 for (size_t i = _NET_DEV_FEAT_SIMPLE_MAX; i < _NET_DEV_FEAT_MAX; i++) {
741 r = set_features_multiple_bit(strings, gfeatures, sfeatures, netdev_feature_table[i], features[i]);
742 if (r < 0)
743 log_debug_errno(r, "ethtool: could not set feature %s for %s, ignoring: %m", netdev_feature_table[i], ifname);
744 }
745
746 ifr = (struct ifreq) {
747 .ifr_data = (void*) sfeatures,
748 };
749 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
750
751 if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
752 return log_debug_errno(errno, "ethtool: could not set ethtool features for %s", ifname);
753
754 return 0;
755 }
756
757 static int get_glinksettings(int fd, struct ifreq *ifr, struct ethtool_link_usettings **ret) {
758 struct ecmd {
759 struct ethtool_link_settings req;
760 uint32_t link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
761 } ecmd = {
762 .req.cmd = ETHTOOL_GLINKSETTINGS,
763 };
764 struct ethtool_link_usettings *u;
765 unsigned offset;
766
767 assert(fd >= 0);
768 assert(ifr);
769 assert(ret);
770
771 /* The interaction user/kernel via the new API requires a small ETHTOOL_GLINKSETTINGS
772 handshake first to agree on the length of the link mode bitmaps. If kernel doesn't
773 agree with user, it returns the bitmap length it is expecting from user as a negative
774 length (and cmd field is 0). When kernel and user agree, kernel returns valid info in
775 all fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS). Based on
776 https://github.com/torvalds/linux/commit/3f1ac7a700d039c61d8d8b99f28d605d489a60cf
777 */
778
779 ifr->ifr_data = (void *) &ecmd;
780
781 if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
782 return -errno;
783
784 if (ecmd.req.link_mode_masks_nwords >= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
785 return -EOPNOTSUPP;
786
787 ecmd.req.link_mode_masks_nwords = -ecmd.req.link_mode_masks_nwords;
788
789 ifr->ifr_data = (void *) &ecmd;
790
791 if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
792 return -errno;
793
794 if (ecmd.req.link_mode_masks_nwords <= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
795 return -EOPNOTSUPP;
796
797 u = new(struct ethtool_link_usettings, 1);
798 if (!u)
799 return -ENOMEM;
800
801 *u = (struct ethtool_link_usettings) {
802 .base = ecmd.req,
803 };
804
805 offset = 0;
806 memcpy(u->link_modes.supported, &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.advertising, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords);
810
811 offset += ecmd.req.link_mode_masks_nwords;
812 memcpy(u->link_modes.lp_advertising, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords);
813
814 *ret = u;
815
816 return 0;
817 }
818
819 static int get_gset(int fd, struct ifreq *ifr, struct ethtool_link_usettings **ret) {
820 struct ethtool_link_usettings *e;
821 struct ethtool_cmd ecmd = {
822 .cmd = ETHTOOL_GSET,
823 };
824
825 assert(fd >= 0);
826 assert(ifr);
827 assert(ret);
828
829 ifr->ifr_data = (void *) &ecmd;
830
831 if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
832 return -errno;
833
834 e = new(struct ethtool_link_usettings, 1);
835 if (!e)
836 return -ENOMEM;
837
838 *e = (struct ethtool_link_usettings) {
839 .base.cmd = ETHTOOL_GSET,
840 .base.link_mode_masks_nwords = 1,
841 .base.speed = ethtool_cmd_speed(&ecmd),
842 .base.duplex = ecmd.duplex,
843 .base.port = ecmd.port,
844 .base.phy_address = ecmd.phy_address,
845 .base.autoneg = ecmd.autoneg,
846 .base.mdio_support = ecmd.mdio_support,
847 .base.eth_tp_mdix = ecmd.eth_tp_mdix,
848 .base.eth_tp_mdix_ctrl = ecmd.eth_tp_mdix_ctrl,
849
850 .link_modes.supported[0] = ecmd.supported,
851 .link_modes.advertising[0] = ecmd.advertising,
852 .link_modes.lp_advertising[0] = ecmd.lp_advertising,
853 };
854
855 *ret = e;
856
857 return 0;
858 }
859
860 static int set_slinksettings(int fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) {
861 struct {
862 struct ethtool_link_settings req;
863 uint32_t link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
864 } ecmd = {};
865 unsigned offset;
866
867 assert(fd >= 0);
868 assert(ifr);
869 assert(u);
870
871 if (u->base.cmd != ETHTOOL_GLINKSETTINGS || u->base.link_mode_masks_nwords <= 0)
872 return -EINVAL;
873
874 ecmd.req = u->base;
875 ecmd.req.cmd = ETHTOOL_SLINKSETTINGS;
876 offset = 0;
877 memcpy(&ecmd.link_mode_data[offset], u->link_modes.supported, 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.advertising, 4 * ecmd.req.link_mode_masks_nwords);
881
882 offset += ecmd.req.link_mode_masks_nwords;
883 memcpy(&ecmd.link_mode_data[offset], u->link_modes.lp_advertising, 4 * ecmd.req.link_mode_masks_nwords);
884
885 ifr->ifr_data = (void *) &ecmd;
886
887 return RET_NERRNO(ioctl(fd, SIOCETHTOOL, ifr));
888 }
889
890 static int set_sset(int fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) {
891 struct ethtool_cmd ecmd = {
892 .cmd = ETHTOOL_SSET,
893 };
894
895 assert(fd >= 0);
896 assert(ifr);
897 assert(u);
898
899 if (u->base.cmd != ETHTOOL_GSET || u->base.link_mode_masks_nwords <= 0)
900 return -EINVAL;
901
902 ecmd.supported = u->link_modes.supported[0];
903 ecmd.advertising = u->link_modes.advertising[0];
904 ecmd.lp_advertising = u->link_modes.lp_advertising[0];
905
906 ethtool_cmd_speed_set(&ecmd, u->base.speed);
907
908 ecmd.duplex = u->base.duplex;
909 ecmd.port = u->base.port;
910 ecmd.phy_address = u->base.phy_address;
911 ecmd.autoneg = u->base.autoneg;
912 ecmd.mdio_support = u->base.mdio_support;
913 ecmd.eth_tp_mdix = u->base.eth_tp_mdix;
914 ecmd.eth_tp_mdix_ctrl = u->base.eth_tp_mdix_ctrl;
915
916 ifr->ifr_data = (void *) &ecmd;
917
918 return RET_NERRNO(ioctl(fd, SIOCETHTOOL, ifr));
919 }
920
921 int ethtool_set_glinksettings(
922 int *fd,
923 const char *ifname,
924 int autonegotiation,
925 const uint32_t advertise[static N_ADVERTISE],
926 uint64_t speed,
927 Duplex duplex,
928 NetDevPort port,
929 uint8_t mdi) {
930
931 _cleanup_free_ struct ethtool_link_usettings *u = NULL;
932 struct ifreq ifr = {};
933 bool changed = false;
934 int r;
935
936 assert(fd);
937 assert(ifname);
938 assert(advertise);
939
940 if (autonegotiation < 0 && memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE) &&
941 speed == 0 && duplex < 0 && port < 0 && mdi == ETH_TP_MDI_INVALID)
942 return 0;
943
944 /* If autonegotiation is disabled, the speed and duplex represent the fixed link mode and are
945 * writable if the driver supports multiple link modes. If it is enabled then they are
946 * read-only. If the link is up they represent the negotiated link mode; if the link is down,
947 * the speed is 0, %SPEED_UNKNOWN or the highest enabled speed and @duplex is %DUPLEX_UNKNOWN
948 * or the best enabled duplex mode. */
949
950 if (speed > 0 || duplex >= 0 || port >= 0) {
951 if (autonegotiation == AUTONEG_ENABLE || !memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE)) {
952 log_debug("ethtool: autonegotiation is enabled, ignoring speed, duplex, or port settings.");
953 speed = 0;
954 duplex = _DUP_INVALID;
955 port = _NET_DEV_PORT_INVALID;
956 } else {
957 log_debug("ethtool: setting speed, duplex, or port, disabling autonegotiation.");
958 autonegotiation = AUTONEG_DISABLE;
959 }
960 }
961
962 r = ethtool_connect(fd);
963 if (r < 0)
964 return r;
965
966 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
967
968 r = get_glinksettings(*fd, &ifr, &u);
969 if (r < 0) {
970 r = get_gset(*fd, &ifr, &u);
971 if (r < 0)
972 return log_debug_errno(r, "ethtool: Cannot get device settings for %s: %m", ifname);
973 }
974
975 if (speed > 0)
976 UPDATE(u->base.speed, DIV_ROUND_UP(speed, 1000000), changed);
977
978 if (duplex >= 0)
979 UPDATE(u->base.duplex, duplex, changed);
980
981 if (port >= 0)
982 UPDATE(u->base.port, port, changed);
983
984 if (autonegotiation >= 0)
985 UPDATE(u->base.autoneg, autonegotiation, changed);
986
987 if (!memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE)) {
988 UPDATE(u->base.autoneg, AUTONEG_ENABLE, changed);
989
990 changed = changed ||
991 memcmp(&u->link_modes.advertising, advertise, sizeof(uint32_t) * N_ADVERTISE) != 0 ||
992 !memeqzero((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 memcpy(&u->link_modes.advertising, advertise, sizeof(uint32_t) * N_ADVERTISE);
995 memzero((uint8_t*) &u->link_modes.advertising + sizeof(uint32_t) * N_ADVERTISE,
996 ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES - sizeof(uint32_t) * N_ADVERTISE);
997 }
998
999 if (mdi != ETH_TP_MDI_INVALID) {
1000 if (u->base.eth_tp_mdix_ctrl == ETH_TP_MDI_INVALID)
1001 log_debug("ethtool: setting MDI not supported for %s, ignoring.", ifname);
1002 else
1003 UPDATE(u->base.eth_tp_mdix_ctrl, mdi, changed);
1004 }
1005
1006 if (!changed)
1007 return 0;
1008
1009 if (u->base.cmd == ETHTOOL_GLINKSETTINGS)
1010 r = set_slinksettings(*fd, &ifr, u);
1011 else
1012 r = set_sset(*fd, &ifr, u);
1013 if (r < 0)
1014 return log_debug_errno(r, "ethtool: Cannot set device settings for %s: %m", ifname);
1015
1016 return r;
1017 }
1018
1019 int ethtool_set_channels(int *fd, const char *ifname, const netdev_channels *channels) {
1020 struct ethtool_channels ecmd = {
1021 .cmd = ETHTOOL_GCHANNELS,
1022 };
1023 struct ifreq ifr = {
1024 .ifr_data = (void*) &ecmd,
1025 };
1026 bool need_update = false;
1027 int r;
1028
1029 assert(fd);
1030 assert(ifname);
1031 assert(channels);
1032
1033 if (!channels->rx.set &&
1034 !channels->tx.set &&
1035 !channels->other.set &&
1036 !channels->combined.set)
1037 return 0;
1038
1039 r = ethtool_connect(fd);
1040 if (r < 0)
1041 return r;
1042
1043 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
1044
1045 if (ioctl(*fd, SIOCETHTOOL, &ifr) < 0)
1046 return -errno;
1047
1048 if (channels->rx.set)
1049 UPDATE_WITH_MAX(ecmd.rx_count, ecmd.max_rx, channels->rx.value, need_update);
1050
1051 if (channels->tx.set)
1052 UPDATE_WITH_MAX(ecmd.tx_count, ecmd.max_tx, channels->tx.value, need_update);
1053
1054 if (channels->other.set)
1055 UPDATE_WITH_MAX(ecmd.other_count, ecmd.max_other, channels->other.value, need_update);
1056
1057 if (channels->combined.set)
1058 UPDATE_WITH_MAX(ecmd.combined_count, ecmd.max_combined, channels->combined.value, need_update);
1059
1060 if (!need_update)
1061 return 0;
1062
1063 ecmd.cmd = ETHTOOL_SCHANNELS;
1064 return RET_NERRNO(ioctl(*fd, SIOCETHTOOL, &ifr));
1065 }
1066
1067 int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int autoneg) {
1068 struct ethtool_pauseparam ecmd = {
1069 .cmd = ETHTOOL_GPAUSEPARAM,
1070 };
1071 struct ifreq ifr = {
1072 .ifr_data = (void*) &ecmd,
1073 };
1074 bool need_update = false;
1075 int r;
1076
1077 assert(fd);
1078 assert(ifname);
1079
1080 if (rx < 0 && tx < 0 && autoneg < 0)
1081 return 0;
1082
1083 r = ethtool_connect(fd);
1084 if (r < 0)
1085 return r;
1086
1087 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
1088
1089 if (ioctl(*fd, SIOCETHTOOL, &ifr) < 0)
1090 return -errno;
1091
1092 if (rx >= 0)
1093 UPDATE(ecmd.rx_pause, (uint32_t) rx, need_update);
1094
1095 if (tx >= 0)
1096 UPDATE(ecmd.tx_pause, (uint32_t) tx, need_update);
1097
1098 if (autoneg >= 0)
1099 UPDATE(ecmd.autoneg, (uint32_t) autoneg, need_update);
1100
1101 if (!need_update)
1102 return 0;
1103
1104 ecmd.cmd = ETHTOOL_SPAUSEPARAM;
1105 return RET_NERRNO(ioctl(*fd, SIOCETHTOOL, &ifr));
1106 }
1107
1108 int ethtool_set_nic_coalesce_settings(int *ethtool_fd, const char *ifname, const netdev_coalesce_param *coalesce) {
1109 struct ethtool_coalesce ecmd = {
1110 .cmd = ETHTOOL_GCOALESCE,
1111 };
1112 struct ifreq ifr = {
1113 .ifr_data = (void*) &ecmd,
1114 };
1115 bool need_update = false;
1116 int r;
1117
1118 assert(ethtool_fd);
1119 assert(ifname);
1120 assert(coalesce);
1121
1122 if (coalesce->use_adaptive_rx_coalesce < 0 &&
1123 coalesce->use_adaptive_tx_coalesce < 0 &&
1124 !coalesce->rx_coalesce_usecs.set &&
1125 !coalesce->rx_max_coalesced_frames.set &&
1126 !coalesce->rx_coalesce_usecs_irq.set &&
1127 !coalesce->rx_max_coalesced_frames_irq.set &&
1128 !coalesce->tx_coalesce_usecs.set &&
1129 !coalesce->tx_max_coalesced_frames.set &&
1130 !coalesce->tx_coalesce_usecs_irq.set &&
1131 !coalesce->tx_max_coalesced_frames_irq.set &&
1132 !coalesce->stats_block_coalesce_usecs.set &&
1133 !coalesce->pkt_rate_low.set &&
1134 !coalesce->rx_coalesce_usecs_low.set &&
1135 !coalesce->rx_max_coalesced_frames_low.set &&
1136 !coalesce->tx_coalesce_usecs_low.set &&
1137 !coalesce->tx_max_coalesced_frames_low.set &&
1138 !coalesce->pkt_rate_high.set &&
1139 !coalesce->rx_coalesce_usecs_high.set &&
1140 !coalesce->rx_max_coalesced_frames_high.set &&
1141 !coalesce->tx_coalesce_usecs_high.set &&
1142 !coalesce->tx_max_coalesced_frames_high.set &&
1143 !coalesce->rate_sample_interval.set)
1144 return 0;
1145
1146 r = ethtool_connect(ethtool_fd);
1147 if (r < 0)
1148 return r;
1149
1150 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
1151
1152 if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
1153 return -errno;
1154
1155 if (coalesce->use_adaptive_rx_coalesce >= 0)
1156 UPDATE(ecmd.use_adaptive_rx_coalesce, (uint32_t) coalesce->use_adaptive_rx_coalesce, need_update);
1157
1158 if (coalesce->use_adaptive_tx_coalesce >= 0)
1159 UPDATE(ecmd.use_adaptive_tx_coalesce, (uint32_t) coalesce->use_adaptive_tx_coalesce, need_update);
1160
1161 if (coalesce->rx_coalesce_usecs.set)
1162 UPDATE(ecmd.rx_coalesce_usecs, coalesce->rx_coalesce_usecs.value, need_update);
1163
1164 if (coalesce->rx_max_coalesced_frames.set)
1165 UPDATE(ecmd.rx_max_coalesced_frames, coalesce->rx_max_coalesced_frames.value, need_update);
1166
1167 if (coalesce->rx_coalesce_usecs_irq.set)
1168 UPDATE(ecmd.rx_coalesce_usecs_irq, coalesce->rx_coalesce_usecs_irq.value, need_update);
1169
1170 if (coalesce->rx_max_coalesced_frames_irq.set)
1171 UPDATE(ecmd.rx_max_coalesced_frames_irq, coalesce->rx_max_coalesced_frames_irq.value, need_update);
1172
1173 if (coalesce->tx_coalesce_usecs.set)
1174 UPDATE(ecmd.tx_coalesce_usecs, coalesce->tx_coalesce_usecs.value, need_update);
1175
1176 if (coalesce->tx_max_coalesced_frames.set)
1177 UPDATE(ecmd.tx_max_coalesced_frames, coalesce->tx_max_coalesced_frames.value, need_update);
1178
1179 if (coalesce->tx_coalesce_usecs_irq.set)
1180 UPDATE(ecmd.tx_coalesce_usecs_irq, coalesce->tx_coalesce_usecs_irq.value, need_update);
1181
1182 if (coalesce->tx_max_coalesced_frames_irq.set)
1183 UPDATE(ecmd.tx_max_coalesced_frames_irq, coalesce->tx_max_coalesced_frames_irq.value, need_update);
1184
1185 if (coalesce->stats_block_coalesce_usecs.set)
1186 UPDATE(ecmd.stats_block_coalesce_usecs, coalesce->stats_block_coalesce_usecs.value, need_update);
1187
1188 if (coalesce->pkt_rate_low.set)
1189 UPDATE(ecmd.pkt_rate_low, coalesce->pkt_rate_low.value, need_update);
1190
1191 if (coalesce->rx_coalesce_usecs_low.set)
1192 UPDATE(ecmd.rx_coalesce_usecs_low, coalesce->rx_coalesce_usecs_low.value, need_update);
1193
1194 if (coalesce->rx_max_coalesced_frames_low.set)
1195 UPDATE(ecmd.rx_max_coalesced_frames_low, coalesce->rx_max_coalesced_frames_low.value, need_update);
1196
1197 if (coalesce->tx_coalesce_usecs_low.set)
1198 UPDATE(ecmd.tx_coalesce_usecs_low, coalesce->tx_coalesce_usecs_low.value, need_update);
1199
1200 if (coalesce->tx_max_coalesced_frames_low.set)
1201 UPDATE(ecmd.tx_max_coalesced_frames_low, coalesce->tx_max_coalesced_frames_low.value, need_update);
1202
1203 if (coalesce->pkt_rate_high.set)
1204 UPDATE(ecmd.pkt_rate_high, coalesce->pkt_rate_high.value, need_update);
1205
1206 if (coalesce->rx_coalesce_usecs_high.set)
1207 UPDATE(ecmd.rx_coalesce_usecs_high, coalesce->rx_coalesce_usecs_high.value, need_update);
1208
1209 if (coalesce->rx_max_coalesced_frames_high.set)
1210 UPDATE(ecmd.rx_max_coalesced_frames_high, coalesce->rx_max_coalesced_frames_high.value, need_update);
1211
1212 if (coalesce->tx_coalesce_usecs_high.set)
1213 UPDATE(ecmd.tx_coalesce_usecs_high, coalesce->tx_coalesce_usecs_high.value, need_update);
1214
1215 if (coalesce->tx_max_coalesced_frames_high.set)
1216 UPDATE(ecmd.tx_max_coalesced_frames_high, coalesce->tx_max_coalesced_frames_high.value, need_update);
1217
1218 if (coalesce->rate_sample_interval.set)
1219 UPDATE(ecmd.rate_sample_interval, DIV_ROUND_UP(coalesce->rate_sample_interval.value, USEC_PER_SEC), need_update);
1220
1221 if (!need_update)
1222 return 0;
1223
1224 ecmd.cmd = ETHTOOL_SCOALESCE;
1225 return RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr));
1226 }
1227
1228 int config_parse_advertise(
1229 const char *unit,
1230 const char *filename,
1231 unsigned line,
1232 const char *section,
1233 unsigned section_line,
1234 const char *lvalue,
1235 int ltype,
1236 const char *rvalue,
1237 void *data,
1238 void *userdata) {
1239
1240 uint32_t *advertise = ASSERT_PTR(data);
1241 int r;
1242
1243 assert(filename);
1244 assert(section);
1245 assert(lvalue);
1246 assert(rvalue);
1247
1248 if (isempty(rvalue)) {
1249 /* Empty string resets the value. */
1250 memzero(advertise, sizeof(uint32_t) * N_ADVERTISE);
1251 return 0;
1252 }
1253
1254 for (const char *p = rvalue;;) {
1255 _cleanup_free_ char *w = NULL;
1256 enum ethtool_link_mode_bit_indices mode;
1257
1258 r = extract_first_word(&p, &w, NULL, 0);
1259 if (r == -ENOMEM)
1260 return log_oom();
1261 if (r < 0) {
1262 log_syntax(unit, LOG_WARNING, filename, line, r,
1263 "Failed to split advertise modes '%s', ignoring assignment: %m", rvalue);
1264 return 0;
1265 }
1266 if (r == 0)
1267 return 0;
1268
1269 mode = ethtool_link_mode_bit_from_string(w);
1270 /* We reuse the kernel provided enum which does not contain negative value. So, the cast
1271 * below is mandatory. Otherwise, the check below always passes and access an invalid address. */
1272 if ((int) mode < 0) {
1273 log_syntax(unit, LOG_WARNING, filename, line, mode,
1274 "Failed to parse advertise mode, ignoring: %s", w);
1275 continue;
1276 }
1277
1278 advertise[mode / 32] |= 1UL << (mode % 32);
1279 }
1280 }
1281
1282 int config_parse_mdi(
1283 const char *unit,
1284 const char *filename,
1285 unsigned line,
1286 const char *section,
1287 unsigned section_line,
1288 const char *lvalue,
1289 int ltype,
1290 const char *rvalue,
1291 void *data,
1292 void *userdata) {
1293
1294 uint8_t *mdi = ASSERT_PTR(data);
1295
1296 assert(filename);
1297 assert(rvalue);
1298
1299 if (isempty(rvalue)) {
1300 *mdi = ETH_TP_MDI_INVALID;
1301 return 0;
1302 }
1303
1304 if (STR_IN_SET(rvalue, "mdi", "straight")) {
1305 *mdi = ETH_TP_MDI;
1306 return 0;
1307 }
1308
1309 if (STR_IN_SET(rvalue, "mdi-x", "mdix", "crossover")) {
1310 *mdi = ETH_TP_MDI_X;
1311 return 0;
1312 }
1313
1314 if (streq(rvalue, "auto")) {
1315 *mdi = ETH_TP_MDI_AUTO;
1316 return 0;
1317 }
1318
1319 log_syntax(unit, LOG_WARNING, filename, line, 0,
1320 "Failed to parse %s= setting, ignoring assignment: %s", lvalue, rvalue);
1321 return 0;
1322 }
1323
1324 int config_parse_ring_buffer_or_channel(
1325 const char *unit,
1326 const char *filename,
1327 unsigned line,
1328 const char *section,
1329 unsigned section_line,
1330 const char *lvalue,
1331 int ltype,
1332 const char *rvalue,
1333 void *data,
1334 void *userdata) {
1335
1336 u32_opt *dst = ASSERT_PTR(data);
1337 uint32_t k;
1338 int r;
1339
1340 assert(filename);
1341 assert(section);
1342 assert(lvalue);
1343 assert(rvalue);
1344
1345 if (isempty(rvalue)) {
1346 dst->value = 0;
1347 dst->set = false;
1348 return 0;
1349 }
1350
1351 if (streq(rvalue, "max")) {
1352 dst->value = 0;
1353 dst->set = true;
1354 return 0;
1355 }
1356
1357 r = safe_atou32(rvalue, &k);
1358 if (r < 0) {
1359 log_syntax(unit, LOG_WARNING, filename, line, r,
1360 "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
1361 return 0;
1362 }
1363 if (k < 1) {
1364 log_syntax(unit, LOG_WARNING, filename, line, 0,
1365 "Invalid %s= value, ignoring: %s", lvalue, rvalue);
1366 return 0;
1367 }
1368
1369 dst->value = k;
1370 dst->set = true;
1371 return 0;
1372 }
1373
1374 int config_parse_wol(
1375 const char *unit,
1376 const char *filename,
1377 unsigned line,
1378 const char *section,
1379 unsigned section_line,
1380 const char *lvalue,
1381 int ltype,
1382 const char *rvalue,
1383 void *data,
1384 void *userdata) {
1385
1386 uint32_t new_opts = 0, *opts = data;
1387 int r;
1388
1389 assert(filename);
1390 assert(section);
1391 assert(lvalue);
1392 assert(rvalue);
1393 assert(data);
1394
1395 if (isempty(rvalue)) {
1396 *opts = UINT32_MAX; /* Do not update WOL option. */
1397 return 0;
1398 }
1399
1400 if (streq(rvalue, "off")) {
1401 *opts = 0; /* Disable WOL. */
1402 return 0;
1403 }
1404
1405 for (const char *p = rvalue;;) {
1406 _cleanup_free_ char *w = NULL;
1407 bool found = false;
1408
1409 r = extract_first_word(&p, &w, NULL, 0);
1410 if (r == -ENOMEM)
1411 return log_oom();
1412 if (r < 0) {
1413 log_syntax(unit, LOG_WARNING, filename, line, r,
1414 "Failed to split wake-on-lan modes '%s', ignoring assignment: %m", rvalue);
1415 return 0;
1416 }
1417 if (r == 0)
1418 break;
1419
1420 for (size_t i = 0; i < ELEMENTSOF(wol_option_map); i++)
1421 if (streq(w, wol_option_map[i].name)) {
1422 new_opts |= wol_option_map[i].opt;
1423 found = true;
1424 break;
1425 }
1426
1427 if (!found)
1428 log_syntax(unit, LOG_WARNING, filename, line, 0,
1429 "Unknown wake-on-lan mode '%s', ignoring.", w);
1430 }
1431
1432 if (*opts == UINT32_MAX)
1433 *opts = new_opts;
1434 else
1435 *opts |= new_opts;
1436
1437 return 0;
1438 }
1439
1440 int config_parse_coalesce_u32(
1441 const char *unit,
1442 const char *filename,
1443 unsigned line,
1444 const char *section,
1445 unsigned section_line,
1446 const char *lvalue,
1447 int ltype,
1448 const char *rvalue,
1449 void *data,
1450 void *userdata) {
1451 u32_opt *dst = data;
1452 uint32_t k;
1453 int r;
1454
1455 if (isempty(rvalue)) {
1456 dst->value = 0;
1457 dst->set = false;
1458 return 0;
1459 }
1460
1461 r = safe_atou32(rvalue, &k);
1462 if (r < 0) {
1463 log_syntax(unit, LOG_WARNING, filename, line, r,
1464 "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
1465 return 0;
1466 }
1467
1468 dst->value = k;
1469 dst->set = true;
1470 return 0;
1471 }
1472
1473 int config_parse_coalesce_sec(
1474 const char *unit,
1475 const char *filename,
1476 unsigned line,
1477 const char *section,
1478 unsigned section_line,
1479 const char *lvalue,
1480 int ltype,
1481 const char *rvalue,
1482 void *data,
1483 void *userdata) {
1484 u32_opt *dst = data;
1485 usec_t usec;
1486 int r;
1487
1488 if (isempty(rvalue)) {
1489 dst->value = 0;
1490 dst->set = false;
1491 return 0;
1492 }
1493
1494 r = parse_sec(rvalue, &usec);
1495 if (r < 0) {
1496 log_syntax(unit, LOG_WARNING, filename, line, r,
1497 "Failed to parse coalesce setting value, ignoring: %s", rvalue);
1498 return 0;
1499 }
1500
1501 if (usec > UINT32_MAX) {
1502 log_syntax(unit, LOG_WARNING, filename, line, 0,
1503 "Too large %s= value, ignoring: %s", lvalue, rvalue);
1504 return 0;
1505 }
1506
1507 if (STR_IN_SET(lvalue, "StatisticsBlockCoalesceSec", "CoalescePacketRateSampleIntervalSec") && usec < 1) {
1508 log_syntax(unit, LOG_WARNING, filename, line, 0,
1509 "Invalid %s= value, ignoring: %s", lvalue, rvalue);
1510 return 0;
1511 }
1512
1513 dst->value = (uint32_t) usec;
1514 dst->set = true;
1515
1516 return 0;
1517 }