xref: /aosp_15_r20/external/ethtool/netlink/settings.c (revision 1b481fc3bb1b45d4cf28d1ec12969dc1055f555d)
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