xref: /aosp_15_r20/external/ethtool/netlink/stats.c (revision 1b481fc3bb1b45d4cf28d1ec12969dc1055f555d)
1 /*
2  * stats.c - netlink implementation of stats
3  *
4  * Implementation of "ethtool -S <dev> [--groups <types>] etc."
5  */
6 
7 #include <errno.h>
8 #include <ctype.h>
9 #include <inttypes.h>
10 #include <string.h>
11 #include <stdio.h>
12 
13 #include "../internal.h"
14 #include "../common.h"
15 #include "netlink.h"
16 #include "parser.h"
17 #include "strset.h"
18 
parse_rmon_hist_one(const char * grp_name,const struct nlattr * hist,const char * dir)19 static int parse_rmon_hist_one(const char *grp_name, const struct nlattr *hist,
20 			       const char *dir)
21 {
22 	const struct nlattr *tb[ETHTOOL_A_STATS_GRP_HIST_VAL + 1] = {};
23 	DECLARE_ATTR_TB_INFO(tb);
24 	unsigned long long val;
25 	unsigned int low, hi;
26 	int ret;
27 
28 	ret = mnl_attr_parse_nested(hist, attr_cb, &tb_info);
29 	if (ret < 0) {
30 		fprintf(stderr, "invalid kernel response - malformed histogram entry\n");
31 		return 1;
32 	}
33 
34 	if (!tb[ETHTOOL_A_STATS_GRP_HIST_BKT_LOW] ||
35 	    !tb[ETHTOOL_A_STATS_GRP_HIST_BKT_HI] ||
36 	    !tb[ETHTOOL_A_STATS_GRP_HIST_VAL]) {
37 		fprintf(stderr, "invalid kernel response - histogram entry missing attributes\n");
38 		return 1;
39 	}
40 
41 	low = mnl_attr_get_u32(tb[ETHTOOL_A_STATS_GRP_HIST_BKT_LOW]);
42 	hi = mnl_attr_get_u32(tb[ETHTOOL_A_STATS_GRP_HIST_BKT_HI]);
43 	val = mnl_attr_get_u64(tb[ETHTOOL_A_STATS_GRP_HIST_VAL]);
44 
45 	if (!is_json_context()) {
46 		fprintf(stdout, "%s-%s-etherStatsPkts", dir, grp_name);
47 
48 		if (low && hi) {
49 			fprintf(stdout, "%uto%uOctets: %llu\n", low, hi, val);
50 		} else if (hi) {
51 			fprintf(stdout, "%uOctets: %llu\n", hi, val);
52 		} else if (low) {
53 			fprintf(stdout, "%utoMaxOctets: %llu\n", low, val);
54 		} else {
55 			fprintf(stderr, "invalid kernel response - bad histogram entry bounds\n");
56 			return 1;
57 		}
58 	} else {
59 		open_json_object(NULL);
60 		print_uint(PRINT_JSON, "low", NULL, low);
61 		print_uint(PRINT_JSON, "high", NULL, hi);
62 		print_u64(PRINT_JSON, "val", NULL, val);
63 		close_json_object();
64 	}
65 
66 	return 0;
67 }
68 
parse_rmon_hist(const struct nlattr * grp,const char * grp_name,const char * name,const char * dir,unsigned int type)69 static int parse_rmon_hist(const struct nlattr *grp, const char *grp_name,
70 			   const char *name, const char *dir, unsigned int type)
71 {
72 	const struct nlattr *attr;
73 
74 	open_json_array(name, "");
75 
76 	mnl_attr_for_each_nested(attr, grp) {
77 		if (mnl_attr_get_type(attr) == type &&
78 		    parse_rmon_hist_one(grp_name, attr, dir))
79 			goto err_close_rmon;
80 	}
81 	close_json_array("");
82 
83 	return 0;
84 
85 err_close_rmon:
86 	close_json_array("");
87 	return 1;
88 }
89 
parse_grp(struct nl_context * nlctx,const struct nlattr * grp,const struct stringset * std_str)90 static int parse_grp(struct nl_context *nlctx, const struct nlattr *grp,
91 		     const struct stringset *std_str)
92 {
93 	const struct nlattr *tb[ETHTOOL_A_STATS_GRP_SS_ID + 1] = {};
94 	DECLARE_ATTR_TB_INFO(tb);
95 	bool hist_rx = false, hist_tx = false;
96 	const struct stringset *stat_str;
97 	const struct nlattr *attr, *stat;
98 	const char *std_name, *name;
99 	unsigned int ss_id, id, s;
100 	unsigned long long val;
101 	int ret;
102 
103 	ret = mnl_attr_parse_nested(grp, attr_cb, &tb_info);
104 	if (ret < 0)
105 		return 1;
106 
107 	if (!tb[ETHTOOL_A_STATS_GRP_ID])
108 		return 1;
109 	if (!tb[ETHTOOL_A_STATS_GRP_SS_ID])
110 		return 0;
111 
112 	id = mnl_attr_get_u32(tb[ETHTOOL_A_STATS_GRP_ID]);
113 	ss_id = mnl_attr_get_u32(tb[ETHTOOL_A_STATS_GRP_SS_ID]);
114 
115 	stat_str = global_stringset(ss_id, nlctx->ethnl2_socket);
116 
117 	std_name = get_string(std_str, id);
118 	open_json_object(std_name);
119 
120 	mnl_attr_for_each_nested(attr, grp) {
121 		switch (mnl_attr_get_type(attr)) {
122 		case ETHTOOL_A_STATS_GRP_STAT:
123 			break;
124 		case ETHTOOL_A_STATS_GRP_HIST_RX:
125 			hist_rx = true;
126 			continue;
127 		case ETHTOOL_A_STATS_GRP_HIST_TX:
128 			hist_tx = true;
129 			continue;
130 		default:
131 			continue;
132 		}
133 
134 		stat = mnl_attr_get_payload(attr);
135 		ret = mnl_attr_validate(stat, MNL_TYPE_U64);
136 		if (ret) {
137 			fprintf(stderr, "invalid kernel response - bad statistic entry\n");
138 			goto err_close_grp;
139 		}
140 		s = mnl_attr_get_type(stat);
141 		name = get_string(stat_str, s);
142 		if (!name || !name[0])
143 			continue;
144 
145 		if (!is_json_context())
146 			fprintf(stdout, "%s-%s: ", std_name, name);
147 
148 		val = mnl_attr_get_u64(stat);
149 		print_u64(PRINT_ANY, name, "%llu\n", val);
150 	}
151 
152 	if (hist_rx)
153 		parse_rmon_hist(grp, std_name, "rx-pktsNtoM", "rx",
154 				ETHTOOL_A_STATS_GRP_HIST_RX);
155 	if (hist_tx)
156 		parse_rmon_hist(grp, std_name, "tx-pktsNtoM", "tx",
157 				ETHTOOL_A_STATS_GRP_HIST_TX);
158 
159 	close_json_object();
160 
161 	return 0;
162 
163 err_close_grp:
164 	close_json_object();
165 	return 1;
166 }
167 
stats_reply_cb(const struct nlmsghdr * nlhdr,void * data)168 static int stats_reply_cb(const struct nlmsghdr *nlhdr, void *data)
169 {
170 	const struct nlattr *tb[ETHTOOL_A_STATS_MAX + 1] = {};
171 	DECLARE_ATTR_TB_INFO(tb);
172 	struct nl_context *nlctx = data;
173 	const struct stringset *std_str;
174 	const struct nlattr *attr;
175 	bool silent;
176 	int err_ret;
177 	int ret;
178 
179 	silent = nlctx->is_dump || nlctx->is_monitor;
180 	err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
181 	ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
182 	if (ret < 0)
183 		return err_ret;
184 	nlctx->devname = get_dev_name(tb[ETHTOOL_A_STATS_HEADER]);
185 	if (!dev_ok(nlctx))
186 		return err_ret;
187 
188 	ret = netlink_init_ethnl2_socket(nlctx);
189 	if (ret < 0)
190 		return err_ret;
191 	std_str = global_stringset(ETH_SS_STATS_STD, nlctx->ethnl2_socket);
192 
193 	if (silent)
194 		print_nl();
195 
196 	open_json_object(NULL);
197 
198 	print_string(PRINT_ANY, "ifname", "Standard stats for %s:\n",
199 		     nlctx->devname);
200 
201 	mnl_attr_for_each(attr, nlhdr, GENL_HDRLEN) {
202 		if (mnl_attr_get_type(attr) == ETHTOOL_A_STATS_GRP) {
203 			ret = parse_grp(nlctx, attr, std_str);
204 			if (ret)
205 				goto err_close_dev;
206 		}
207 	}
208 
209 	close_json_object();
210 
211 	return MNL_CB_OK;
212 
213 err_close_dev:
214 	close_json_object();
215 	return err_ret;
216 }
217 
218 static const struct bitset_parser_data stats_parser_data = {
219 	.no_mask	= true,
220 	.force_hex	= false,
221 };
222 
stats_parse_all_groups(struct nl_context * nlctx,uint16_t type,const void * data,struct nl_msg_buff * msgbuff,void * dest)223 static int stats_parse_all_groups(struct nl_context *nlctx, uint16_t type,
224 				  const void *data, struct nl_msg_buff *msgbuff,
225 				  void *dest)
226 {
227 	const struct stringset *std_str;
228 	struct nlattr *nest;
229 	int i, ret, nbits;
230 	uint32_t *bits;
231 
232 	if (data || dest)
233 		return -EFAULT;
234 
235 	/* ethnl2 and strset code already does caching */
236 	ret = netlink_init_ethnl2_socket(nlctx);
237 	if (ret < 0)
238 		return ret;
239 	std_str = global_stringset(ETH_SS_STATS_STD, nlctx->ethnl2_socket);
240 
241 	nbits = get_count(std_str);
242 	bits = calloc(DIV_ROUND_UP(nbits, 32), sizeof(uint32_t));
243 	if (!bits)
244 		return -ENOMEM;
245 
246 	for (i = 0; i < nbits; i++)
247 		bits[i / 32] |= 1U << (i % 32);
248 
249 	ret = -EMSGSIZE;
250 	nest = ethnla_nest_start(msgbuff, type);
251 	if (!nest)
252 		goto err_free;
253 
254 	if (ethnla_put_flag(msgbuff, ETHTOOL_A_BITSET_NOMASK, true) ||
255 	    ethnla_put_u32(msgbuff, ETHTOOL_A_BITSET_SIZE, nbits) ||
256 	    ethnla_put(msgbuff, ETHTOOL_A_BITSET_VALUE,
257 		       DIV_ROUND_UP(nbits, 32) * sizeof(uint32_t), bits))
258 		goto err_cancel;
259 
260 	ethnla_nest_end(msgbuff, nest);
261 	free(bits);
262 	return 0;
263 
264 err_cancel:
265 	ethnla_nest_cancel(msgbuff, nest);
266 err_free:
267 	free(bits);
268 	return ret;
269 }
270 
271 static const struct lookup_entry_u32 stats_src_values[] = {
272 	{ .arg = "aggregate",	.val = ETHTOOL_MAC_STATS_SRC_AGGREGATE },
273 	{ .arg = "emac",	.val = ETHTOOL_MAC_STATS_SRC_EMAC },
274 	{ .arg = "pmac",	.val = ETHTOOL_MAC_STATS_SRC_PMAC },
275 	{}
276 };
277 
278 static const struct param_parser stats_params[] = {
279 	{
280 		.arg		= "--groups",
281 		.type		= ETHTOOL_A_STATS_GROUPS,
282 		.handler	= nl_parse_bitset,
283 		.handler_data	= &stats_parser_data,
284 		.min_argc	= 1,
285 		.alt_group	= 1,
286 	},
287 	{
288 		.arg		= "--all-groups",
289 		.type		= ETHTOOL_A_STATS_GROUPS,
290 		.handler	= stats_parse_all_groups,
291 		.alt_group	= 1,
292 	},
293 	{
294 		.arg		= "--src",
295 		.type		= ETHTOOL_A_STATS_SRC,
296 		.handler	= nl_parse_lookup_u32,
297 		.handler_data	= stats_src_values,
298 		.min_argc	= 1,
299 	},
300 	{}
301 };
302 
nl_gstats(struct cmd_context * ctx)303 int nl_gstats(struct cmd_context *ctx)
304 {
305 	struct nl_context *nlctx = ctx->nlctx;
306 	struct nl_socket *nlsk = nlctx->ethnl_socket;
307 	int ret;
308 
309 	ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_STATS_GET,
310 				      ETHTOOL_A_STATS_HEADER, 0);
311 	if (ret < 0)
312 		return ret;
313 
314 	nlctx->cmd = "-S";
315 	nlctx->argp = ctx->argp;
316 	nlctx->argc = ctx->argc;
317 	nlctx->devname = ctx->devname;
318 	nlsk = nlctx->ethnl_socket;
319 
320 	ret = nl_parser(nlctx, stats_params, NULL, PARSER_GROUP_NONE, NULL);
321 	if (ret < 0)
322 		return 1;
323 
324 	new_json_obj(ctx->json);
325 	ret = nlsock_send_get_request(nlsk, stats_reply_cb);
326 	delete_json_obj();
327 	return ret;
328 }
329 
nl_gstats_chk(struct cmd_context * ctx)330 bool nl_gstats_chk(struct cmd_context *ctx)
331 {
332 	return ctx->argc;
333 }
334