• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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