1 /*
2 * settings.c - netlink implementation of settings commands
3 *
4 * Implementation of "ethtool <dev>" and "ethtool -s <dev> ...".
5 */
6
7 #include <errno.h>
8 #include <inttypes.h>
9 #include <string.h>
10 #include <stdio.h>
11
12 #include "../internal.h"
13 #include "../common.h"
14 #include "netlink.h"
15 #include "strset.h"
16 #include "bitset.h"
17 #include "parser.h"
18
19 /* GET_SETTINGS */
20
21 struct link_mode_info {
22 enum link_mode_class class;
23 u32 speed;
24 u8 duplex;
25 };
26
27 static const char *const names_duplex[] = {
28 [DUPLEX_HALF] = "Half",
29 [DUPLEX_FULL] = "Full",
30 };
31
32 static const char *const names_master_slave_state[] = {
33 [MASTER_SLAVE_STATE_UNKNOWN] = "unknown",
34 [MASTER_SLAVE_STATE_MASTER] = "master",
35 [MASTER_SLAVE_STATE_SLAVE] = "slave",
36 [MASTER_SLAVE_STATE_ERR] = "resolution error",
37 };
38
39 static const char *const names_master_slave_cfg[] = {
40 [MASTER_SLAVE_CFG_UNKNOWN] = "unknown",
41 [MASTER_SLAVE_CFG_MASTER_PREFERRED] = "preferred master",
42 [MASTER_SLAVE_CFG_SLAVE_PREFERRED] = "preferred slave",
43 [MASTER_SLAVE_CFG_MASTER_FORCE] = "forced master",
44 [MASTER_SLAVE_CFG_SLAVE_FORCE] = "forced slave",
45 };
46
47 static const char *const names_port[] = {
48 [PORT_TP] = "Twisted Pair",
49 [PORT_AUI] = "AUI",
50 [PORT_BNC] = "BNC",
51 [PORT_MII] = "MII",
52 [PORT_FIBRE] = "FIBRE",
53 [PORT_DA] = "Direct Attach Copper",
54 [PORT_NONE] = "None",
55 [PORT_OTHER] = "Other",
56 };
57
58 static const char *const names_transceiver[] = {
59 [XCVR_INTERNAL] = "internal",
60 [XCVR_EXTERNAL] = "external",
61 };
62
63 /* the practice of putting completely unrelated flags into link mode bitmaps
64 * is rather unfortunate but as even ethtool_link_ksettings preserved that,
65 * there is little chance of getting them separated any time soon so let's
66 * sort them out ourselves
67 */
68 #define __REAL(_speed) \
69 { .class = LM_CLASS_REAL, .speed = _speed, .duplex = DUPLEX_FULL }
70 #define __HALF_DUPLEX(_speed) \
71 { .class = LM_CLASS_REAL, .speed = _speed, .duplex = DUPLEX_HALF }
72 #define __SPECIAL(_class) \
73 { .class = LM_CLASS_ ## _class }
74
75 static const struct link_mode_info link_modes[] = {
76 [ETHTOOL_LINK_MODE_10baseT_Half_BIT] = __HALF_DUPLEX(10),
77 [ETHTOOL_LINK_MODE_10baseT_Full_BIT] = __REAL(10),
78 [ETHTOOL_LINK_MODE_100baseT_Half_BIT] = __HALF_DUPLEX(100),
79 [ETHTOOL_LINK_MODE_100baseT_Full_BIT] = __REAL(100),
80 [ETHTOOL_LINK_MODE_1000baseT_Half_BIT] = __HALF_DUPLEX(1000),
81 [ETHTOOL_LINK_MODE_1000baseT_Full_BIT] = __REAL(1000),
82 [ETHTOOL_LINK_MODE_Autoneg_BIT] = __SPECIAL(AUTONEG),
83 [ETHTOOL_LINK_MODE_TP_BIT] = __SPECIAL(PORT),
84 [ETHTOOL_LINK_MODE_AUI_BIT] = __SPECIAL(PORT),
85 [ETHTOOL_LINK_MODE_MII_BIT] = __SPECIAL(PORT),
86 [ETHTOOL_LINK_MODE_FIBRE_BIT] = __SPECIAL(PORT),
87 [ETHTOOL_LINK_MODE_BNC_BIT] = __SPECIAL(PORT),
88 [ETHTOOL_LINK_MODE_10000baseT_Full_BIT] = __REAL(10000),
89 [ETHTOOL_LINK_MODE_Pause_BIT] = __SPECIAL(PAUSE),
90 [ETHTOOL_LINK_MODE_Asym_Pause_BIT] = __SPECIAL(PAUSE),
91 [ETHTOOL_LINK_MODE_2500baseX_Full_BIT] = __REAL(2500),
92 [ETHTOOL_LINK_MODE_Backplane_BIT] = __SPECIAL(PORT),
93 [ETHTOOL_LINK_MODE_1000baseKX_Full_BIT] = __REAL(1000),
94 [ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT] = __REAL(10000),
95 [ETHTOOL_LINK_MODE_10000baseKR_Full_BIT] = __REAL(10000),
96 [ETHTOOL_LINK_MODE_10000baseR_FEC_BIT] = __REAL(10000),
97 [ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT] = __REAL(20000),
98 [ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT] = __REAL(20000),
99 [ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT] = __REAL(40000),
100 [ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT] = __REAL(40000),
101 [ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT] = __REAL(40000),
102 [ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT] = __REAL(40000),
103 [ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT] = __REAL(56000),
104 [ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT] = __REAL(56000),
105 [ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT] = __REAL(56000),
106 [ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT] = __REAL(56000),
107 [ETHTOOL_LINK_MODE_25000baseCR_Full_BIT] = __REAL(25000),
108 [ETHTOOL_LINK_MODE_25000baseKR_Full_BIT] = __REAL(25000),
109 [ETHTOOL_LINK_MODE_25000baseSR_Full_BIT] = __REAL(25000),
110 [ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT] = __REAL(50000),
111 [ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT] = __REAL(50000),
112 [ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT] = __REAL(100000),
113 [ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT] = __REAL(100000),
114 [ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT] = __REAL(100000),
115 [ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT] = __REAL(100000),
116 [ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT] = __REAL(50000),
117 [ETHTOOL_LINK_MODE_1000baseX_Full_BIT] = __REAL(1000),
118 [ETHTOOL_LINK_MODE_10000baseCR_Full_BIT] = __REAL(10000),
119 [ETHTOOL_LINK_MODE_10000baseSR_Full_BIT] = __REAL(10000),
120 [ETHTOOL_LINK_MODE_10000baseLR_Full_BIT] = __REAL(10000),
121 [ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT] = __REAL(10000),
122 [ETHTOOL_LINK_MODE_10000baseER_Full_BIT] = __REAL(10000),
123 [ETHTOOL_LINK_MODE_2500baseT_Full_BIT] = __REAL(2500),
124 [ETHTOOL_LINK_MODE_5000baseT_Full_BIT] = __REAL(5000),
125 [ETHTOOL_LINK_MODE_FEC_NONE_BIT] = __SPECIAL(FEC),
126 [ETHTOOL_LINK_MODE_FEC_RS_BIT] = __SPECIAL(FEC),
127 [ETHTOOL_LINK_MODE_FEC_BASER_BIT] = __SPECIAL(FEC),
128 [ETHTOOL_LINK_MODE_50000baseKR_Full_BIT] = __REAL(50000),
129 [ETHTOOL_LINK_MODE_50000baseSR_Full_BIT] = __REAL(50000),
130 [ETHTOOL_LINK_MODE_50000baseCR_Full_BIT] = __REAL(50000),
131 [ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT] = __REAL(50000),
132 [ETHTOOL_LINK_MODE_50000baseDR_Full_BIT] = __REAL(50000),
133 [ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT] = __REAL(100000),
134 [ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT] = __REAL(100000),
135 [ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT] = __REAL(100000),
136 [ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT] = __REAL(100000),
137 [ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT] = __REAL(100000),
138 [ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT] = __REAL(200000),
139 [ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT] = __REAL(200000),
140 [ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT] = __REAL(200000),
141 [ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT] = __REAL(200000),
142 [ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT] = __REAL(200000),
143 [ETHTOOL_LINK_MODE_100baseT1_Full_BIT] = __REAL(100),
144 [ETHTOOL_LINK_MODE_1000baseT1_Full_BIT] = __REAL(1000),
145 [ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT] = __REAL(400000),
146 [ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT] = __REAL(400000),
147 [ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT] = __REAL(400000),
148 [ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT] = __REAL(400000),
149 [ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT] = __REAL(400000),
150 [ETHTOOL_LINK_MODE_FEC_LLRS_BIT] = __SPECIAL(FEC),
151 [ETHTOOL_LINK_MODE_100000baseKR_Full_BIT] = __REAL(100000),
152 [ETHTOOL_LINK_MODE_100000baseSR_Full_BIT] = __REAL(100000),
153 [ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT] = __REAL(100000),
154 [ETHTOOL_LINK_MODE_100000baseCR_Full_BIT] = __REAL(100000),
155 [ETHTOOL_LINK_MODE_100000baseDR_Full_BIT] = __REAL(100000),
156 [ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT] = __REAL(200000),
157 [ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT] = __REAL(200000),
158 [ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT] = __REAL(200000),
159 [ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT] = __REAL(200000),
160 [ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT] = __REAL(200000),
161 [ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT] = __REAL(400000),
162 [ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT] = __REAL(400000),
163 [ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT] = __REAL(400000),
164 [ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT] = __REAL(400000),
165 [ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT] = __REAL(400000),
166 [ETHTOOL_LINK_MODE_100baseFX_Half_BIT] = __HALF_DUPLEX(100),
167 [ETHTOOL_LINK_MODE_100baseFX_Full_BIT] = __REAL(100),
168 [ETHTOOL_LINK_MODE_10baseT1L_Full_BIT] = __REAL(10),
169 [ETHTOOL_LINK_MODE_800000baseCR8_Full_BIT] = __REAL(800000),
170 [ETHTOOL_LINK_MODE_800000baseKR8_Full_BIT] = __REAL(800000),
171 [ETHTOOL_LINK_MODE_800000baseDR8_Full_BIT] = __REAL(800000),
172 [ETHTOOL_LINK_MODE_800000baseDR8_2_Full_BIT] = __REAL(800000),
173 [ETHTOOL_LINK_MODE_800000baseSR8_Full_BIT] = __REAL(800000),
174 [ETHTOOL_LINK_MODE_800000baseVR8_Full_BIT] = __REAL(800000),
175 [ETHTOOL_LINK_MODE_10baseT1S_Full_BIT] = __REAL(10),
176 [ETHTOOL_LINK_MODE_10baseT1S_Half_BIT] = __HALF_DUPLEX(10),
177 [ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT] = __HALF_DUPLEX(10),
178 };
179 const unsigned int link_modes_count = ARRAY_SIZE(link_modes);
180
181 #undef __REAL
182 #undef __HALF_DUPLEX
183 #undef __SPECIAL
184
lm_class_match(unsigned int mode,enum link_mode_class class)185 static bool lm_class_match(unsigned int mode, enum link_mode_class class)
186 {
187 unsigned int mode_class = (mode < link_modes_count) ?
188 link_modes[mode].class : LM_CLASS_UNKNOWN;
189
190 return mode_class == class ||
191 (class == LM_CLASS_REAL && mode_class == LM_CLASS_UNKNOWN);
192 }
193
print_enum(const char * const * info,unsigned int n_info,unsigned int val,const char * label)194 static void print_enum(const char *const *info, unsigned int n_info,
195 unsigned int val, const char *label)
196 {
197 if (val >= n_info || !info[val])
198 printf("\t%s: Unknown! (%d)\n", label, val);
199 else
200 printf("\t%s: %s\n", label, info[val]);
201 }
202
dump_pause(const struct nlattr * attr,bool mask,const char * label)203 static int dump_pause(const struct nlattr *attr, bool mask, const char *label)
204 {
205 bool pause, asym;
206 int ret = 0;
207
208 pause = bitset_get_bit(attr, mask, ETHTOOL_LINK_MODE_Pause_BIT, &ret);
209 if (ret < 0)
210 goto err;
211 asym = bitset_get_bit(attr, mask, ETHTOOL_LINK_MODE_Asym_Pause_BIT,
212 &ret);
213 if (ret < 0)
214 goto err;
215
216 printf("\t%s", label);
217 if (pause)
218 printf("%s\n", asym ? "Symmetric Receive-only" : "Symmetric");
219 else
220 printf("%s\n", asym ? "Transmit-only" : "No");
221
222 return 0;
223 err:
224 fprintf(stderr, "malformed netlink message (pause modes)\n");
225 return ret;
226 }
227
print_banner(struct nl_context * nlctx)228 static void print_banner(struct nl_context *nlctx)
229 {
230 if (nlctx->no_banner)
231 return;
232 printf("Settings for %s:\n", nlctx->devname);
233 nlctx->no_banner = true;
234 }
235
dump_link_modes(struct nl_context * nlctx,const struct nlattr * bitset,bool mask,unsigned int class,const char * before,const char * between,const char * after,const char * if_none)236 int dump_link_modes(struct nl_context *nlctx, const struct nlattr *bitset,
237 bool mask, unsigned int class, const char *before,
238 const char *between, const char *after, const char *if_none)
239 {
240 const struct nlattr *bitset_tb[ETHTOOL_A_BITSET_MAX + 1] = {};
241 DECLARE_ATTR_TB_INFO(bitset_tb);
242 const unsigned int before_len = strlen(before);
243 unsigned int prev = UINT_MAX - 1;
244 const struct nlattr *bits;
245 const struct nlattr *bit;
246 bool first = true;
247 bool nomask;
248 int ret;
249
250 ret = mnl_attr_parse_nested(bitset, attr_cb, &bitset_tb_info);
251 if (ret < 0)
252 goto err_nonl;
253
254 nomask = bitset_tb[ETHTOOL_A_BITSET_NOMASK];
255 /* Trying to print the mask of a "no mask" bitset doesn't make sense */
256 if (mask && nomask) {
257 ret = -EFAULT;
258 goto err_nonl;
259 }
260
261 bits = bitset_tb[ETHTOOL_A_BITSET_BITS];
262
263 if (!bits) {
264 const struct stringset *lm_strings;
265 unsigned int count;
266 unsigned int idx;
267 const char *name;
268
269 ret = netlink_init_ethnl2_socket(nlctx);
270 if (ret < 0)
271 goto err_nonl;
272 lm_strings = global_stringset(ETH_SS_LINK_MODES,
273 nlctx->ethnl2_socket);
274 bits = mask ? bitset_tb[ETHTOOL_A_BITSET_MASK] :
275 bitset_tb[ETHTOOL_A_BITSET_VALUE];
276 ret = -EFAULT;
277 if (!bits || !bitset_tb[ETHTOOL_A_BITSET_SIZE])
278 goto err_nonl;
279 count = mnl_attr_get_u32(bitset_tb[ETHTOOL_A_BITSET_SIZE]);
280 if (mnl_attr_get_payload_len(bits) / 4 < (count + 31) / 32)
281 goto err_nonl;
282
283 printf("\t%s", before);
284 for (idx = 0; idx < count; idx++) {
285 const uint32_t *raw_data = mnl_attr_get_payload(bits);
286 char buff[14];
287
288 if (!(raw_data[idx / 32] & (1U << (idx % 32))))
289 continue;
290 if (!lm_class_match(idx, class))
291 continue;
292 name = get_string(lm_strings, idx);
293 if (!name) {
294 snprintf(buff, sizeof(buff), "BIT%u", idx);
295 name = buff;
296 }
297 if (first)
298 first = false;
299 /* ugly hack to preserve old output format */
300 if (class == LM_CLASS_REAL && (idx == prev + 1) &&
301 prev < link_modes_count &&
302 link_modes[prev].class == LM_CLASS_REAL &&
303 link_modes[prev].duplex == DUPLEX_HALF)
304 putchar(' ');
305 else if (between)
306 printf("\t%s", between);
307 else
308 printf("\n\t%*s", before_len, "");
309 printf("%s", name);
310 prev = idx;
311 }
312 goto after;
313 }
314
315 printf("\t%s", before);
316 mnl_attr_for_each_nested(bit, bits) {
317 const struct nlattr *tb[ETHTOOL_A_BITSET_BIT_MAX + 1] = {};
318 DECLARE_ATTR_TB_INFO(tb);
319 unsigned int idx;
320 const char *name;
321
322 if (mnl_attr_get_type(bit) != ETHTOOL_A_BITSET_BITS_BIT)
323 continue;
324 ret = mnl_attr_parse_nested(bit, attr_cb, &tb_info);
325 if (ret < 0)
326 goto err;
327 ret = -EFAULT;
328 if (!tb[ETHTOOL_A_BITSET_BIT_INDEX] ||
329 !tb[ETHTOOL_A_BITSET_BIT_NAME])
330 goto err;
331 if (!mask && !nomask && !tb[ETHTOOL_A_BITSET_BIT_VALUE])
332 continue;
333
334 idx = mnl_attr_get_u32(tb[ETHTOOL_A_BITSET_BIT_INDEX]);
335 name = mnl_attr_get_str(tb[ETHTOOL_A_BITSET_BIT_NAME]);
336 if (!lm_class_match(idx, class))
337 continue;
338 if (first) {
339 first = false;
340 } else {
341 /* ugly hack to preserve old output format */
342 if ((class == LM_CLASS_REAL) && (idx == prev + 1) &&
343 (prev < link_modes_count) &&
344 (link_modes[prev].class == LM_CLASS_REAL) &&
345 (link_modes[prev].duplex == DUPLEX_HALF))
346 putchar(' ');
347 else if (between)
348 printf("\t%s", between);
349 else
350 printf("\n\t%*s", before_len, "");
351 }
352 printf("%s", name);
353 prev = idx;
354 }
355 after:
356 if (first && if_none)
357 printf("%s", if_none);
358 printf("%s", after);
359
360 return 0;
361 err:
362 putchar('\n');
363 err_nonl:
364 fflush(stdout);
365 fprintf(stderr, "malformed netlink message (link_modes)\n");
366 return ret;
367 }
368
dump_our_modes(struct nl_context * nlctx,const struct nlattr * attr)369 static int dump_our_modes(struct nl_context *nlctx, const struct nlattr *attr)
370 {
371 bool autoneg;
372 int ret;
373
374 print_banner(nlctx);
375 ret = dump_link_modes(nlctx, attr, true, LM_CLASS_PORT,
376 "Supported ports: [ ", " ", " ]\n", NULL);
377 if (ret < 0)
378 return ret;
379
380 ret = dump_link_modes(nlctx, attr, true, LM_CLASS_REAL,
381 "Supported link modes: ", NULL, "\n",
382 "Not reported");
383 if (ret < 0)
384 return ret;
385 ret = dump_pause(attr, true, "Supported pause frame use: ");
386 if (ret < 0)
387 return ret;
388
389 autoneg = bitset_get_bit(attr, true, ETHTOOL_LINK_MODE_Autoneg_BIT,
390 &ret);
391 if (ret < 0)
392 return ret;
393 printf("\tSupports auto-negotiation: %s\n", autoneg ? "Yes" : "No");
394
395 ret = dump_link_modes(nlctx, attr, true, LM_CLASS_FEC,
396 "Supported FEC modes: ", " ", "\n",
397 "Not reported");
398 if (ret < 0)
399 return ret;
400
401 ret = dump_link_modes(nlctx, attr, false, LM_CLASS_REAL,
402 "Advertised link modes: ", NULL, "\n",
403 "Not reported");
404 if (ret < 0)
405 return ret;
406
407 ret = dump_pause(attr, false, "Advertised pause frame use: ");
408 if (ret < 0)
409 return ret;
410 autoneg = bitset_get_bit(attr, false, ETHTOOL_LINK_MODE_Autoneg_BIT,
411 &ret);
412 if (ret < 0)
413 return ret;
414 printf("\tAdvertised auto-negotiation: %s\n", autoneg ? "Yes" : "No");
415
416 ret = dump_link_modes(nlctx, attr, false, LM_CLASS_FEC,
417 "Advertised FEC modes: ", " ", "\n",
418 "Not reported");
419 return ret;
420 }
421
dump_peer_modes(struct nl_context * nlctx,const struct nlattr * attr)422 static int dump_peer_modes(struct nl_context *nlctx, const struct nlattr *attr)
423 {
424 bool autoneg;
425 int ret;
426
427 print_banner(nlctx);
428 ret = dump_link_modes(nlctx, attr, false, LM_CLASS_REAL,
429 "Link partner advertised link modes: ",
430 NULL, "\n", "Not reported");
431 if (ret < 0)
432 return ret;
433
434 ret = dump_pause(attr, false,
435 "Link partner advertised pause frame use: ");
436 if (ret < 0)
437 return ret;
438
439 autoneg = bitset_get_bit(attr, false,
440 ETHTOOL_LINK_MODE_Autoneg_BIT, &ret);
441 if (ret < 0)
442 return ret;
443 printf("\tLink partner advertised auto-negotiation: %s\n",
444 autoneg ? "Yes" : "No");
445
446 ret = dump_link_modes(nlctx, attr, false, LM_CLASS_FEC,
447 "Link partner advertised FEC modes: ",
448 " ", "\n", "Not reported");
449 return ret;
450 }
451
linkmodes_reply_cb(const struct nlmsghdr * nlhdr,void * data)452 int linkmodes_reply_cb(const struct nlmsghdr *nlhdr, void *data)
453 {
454 const struct nlattr *tb[ETHTOOL_A_LINKMODES_MAX + 1] = {};
455 DECLARE_ATTR_TB_INFO(tb);
456 struct nl_context *nlctx = data;
457 int ret;
458
459 if (nlctx->is_dump || nlctx->is_monitor)
460 nlctx->no_banner = false;
461 ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
462 if (ret < 0)
463 return ret;
464 nlctx->devname = get_dev_name(tb[ETHTOOL_A_LINKMODES_HEADER]);
465 if (!dev_ok(nlctx))
466 return MNL_CB_OK;
467
468 if (tb[ETHTOOL_A_LINKMODES_OURS]) {
469 ret = dump_our_modes(nlctx, tb[ETHTOOL_A_LINKMODES_OURS]);
470 if (ret < 0)
471 goto err;
472 }
473 if (tb[ETHTOOL_A_LINKMODES_PEER]) {
474 ret = dump_peer_modes(nlctx, tb[ETHTOOL_A_LINKMODES_PEER]);
475 if (ret < 0)
476 goto err;
477 }
478 if (tb[ETHTOOL_A_LINKMODES_SPEED]) {
479 uint32_t val = mnl_attr_get_u32(tb[ETHTOOL_A_LINKMODES_SPEED]);
480
481 print_banner(nlctx);
482 if (val == 0 || val == (uint16_t)(-1) || val == (uint32_t)(-1))
483 printf("\tSpeed: Unknown!\n");
484 else
485 printf("\tSpeed: %uMb/s\n", val);
486 }
487 if (tb[ETHTOOL_A_LINKMODES_LANES]) {
488 uint32_t val = mnl_attr_get_u32(tb[ETHTOOL_A_LINKMODES_LANES]);
489
490 print_banner(nlctx);
491 printf("\tLanes: %u\n", val);
492 }
493 if (tb[ETHTOOL_A_LINKMODES_DUPLEX]) {
494 uint8_t val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKMODES_DUPLEX]);
495
496 print_banner(nlctx);
497 print_enum(names_duplex, ARRAY_SIZE(names_duplex), val,
498 "Duplex");
499 }
500 if (tb[ETHTOOL_A_LINKMODES_AUTONEG]) {
501 int autoneg = mnl_attr_get_u8(tb[ETHTOOL_A_LINKMODES_AUTONEG]);
502
503 print_banner(nlctx);
504 printf("\tAuto-negotiation: %s\n",
505 (autoneg == AUTONEG_DISABLE) ? "off" : "on");
506 }
507 if (tb[ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG]) {
508 uint8_t val;
509
510 val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG]);
511
512 print_banner(nlctx);
513 print_enum(names_master_slave_cfg,
514 ARRAY_SIZE(names_master_slave_cfg), val,
515 "master-slave cfg");
516 }
517 if (tb[ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE]) {
518 uint8_t val;
519
520 val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE]);
521 print_banner(nlctx);
522 print_enum(names_master_slave_state,
523 ARRAY_SIZE(names_master_slave_state), val,
524 "master-slave status");
525 }
526
527 return MNL_CB_OK;
528 err:
529 if (nlctx->is_monitor || nlctx->is_dump)
530 return MNL_CB_OK;
531 fputs("No data available\n", stdout);
532 nlctx->exit_code = 75;
533 return MNL_CB_ERROR;
534 }
535
linkinfo_reply_cb(const struct nlmsghdr * nlhdr,void * data)536 int linkinfo_reply_cb(const struct nlmsghdr *nlhdr, void *data)
537 {
538 const struct nlattr *tb[ETHTOOL_A_LINKINFO_MAX + 1] = {};
539 DECLARE_ATTR_TB_INFO(tb);
540 struct nl_context *nlctx = data;
541 int port = -1;
542 int ret;
543
544 if (nlctx->is_dump || nlctx->is_monitor)
545 nlctx->no_banner = false;
546 ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
547 if (ret < 0)
548 return ret;
549 nlctx->devname = get_dev_name(tb[ETHTOOL_A_LINKINFO_HEADER]);
550 if (!dev_ok(nlctx))
551 return MNL_CB_OK;
552
553 if (tb[ETHTOOL_A_LINKINFO_PORT]) {
554 uint8_t val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKINFO_PORT]);
555
556 print_banner(nlctx);
557 print_enum(names_port, ARRAY_SIZE(names_port), val, "Port");
558 port = val;
559 }
560 if (tb[ETHTOOL_A_LINKINFO_PHYADDR]) {
561 uint8_t val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKINFO_PHYADDR]);
562
563 print_banner(nlctx);
564 printf("\tPHYAD: %u\n", val);
565 }
566 if (tb[ETHTOOL_A_LINKINFO_TRANSCEIVER]) {
567 uint8_t val;
568
569 val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKINFO_TRANSCEIVER]);
570 print_banner(nlctx);
571 print_enum(names_transceiver, ARRAY_SIZE(names_transceiver),
572 val, "Transceiver");
573 }
574 if (tb[ETHTOOL_A_LINKINFO_TP_MDIX] && tb[ETHTOOL_A_LINKINFO_TP_MDIX_CTRL] &&
575 port == PORT_TP) {
576 uint8_t mdix = mnl_attr_get_u8(tb[ETHTOOL_A_LINKINFO_TP_MDIX]);
577 uint8_t mdix_ctrl =
578 mnl_attr_get_u8(tb[ETHTOOL_A_LINKINFO_TP_MDIX_CTRL]);
579
580 print_banner(nlctx);
581 dump_mdix(mdix, mdix_ctrl);
582 }
583
584 return MNL_CB_OK;
585 }
586
get_enum_string(const char * const * string_table,unsigned int n_string_table,unsigned int val)587 static const char *get_enum_string(const char *const *string_table, unsigned int n_string_table,
588 unsigned int val)
589 {
590 if (val >= n_string_table || !string_table[val])
591 return NULL;
592 else
593 return string_table[val];
594 }
595
596 static const char *const names_link_ext_state[] = {
597 [ETHTOOL_LINK_EXT_STATE_AUTONEG] = "Autoneg",
598 [ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE] = "Link training failure",
599 [ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH] = "Logical mismatch",
600 [ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY] = "Bad signal integrity",
601 [ETHTOOL_LINK_EXT_STATE_NO_CABLE] = "No cable",
602 [ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE] = "Cable issue",
603 [ETHTOOL_LINK_EXT_STATE_EEPROM_ISSUE] = "EEPROM issue",
604 [ETHTOOL_LINK_EXT_STATE_CALIBRATION_FAILURE] = "Calibration failure",
605 [ETHTOOL_LINK_EXT_STATE_POWER_BUDGET_EXCEEDED] = "Power budget exceeded",
606 [ETHTOOL_LINK_EXT_STATE_OVERHEAT] = "Overheat",
607 [ETHTOOL_LINK_EXT_STATE_MODULE] = "Module",
608 };
609
610 static const char *const names_autoneg_link_ext_substate[] = {
611 [ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED] =
612 "No partner detected",
613 [ETHTOOL_LINK_EXT_SUBSTATE_AN_ACK_NOT_RECEIVED] =
614 "Ack not received",
615 [ETHTOOL_LINK_EXT_SUBSTATE_AN_NEXT_PAGE_EXCHANGE_FAILED] =
616 "Next page exchange failed",
617 [ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED_FORCE_MODE] =
618 "No partner detected during force mode",
619 [ETHTOOL_LINK_EXT_SUBSTATE_AN_FEC_MISMATCH_DURING_OVERRIDE] =
620 "FEC mismatch during override",
621 [ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_HCD] =
622 "No HCD",
623 };
624
625 static const char *const names_link_training_link_ext_substate[] = {
626 [ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_FRAME_LOCK_NOT_ACQUIRED] =
627 "KR frame lock not acquired",
628 [ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_INHIBIT_TIMEOUT] =
629 "KR link inhibit timeout",
630 [ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_LINK_PARTNER_DID_NOT_SET_RECEIVER_READY] =
631 "KR Link partner did not set receiver ready",
632 [ETHTOOL_LINK_EXT_SUBSTATE_LT_REMOTE_FAULT] =
633 "Remote side is not ready yet",
634 };
635
636 static const char *const names_link_logical_mismatch_link_ext_substate[] = {
637 [ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_BLOCK_LOCK] =
638 "PCS did not acquire block lock",
639 [ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_AM_LOCK] =
640 "PCS did not acquire AM lock",
641 [ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_GET_ALIGN_STATUS] =
642 "PCS did not get align_status",
643 [ETHTOOL_LINK_EXT_SUBSTATE_LLM_FC_FEC_IS_NOT_LOCKED] =
644 "FC FEC is not locked",
645 [ETHTOOL_LINK_EXT_SUBSTATE_LLM_RS_FEC_IS_NOT_LOCKED] =
646 "RS FEC is not locked",
647 };
648
649 static const char *const names_bad_signal_integrity_link_ext_substate[] = {
650 [ETHTOOL_LINK_EXT_SUBSTATE_BSI_LARGE_NUMBER_OF_PHYSICAL_ERRORS] =
651 "Large number of physical errors",
652 [ETHTOOL_LINK_EXT_SUBSTATE_BSI_UNSUPPORTED_RATE] =
653 "Unsupported rate",
654 [ETHTOOL_LINK_EXT_SUBSTATE_BSI_SERDES_REFERENCE_CLOCK_LOST] =
655 "Serdes reference clock lost",
656 [ETHTOOL_LINK_EXT_SUBSTATE_BSI_SERDES_ALOS] =
657 "Serdes ALOS",
658 };
659
660 static const char *const names_cable_issue_link_ext_substate[] = {
661 [ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE] =
662 "Unsupported cable",
663 [ETHTOOL_LINK_EXT_SUBSTATE_CI_CABLE_TEST_FAILURE] =
664 "Cable test failure",
665 };
666
667 static const char *const names_module_link_ext_substate[] = {
668 [ETHTOOL_LINK_EXT_SUBSTATE_MODULE_CMIS_NOT_READY] =
669 "CMIS module is not in ModuleReady state",
670 };
671
link_ext_substate_get(uint8_t link_ext_state_val,uint8_t link_ext_substate_val)672 static const char *link_ext_substate_get(uint8_t link_ext_state_val, uint8_t link_ext_substate_val)
673 {
674 switch (link_ext_state_val) {
675 case ETHTOOL_LINK_EXT_STATE_AUTONEG:
676 return get_enum_string(names_autoneg_link_ext_substate,
677 ARRAY_SIZE(names_autoneg_link_ext_substate),
678 link_ext_substate_val);
679 case ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE:
680 return get_enum_string(names_link_training_link_ext_substate,
681 ARRAY_SIZE(names_link_training_link_ext_substate),
682 link_ext_substate_val);
683 case ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH:
684 return get_enum_string(names_link_logical_mismatch_link_ext_substate,
685 ARRAY_SIZE(names_link_logical_mismatch_link_ext_substate),
686 link_ext_substate_val);
687 case ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY:
688 return get_enum_string(names_bad_signal_integrity_link_ext_substate,
689 ARRAY_SIZE(names_bad_signal_integrity_link_ext_substate),
690 link_ext_substate_val);
691 case ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE:
692 return get_enum_string(names_cable_issue_link_ext_substate,
693 ARRAY_SIZE(names_cable_issue_link_ext_substate),
694 link_ext_substate_val);
695 case ETHTOOL_LINK_EXT_STATE_MODULE:
696 return get_enum_string(names_module_link_ext_substate,
697 ARRAY_SIZE(names_module_link_ext_substate),
698 link_ext_substate_val);
699 default:
700 return NULL;
701 }
702 }
703
linkstate_link_ext_substate_print(const struct nlattr * tb[],uint8_t link_ext_state_val)704 static void linkstate_link_ext_substate_print(const struct nlattr *tb[],
705 uint8_t link_ext_state_val)
706 {
707 uint8_t link_ext_substate_val;
708 const char *link_ext_substate_str;
709
710 if (!tb[ETHTOOL_A_LINKSTATE_EXT_SUBSTATE])
711 return;
712
713 link_ext_substate_val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKSTATE_EXT_SUBSTATE]);
714
715 link_ext_substate_str = link_ext_substate_get(link_ext_state_val, link_ext_substate_val);
716 if (!link_ext_substate_str)
717 printf(", %u", link_ext_substate_val);
718 else
719 printf(", %s", link_ext_substate_str);
720 }
721
linkstate_link_ext_state_print(const struct nlattr * tb[])722 static void linkstate_link_ext_state_print(const struct nlattr *tb[])
723 {
724 uint8_t link_ext_state_val;
725 const char *link_ext_state_str;
726
727 if (!tb[ETHTOOL_A_LINKSTATE_EXT_STATE])
728 return;
729
730 link_ext_state_val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKSTATE_EXT_STATE]);
731
732 link_ext_state_str = get_enum_string(names_link_ext_state,
733 ARRAY_SIZE(names_link_ext_state),
734 link_ext_state_val);
735 if (!link_ext_state_str)
736 printf(" (%u", link_ext_state_val);
737 else
738 printf(" (%s", link_ext_state_str);
739
740 linkstate_link_ext_substate_print(tb, link_ext_state_val);
741 printf(")");
742 }
743
linkstate_reply_cb(const struct nlmsghdr * nlhdr,void * data)744 int linkstate_reply_cb(const struct nlmsghdr *nlhdr, void *data)
745 {
746 const struct nlattr *tb[ETHTOOL_A_LINKSTATE_MAX + 1] = {};
747 DECLARE_ATTR_TB_INFO(tb);
748 struct nl_context *nlctx = data;
749 int ret;
750
751 if (nlctx->is_dump || nlctx->is_monitor)
752 nlctx->no_banner = false;
753 ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
754 if (ret < 0)
755 return ret;
756 nlctx->devname = get_dev_name(tb[ETHTOOL_A_LINKSTATE_HEADER]);
757 if (!dev_ok(nlctx))
758 return MNL_CB_OK;
759
760 if (tb[ETHTOOL_A_LINKSTATE_LINK]) {
761 uint8_t val = mnl_attr_get_u8(tb[ETHTOOL_A_LINKSTATE_LINK]);
762
763 print_banner(nlctx);
764 printf("\tLink detected: %s", val ? "yes" : "no");
765 linkstate_link_ext_state_print(tb);
766 printf("\n");
767 }
768
769 if (tb[ETHTOOL_A_LINKSTATE_SQI]) {
770 uint32_t val = mnl_attr_get_u32(tb[ETHTOOL_A_LINKSTATE_SQI]);
771
772 print_banner(nlctx);
773 printf("\tSQI: %u", val);
774
775 if (tb[ETHTOOL_A_LINKSTATE_SQI_MAX]) {
776 uint32_t max;
777
778 max = mnl_attr_get_u32(tb[ETHTOOL_A_LINKSTATE_SQI_MAX]);
779 printf("/%u\n", max);
780 } else {
781 printf("\n");
782 }
783 }
784
785 if (tb[ETHTOOL_A_LINKSTATE_EXT_DOWN_CNT]) {
786 uint32_t val;
787
788 val = mnl_attr_get_u32(tb[ETHTOOL_A_LINKSTATE_EXT_DOWN_CNT]);
789 printf("\tLink Down Events: %u\n", val);
790 }
791
792 return MNL_CB_OK;
793 }
794
wol_modes_cb(unsigned int idx,const char * name __maybe_unused,bool val,void * data)795 void wol_modes_cb(unsigned int idx, const char *name __maybe_unused, bool val,
796 void *data)
797 {
798 struct ethtool_wolinfo *wol = data;
799
800 if (idx >= 32)
801 return;
802 wol->supported |= (1U << idx);
803 if (val)
804 wol->wolopts |= (1U << idx);
805 }
806
wol_reply_cb(const struct nlmsghdr * nlhdr,void * data)807 int wol_reply_cb(const struct nlmsghdr *nlhdr, void *data)
808 {
809 const struct nlattr *tb[ETHTOOL_A_WOL_MAX + 1] = {};
810 DECLARE_ATTR_TB_INFO(tb);
811 struct nl_context *nlctx = data;
812 struct ethtool_wolinfo wol = {};
813 int ret;
814
815 if (nlctx->is_dump || nlctx->is_monitor)
816 nlctx->no_banner = false;
817 ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
818 if (ret < 0)
819 return ret;
820 nlctx->devname = get_dev_name(tb[ETHTOOL_A_WOL_HEADER]);
821 if (!dev_ok(nlctx))
822 return MNL_CB_OK;
823
824 if (tb[ETHTOOL_A_WOL_MODES])
825 walk_bitset(tb[ETHTOOL_A_WOL_MODES], NULL, wol_modes_cb, &wol);
826 if (tb[ETHTOOL_A_WOL_SOPASS]) {
827 unsigned int len;
828
829 len = mnl_attr_get_payload_len(tb[ETHTOOL_A_WOL_SOPASS]);
830 if (len != SOPASS_MAX)
831 fprintf(stderr, "invalid SecureOn password length %u (should be %u)\n",
832 len, SOPASS_MAX);
833 else
834 memcpy(wol.sopass,
835 mnl_attr_get_payload(tb[ETHTOOL_A_WOL_SOPASS]),
836 SOPASS_MAX);
837 }
838 print_banner(nlctx);
839 dump_wol(&wol);
840
841 return MNL_CB_OK;
842 }
843
msgmask_cb(unsigned int idx,const char * name __maybe_unused,bool val,void * data)844 void msgmask_cb(unsigned int idx, const char *name __maybe_unused, bool val,
845 void *data)
846 {
847 u32 *msg_mask = data;
848
849 if (idx >= 32)
850 return;
851 if (val)
852 *msg_mask |= (1U << idx);
853 }
854
msgmask_cb2(unsigned int idx __maybe_unused,const char * name,bool val,void * data __maybe_unused)855 void msgmask_cb2(unsigned int idx __maybe_unused, const char *name,
856 bool val, void *data __maybe_unused)
857 {
858 if (val)
859 printf(" %s", name);
860 }
861
debug_reply_cb(const struct nlmsghdr * nlhdr,void * data)862 int debug_reply_cb(const struct nlmsghdr *nlhdr, void *data)
863 {
864 const struct nlattr *tb[ETHTOOL_A_DEBUG_MAX + 1] = {};
865 DECLARE_ATTR_TB_INFO(tb);
866 const struct stringset *msgmask_strings = NULL;
867 struct nl_context *nlctx = data;
868 u32 msg_mask = 0;
869 int ret;
870
871 if (nlctx->is_dump || nlctx->is_monitor)
872 nlctx->no_banner = false;
873 ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
874 if (ret < 0)
875 return ret;
876 nlctx->devname = get_dev_name(tb[ETHTOOL_A_DEBUG_HEADER]);
877 if (!dev_ok(nlctx))
878 return MNL_CB_OK;
879
880 if (!tb[ETHTOOL_A_DEBUG_MSGMASK])
881 return MNL_CB_OK;
882 if (bitset_is_compact(tb[ETHTOOL_A_DEBUG_MSGMASK])) {
883 ret = netlink_init_ethnl2_socket(nlctx);
884 if (ret < 0)
885 return MNL_CB_OK;
886 msgmask_strings = global_stringset(ETH_SS_MSG_CLASSES,
887 nlctx->ethnl2_socket);
888 }
889
890 print_banner(nlctx);
891 walk_bitset(tb[ETHTOOL_A_DEBUG_MSGMASK], NULL, msgmask_cb, &msg_mask);
892 printf(" Current message level: 0x%08x (%u)\n"
893 " ",
894 msg_mask, msg_mask);
895 walk_bitset(tb[ETHTOOL_A_DEBUG_MSGMASK], msgmask_strings, msgmask_cb2,
896 NULL);
897 fputc('\n', stdout);
898
899 return MNL_CB_OK;
900 }
901
plca_cfg_reply_cb(const struct nlmsghdr * nlhdr,void * data)902 int plca_cfg_reply_cb(const struct nlmsghdr *nlhdr, void *data)
903 {
904 const struct nlattr *tb[ETHTOOL_A_PLCA_MAX + 1] = {};
905 DECLARE_ATTR_TB_INFO(tb);
906 struct nl_context *nlctx = data;
907 int ret;
908
909 if (nlctx->is_dump || nlctx->is_monitor)
910 nlctx->no_banner = false;
911 ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
912 if (ret < 0)
913 return ret;
914 nlctx->devname = get_dev_name(tb[ETHTOOL_A_PLCA_HEADER]);
915 if (!dev_ok(nlctx))
916 return MNL_CB_OK;
917
918 print_banner(nlctx);
919 printf("\tPLCA support: ");
920
921 if (tb[ETHTOOL_A_PLCA_VERSION]) {
922 uint16_t val = mnl_attr_get_u16(tb[ETHTOOL_A_PLCA_VERSION]);
923
924 printf("OPEN Alliance v%u.%u",
925 (unsigned int)((val >> 4) & 0xF),
926 (unsigned int)(val & 0xF));
927 } else
928 printf("non-standard");
929
930 printf("\n");
931
932 return MNL_CB_OK;
933 }
934
plca_status_reply_cb(const struct nlmsghdr * nlhdr,void * data)935 int plca_status_reply_cb(const struct nlmsghdr *nlhdr, void *data)
936 {
937 const struct nlattr *tb[ETHTOOL_A_PLCA_MAX + 1] = {};
938 DECLARE_ATTR_TB_INFO(tb);
939 struct nl_context *nlctx = data;
940 int ret;
941
942 if (nlctx->is_dump || nlctx->is_monitor)
943 nlctx->no_banner = false;
944 ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
945 if (ret < 0)
946 return ret;
947 nlctx->devname = get_dev_name(tb[ETHTOOL_A_PLCA_HEADER]);
948 if (!dev_ok(nlctx))
949 return MNL_CB_OK;
950
951 print_banner(nlctx);
952 printf("\tPLCA status: ");
953
954 if (tb[ETHTOOL_A_PLCA_STATUS]) {
955 uint8_t val = mnl_attr_get_u8(tb[ETHTOOL_A_PLCA_STATUS]);
956
957 printf(val ? "up" : "down");
958 } else
959 printf("unknown");
960
961 printf("\n");
962
963 return MNL_CB_OK;
964 }
965
gset_request(struct cmd_context * ctx,uint8_t msg_type,uint16_t hdr_attr,mnl_cb_t cb)966 static int gset_request(struct cmd_context *ctx, uint8_t msg_type,
967 uint16_t hdr_attr, mnl_cb_t cb)
968 {
969 struct nl_context *nlctx = ctx->nlctx;
970 struct nl_socket *nlsk = nlctx->ethnl_socket;
971 u32 flags;
972 int ret;
973
974 if (netlink_cmd_check(ctx, msg_type, true))
975 return 0;
976
977 flags = get_stats_flag(nlctx, msg_type, hdr_attr);
978
979 ret = nlsock_prep_get_request(nlsk, msg_type, hdr_attr, flags);
980 if (ret < 0)
981 return ret;
982 return nlsock_send_get_request(nlsk, cb);
983 }
984
nl_gset(struct cmd_context * ctx)985 int nl_gset(struct cmd_context *ctx)
986 {
987 int ret;
988
989 /* Check for the base set of commands */
990 if (netlink_cmd_check(ctx, ETHTOOL_MSG_LINKMODES_GET, true) ||
991 netlink_cmd_check(ctx, ETHTOOL_MSG_LINKINFO_GET, true) ||
992 netlink_cmd_check(ctx, ETHTOOL_MSG_WOL_GET, true) ||
993 netlink_cmd_check(ctx, ETHTOOL_MSG_DEBUG_GET, true) ||
994 netlink_cmd_check(ctx, ETHTOOL_MSG_LINKSTATE_GET, true))
995 return -EOPNOTSUPP;
996
997 ctx->nlctx->suppress_nlerr = 1;
998
999 ret = gset_request(ctx, ETHTOOL_MSG_LINKMODES_GET,
1000 ETHTOOL_A_LINKMODES_HEADER, linkmodes_reply_cb);
1001 if (ret == -ENODEV)
1002 return ret;
1003
1004 ret = gset_request(ctx, ETHTOOL_MSG_LINKINFO_GET,
1005 ETHTOOL_A_LINKINFO_HEADER, linkinfo_reply_cb);
1006 if (ret == -ENODEV)
1007 return ret;
1008
1009 ret = gset_request(ctx, ETHTOOL_MSG_WOL_GET, ETHTOOL_A_WOL_HEADER,
1010 wol_reply_cb);
1011 if (ret == -ENODEV)
1012 return ret;
1013
1014 ret = gset_request(ctx, ETHTOOL_MSG_PLCA_GET_CFG,
1015 ETHTOOL_A_PLCA_HEADER, plca_cfg_reply_cb);
1016 if (ret == -ENODEV)
1017 return ret;
1018
1019 ret = gset_request(ctx, ETHTOOL_MSG_DEBUG_GET, ETHTOOL_A_DEBUG_HEADER,
1020 debug_reply_cb);
1021 if (ret == -ENODEV)
1022 return ret;
1023
1024 ret = gset_request(ctx, ETHTOOL_MSG_LINKSTATE_GET,
1025 ETHTOOL_A_LINKSTATE_HEADER, linkstate_reply_cb);
1026 if (ret == -ENODEV)
1027 return ret;
1028
1029 ret = gset_request(ctx, ETHTOOL_MSG_PLCA_GET_STATUS,
1030 ETHTOOL_A_PLCA_HEADER, plca_status_reply_cb);
1031 if (ret == -ENODEV)
1032 return ret;
1033
1034 if (!ctx->nlctx->no_banner) {
1035 printf("No data available\n");
1036 return 75;
1037 }
1038
1039 return 0;
1040 }
1041
1042 /* SET_SETTINGS */
1043
1044 enum {
1045 WAKE_PHY_BIT = 0,
1046 WAKE_UCAST_BIT = 1,
1047 WAKE_MCAST_BIT = 2,
1048 WAKE_BCAST_BIT = 3,
1049 WAKE_ARP_BIT = 4,
1050 WAKE_MAGIC_BIT = 5,
1051 WAKE_MAGICSECURE_BIT = 6,
1052 WAKE_FILTER_BIT = 7,
1053 };
1054
1055 #define WAKE_ALL (WAKE_PHY | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_ARP | \
1056 WAKE_MAGIC | WAKE_MAGICSECURE)
1057
1058 static const struct lookup_entry_u8 port_values[] = {
1059 { .arg = "tp", .val = PORT_TP },
1060 { .arg = "aui", .val = PORT_AUI },
1061 { .arg = "mii", .val = PORT_MII },
1062 { .arg = "fibre", .val = PORT_FIBRE },
1063 { .arg = "bnc", .val = PORT_BNC },
1064 { .arg = "da", .val = PORT_DA },
1065 {}
1066 };
1067
1068 static const struct lookup_entry_u8 mdix_values[] = {
1069 { .arg = "auto", .val = ETH_TP_MDI_AUTO },
1070 { .arg = "on", .val = ETH_TP_MDI_X },
1071 { .arg = "off", .val = ETH_TP_MDI },
1072 {}
1073 };
1074
1075 static const struct error_parser_data xcvr_parser_data = {
1076 .err_msg = "deprecated parameter '%s' not supported by kernel\n",
1077 .ret_val = -EINVAL,
1078 .extra_args = 1,
1079 };
1080
1081 static const struct lookup_entry_u8 autoneg_values[] = {
1082 { .arg = "off", .val = AUTONEG_DISABLE },
1083 { .arg = "on", .val = AUTONEG_ENABLE },
1084 {}
1085 };
1086
1087 static const struct bitset_parser_data advertise_parser_data = {
1088 .no_mask = false,
1089 .force_hex = true,
1090 };
1091
1092 static const struct lookup_entry_u8 duplex_values[] = {
1093 { .arg = "half", .val = DUPLEX_HALF },
1094 { .arg = "full", .val = DUPLEX_FULL },
1095 {}
1096 };
1097
1098 static const struct lookup_entry_u8 master_slave_values[] = {
1099 { .arg = "preferred-master", .val = MASTER_SLAVE_CFG_MASTER_PREFERRED },
1100 { .arg = "preferred-slave", .val = MASTER_SLAVE_CFG_SLAVE_PREFERRED },
1101 { .arg = "forced-master", .val = MASTER_SLAVE_CFG_MASTER_FORCE },
1102 { .arg = "forced-slave", .val = MASTER_SLAVE_CFG_SLAVE_FORCE },
1103 {}
1104 };
1105
1106 char wol_bit_chars[WOL_MODE_COUNT] = {
1107 [WAKE_PHY_BIT] = 'p',
1108 [WAKE_UCAST_BIT] = 'u',
1109 [WAKE_MCAST_BIT] = 'm',
1110 [WAKE_BCAST_BIT] = 'b',
1111 [WAKE_ARP_BIT] = 'a',
1112 [WAKE_MAGIC_BIT] = 'g',
1113 [WAKE_MAGICSECURE_BIT] = 's',
1114 [WAKE_FILTER_BIT] = 'f',
1115 };
1116
1117 const struct char_bitset_parser_data wol_parser_data = {
1118 .bit_chars = wol_bit_chars,
1119 .nbits = WOL_MODE_COUNT,
1120 .reset_char = 'd',
1121 };
1122
1123 const struct byte_str_parser_data sopass_parser_data = {
1124 .min_len = 6,
1125 .max_len = 6,
1126 .delim = ':',
1127 };
1128
1129 static const struct bitset_parser_data msglvl_parser_data = {
1130 .no_mask = false,
1131 .force_hex = false,
1132 };
1133
1134 static const struct param_parser sset_params[] = {
1135 {
1136 .arg = "port",
1137 .group = ETHTOOL_MSG_LINKINFO_SET,
1138 .type = ETHTOOL_A_LINKINFO_PORT,
1139 .handler = nl_parse_lookup_u8,
1140 .handler_data = port_values,
1141 .min_argc = 1,
1142 },
1143 {
1144 .arg = "mdix",
1145 .group = ETHTOOL_MSG_LINKINFO_SET,
1146 .type = ETHTOOL_A_LINKINFO_TP_MDIX_CTRL,
1147 .handler = nl_parse_lookup_u8,
1148 .handler_data = mdix_values,
1149 .min_argc = 1,
1150 },
1151 {
1152 .arg = "phyad",
1153 .group = ETHTOOL_MSG_LINKINFO_SET,
1154 .type = ETHTOOL_A_LINKINFO_PHYADDR,
1155 .handler = nl_parse_direct_u8,
1156 .min_argc = 1,
1157 },
1158 {
1159 .arg = "xcvr",
1160 .group = ETHTOOL_MSG_LINKINFO_SET,
1161 .handler = nl_parse_error,
1162 .handler_data = &xcvr_parser_data,
1163 .min_argc = 1,
1164 },
1165 {
1166 .arg = "autoneg",
1167 .group = ETHTOOL_MSG_LINKMODES_SET,
1168 .type = ETHTOOL_A_LINKMODES_AUTONEG,
1169 .handler = nl_parse_lookup_u8,
1170 .handler_data = autoneg_values,
1171 .min_argc = 1,
1172 },
1173 {
1174 .arg = "advertise",
1175 .group = ETHTOOL_MSG_LINKMODES_SET,
1176 .type = ETHTOOL_A_LINKMODES_OURS,
1177 .handler = nl_parse_bitset,
1178 .handler_data = &advertise_parser_data,
1179 .min_argc = 1,
1180 },
1181 {
1182 .arg = "speed",
1183 .group = ETHTOOL_MSG_LINKMODES_SET,
1184 .type = ETHTOOL_A_LINKMODES_SPEED,
1185 .handler = nl_parse_direct_u32,
1186 .min_argc = 1,
1187 },
1188 {
1189 .arg = "lanes",
1190 .group = ETHTOOL_MSG_LINKMODES_SET,
1191 .type = ETHTOOL_A_LINKMODES_LANES,
1192 .handler = nl_parse_direct_u32,
1193 .min_argc = 1,
1194 },
1195 {
1196 .arg = "duplex",
1197 .group = ETHTOOL_MSG_LINKMODES_SET,
1198 .type = ETHTOOL_A_LINKMODES_DUPLEX,
1199 .handler = nl_parse_lookup_u8,
1200 .handler_data = duplex_values,
1201 .min_argc = 1,
1202 },
1203 {
1204 .arg = "master-slave",
1205 .group = ETHTOOL_MSG_LINKMODES_SET,
1206 .type = ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG,
1207 .handler = nl_parse_lookup_u8,
1208 .handler_data = master_slave_values,
1209 .min_argc = 1,
1210 },
1211 {
1212 .arg = "wol",
1213 .group = ETHTOOL_MSG_WOL_SET,
1214 .type = ETHTOOL_A_WOL_MODES,
1215 .handler = nl_parse_char_bitset,
1216 .handler_data = &wol_parser_data,
1217 .min_argc = 1,
1218 },
1219 {
1220 .arg = "sopass",
1221 .group = ETHTOOL_MSG_WOL_SET,
1222 .type = ETHTOOL_A_WOL_SOPASS,
1223 .handler = nl_parse_byte_str,
1224 .handler_data = &sopass_parser_data,
1225 .min_argc = 1,
1226 },
1227 {
1228 .arg = "msglvl",
1229 .group = ETHTOOL_MSG_DEBUG_SET,
1230 .type = ETHTOOL_A_DEBUG_MSGMASK,
1231 .handler = nl_parse_bitset,
1232 .handler_data = &msglvl_parser_data,
1233 .min_argc = 1,
1234 },
1235 {}
1236 };
1237
1238 /* Maximum number of request messages sent to kernel; must be equal to the
1239 * number of different .group values in sset_params[] array.
1240 */
1241 #define SSET_MAX_MSGS 4
1242
linkmodes_reply_advert_all_cb(const struct nlmsghdr * nlhdr,void * data)1243 static int linkmodes_reply_advert_all_cb(const struct nlmsghdr *nlhdr,
1244 void *data)
1245 {
1246 const struct nlattr *tb[ETHTOOL_A_LINKMODES_MAX + 1] = {};
1247 DECLARE_ATTR_TB_INFO(tb);
1248 struct nl_msg_buff *req_msgbuff = data;
1249 const struct nlattr *ours_attr;
1250 struct nlattr *req_bitset;
1251 uint32_t *supported_modes;
1252 unsigned int modes_count;
1253 unsigned int i;
1254 int ret;
1255
1256 ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
1257 if (ret < 0)
1258 return MNL_CB_ERROR;
1259 ours_attr = tb[ETHTOOL_A_LINKMODES_OURS];
1260 if (!ours_attr)
1261 return MNL_CB_ERROR;
1262 modes_count = bitset_get_count(tb[ETHTOOL_A_LINKMODES_OURS], &ret);
1263 if (ret < 0)
1264 return MNL_CB_ERROR;
1265 supported_modes = get_compact_bitset_mask(tb[ETHTOOL_A_LINKMODES_OURS]);
1266 if (!supported_modes)
1267 return MNL_CB_ERROR;
1268
1269 /* keep only "real" link modes */
1270 for (i = 0; i < modes_count; i++)
1271 if (!lm_class_match(i, LM_CLASS_REAL))
1272 supported_modes[i / 32] &= ~((uint32_t)1 << (i % 32));
1273
1274 req_bitset = ethnla_nest_start(req_msgbuff, ETHTOOL_A_LINKMODES_OURS);
1275 if (!req_bitset)
1276 return MNL_CB_ERROR;
1277
1278 if (ethnla_put_u32(req_msgbuff, ETHTOOL_A_BITSET_SIZE, modes_count) ||
1279 ethnla_put(req_msgbuff, ETHTOOL_A_BITSET_VALUE,
1280 DIV_ROUND_UP(modes_count, 32) * sizeof(uint32_t),
1281 supported_modes) ||
1282 ethnla_put(req_msgbuff, ETHTOOL_A_BITSET_MASK,
1283 DIV_ROUND_UP(modes_count, 32) * sizeof(uint32_t),
1284 supported_modes)) {
1285 ethnla_nest_cancel(req_msgbuff, req_bitset);
1286 return MNL_CB_ERROR;
1287 }
1288
1289 ethnla_nest_end(req_msgbuff, req_bitset);
1290 return MNL_CB_OK;
1291 }
1292
1293 /* For compatibility reasons with ioctl-based ethtool, when "autoneg on" is
1294 * specified without "advertise", "speed", "duplex" and "lanes", we need to
1295 * query the supported link modes from the kernel and advertise all the "real"
1296 * ones.
1297 */
nl_sset_compat_linkmodes(struct nl_context * nlctx,struct nl_msg_buff * msgbuff)1298 static int nl_sset_compat_linkmodes(struct nl_context *nlctx,
1299 struct nl_msg_buff *msgbuff)
1300 {
1301 const struct nlattr *tb[ETHTOOL_A_LINKMODES_MAX + 1] = {};
1302 DECLARE_ATTR_TB_INFO(tb);
1303 struct nl_socket *nlsk = nlctx->ethnl_socket;
1304 int ret;
1305
1306 ret = mnl_attr_parse(msgbuff->nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
1307 if (ret < 0)
1308 return ret;
1309 if (!tb[ETHTOOL_A_LINKMODES_AUTONEG] || tb[ETHTOOL_A_LINKMODES_OURS] ||
1310 tb[ETHTOOL_A_LINKMODES_SPEED] || tb[ETHTOOL_A_LINKMODES_DUPLEX] ||
1311 tb[ETHTOOL_A_LINKMODES_LANES])
1312 return 0;
1313 if (!mnl_attr_get_u8(tb[ETHTOOL_A_LINKMODES_AUTONEG]))
1314 return 0;
1315
1316 /* all conditions satisfied, create ETHTOOL_A_LINKMODES_OURS */
1317 if (netlink_cmd_check(nlctx->ctx, ETHTOOL_MSG_LINKMODES_GET, false) ||
1318 netlink_cmd_check(nlctx->ctx, ETHTOOL_MSG_LINKMODES_SET, false))
1319 return -EOPNOTSUPP;
1320 ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_LINKMODES_GET,
1321 ETHTOOL_A_LINKMODES_HEADER,
1322 ETHTOOL_FLAG_COMPACT_BITSETS);
1323 if (ret < 0)
1324 return ret;
1325 ret = nlsock_sendmsg(nlsk, NULL);
1326 if (ret < 0)
1327 return ret;
1328 return nlsock_process_reply(nlsk, linkmodes_reply_advert_all_cb,
1329 msgbuff);
1330 }
1331
nl_sset(struct cmd_context * ctx)1332 int nl_sset(struct cmd_context *ctx)
1333 {
1334 struct nl_msg_buff *msgbuffs[SSET_MAX_MSGS] = {};
1335 struct nl_context *nlctx = ctx->nlctx;
1336 unsigned int i;
1337 int ret;
1338
1339 nlctx->cmd = "-s";
1340 nlctx->argp = ctx->argp;
1341 nlctx->argc = ctx->argc;
1342 nlctx->devname = ctx->devname;
1343
1344 ret = nl_parser(nlctx, sset_params, NULL, PARSER_GROUP_MSG, msgbuffs);
1345 if (ret == -EOPNOTSUPP)
1346 return ret;
1347
1348 if (ret < 0) {
1349 ret = 1;
1350 goto out_free;
1351 }
1352
1353 for (i = 0; i < SSET_MAX_MSGS && msgbuffs[i]; i++) {
1354 struct nl_socket *nlsk = nlctx->ethnl_socket;
1355
1356 if (msgbuffs[i]->genlhdr->cmd == ETHTOOL_MSG_LINKMODES_SET) {
1357 ret = nl_sset_compat_linkmodes(nlctx, msgbuffs[i]);
1358 if (ret < 0)
1359 goto out_free;
1360 }
1361 ret = nlsock_sendmsg(nlsk, msgbuffs[i]);
1362 if (ret < 0)
1363 goto out_free;
1364 ret = nlsock_process_reply(nlsk, nomsg_reply_cb, NULL);
1365 if (ret < 0)
1366 goto out_free;
1367 }
1368
1369 out_free:
1370 for (i = 0; i < SSET_MAX_MSGS && msgbuffs[i]; i++) {
1371 msgbuff_done(msgbuffs[i]);
1372 free(msgbuffs[i]);
1373 }
1374 if (ret >= 0)
1375 return ret;
1376 return nlctx->exit_code ?: 75;
1377 }
1378