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