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