]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/ethtool-util.c
tree-wide: use CLEANUP_ERASE() at various places
[thirdparty/systemd.git] / src / shared / ethtool-util.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
a5010333 2
a5010333 3#include <net/if.h>
8b43440b 4#include <sys/ioctl.h>
a5010333 5#include <linux/ethtool.h>
79b4428a 6#include <linux/netdevice.h>
a5010333
TG
7#include <linux/sockios.h>
8
8b43440b 9#include "conf-parser.h"
a5010333 10#include "ethtool-util.h"
5c2316c6 11#include "extract-word.h"
6666c4fa 12#include "fd-util.h"
ab1263d7 13#include "log.h"
0a970718 14#include "memory-util.h"
429b4350 15#include "socket-util.h"
8b43440b 16#include "string-table.h"
6c35ea5e 17#include "strv.h"
a5010333 18#include "strxcpyx.h"
5fde13d7 19
2c5859af 20static const char* const duplex_table[_DUP_MAX] = {
5fde13d7
TG
21 [DUP_FULL] = "full",
22 [DUP_HALF] = "half"
23};
24
25DEFINE_STRING_TABLE_LOOKUP(duplex, Duplex);
26DEFINE_CONFIG_PARSE_ENUM(config_parse_duplex, duplex, Duplex, "Failed to parse duplex setting");
27
c50404ae
YW
28static 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", },
5fde13d7
TG
39};
40
c50404ae
YW
41int wol_options_to_string_alloc(uint32_t opts, char **ret) {
42 _cleanup_free_ char *str = NULL;
43
44 assert(ret);
45
b4b2a492
YW
46 if (opts == UINT32_MAX) {
47 *ret = NULL;
48 return 0;
49 }
50
c50404ae
YW
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);
b4b2a492 63 return 1;
c50404ae 64}
a5010333 65
44909f1c 66static const char* const port_table[] = {
593022fa
SS
67 [NET_DEV_PORT_TP] = "tp",
68 [NET_DEV_PORT_AUI] = "aui",
69 [NET_DEV_PORT_MII] = "mii",
70 [NET_DEV_PORT_FIBRE] = "fibre",
44909f1c 71 [NET_DEV_PORT_BNC] = "bnc",
593022fa
SS
72};
73
74DEFINE_STRING_TABLE_LOOKUP(port, NetDevPort);
75DEFINE_CONFIG_PARSE_ENUM(config_parse_port, port, NetDevPort, "Failed to parse Port setting");
76
18f84f8a
YW
77static 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
84DEFINE_STRING_TABLE_LOOKUP_TO_STRING(mdi, int);
85
50725d10 86static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = {
77bf5c31
YW
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",
7a4f2035 149
77bf5c31 150 [NET_DEV_FEAT_TXCSUM] = "tx-checksum-", /* The suffix "-" means any feature beginning with "tx-checksum-" */
50725d10
SS
151};
152
64d9f756 153static const char* const ethtool_link_mode_bit_table[] = {
6d028889
YW
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",
41f42696
YW
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",
fe2aeef8
YW
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",
131e4dea
YW
244 [ETHTOOL_LINK_MODE_100baseFX_Half_BIT] = "100basefx-half",
245 [ETHTOOL_LINK_MODE_100baseFX_Full_BIT] = "100basefx-full",
6cf0a204 246};
5dd10118 247/* Make sure the array is large enough to fit all bits */
5c2316c6 248assert_cc((ELEMENTSOF(ethtool_link_mode_bit_table)-1) / 32 < N_ADVERTISE);
6cf0a204 249
2d18ac44 250DEFINE_STRING_TABLE_LOOKUP(ethtool_link_mode_bit, enum ethtool_link_mode_bit_indices);
6cf0a204 251
4f504031 252static int ethtool_connect(int *ethtool_fd) {
a5010333
TG
253 int fd;
254
4f504031
YW
255 assert(ethtool_fd);
256
257 /* This does nothing if already connected. */
258 if (*ethtool_fd >= 0)
259 return 0;
a5010333 260
429b4350 261 fd = socket_ioctl_fd();
ece174c5 262 if (fd < 0)
11a288e8 263 return log_debug_errno(fd, "ethtool: could not create control socket: %m");
2b44daaa 264
4f504031 265 *ethtool_fd = fd;
a5010333
TG
266 return 0;
267}
268
64be35ab 269int ethtool_get_driver(int *ethtool_fd, const char *ifname, char **ret) {
61f3af4f 270 struct ethtool_drvinfo ecmd = {
a93187ce 271 .cmd = ETHTOOL_GDRVINFO,
61f3af4f
LP
272 };
273 struct ifreq ifr = {
a93187ce 274 .ifr_data = (void*) &ecmd,
61f3af4f
LP
275 };
276 char *d;
847a8a5f
TG
277 int r;
278
a93187ce
YW
279 assert(ethtool_fd);
280 assert(ifname);
281 assert(ret);
282
4f504031
YW
283 r = ethtool_connect(ethtool_fd);
284 if (r < 0)
285 return r;
aedca892 286
6d9a72f3 287 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
847a8a5f 288
4253dab5 289 if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
847a8a5f
TG
290 return -errno;
291
861de64e
YW
292 if (isempty(ecmd.driver))
293 return -ENODATA;
294
61f3af4f
LP
295 d = strdup(ecmd.driver);
296 if (!d)
847a8a5f
TG
297 return -ENOMEM;
298
61f3af4f 299 *ret = d;
847a8a5f
TG
300 return 0;
301}
302
a93187ce
YW
303int 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
33a8695f
YW
311 struct ethtool_cmd ecmd = {
312 .cmd = ETHTOOL_GSET,
313 };
314 struct ifreq ifr = {
315 .ifr_data = (void*) &ecmd,
316 };
317 int r;
318
a93187ce
YW
319 assert(ethtool_fd);
320 assert(ifname);
321
4f504031
YW
322 r = ethtool_connect(ethtool_fd);
323 if (r < 0)
324 return r;
33a8695f 325
6d9a72f3 326 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
33a8695f 327
4253dab5 328 if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
33a8695f
YW
329 return -errno;
330
331 if (ret_autonegotiation)
332 *ret_autonegotiation = ecmd.autoneg;
333
d16c2728
YW
334 if (ret_speed) {
335 uint32_t speed;
336
337 speed = ethtool_cmd_speed(&ecmd);
338 *ret_speed = speed == (uint32_t) SPEED_UNKNOWN ?
2f665f24 339 UINT64_MAX : (uint64_t) speed * 1000 * 1000;
d16c2728 340 }
33a8695f
YW
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
ed9fa69f 351int ethtool_get_permanent_hw_addr(int *ethtool_fd, const char *ifname, struct hw_addr_data *ret) {
254d1313 352 _cleanup_close_ int fd = -EBADF;
0475919b
ZJS
353 struct {
354 struct ethtool_perm_addr addr;
ed9fa69f 355 uint8_t space[HW_ADDR_MAX_SIZE];
0475919b
ZJS
356 } epaddr = {
357 .addr.cmd = ETHTOOL_GPERMADDR,
ed9fa69f 358 .addr.size = HW_ADDR_MAX_SIZE,
0475919b
ZJS
359 };
360 struct ifreq ifr = {
361 .ifr_data = (caddr_t) &epaddr,
362 };
79b4428a
YW
363 int r;
364
79b4428a
YW
365 assert(ifname);
366 assert(ret);
367
6666c4fa
ZJS
368 if (!ethtool_fd)
369 ethtool_fd = &fd;
4f504031
YW
370 r = ethtool_connect(ethtool_fd);
371 if (r < 0)
372 return r;
79b4428a 373
6d9a72f3 374 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
79b4428a 375
4253dab5 376 if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
79b4428a
YW
377 return -errno;
378
ed9fa69f
YW
379 if (epaddr.addr.size == 0)
380 return -ENODATA;
79b4428a 381
ed9fa69f
YW
382 if (epaddr.addr.size > HW_ADDR_MAX_SIZE)
383 return -EINVAL;
79b4428a 384
ed9fa69f
YW
385 ret->length = epaddr.addr.size;
386 memcpy(ret->bytes, epaddr.addr.data, epaddr.addr.size);
79b4428a
YW
387 return 0;
388}
389
ba103059
YW
390#define UPDATE(dest, val, updated) \
391 do { \
392 typeof(val) _v = (val); \
393 if (dest != _v) \
394 updated = true; \
395 dest = _v; \
79893116 396 } while (false)
ba103059 397
0d341ecc
YW
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; \
79893116 407 } while (false)
0d341ecc 408
9bd3ecdd
YW
409int ethtool_set_wol(
410 int *ethtool_fd,
411 const char *ifname,
412 uint32_t wolopts,
413 const uint8_t password[SOPASS_MAX]) {
414
6c0519c0 415 struct ethtool_wolinfo ecmd = {
a93187ce 416 .cmd = ETHTOOL_GWOL,
6c0519c0
TG
417 };
418 struct ifreq ifr = {
a93187ce 419 .ifr_data = (void*) &ecmd,
6c0519c0 420 };
0a2c2294 421 bool need_update = false;
a5010333
TG
422 int r;
423
a93187ce
YW
424 assert(ethtool_fd);
425 assert(ifname);
426
9bd3ecdd
YW
427 if (wolopts == UINT32_MAX && !password)
428 /* Nothing requested. Return earlier. */
a5010333
TG
429 return 0;
430
4f504031
YW
431 r = ethtool_connect(ethtool_fd);
432 if (r < 0)
433 return r;
aedca892 434
6d9a72f3 435 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
a5010333 436
692597c8
LP
437 CLEANUP_ERASE(ecmd);
438
4253dab5 439 if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
a5010333
TG
440 return -errno;
441
9bd3ecdd
YW
442 if (wolopts == UINT32_MAX) {
443 /* When password is specified without valid WoL options specified, then enable
444 * WAKE_MAGICSECURE flag if supported. */
445 wolopts = ecmd.wolopts;
446 if (password && FLAGS_SET(ecmd.supported, WAKE_MAGICSECURE))
447 wolopts |= WAKE_MAGICSECURE;
448 }
449
20274ab8
YW
450 if ((wolopts & ~ecmd.supported) != 0) {
451 _cleanup_free_ char *str = NULL;
452
453 (void) wol_options_to_string_alloc(wolopts & ~ecmd.supported, &str);
0923b425 454 log_debug("Network interface %s does not support requested Wake on LAN options \"%s\", ignoring.",
20274ab8
YW
455 ifname, strna(str));
456
457 wolopts &= ecmd.supported;
458 }
459
9bd3ecdd
YW
460 if (!FLAGS_SET(wolopts, WAKE_MAGICSECURE))
461 /* When WAKE_MAGICSECURE flag is not set, then ignore password. */
462 password = NULL;
463
c50404ae 464 UPDATE(ecmd.wolopts, wolopts, need_update);
9bd3ecdd
YW
465 if (password &&
466 memcmp(ecmd.sopass, password, sizeof(ecmd.sopass)) != 0) {
467 memcpy(ecmd.sopass, password, sizeof(ecmd.sopass));
468 need_update = true;
469 }
a5010333 470
692597c8 471 if (!need_update)
c50404ae 472 return 0;
a5010333 473
c50404ae 474 ecmd.cmd = ETHTOOL_SWOL;
692597c8 475 return RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr));
a5010333 476}
50725d10 477
cadc7ed2 478int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netdev_ring_param *ring) {
224ded67 479 struct ethtool_ringparam ecmd = {
a93187ce 480 .cmd = ETHTOOL_GRINGPARAM,
224ded67
SS
481 };
482 struct ifreq ifr = {
a93187ce 483 .ifr_data = (void*) &ecmd,
224ded67
SS
484 };
485 bool need_update = false;
486 int r;
487
a93187ce
YW
488 assert(ethtool_fd);
489 assert(ifname);
490 assert(ring);
491
0d341ecc
YW
492 if (!ring->rx.set &&
493 !ring->rx_mini.set &&
494 !ring->rx_jumbo.set &&
495 !ring->tx.set)
ba103059
YW
496 return 0;
497
4f504031
YW
498 r = ethtool_connect(ethtool_fd);
499 if (r < 0)
500 return r;
224ded67 501
6d9a72f3 502 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
224ded67 503
4253dab5 504 if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
224ded67
SS
505 return -errno;
506
0d341ecc
YW
507 if (ring->rx.set)
508 UPDATE_WITH_MAX(ecmd.rx_pending, ecmd.rx_max_pending, ring->rx.value, need_update);
224ded67 509
0d341ecc
YW
510 if (ring->rx_mini.set)
511 UPDATE_WITH_MAX(ecmd.rx_mini_pending, ecmd.rx_mini_max_pending, ring->rx_mini.value, need_update);
e81f5fc4 512
0d341ecc
YW
513 if (ring->rx_jumbo.set)
514 UPDATE_WITH_MAX(ecmd.rx_jumbo_pending, ecmd.rx_jumbo_max_pending, ring->rx_jumbo.value, need_update);
e81f5fc4 515
0d341ecc
YW
516 if (ring->tx.set)
517 UPDATE_WITH_MAX(ecmd.tx_pending, ecmd.tx_max_pending, ring->tx.value, need_update);
224ded67 518
ba103059
YW
519 if (!need_update)
520 return 0;
224ded67 521
ba103059 522 ecmd.cmd = ETHTOOL_SRINGPARAM;
7c248223 523 return RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr));
224ded67
SS
524}
525
008d3a37 526static int get_stringset(int ethtool_fd, const char *ifname, enum ethtool_stringset stringset_id, struct ethtool_gstrings **ret) {
50725d10 527 _cleanup_free_ struct ethtool_gstrings *strings = NULL;
a9dee27f
SS
528 struct {
529 struct ethtool_sset_info info;
530 uint32_t space;
531 } buffer = {
008d3a37
YW
532 .info.cmd = ETHTOOL_GSSET_INFO,
533 .info.sset_mask = UINT64_C(1) << stringset_id,
50725d10 534 };
008d3a37
YW
535 struct ifreq ifr = {
536 .ifr_data = (void*) &buffer,
537 };
538 uint32_t len;
50725d10 539
a93187ce 540 assert(ethtool_fd >= 0);
008d3a37 541 assert(ifname);
a93187ce
YW
542 assert(ret);
543
008d3a37 544 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
50725d10 545
008d3a37 546 if (ioctl(ethtool_fd, SIOCETHTOOL, &ifr) < 0)
50725d10
SS
547 return -errno;
548
008d3a37
YW
549 if (buffer.info.sset_mask == 0)
550 return -EOPNOTSUPP;
50725d10 551
94c0c5b7
ZJS
552#pragma GCC diagnostic push
553#if HAVE_ZERO_LENGTH_BOUNDS
554# pragma GCC diagnostic ignored "-Wzero-length-bounds"
555#endif
a9dee27f 556 len = buffer.info.data[0];
94c0c5b7 557#pragma GCC diagnostic pop
008d3a37
YW
558 if (len == 0)
559 return -EOPNOTSUPP;
50725d10 560
008d3a37 561 strings = malloc0(offsetof(struct ethtool_gstrings, data) + len * ETH_GSTRING_LEN);
50725d10
SS
562 if (!strings)
563 return -ENOMEM;
564
565 strings->cmd = ETHTOOL_GSTRINGS;
566 strings->string_set = stringset_id;
567 strings->len = len;
568
008d3a37 569 ifr.ifr_data = (void*) strings;
50725d10 570
008d3a37 571 if (ioctl(ethtool_fd, SIOCETHTOOL, &ifr) < 0)
50725d10
SS
572 return -errno;
573
a93187ce 574 *ret = TAKE_PTR(strings);
008d3a37
YW
575 return 0;
576}
577
578static int get_features(int ethtool_fd, const char *ifname, uint32_t n_features, struct ethtool_gfeatures **ret) {
579 _cleanup_free_ struct ethtool_gfeatures *gfeatures = NULL;
580 struct ifreq ifr;
581
582 assert(ethtool_fd >= 0);
583 assert(ifname);
584 assert(ret);
585 assert(n_features > 0);
586
587 gfeatures = malloc0(offsetof(struct ethtool_gfeatures, features) +
588 DIV_ROUND_UP(n_features, 32U) * sizeof(gfeatures->features[0]));
589 if (!gfeatures)
590 return -ENOMEM;
591
592 gfeatures->cmd = ETHTOOL_GFEATURES;
593 gfeatures->size = DIV_ROUND_UP(n_features, 32U);
594
595 ifr = (struct ifreq) {
596 .ifr_data = (void*) gfeatures,
597 };
598 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
50725d10 599
008d3a37
YW
600 if (ioctl(ethtool_fd, SIOCETHTOOL, &ifr) < 0)
601 return -errno;
602
603 *ret = TAKE_PTR(gfeatures);
50725d10
SS
604 return 0;
605}
606
bf2334c0
YW
607static int set_features_bit(
608 const struct ethtool_gstrings *strings,
008d3a37
YW
609 const struct ethtool_gfeatures *gfeatures,
610 struct ethtool_sfeatures *sfeatures,
bf2334c0 611 const char *feature,
008d3a37
YW
612 int flag) {
613
7a4f2035
YW
614 assert(strings);
615 assert(gfeatures);
616 assert(sfeatures);
617 assert(feature);
618
619 if (flag < 0)
620 return 0;
621
622 for (uint32_t i = 0; i < strings->len; i++) {
623 uint32_t block, mask;
624
625 if (!strneq((const char*) &strings->data[i * ETH_GSTRING_LEN], feature, ETH_GSTRING_LEN))
626 continue;
627
628 block = i / 32;
629 mask = UINT32_C(1) << (i % 32);
630
631 if (!FLAGS_SET(gfeatures->features[block].available, mask) ||
632 FLAGS_SET(gfeatures->features[block].never_changed, mask))
633 return -EOPNOTSUPP;
634
635 sfeatures->features[block].valid |= mask;
636 SET_FLAG(sfeatures->features[block].requested, mask, flag);
637
638 return 0;
639 }
640
641 return -ENODATA;
642}
643
644static int set_features_multiple_bit(
645 const struct ethtool_gstrings *strings,
646 const struct ethtool_gfeatures *gfeatures,
647 struct ethtool_sfeatures *sfeatures,
648 const char *feature,
649 int flag) {
650
bf2334c0 651 bool found = false;
008d3a37 652 int r = -ENODATA;
50725d10 653
bf2334c0 654 assert(strings);
008d3a37 655 assert(gfeatures);
bf2334c0 656 assert(sfeatures);
008d3a37 657 assert(feature);
bf2334c0 658
008d3a37
YW
659 if (flag < 0)
660 return 0;
661
662 for (uint32_t i = 0; i < strings->len; i++) {
663 uint32_t block, mask;
664
7a4f2035 665 if (!startswith((const char*) &strings->data[i * ETH_GSTRING_LEN], feature))
008d3a37 666 continue;
50725d10 667
008d3a37
YW
668 block = i / 32;
669 mask = UINT32_C(1) << (i % 32);
bf2334c0 670
008d3a37
YW
671 if (!FLAGS_SET(gfeatures->features[block].available, mask) ||
672 FLAGS_SET(gfeatures->features[block].never_changed, mask)) {
673 r = -EOPNOTSUPP;
674 continue;
bf2334c0
YW
675 }
676
7a4f2035
YW
677 /* The flags is explicitly set by set_features_bit() */
678 if (FLAGS_SET(sfeatures->features[block].valid, mask))
679 continue;
680
008d3a37
YW
681 sfeatures->features[block].valid |= mask;
682 SET_FLAG(sfeatures->features[block].requested, mask, flag);
683
684 found = true;
685 }
686
687 return found ? 0 : r;
50725d10
SS
688}
689
0db68800 690int ethtool_set_features(int *ethtool_fd, const char *ifname, const int features[static _NET_DEV_FEAT_MAX]) {
50725d10 691 _cleanup_free_ struct ethtool_gstrings *strings = NULL;
008d3a37
YW
692 _cleanup_free_ struct ethtool_gfeatures *gfeatures = NULL;
693 _cleanup_free_ struct ethtool_sfeatures *sfeatures = NULL;
694 struct ifreq ifr;
c2f2250e
YW
695 bool have = false;
696 int r;
50725d10 697
a93187ce
YW
698 assert(ethtool_fd);
699 assert(ifname);
700 assert(features);
701
c2f2250e
YW
702 for (size_t i = 0; i < _NET_DEV_FEAT_MAX; i++)
703 if (features[i] >= 0) {
704 have = true;
705 break;
706 }
707
708 if (!have)
709 return 0;
710
4f504031
YW
711 r = ethtool_connect(ethtool_fd);
712 if (r < 0)
713 return r;
50725d10 714
008d3a37
YW
715 r = get_stringset(*ethtool_fd, ifname, ETH_SS_FEATURES, &strings);
716 if (r < 0)
717 return log_debug_errno(r, "ethtool: could not get ethtool feature strings: %m");
50725d10 718
008d3a37 719 r = get_features(*ethtool_fd, ifname, strings->len, &gfeatures);
50725d10 720 if (r < 0)
008d3a37
YW
721 return log_debug_errno(r, "ethtool: could not get ethtool features for %s: %m", ifname);
722
723 sfeatures = malloc0(offsetof(struct ethtool_sfeatures, features) +
724 DIV_ROUND_UP(strings->len, 32U) * sizeof(sfeatures->features[0]));
725 if (!sfeatures)
726 return log_oom_debug();
50725d10 727
50725d10
SS
728 sfeatures->cmd = ETHTOOL_SFEATURES;
729 sfeatures->size = DIV_ROUND_UP(strings->len, 32U);
730
7a4f2035 731 for (size_t i = 0; i < _NET_DEV_FEAT_SIMPLE_MAX; i++) {
008d3a37
YW
732 r = set_features_bit(strings, gfeatures, sfeatures, netdev_feature_table[i], features[i]);
733 if (r < 0)
734 log_debug_errno(r, "ethtool: could not set feature %s for %s, ignoring: %m", netdev_feature_table[i], ifname);
7a4f2035
YW
735 }
736
737 for (size_t i = _NET_DEV_FEAT_SIMPLE_MAX; i < _NET_DEV_FEAT_MAX; i++) {
738 r = set_features_multiple_bit(strings, gfeatures, sfeatures, netdev_feature_table[i], features[i]);
739 if (r < 0)
740 log_debug_errno(r, "ethtool: could not set feature %s for %s, ignoring: %m", netdev_feature_table[i], ifname);
008d3a37 741 }
50725d10 742
008d3a37
YW
743 ifr = (struct ifreq) {
744 .ifr_data = (void*) sfeatures,
745 };
746 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
50725d10 747
4253dab5
YW
748 if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
749 return log_debug_errno(errno, "ethtool: could not set ethtool features for %s", ifname);
50725d10
SS
750
751 return 0;
752}
a39f92d3 753
a93187ce 754static int get_glinksettings(int fd, struct ifreq *ifr, struct ethtool_link_usettings **ret) {
a39f92d3
SS
755 struct ecmd {
756 struct ethtool_link_settings req;
95fe7b28 757 uint32_t link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
a39f92d3
SS
758 } ecmd = {
759 .req.cmd = ETHTOOL_GLINKSETTINGS,
760 };
761 struct ethtool_link_usettings *u;
762 unsigned offset;
a39f92d3 763
a93187ce
YW
764 assert(fd >= 0);
765 assert(ifr);
766 assert(ret);
767
a39f92d3
SS
768 /* The interaction user/kernel via the new API requires a small ETHTOOL_GLINKSETTINGS
769 handshake first to agree on the length of the link mode bitmaps. If kernel doesn't
770 agree with user, it returns the bitmap length it is expecting from user as a negative
771 length (and cmd field is 0). When kernel and user agree, kernel returns valid info in
772 all fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS). Based on
773 https://github.com/torvalds/linux/commit/3f1ac7a700d039c61d8d8b99f28d605d489a60cf
774 */
775
776 ifr->ifr_data = (void *) &ecmd;
777
4253dab5 778 if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
a39f92d3
SS
779 return -errno;
780
781 if (ecmd.req.link_mode_masks_nwords >= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
6b44a121 782 return -EOPNOTSUPP;
a39f92d3
SS
783
784 ecmd.req.link_mode_masks_nwords = -ecmd.req.link_mode_masks_nwords;
785
786 ifr->ifr_data = (void *) &ecmd;
787
4253dab5 788 if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
a39f92d3
SS
789 return -errno;
790
791 if (ecmd.req.link_mode_masks_nwords <= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
6b44a121 792 return -EOPNOTSUPP;
a39f92d3 793
b9bc7d42 794 u = new(struct ethtool_link_usettings, 1);
a39f92d3
SS
795 if (!u)
796 return -ENOMEM;
797
b9bc7d42
YW
798 *u = (struct ethtool_link_usettings) {
799 .base = ecmd.req,
800 };
a39f92d3
SS
801
802 offset = 0;
803 memcpy(u->link_modes.supported, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords);
804
805 offset += ecmd.req.link_mode_masks_nwords;
806 memcpy(u->link_modes.advertising, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords);
807
808 offset += ecmd.req.link_mode_masks_nwords;
809 memcpy(u->link_modes.lp_advertising, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords);
810
a93187ce 811 *ret = u;
a39f92d3
SS
812
813 return 0;
814}
815
a93187ce 816static int get_gset(int fd, struct ifreq *ifr, struct ethtool_link_usettings **ret) {
a39f92d3
SS
817 struct ethtool_link_usettings *e;
818 struct ethtool_cmd ecmd = {
819 .cmd = ETHTOOL_GSET,
820 };
a39f92d3 821
a93187ce
YW
822 assert(fd >= 0);
823 assert(ifr);
824 assert(ret);
825
a39f92d3
SS
826 ifr->ifr_data = (void *) &ecmd;
827
4253dab5 828 if (ioctl(fd, SIOCETHTOOL, ifr) < 0)
a39f92d3
SS
829 return -errno;
830
b9bc7d42 831 e = new(struct ethtool_link_usettings, 1);
a39f92d3
SS
832 if (!e)
833 return -ENOMEM;
834
b9bc7d42
YW
835 *e = (struct ethtool_link_usettings) {
836 .base.cmd = ETHTOOL_GSET,
837 .base.link_mode_masks_nwords = 1,
838 .base.speed = ethtool_cmd_speed(&ecmd),
839 .base.duplex = ecmd.duplex,
840 .base.port = ecmd.port,
841 .base.phy_address = ecmd.phy_address,
842 .base.autoneg = ecmd.autoneg,
843 .base.mdio_support = ecmd.mdio_support,
18f84f8a
YW
844 .base.eth_tp_mdix = ecmd.eth_tp_mdix,
845 .base.eth_tp_mdix_ctrl = ecmd.eth_tp_mdix_ctrl,
b9bc7d42
YW
846
847 .link_modes.supported[0] = ecmd.supported,
848 .link_modes.advertising[0] = ecmd.advertising,
849 .link_modes.lp_advertising[0] = ecmd.lp_advertising,
850 };
a39f92d3 851
a93187ce 852 *ret = e;
a39f92d3
SS
853
854 return 0;
855}
856
2b44daaa 857static int set_slinksettings(int fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) {
a39f92d3
SS
858 struct {
859 struct ethtool_link_settings req;
95fe7b28 860 uint32_t link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
94d4acbe 861 } ecmd = {};
14cb109d 862 unsigned offset;
a39f92d3 863
a93187ce
YW
864 assert(fd >= 0);
865 assert(ifr);
866 assert(u);
867
a39f92d3
SS
868 if (u->base.cmd != ETHTOOL_GLINKSETTINGS || u->base.link_mode_masks_nwords <= 0)
869 return -EINVAL;
870
89e1ba0a 871 ecmd.req = u->base;
94d4acbe 872 ecmd.req.cmd = ETHTOOL_SLINKSETTINGS;
a39f92d3
SS
873 offset = 0;
874 memcpy(&ecmd.link_mode_data[offset], u->link_modes.supported, 4 * ecmd.req.link_mode_masks_nwords);
875
876 offset += ecmd.req.link_mode_masks_nwords;
877 memcpy(&ecmd.link_mode_data[offset], u->link_modes.advertising, 4 * ecmd.req.link_mode_masks_nwords);
878
879 offset += ecmd.req.link_mode_masks_nwords;
880 memcpy(&ecmd.link_mode_data[offset], u->link_modes.lp_advertising, 4 * ecmd.req.link_mode_masks_nwords);
881
882 ifr->ifr_data = (void *) &ecmd;
883
7c248223 884 return RET_NERRNO(ioctl(fd, SIOCETHTOOL, ifr));
a39f92d3
SS
885}
886
2b44daaa 887static int set_sset(int fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) {
a39f92d3
SS
888 struct ethtool_cmd ecmd = {
889 .cmd = ETHTOOL_SSET,
890 };
a39f92d3 891
a93187ce
YW
892 assert(fd >= 0);
893 assert(ifr);
894 assert(u);
895
a39f92d3
SS
896 if (u->base.cmd != ETHTOOL_GSET || u->base.link_mode_masks_nwords <= 0)
897 return -EINVAL;
898
899 ecmd.supported = u->link_modes.supported[0];
900 ecmd.advertising = u->link_modes.advertising[0];
901 ecmd.lp_advertising = u->link_modes.lp_advertising[0];
902
903 ethtool_cmd_speed_set(&ecmd, u->base.speed);
904
905 ecmd.duplex = u->base.duplex;
906 ecmd.port = u->base.port;
907 ecmd.phy_address = u->base.phy_address;
908 ecmd.autoneg = u->base.autoneg;
909 ecmd.mdio_support = u->base.mdio_support;
6cf0a204
SS
910 ecmd.eth_tp_mdix = u->base.eth_tp_mdix;
911 ecmd.eth_tp_mdix_ctrl = u->base.eth_tp_mdix_ctrl;
a39f92d3
SS
912
913 ifr->ifr_data = (void *) &ecmd;
914
7c248223 915 return RET_NERRNO(ioctl(fd, SIOCETHTOOL, ifr));
a39f92d3
SS
916}
917
5c2316c6
YW
918int ethtool_set_glinksettings(
919 int *fd,
920 const char *ifname,
921 int autonegotiation,
cadc7ed2 922 const uint32_t advertise[static N_ADVERTISE],
50299121 923 uint64_t speed,
5c2316c6 924 Duplex duplex,
18f84f8a
YW
925 NetDevPort port,
926 uint8_t mdi) {
a93187ce 927
a39f92d3
SS
928 _cleanup_free_ struct ethtool_link_usettings *u = NULL;
929 struct ifreq ifr = {};
ba103059 930 bool changed = false;
a39f92d3
SS
931 int r;
932
a93187ce
YW
933 assert(fd);
934 assert(ifname);
2caa38e9
LP
935 assert(advertise);
936
ba103059 937 if (autonegotiation < 0 && memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE) &&
18f84f8a 938 speed == 0 && duplex < 0 && port < 0 && mdi == ETH_TP_MDI_INVALID)
ba103059
YW
939 return 0;
940
4323046c
YW
941 /* If autonegotiation is disabled, the speed and duplex represent the fixed link mode and are
942 * writable if the driver supports multiple link modes. If it is enabled then they are
943 * read-only. If the link is up they represent the negotiated link mode; if the link is down,
944 * the speed is 0, %SPEED_UNKNOWN or the highest enabled speed and @duplex is %DUPLEX_UNKNOWN
945 * or the best enabled duplex mode. */
946
c8e644b1
YW
947 if (speed > 0 || duplex >= 0 || port >= 0) {
948 if (autonegotiation == AUTONEG_ENABLE || !memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE)) {
949 log_debug("ethtool: autonegotiation is enabled, ignoring speed, duplex, or port settings.");
950 speed = 0;
951 duplex = _DUP_INVALID;
952 port = _NET_DEV_PORT_INVALID;
953 } else {
954 log_debug("ethtool: setting speed, duplex, or port, disabling autonegotiation.");
955 autonegotiation = AUTONEG_DISABLE;
956 }
a39f92d3
SS
957 }
958
4f504031
YW
959 r = ethtool_connect(fd);
960 if (r < 0)
961 return r;
a39f92d3 962
6d9a72f3 963 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
a39f92d3 964
2b44daaa 965 r = get_glinksettings(*fd, &ifr, &u);
a39f92d3 966 if (r < 0) {
2b44daaa 967 r = get_gset(*fd, &ifr, &u);
a39f92d3 968 if (r < 0)
18f84f8a 969 return log_debug_errno(r, "ethtool: Cannot get device settings for %s: %m", ifname);
a39f92d3
SS
970 }
971
5c2316c6 972 if (speed > 0)
ba103059 973 UPDATE(u->base.speed, DIV_ROUND_UP(speed, 1000000), changed);
593022fa 974
ba103059
YW
975 if (duplex >= 0)
976 UPDATE(u->base.duplex, duplex, changed);
a39f92d3 977
ba103059
YW
978 if (port >= 0)
979 UPDATE(u->base.port, port, changed);
a39f92d3 980
5c2316c6 981 if (autonegotiation >= 0)
ba103059 982 UPDATE(u->base.autoneg, autonegotiation, changed);
a39f92d3 983
5c2316c6 984 if (!memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE)) {
ba103059
YW
985 UPDATE(u->base.autoneg, AUTONEG_ENABLE, changed);
986
987 changed = changed ||
988 memcmp(&u->link_modes.advertising, advertise, sizeof(uint32_t) * N_ADVERTISE) != 0 ||
989 !memeqzero((uint8_t*) &u->link_modes.advertising + sizeof(uint32_t) * N_ADVERTISE,
990 ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES - sizeof(uint32_t) * N_ADVERTISE);
5c2316c6
YW
991 memcpy(&u->link_modes.advertising, advertise, sizeof(uint32_t) * N_ADVERTISE);
992 memzero((uint8_t*) &u->link_modes.advertising + sizeof(uint32_t) * N_ADVERTISE,
993 ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES - sizeof(uint32_t) * N_ADVERTISE);
5dd10118 994 }
6cf0a204 995
18f84f8a
YW
996 if (mdi != ETH_TP_MDI_INVALID) {
997 if (u->base.eth_tp_mdix_ctrl == ETH_TP_MDI_INVALID)
998 log_debug("ethtool: setting MDI not supported for %s, ignoring.", ifname);
999 else
1000 UPDATE(u->base.eth_tp_mdix_ctrl, mdi, changed);
1001 }
1002
ba103059
YW
1003 if (!changed)
1004 return 0;
1005
a39f92d3 1006 if (u->base.cmd == ETHTOOL_GLINKSETTINGS)
2b44daaa 1007 r = set_slinksettings(*fd, &ifr, u);
a39f92d3 1008 else
2b44daaa 1009 r = set_sset(*fd, &ifr, u);
a39f92d3 1010 if (r < 0)
11a288e8 1011 return log_debug_errno(r, "ethtool: Cannot set device settings for %s: %m", ifname);
a39f92d3
SS
1012
1013 return r;
1014}
5f945202 1015
cadc7ed2 1016int ethtool_set_channels(int *fd, const char *ifname, const netdev_channels *channels) {
5f945202 1017 struct ethtool_channels ecmd = {
a93187ce 1018 .cmd = ETHTOOL_GCHANNELS,
5f945202
SS
1019 };
1020 struct ifreq ifr = {
a93187ce 1021 .ifr_data = (void*) &ecmd,
5f945202 1022 };
5f945202
SS
1023 bool need_update = false;
1024 int r;
1025
a93187ce
YW
1026 assert(fd);
1027 assert(ifname);
1028 assert(channels);
1029
0d341ecc
YW
1030 if (!channels->rx.set &&
1031 !channels->tx.set &&
1032 !channels->other.set &&
1033 !channels->combined.set)
ba103059
YW
1034 return 0;
1035
4f504031
YW
1036 r = ethtool_connect(fd);
1037 if (r < 0)
1038 return r;
5f945202 1039
6d9a72f3 1040 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
5f945202 1041
4253dab5 1042 if (ioctl(*fd, SIOCETHTOOL, &ifr) < 0)
5f945202
SS
1043 return -errno;
1044
0d341ecc
YW
1045 if (channels->rx.set)
1046 UPDATE_WITH_MAX(ecmd.rx_count, ecmd.max_rx, channels->rx.value, need_update);
5f945202 1047
0d341ecc
YW
1048 if (channels->tx.set)
1049 UPDATE_WITH_MAX(ecmd.tx_count, ecmd.max_tx, channels->tx.value, need_update);
5f945202 1050
0d341ecc
YW
1051 if (channels->other.set)
1052 UPDATE_WITH_MAX(ecmd.other_count, ecmd.max_other, channels->other.value, need_update);
5f945202 1053
0d341ecc
YW
1054 if (channels->combined.set)
1055 UPDATE_WITH_MAX(ecmd.combined_count, ecmd.max_combined, channels->combined.value, need_update);
5f945202 1056
ba103059
YW
1057 if (!need_update)
1058 return 0;
a34811e4 1059
ba103059 1060 ecmd.cmd = ETHTOOL_SCHANNELS;
7c248223 1061 return RET_NERRNO(ioctl(*fd, SIOCETHTOOL, &ifr));
a34811e4
YW
1062}
1063
1064int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int autoneg) {
1065 struct ethtool_pauseparam ecmd = {
a93187ce 1066 .cmd = ETHTOOL_GPAUSEPARAM,
a34811e4
YW
1067 };
1068 struct ifreq ifr = {
a93187ce 1069 .ifr_data = (void*) &ecmd,
a34811e4 1070 };
a34811e4
YW
1071 bool need_update = false;
1072 int r;
1073
a93187ce
YW
1074 assert(fd);
1075 assert(ifname);
1076
ba103059
YW
1077 if (rx < 0 && tx < 0 && autoneg < 0)
1078 return 0;
1079
4f504031
YW
1080 r = ethtool_connect(fd);
1081 if (r < 0)
1082 return r;
a34811e4 1083
6d9a72f3 1084 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
a34811e4 1085
4253dab5 1086 if (ioctl(*fd, SIOCETHTOOL, &ifr) < 0)
a34811e4
YW
1087 return -errno;
1088
ba103059
YW
1089 if (rx >= 0)
1090 UPDATE(ecmd.rx_pause, (uint32_t) rx, need_update);
a34811e4 1091
ba103059
YW
1092 if (tx >= 0)
1093 UPDATE(ecmd.tx_pause, (uint32_t) tx, need_update);
a34811e4 1094
ba103059
YW
1095 if (autoneg >= 0)
1096 UPDATE(ecmd.autoneg, (uint32_t) autoneg, need_update);
a34811e4 1097
ba103059
YW
1098 if (!need_update)
1099 return 0;
5f945202 1100
ba103059 1101 ecmd.cmd = ETHTOOL_SPAUSEPARAM;
7c248223 1102 return RET_NERRNO(ioctl(*fd, SIOCETHTOOL, &ifr));
5f945202 1103}
6cf0a204 1104
72328a59
YW
1105int ethtool_set_nic_coalesce_settings(int *ethtool_fd, const char *ifname, const netdev_coalesce_param *coalesce) {
1106 struct ethtool_coalesce ecmd = {
1107 .cmd = ETHTOOL_GCOALESCE,
1108 };
1109 struct ifreq ifr = {
1110 .ifr_data = (void*) &ecmd,
1111 };
1112 bool need_update = false;
1113 int r;
1114
1115 assert(ethtool_fd);
1116 assert(ifname);
1117 assert(coalesce);
1118
1119 if (coalesce->use_adaptive_rx_coalesce < 0 &&
1120 coalesce->use_adaptive_tx_coalesce < 0 &&
1121 !coalesce->rx_coalesce_usecs.set &&
1122 !coalesce->rx_max_coalesced_frames.set &&
1123 !coalesce->rx_coalesce_usecs_irq.set &&
1124 !coalesce->rx_max_coalesced_frames_irq.set &&
1125 !coalesce->tx_coalesce_usecs.set &&
1126 !coalesce->tx_max_coalesced_frames.set &&
1127 !coalesce->tx_coalesce_usecs_irq.set &&
1128 !coalesce->tx_max_coalesced_frames_irq.set &&
1129 !coalesce->stats_block_coalesce_usecs.set &&
1130 !coalesce->pkt_rate_low.set &&
1131 !coalesce->rx_coalesce_usecs_low.set &&
1132 !coalesce->rx_max_coalesced_frames_low.set &&
1133 !coalesce->tx_coalesce_usecs_low.set &&
1134 !coalesce->tx_max_coalesced_frames_low.set &&
1135 !coalesce->pkt_rate_high.set &&
1136 !coalesce->rx_coalesce_usecs_high.set &&
1137 !coalesce->rx_max_coalesced_frames_high.set &&
1138 !coalesce->tx_coalesce_usecs_high.set &&
1139 !coalesce->tx_max_coalesced_frames_high.set &&
1140 !coalesce->rate_sample_interval.set)
1141 return 0;
1142
1143 r = ethtool_connect(ethtool_fd);
1144 if (r < 0)
1145 return r;
1146
6d9a72f3 1147 strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
72328a59 1148
4253dab5 1149 if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
72328a59
YW
1150 return -errno;
1151
1152 if (coalesce->use_adaptive_rx_coalesce >= 0)
1153 UPDATE(ecmd.use_adaptive_rx_coalesce, (uint32_t) coalesce->use_adaptive_rx_coalesce, need_update);
1154
1155 if (coalesce->use_adaptive_tx_coalesce >= 0)
1156 UPDATE(ecmd.use_adaptive_tx_coalesce, (uint32_t) coalesce->use_adaptive_tx_coalesce, need_update);
1157
1158 if (coalesce->rx_coalesce_usecs.set)
1159 UPDATE(ecmd.rx_coalesce_usecs, coalesce->rx_coalesce_usecs.value, need_update);
1160
1161 if (coalesce->rx_max_coalesced_frames.set)
1162 UPDATE(ecmd.rx_max_coalesced_frames, coalesce->rx_max_coalesced_frames.value, need_update);
1163
1164 if (coalesce->rx_coalesce_usecs_irq.set)
1165 UPDATE(ecmd.rx_coalesce_usecs_irq, coalesce->rx_coalesce_usecs_irq.value, need_update);
1166
1167 if (coalesce->rx_max_coalesced_frames_irq.set)
1168 UPDATE(ecmd.rx_max_coalesced_frames_irq, coalesce->rx_max_coalesced_frames_irq.value, need_update);
1169
1170 if (coalesce->tx_coalesce_usecs.set)
1171 UPDATE(ecmd.tx_coalesce_usecs, coalesce->tx_coalesce_usecs.value, need_update);
1172
1173 if (coalesce->tx_max_coalesced_frames.set)
1174 UPDATE(ecmd.tx_max_coalesced_frames, coalesce->tx_max_coalesced_frames.value, need_update);
1175
1176 if (coalesce->tx_coalesce_usecs_irq.set)
1177 UPDATE(ecmd.tx_coalesce_usecs_irq, coalesce->tx_coalesce_usecs_irq.value, need_update);
1178
1179 if (coalesce->tx_max_coalesced_frames_irq.set)
1180 UPDATE(ecmd.tx_max_coalesced_frames_irq, coalesce->tx_max_coalesced_frames_irq.value, need_update);
1181
1182 if (coalesce->stats_block_coalesce_usecs.set)
1183 UPDATE(ecmd.stats_block_coalesce_usecs, coalesce->stats_block_coalesce_usecs.value, need_update);
1184
1185 if (coalesce->pkt_rate_low.set)
1186 UPDATE(ecmd.pkt_rate_low, coalesce->pkt_rate_low.value, need_update);
1187
1188 if (coalesce->rx_coalesce_usecs_low.set)
1189 UPDATE(ecmd.rx_coalesce_usecs_low, coalesce->rx_coalesce_usecs_low.value, need_update);
1190
1191 if (coalesce->rx_max_coalesced_frames_low.set)
1192 UPDATE(ecmd.rx_max_coalesced_frames_low, coalesce->rx_max_coalesced_frames_low.value, need_update);
1193
1194 if (coalesce->tx_coalesce_usecs_low.set)
1195 UPDATE(ecmd.tx_coalesce_usecs_low, coalesce->tx_coalesce_usecs_low.value, need_update);
1196
1197 if (coalesce->tx_max_coalesced_frames_low.set)
1198 UPDATE(ecmd.tx_max_coalesced_frames_low, coalesce->tx_max_coalesced_frames_low.value, need_update);
1199
1200 if (coalesce->pkt_rate_high.set)
1201 UPDATE(ecmd.pkt_rate_high, coalesce->pkt_rate_high.value, need_update);
1202
1203 if (coalesce->rx_coalesce_usecs_high.set)
1204 UPDATE(ecmd.rx_coalesce_usecs_high, coalesce->rx_coalesce_usecs_high.value, need_update);
1205
1206 if (coalesce->rx_max_coalesced_frames_high.set)
1207 UPDATE(ecmd.rx_max_coalesced_frames_high, coalesce->rx_max_coalesced_frames_high.value, need_update);
1208
1209 if (coalesce->tx_coalesce_usecs_high.set)
1210 UPDATE(ecmd.tx_coalesce_usecs_high, coalesce->tx_coalesce_usecs_high.value, need_update);
1211
1212 if (coalesce->tx_max_coalesced_frames_high.set)
1213 UPDATE(ecmd.tx_max_coalesced_frames_high, coalesce->tx_max_coalesced_frames_high.value, need_update);
1214
1215 if (coalesce->rate_sample_interval.set)
1216 UPDATE(ecmd.rate_sample_interval, DIV_ROUND_UP(coalesce->rate_sample_interval.value, USEC_PER_SEC), need_update);
1217
1218 if (!need_update)
1219 return 0;
1220
1221 ecmd.cmd = ETHTOOL_SCOALESCE;
7c248223 1222 return RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr));
72328a59
YW
1223}
1224
ba103059
YW
1225int config_parse_advertise(
1226 const char *unit,
1227 const char *filename,
1228 unsigned line,
1229 const char *section,
1230 unsigned section_line,
1231 const char *lvalue,
1232 int ltype,
1233 const char *rvalue,
1234 void *data,
1235 void *userdata) {
1236
99534007 1237 uint32_t *advertise = ASSERT_PTR(data);
6cf0a204
SS
1238 int r;
1239
1240 assert(filename);
1241 assert(section);
1242 assert(lvalue);
1243 assert(rvalue);
6cf0a204
SS
1244
1245 if (isempty(rvalue)) {
1246 /* Empty string resets the value. */
5c2316c6 1247 memzero(advertise, sizeof(uint32_t) * N_ADVERTISE);
6cf0a204
SS
1248 return 0;
1249 }
1250
c50404ae 1251 for (const char *p = rvalue;;) {
6cf0a204 1252 _cleanup_free_ char *w = NULL;
5dd10118 1253 enum ethtool_link_mode_bit_indices mode;
6cf0a204
SS
1254
1255 r = extract_first_word(&p, &w, NULL, 0);
1256 if (r == -ENOMEM)
1257 return log_oom();
1258 if (r < 0) {
4c382a87
YW
1259 log_syntax(unit, LOG_WARNING, filename, line, r,
1260 "Failed to split advertise modes '%s', ignoring assignment: %m", rvalue);
1261 return 0;
6cf0a204
SS
1262 }
1263 if (r == 0)
4c382a87 1264 return 0;
6cf0a204 1265
2d18ac44 1266 mode = ethtool_link_mode_bit_from_string(w);
84fb56d3
YW
1267 /* We reuse the kernel provided enum which does not contain negative value. So, the cast
1268 * below is mandatory. Otherwise, the check below always passes and access an invalid address. */
1269 if ((int) mode < 0) {
b98680b2 1270 log_syntax(unit, LOG_WARNING, filename, line, mode,
4c382a87 1271 "Failed to parse advertise mode, ignoring: %s", w);
6cf0a204
SS
1272 continue;
1273 }
2d18ac44 1274
5c2316c6 1275 advertise[mode / 32] |= 1UL << (mode % 32);
2d18ac44 1276 }
6cf0a204 1277}
224ded67 1278
18f84f8a
YW
1279int config_parse_mdi(
1280 const char *unit,
1281 const char *filename,
1282 unsigned line,
1283 const char *section,
1284 unsigned section_line,
1285 const char *lvalue,
1286 int ltype,
1287 const char *rvalue,
1288 void *data,
1289 void *userdata) {
1290
1291 uint8_t *mdi = ASSERT_PTR(data);
1292
1293 assert(filename);
1294 assert(rvalue);
1295
1296 if (isempty(rvalue)) {
1297 *mdi = ETH_TP_MDI_INVALID;
1298 return 0;
1299 }
1300
1301 if (STR_IN_SET(rvalue, "mdi", "straight")) {
1302 *mdi = ETH_TP_MDI;
1303 return 0;
1304 }
1305
1306 if (STR_IN_SET(rvalue, "mdi-x", "mdix", "crossover")) {
1307 *mdi = ETH_TP_MDI_X;
1308 return 0;
1309 }
1310
1311 if (streq(rvalue, "auto")) {
1312 *mdi = ETH_TP_MDI_AUTO;
1313 return 0;
1314 }
1315
1316 log_syntax(unit, LOG_WARNING, filename, line, 0,
1317 "Failed to parse %s= setting, ignoring assignment: %s", lvalue, rvalue);
1318 return 0;
1319}
1320
0d341ecc 1321int config_parse_ring_buffer_or_channel(
ba103059
YW
1322 const char *unit,
1323 const char *filename,
1324 unsigned line,
1325 const char *section,
1326 unsigned section_line,
1327 const char *lvalue,
1328 int ltype,
1329 const char *rvalue,
1330 void *data,
1331 void *userdata) {
1332
99534007 1333 u32_opt *dst = ASSERT_PTR(data);
224ded67
SS
1334 uint32_t k;
1335 int r;
1336
1337 assert(filename);
1338 assert(section);
1339 assert(lvalue);
1340 assert(rvalue);
224ded67 1341
0d341ecc
YW
1342 if (isempty(rvalue)) {
1343 dst->value = 0;
1344 dst->set = false;
1345 return 0;
1346 }
1347
1348 if (streq(rvalue, "max")) {
1349 dst->value = 0;
1350 dst->set = true;
1351 return 0;
224ded67
SS
1352 }
1353
0d341ecc
YW
1354 r = safe_atou32(rvalue, &k);
1355 if (r < 0) {
1356 log_syntax(unit, LOG_WARNING, filename, line, r,
1357 "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
1358 return 0;
1359 }
1360 if (k < 1) {
1361 log_syntax(unit, LOG_WARNING, filename, line, 0,
1362 "Invalid %s= value, ignoring: %s", lvalue, rvalue);
1363 return 0;
224ded67
SS
1364 }
1365
0d341ecc
YW
1366 dst->value = k;
1367 dst->set = true;
224ded67
SS
1368 return 0;
1369}
c50404ae
YW
1370
1371int config_parse_wol(
1372 const char *unit,
1373 const char *filename,
1374 unsigned line,
1375 const char *section,
1376 unsigned section_line,
1377 const char *lvalue,
1378 int ltype,
1379 const char *rvalue,
1380 void *data,
1381 void *userdata) {
1382
1383 uint32_t new_opts = 0, *opts = data;
1384 int r;
1385
1386 assert(filename);
1387 assert(section);
1388 assert(lvalue);
1389 assert(rvalue);
1390 assert(data);
1391
1392 if (isempty(rvalue)) {
1393 *opts = UINT32_MAX; /* Do not update WOL option. */
1394 return 0;
1395 }
1396
1397 if (streq(rvalue, "off")) {
1398 *opts = 0; /* Disable WOL. */
1399 return 0;
1400 }
1401
1402 for (const char *p = rvalue;;) {
1403 _cleanup_free_ char *w = NULL;
1404 bool found = false;
1405
1406 r = extract_first_word(&p, &w, NULL, 0);
1407 if (r == -ENOMEM)
1408 return log_oom();
1409 if (r < 0) {
1410 log_syntax(unit, LOG_WARNING, filename, line, r,
1411 "Failed to split wake-on-lan modes '%s', ignoring assignment: %m", rvalue);
1412 return 0;
1413 }
1414 if (r == 0)
1415 break;
1416
1417 for (size_t i = 0; i < ELEMENTSOF(wol_option_map); i++)
1418 if (streq(w, wol_option_map[i].name)) {
1419 new_opts |= wol_option_map[i].opt;
1420 found = true;
1421 break;
1422 }
1423
1424 if (!found)
1425 log_syntax(unit, LOG_WARNING, filename, line, 0,
1426 "Unknown wake-on-lan mode '%s', ignoring.", w);
1427 }
1428
1429 if (*opts == UINT32_MAX)
1430 *opts = new_opts;
1431 else
1432 *opts |= new_opts;
1433
1434 return 0;
1435}
6c35ea5e
DDM
1436
1437int config_parse_coalesce_u32(
1438 const char *unit,
1439 const char *filename,
1440 unsigned line,
1441 const char *section,
1442 unsigned section_line,
1443 const char *lvalue,
1444 int ltype,
1445 const char *rvalue,
1446 void *data,
1447 void *userdata) {
1448 u32_opt *dst = data;
1449 uint32_t k;
1450 int r;
1451
1452 if (isempty(rvalue)) {
1453 dst->value = 0;
1454 dst->set = false;
1455 return 0;
1456 }
1457
1458 r = safe_atou32(rvalue, &k);
1459 if (r < 0) {
1460 log_syntax(unit, LOG_WARNING, filename, line, r,
1461 "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
1462 return 0;
1463 }
1464
1465 dst->value = k;
1466 dst->set = true;
1467 return 0;
1468}
1469
1470int config_parse_coalesce_sec(
1471 const char *unit,
1472 const char *filename,
1473 unsigned line,
1474 const char *section,
1475 unsigned section_line,
1476 const char *lvalue,
1477 int ltype,
1478 const char *rvalue,
1479 void *data,
1480 void *userdata) {
1481 u32_opt *dst = data;
1482 usec_t usec;
1483 int r;
1484
1485 if (isempty(rvalue)) {
1486 dst->value = 0;
1487 dst->set = false;
1488 return 0;
1489 }
1490
1491 r = parse_sec(rvalue, &usec);
1492 if (r < 0) {
1493 log_syntax(unit, LOG_WARNING, filename, line, r,
1494 "Failed to parse coalesce setting value, ignoring: %s", rvalue);
1495 return 0;
1496 }
1497
1498 if (usec > UINT32_MAX) {
1499 log_syntax(unit, LOG_WARNING, filename, line, 0,
1500 "Too large %s= value, ignoring: %s", lvalue, rvalue);
1501 return 0;
1502 }
1503
1504 if (STR_IN_SET(lvalue, "StatisticsBlockCoalesceSec", "CoalescePacketRateSampleIntervalSec") && usec < 1) {
1505 log_syntax(unit, LOG_WARNING, filename, line, 0,
1506 "Invalid %s= value, ignoring: %s", lvalue, rvalue);
1507 return 0;
1508 }
1509
1510 dst->value = (uint32_t) usec;
1511 dst->set = true;
1512
1513 return 0;
1514}