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