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