xref: /aosp_15_r20/external/ethtool/netlink/fec.c (revision 1b481fc3bb1b45d4cf28d1ec12969dc1055f555d)
1 /*
2  * fec.c - netlink implementation of FEC commands
3  *
4  * Implementation of "ethtool --show-fec <dev>" and
5  * "ethtool --set-fec <dev> ..."
6  */
7 
8 #include <errno.h>
9 #include <ctype.h>
10 #include <inttypes.h>
11 #include <string.h>
12 #include <strings.h>
13 #include <stdio.h>
14 
15 #include "../internal.h"
16 #include "../common.h"
17 #include "netlink.h"
18 #include "bitset.h"
19 #include "parser.h"
20 
21 /* FEC_GET */
22 
23 static void
fec_mode_walk(unsigned int idx,const char * name,bool val,void * data)24 fec_mode_walk(unsigned int idx, const char *name, bool val, void *data)
25 {
26 	bool *empty = data;
27 
28 	if (!val)
29 		return;
30 	if (empty)
31 		*empty = false;
32 
33 	/* Rename None to Off - in legacy ioctl None means "not supported"
34 	 * rather than supported but disabled.
35 	 */
36 	if (idx == ETHTOOL_LINK_MODE_FEC_NONE_BIT)
37 		name = "Off";
38 	/* Rename to match the ioctl letter case */
39 	else if (idx == ETHTOOL_LINK_MODE_FEC_BASER_BIT)
40 		name = "BaseR";
41 
42 	print_string(PRINT_ANY, NULL, " %s", name);
43 }
44 
fec_show_stats(const struct nlattr * nest)45 static int fec_show_stats(const struct nlattr *nest)
46 {
47 	const struct nlattr *tb[ETHTOOL_A_FEC_STAT_MAX + 1] = {};
48 	DECLARE_ATTR_TB_INFO(tb);
49 	static const struct {
50 		unsigned int attr;
51 		char *name;
52 	} stats[] = {
53 		{ ETHTOOL_A_FEC_STAT_CORRECTED, "corrected_blocks" },
54 		{ ETHTOOL_A_FEC_STAT_UNCORR, "uncorrectable_blocks" },
55 		{ ETHTOOL_A_FEC_STAT_CORR_BITS, "corrected_bits" },
56 	};
57 	bool header = false;
58 	unsigned int i;
59 	int ret;
60 
61 	ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
62 	if (ret < 0)
63 		return ret;
64 
65 	open_json_object("statistics");
66 	for (i = 0; i < ARRAY_SIZE(stats); i++) {
67 		uint64_t *vals;
68 		int lanes, l;
69 
70 		if (!tb[stats[i].attr] ||
71 		    !mnl_attr_get_payload_len(tb[stats[i].attr]))
72 			continue;
73 
74 		if (!header && !is_json_context()) {
75 			printf("Statistics:\n");
76 			header = true;
77 		}
78 
79 		if (mnl_attr_get_payload_len(tb[stats[i].attr]) % 8) {
80 			fprintf(stderr, "malformed netlink message (statistic)\n");
81 			goto err_close_stats;
82 		}
83 
84 		vals = mnl_attr_get_payload(tb[stats[i].attr]);
85 		lanes = mnl_attr_get_payload_len(tb[stats[i].attr]) / 8 - 1;
86 
87 		if (!is_json_context()) {
88 			fprintf(stdout, "  %s: %" PRIu64 "\n",
89 				stats[i].name, *vals++);
90 		} else {
91 			open_json_object(stats[i].name);
92 			print_u64(PRINT_JSON, "total", NULL, *vals++);
93 		}
94 
95 		if (lanes)
96 			open_json_array("lanes", "");
97 		for (l = 0; l < lanes; l++) {
98 			if (!is_json_context())
99 				fprintf(stdout, "    Lane %d: %" PRIu64 "\n",
100 					l, *vals++);
101 			else
102 				print_u64(PRINT_JSON, NULL, NULL, *vals++);
103 		}
104 		if (lanes)
105 			close_json_array("");
106 
107 		close_json_object();
108 	}
109 	close_json_object();
110 
111 	return 0;
112 
113 err_close_stats:
114 	close_json_object();
115 	return -1;
116 }
117 
fec_reply_cb(const struct nlmsghdr * nlhdr,void * data)118 int fec_reply_cb(const struct nlmsghdr *nlhdr, void *data)
119 {
120 	const struct nlattr *tb[ETHTOOL_A_FEC_MAX + 1] = {};
121 	DECLARE_ATTR_TB_INFO(tb);
122 	struct nl_context *nlctx = data;
123 	const struct stringset *lm_strings;
124 	const char *name;
125 	bool fa, empty;
126 	bool silent;
127 	int err_ret;
128 	u32 active;
129 	int ret;
130 
131 	silent = nlctx->is_dump || nlctx->is_monitor;
132 	err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
133 	ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
134 	if (ret < 0)
135 		return err_ret;
136 	nlctx->devname = get_dev_name(tb[ETHTOOL_A_FEC_HEADER]);
137 	if (!dev_ok(nlctx))
138 		return err_ret;
139 
140 	ret = netlink_init_ethnl2_socket(nlctx);
141 	if (ret < 0)
142 		return err_ret;
143 	lm_strings = global_stringset(ETH_SS_LINK_MODES, nlctx->ethnl2_socket);
144 
145 	active = 0;
146 	if (tb[ETHTOOL_A_FEC_ACTIVE])
147 		active = mnl_attr_get_u32(tb[ETHTOOL_A_FEC_ACTIVE]);
148 
149 	if (silent)
150 		print_nl();
151 
152 	open_json_object(NULL);
153 
154 	print_string(PRINT_ANY, "ifname", "FEC parameters for %s:\n",
155 		     nlctx->devname);
156 
157 	open_json_array("config", "Supported/Configured FEC encodings:");
158 	fa = tb[ETHTOOL_A_FEC_AUTO] && mnl_attr_get_u8(tb[ETHTOOL_A_FEC_AUTO]);
159 	if (fa)
160 		print_string(PRINT_ANY, NULL, " %s", "Auto");
161 	empty = !fa;
162 
163 	ret = walk_bitset(tb[ETHTOOL_A_FEC_MODES], lm_strings, fec_mode_walk,
164 			  &empty);
165 	if (ret < 0)
166 		goto err_close_dev;
167 	if (empty)
168 		print_string(PRINT_ANY, NULL, " %s", "None");
169 	close_json_array("\n");
170 
171 	open_json_array("active", "Active FEC encoding:");
172 	if (active) {
173 		name = get_string(lm_strings, active);
174 		if (name)
175 			/* Take care of renames */
176 			fec_mode_walk(active, name, true, NULL);
177 		else
178 			print_uint(PRINT_ANY, NULL, " BIT%u", active);
179 	} else {
180 		print_string(PRINT_ANY, NULL, " %s", "None");
181 	}
182 	close_json_array("\n");
183 
184 	if (tb[ETHTOOL_A_FEC_STATS]) {
185 		ret = fec_show_stats(tb[ETHTOOL_A_FEC_STATS]);
186 		if (ret < 0)
187 			goto err_close_dev;
188 	}
189 
190 	close_json_object();
191 
192 	return MNL_CB_OK;
193 
194 err_close_dev:
195 	close_json_object();
196 	return err_ret;
197 }
198 
nl_gfec(struct cmd_context * ctx)199 int nl_gfec(struct cmd_context *ctx)
200 {
201 	struct nl_context *nlctx = ctx->nlctx;
202 	struct nl_socket *nlsk = nlctx->ethnl_socket;
203 	u32 flags;
204 	int ret;
205 
206 	if (netlink_cmd_check(ctx, ETHTOOL_MSG_FEC_GET, true))
207 		return -EOPNOTSUPP;
208 	if (ctx->argc > 0) {
209 		fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
210 			*ctx->argp);
211 		return 1;
212 	}
213 
214 	flags = get_stats_flag(nlctx, ETHTOOL_MSG_FEC_GET,
215 			       ETHTOOL_A_FEC_HEADER);
216 	ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_FEC_GET,
217 				      ETHTOOL_A_FEC_HEADER, flags);
218 	if (ret < 0)
219 		return ret;
220 
221 	new_json_obj(ctx->json);
222 	ret = nlsock_send_get_request(nlsk, fec_reply_cb);
223 	delete_json_obj();
224 	return ret;
225 }
226 
227 /* FEC_SET */
228 
strupc(char * dst,const char * src)229 static void strupc(char *dst, const char *src)
230 {
231 	while (*src)
232 		*dst++ = toupper(*src++);
233 	*dst = '\0';
234 }
235 
fec_parse_bitset(struct nl_context * nlctx,uint16_t type,const void * data __maybe_unused,struct nl_msg_buff * msgbuff,void * dest)236 static int fec_parse_bitset(struct nl_context *nlctx, uint16_t type,
237 			    const void *data __maybe_unused,
238 			    struct nl_msg_buff *msgbuff, void *dest)
239 {
240 	struct nlattr *bitset_attr;
241 	struct nlattr *bits_attr;
242 	struct nlattr *bit_attr;
243 	char upper[ETH_GSTRING_LEN];
244 	bool fec_auto = false;
245 	int ret;
246 
247 	if (!type || dest) {
248 		fprintf(stderr, "ethtool (%s): internal error parsing '%s'\n",
249 			nlctx->cmd, nlctx->param);
250 		return -EFAULT;
251 	}
252 
253 	bitset_attr = ethnla_nest_start(msgbuff, type);
254 	if (!bitset_attr)
255 		return -EMSGSIZE;
256 	ret = -EMSGSIZE;
257 	if (ethnla_put_flag(msgbuff, ETHTOOL_A_BITSET_NOMASK, true))
258 		goto err;
259 	bits_attr = ethnla_nest_start(msgbuff, ETHTOOL_A_BITSET_BITS);
260 	if (!bits_attr)
261 		goto err;
262 
263 	while (nlctx->argc > 0) {
264 		const char *name = *nlctx->argp;
265 
266 		if (!strcmp(name, "--")) {
267 			nlctx->argp++;
268 			nlctx->argc--;
269 			break;
270 		}
271 
272 		if (!strcasecmp(name, "auto")) {
273 			fec_auto = true;
274 			goto next;
275 		}
276 		if (!strcasecmp(name, "off")) {
277 			name = "None";
278 		} else {
279 			strupc(upper, name);
280 			name = upper;
281 		}
282 
283 		ret = -EMSGSIZE;
284 		bit_attr = ethnla_nest_start(msgbuff,
285 					     ETHTOOL_A_BITSET_BITS_BIT);
286 		if (!bit_attr)
287 			goto err;
288 		if (ethnla_put_strz(msgbuff, ETHTOOL_A_BITSET_BIT_NAME, name))
289 			goto err;
290 		ethnla_nest_end(msgbuff, bit_attr);
291 
292 next:
293 		nlctx->argp++;
294 		nlctx->argc--;
295 	}
296 
297 	ethnla_nest_end(msgbuff, bits_attr);
298 	ethnla_nest_end(msgbuff, bitset_attr);
299 
300 	if (ethnla_put_u8(msgbuff, ETHTOOL_A_FEC_AUTO, fec_auto))
301 		goto err;
302 
303 	return 0;
304 err:
305 	ethnla_nest_cancel(msgbuff, bitset_attr);
306 	return ret;
307 }
308 
309 static const struct param_parser sfec_params[] = {
310 	{
311 		.arg		= "encoding",
312 		.type		= ETHTOOL_A_FEC_MODES,
313 		.handler	= fec_parse_bitset,
314 		.min_argc	= 1,
315 	},
316 	{}
317 };
318 
nl_sfec(struct cmd_context * ctx)319 int nl_sfec(struct cmd_context *ctx)
320 {
321 	struct nl_context *nlctx = ctx->nlctx;
322 	struct nl_msg_buff *msgbuff;
323 	struct nl_socket *nlsk;
324 	int ret;
325 
326 	if (netlink_cmd_check(ctx, ETHTOOL_MSG_FEC_SET, false))
327 		return -EOPNOTSUPP;
328 	if (!ctx->argc) {
329 		fprintf(stderr, "ethtool (--set-fec): parameters missing\n");
330 		return 1;
331 	}
332 
333 	nlctx->cmd = "--set-fec";
334 	nlctx->argp = ctx->argp;
335 	nlctx->argc = ctx->argc;
336 	nlctx->devname = ctx->devname;
337 	nlsk = nlctx->ethnl_socket;
338 	msgbuff = &nlsk->msgbuff;
339 
340 	ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_FEC_SET,
341 		       NLM_F_REQUEST | NLM_F_ACK);
342 	if (ret < 0)
343 		return 2;
344 	if (ethnla_fill_header(msgbuff, ETHTOOL_A_FEC_HEADER,
345 			       ctx->devname, 0))
346 		return -EMSGSIZE;
347 
348 	ret = nl_parser(nlctx, sfec_params, NULL, PARSER_GROUP_NONE, NULL);
349 	if (ret < 0)
350 		return 1;
351 
352 	ret = nlsock_sendmsg(nlsk, NULL);
353 	if (ret < 0)
354 		return 83;
355 	ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx);
356 	if (ret == 0)
357 		return 0;
358 	else
359 		return nlctx->exit_code ?: 83;
360 }
361