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