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
wol_options_to_string_alloc(uint32_t opts,char ** ret)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
ethtool_connect(int * ethtool_fd)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
ethtool_get_driver(int * ethtool_fd,const char * ifname,char ** ret)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
ethtool_get_link_info(int * ethtool_fd,const char * ifname,int * ret_autonegotiation,uint64_t * ret_speed,Duplex * ret_duplex,NetDevPort * ret_port)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
ethtool_get_permanent_hw_addr(int * ethtool_fd,const char * ifname,struct hw_addr_data * ret)351 int ethtool_get_permanent_hw_addr(int *ethtool_fd, const char *ifname, struct hw_addr_data *ret) {
352 _cleanup_close_ int fd = -1;
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
ethtool_set_wol(int * ethtool_fd,const char * ifname,uint32_t wolopts,const uint8_t password[SOPASS_MAX])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 option(s) \"%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
ethtool_set_nic_buffer_size(int * ethtool_fd,const char * ifname,const netdev_ring_param * ring)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
get_stringset(int ethtool_fd,const char * ifname,enum ethtool_stringset stringset_id,struct ethtool_gstrings ** ret)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
get_features(int ethtool_fd,const char * ifname,uint32_t n_features,struct ethtool_gfeatures ** ret)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
set_features_bit(const struct ethtool_gstrings * strings,const struct ethtool_gfeatures * gfeatures,struct ethtool_sfeatures * sfeatures,const char * feature,int flag)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
set_features_multiple_bit(const struct ethtool_gstrings * strings,const struct ethtool_gfeatures * gfeatures,struct ethtool_sfeatures * sfeatures,const char * feature,int flag)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
ethtool_set_features(int * ethtool_fd,const char * ifname,const int features[static _NET_DEV_FEAT_MAX])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
get_glinksettings(int fd,struct ifreq * ifr,struct ethtool_link_usettings ** ret)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
get_gset(int fd,struct ifreq * ifr,struct ethtool_link_usettings ** ret)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
set_slinksettings(int fd,struct ifreq * ifr,const struct ethtool_link_usettings * u)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
set_sset(int fd,struct ifreq * ifr,const struct ethtool_link_usettings * u)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
ethtool_set_glinksettings(int * fd,const char * ifname,int autonegotiation,const uint32_t advertise[static N_ADVERTISE],uint64_t speed,Duplex duplex,NetDevPort port,uint8_t mdi)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
ethtool_set_channels(int * fd,const char * ifname,const netdev_channels * channels)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
ethtool_set_flow_control(int * fd,const char * ifname,int rx,int tx,int autoneg)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
ethtool_set_nic_coalesce_settings(int * ethtool_fd,const char * ifname,const netdev_coalesce_param * coalesce)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
config_parse_advertise(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)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 = data;
1241 int r;
1242
1243 assert(filename);
1244 assert(section);
1245 assert(lvalue);
1246 assert(rvalue);
1247 assert(data);
1248
1249 if (isempty(rvalue)) {
1250 /* Empty string resets the value. */
1251 memzero(advertise, sizeof(uint32_t) * N_ADVERTISE);
1252 return 0;
1253 }
1254
1255 for (const char *p = rvalue;;) {
1256 _cleanup_free_ char *w = NULL;
1257 enum ethtool_link_mode_bit_indices mode;
1258
1259 r = extract_first_word(&p, &w, NULL, 0);
1260 if (r == -ENOMEM)
1261 return log_oom();
1262 if (r < 0) {
1263 log_syntax(unit, LOG_WARNING, filename, line, r,
1264 "Failed to split advertise modes '%s', ignoring assignment: %m", rvalue);
1265 return 0;
1266 }
1267 if (r == 0)
1268 return 0;
1269
1270 mode = ethtool_link_mode_bit_from_string(w);
1271 /* We reuse the kernel provided enum which does not contain negative value. So, the cast
1272 * below is mandatory. Otherwise, the check below always passes and access an invalid address. */
1273 if ((int) mode < 0) {
1274 log_syntax(unit, LOG_WARNING, filename, line, mode,
1275 "Failed to parse advertise mode, ignoring: %s", w);
1276 continue;
1277 }
1278
1279 advertise[mode / 32] |= 1UL << (mode % 32);
1280 }
1281 }
1282
config_parse_mdi(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)1283 int config_parse_mdi(
1284 const char *unit,
1285 const char *filename,
1286 unsigned line,
1287 const char *section,
1288 unsigned section_line,
1289 const char *lvalue,
1290 int ltype,
1291 const char *rvalue,
1292 void *data,
1293 void *userdata) {
1294
1295 uint8_t *mdi = ASSERT_PTR(data);
1296
1297 assert(filename);
1298 assert(rvalue);
1299
1300 if (isempty(rvalue)) {
1301 *mdi = ETH_TP_MDI_INVALID;
1302 return 0;
1303 }
1304
1305 if (STR_IN_SET(rvalue, "mdi", "straight")) {
1306 *mdi = ETH_TP_MDI;
1307 return 0;
1308 }
1309
1310 if (STR_IN_SET(rvalue, "mdi-x", "mdix", "crossover")) {
1311 *mdi = ETH_TP_MDI_X;
1312 return 0;
1313 }
1314
1315 if (streq(rvalue, "auto")) {
1316 *mdi = ETH_TP_MDI_AUTO;
1317 return 0;
1318 }
1319
1320 log_syntax(unit, LOG_WARNING, filename, line, 0,
1321 "Failed to parse %s= setting, ignoring assignment: %s", lvalue, rvalue);
1322 return 0;
1323 }
1324
config_parse_ring_buffer_or_channel(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)1325 int config_parse_ring_buffer_or_channel(
1326 const char *unit,
1327 const char *filename,
1328 unsigned line,
1329 const char *section,
1330 unsigned section_line,
1331 const char *lvalue,
1332 int ltype,
1333 const char *rvalue,
1334 void *data,
1335 void *userdata) {
1336
1337 u32_opt *dst = data;
1338 uint32_t k;
1339 int r;
1340
1341 assert(filename);
1342 assert(section);
1343 assert(lvalue);
1344 assert(rvalue);
1345 assert(data);
1346
1347 if (isempty(rvalue)) {
1348 dst->value = 0;
1349 dst->set = false;
1350 return 0;
1351 }
1352
1353 if (streq(rvalue, "max")) {
1354 dst->value = 0;
1355 dst->set = true;
1356 return 0;
1357 }
1358
1359 r = safe_atou32(rvalue, &k);
1360 if (r < 0) {
1361 log_syntax(unit, LOG_WARNING, filename, line, r,
1362 "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
1363 return 0;
1364 }
1365 if (k < 1) {
1366 log_syntax(unit, LOG_WARNING, filename, line, 0,
1367 "Invalid %s= value, ignoring: %s", lvalue, rvalue);
1368 return 0;
1369 }
1370
1371 dst->value = k;
1372 dst->set = true;
1373 return 0;
1374 }
1375
config_parse_wol(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)1376 int config_parse_wol(
1377 const char *unit,
1378 const char *filename,
1379 unsigned line,
1380 const char *section,
1381 unsigned section_line,
1382 const char *lvalue,
1383 int ltype,
1384 const char *rvalue,
1385 void *data,
1386 void *userdata) {
1387
1388 uint32_t new_opts = 0, *opts = data;
1389 int r;
1390
1391 assert(filename);
1392 assert(section);
1393 assert(lvalue);
1394 assert(rvalue);
1395 assert(data);
1396
1397 if (isempty(rvalue)) {
1398 *opts = UINT32_MAX; /* Do not update WOL option. */
1399 return 0;
1400 }
1401
1402 if (streq(rvalue, "off")) {
1403 *opts = 0; /* Disable WOL. */
1404 return 0;
1405 }
1406
1407 for (const char *p = rvalue;;) {
1408 _cleanup_free_ char *w = NULL;
1409 bool found = false;
1410
1411 r = extract_first_word(&p, &w, NULL, 0);
1412 if (r == -ENOMEM)
1413 return log_oom();
1414 if (r < 0) {
1415 log_syntax(unit, LOG_WARNING, filename, line, r,
1416 "Failed to split wake-on-lan modes '%s', ignoring assignment: %m", rvalue);
1417 return 0;
1418 }
1419 if (r == 0)
1420 break;
1421
1422 for (size_t i = 0; i < ELEMENTSOF(wol_option_map); i++)
1423 if (streq(w, wol_option_map[i].name)) {
1424 new_opts |= wol_option_map[i].opt;
1425 found = true;
1426 break;
1427 }
1428
1429 if (!found)
1430 log_syntax(unit, LOG_WARNING, filename, line, 0,
1431 "Unknown wake-on-lan mode '%s', ignoring.", w);
1432 }
1433
1434 if (*opts == UINT32_MAX)
1435 *opts = new_opts;
1436 else
1437 *opts |= new_opts;
1438
1439 return 0;
1440 }
1441
config_parse_coalesce_u32(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)1442 int config_parse_coalesce_u32(
1443 const char *unit,
1444 const char *filename,
1445 unsigned line,
1446 const char *section,
1447 unsigned section_line,
1448 const char *lvalue,
1449 int ltype,
1450 const char *rvalue,
1451 void *data,
1452 void *userdata) {
1453 u32_opt *dst = data;
1454 uint32_t k;
1455 int r;
1456
1457 if (isempty(rvalue)) {
1458 dst->value = 0;
1459 dst->set = false;
1460 return 0;
1461 }
1462
1463 r = safe_atou32(rvalue, &k);
1464 if (r < 0) {
1465 log_syntax(unit, LOG_WARNING, filename, line, r,
1466 "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
1467 return 0;
1468 }
1469
1470 dst->value = k;
1471 dst->set = true;
1472 return 0;
1473 }
1474
config_parse_coalesce_sec(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)1475 int config_parse_coalesce_sec(
1476 const char *unit,
1477 const char *filename,
1478 unsigned line,
1479 const char *section,
1480 unsigned section_line,
1481 const char *lvalue,
1482 int ltype,
1483 const char *rvalue,
1484 void *data,
1485 void *userdata) {
1486 u32_opt *dst = data;
1487 usec_t usec;
1488 int r;
1489
1490 if (isempty(rvalue)) {
1491 dst->value = 0;
1492 dst->set = false;
1493 return 0;
1494 }
1495
1496 r = parse_sec(rvalue, &usec);
1497 if (r < 0) {
1498 log_syntax(unit, LOG_WARNING, filename, line, r,
1499 "Failed to parse coalesce setting value, ignoring: %s", rvalue);
1500 return 0;
1501 }
1502
1503 if (usec > UINT32_MAX) {
1504 log_syntax(unit, LOG_WARNING, filename, line, 0,
1505 "Too large %s= value, ignoring: %s", lvalue, rvalue);
1506 return 0;
1507 }
1508
1509 if (STR_IN_SET(lvalue, "StatisticsBlockCoalesceSec", "CoalescePacketRateSampleIntervalSec") && usec < 1) {
1510 log_syntax(unit, LOG_WARNING, filename, line, 0,
1511 "Invalid %s= value, ignoring: %s", lvalue, rvalue);
1512 return 0;
1513 }
1514
1515 dst->value = (uint32_t) usec;
1516 dst->set = true;
1517
1518 return 0;
1519 }
1520