]>
Commit | Line | Data |
---|---|---|
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 | 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] = { |
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 | 62 | static 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 | 157 | assert_cc((ELEMENTSOF(ethtool_link_mode_bit_table)-1) / 32 < N_ADVERTISE); |
6cf0a204 | 158 | |
2d18ac44 | 159 | DEFINE_STRING_TABLE_LOOKUP(ethtool_link_mode_bit, enum ethtool_link_mode_bit_indices); |
6cf0a204 | 160 | |
7864b16b | 161 | static 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 | 176 | int 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 |
213 | int 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 | 264 | int 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 | 310 | int 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 | 371 | int 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 | 463 | int 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 | 520 | static 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 |
573 | static 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 | 600 | int 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 | 644 | static 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 | 709 | static 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 | 750 | static 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 | 785 | static 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 |
827 | int 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 | 893 | int 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 | ||
950 | int 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 |
1001 | int 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 |
1050 | int 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 | |
1104 | int 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 | } |