xref: /aosp_15_r20/external/ethtool/netlink/pause.c (revision 1b481fc3bb1b45d4cf28d1ec12969dc1055f555d)
1 /*
2  * pause.c - netlink implementation of pause commands
3  *
4  * Implementation of "ethtool -a <dev>" and "ethtool -A <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 "bitset.h"
16 #include "parser.h"
17 
18 /* PAUSE_GET */
19 
20 struct pause_autoneg_status {
21 	bool	pause;
22 	bool	asym_pause;
23 };
24 
pause_autoneg_walker(unsigned int idx,const char * name __maybe_unused,bool val,void * data)25 static void pause_autoneg_walker(unsigned int idx,
26 				 const char *name __maybe_unused, bool val,
27 				 void *data)
28 {
29 	struct pause_autoneg_status *status = data;
30 
31 	if (idx == ETHTOOL_LINK_MODE_Pause_BIT)
32 		status->pause = val;
33 	if (idx == ETHTOOL_LINK_MODE_Asym_Pause_BIT)
34 		status->asym_pause = val;
35 }
36 
pause_autoneg_cb(const struct nlmsghdr * nlhdr,void * data)37 static int pause_autoneg_cb(const struct nlmsghdr *nlhdr, void *data)
38 {
39 	const struct nlattr *tb[ETHTOOL_A_LINKMODES_MAX + 1] = {};
40 	DECLARE_ATTR_TB_INFO(tb);
41 	struct pause_autoneg_status ours = {};
42 	struct pause_autoneg_status peer = {};
43 	struct nl_context *nlctx = data;
44 	uint8_t rx_status = false;
45 	uint8_t tx_status = false;
46 	bool silent;
47 	int err_ret;
48 	int ret;
49 
50 	silent = nlctx->is_dump || nlctx->is_monitor;
51 	err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
52 	ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
53 	if (ret < 0)
54 		return err_ret;
55 
56 	if (!tb[ETHTOOL_A_LINKMODES_OURS] || !tb[ETHTOOL_A_LINKMODES_PEER])
57 		return MNL_CB_OK;
58 	ret = walk_bitset(tb[ETHTOOL_A_LINKMODES_OURS], NULL,
59 			  pause_autoneg_walker, &ours);
60 	if (ret < 0)
61 		return err_ret;
62 	ret = walk_bitset(tb[ETHTOOL_A_LINKMODES_PEER], NULL,
63 			  pause_autoneg_walker, &peer);
64 	if (ret < 0)
65 		return err_ret;
66 
67 	if (ours.pause && peer.pause) {
68 		rx_status = true;
69 		tx_status = true;
70 	} else if (ours.asym_pause && peer.asym_pause) {
71 		if (ours.pause)
72 			rx_status = true;
73 		else if (peer.pause)
74 			tx_status = true;
75 	}
76 
77 	open_json_object("negotiated");
78 	show_bool_val("rx", "RX negotiated: %s\n", &rx_status);
79 	show_bool_val("tx", "TX negotiated: %s\n", &tx_status);
80 	close_json_object();
81 
82 	return MNL_CB_OK;
83 }
84 
show_pause_autoneg_status(struct nl_context * nlctx)85 static int show_pause_autoneg_status(struct nl_context *nlctx)
86 {
87 	const char *saved_devname;
88 	int ret;
89 
90 	saved_devname = nlctx->ctx->devname;
91 	nlctx->ctx->devname = nlctx->devname;
92 	ret = netlink_init_ethnl2_socket(nlctx);
93 	if (ret < 0)
94 		goto out;
95 
96 	ret = nlsock_prep_get_request(nlctx->ethnl2_socket,
97 				      ETHTOOL_MSG_LINKMODES_GET,
98 				      ETHTOOL_A_LINKMODES_HEADER,
99 				      ETHTOOL_FLAG_COMPACT_BITSETS);
100 	if (ret < 0)
101 		goto out;
102 	ret = nlsock_send_get_request(nlctx->ethnl2_socket, pause_autoneg_cb);
103 
104 out:
105 	nlctx->ctx->devname = saved_devname;
106 	return ret;
107 }
108 
show_pause_stats(const struct nlattr * nest)109 static int show_pause_stats(const struct nlattr *nest)
110 {
111 	const struct nlattr *tb[ETHTOOL_A_PAUSE_STAT_MAX + 1] = {};
112 	DECLARE_ATTR_TB_INFO(tb);
113 	static const struct {
114 		unsigned int attr;
115 		char *name;
116 	} stats[] = {
117 		{ ETHTOOL_A_PAUSE_STAT_TX_FRAMES, "tx_pause_frames" },
118 		{ ETHTOOL_A_PAUSE_STAT_RX_FRAMES, "rx_pause_frames" },
119 	};
120 	bool header = false;
121 	unsigned int i;
122 	size_t n;
123 	int ret;
124 
125 	ret = mnl_attr_parse_nested(nest, attr_cb, &tb_info);
126 	if (ret < 0)
127 		return ret;
128 
129 	open_json_object("statistics");
130 	for (i = 0; i < ARRAY_SIZE(stats); i++) {
131 		char fmt[32];
132 
133 		if (!tb[stats[i].attr])
134 			continue;
135 
136 		if (!header && !is_json_context()) {
137 			printf("Statistics:\n");
138 			header = true;
139 		}
140 
141 		if (mnl_attr_validate(tb[stats[i].attr], MNL_TYPE_U64)) {
142 			fprintf(stderr, "malformed netlink message (statistic)\n");
143 			goto err_close_stats;
144 		}
145 
146 		n = snprintf(fmt, sizeof(fmt), "  %s: %%" PRIu64 "\n",
147 			     stats[i].name);
148 		if (n >= sizeof(fmt)) {
149 			fprintf(stderr, "internal error - malformed label\n");
150 			goto err_close_stats;
151 		}
152 
153 		print_u64(PRINT_ANY, stats[i].name, fmt,
154 			  mnl_attr_get_u64(tb[stats[i].attr]));
155 	}
156 	close_json_object();
157 
158 	return 0;
159 
160 err_close_stats:
161 	close_json_object();
162 	return -1;
163 }
164 
pause_reply_cb(const struct nlmsghdr * nlhdr,void * data)165 int pause_reply_cb(const struct nlmsghdr *nlhdr, void *data)
166 {
167 	const struct nlattr *tb[ETHTOOL_A_PAUSE_MAX + 1] = {};
168 	DECLARE_ATTR_TB_INFO(tb);
169 	struct nl_context *nlctx = data;
170 	bool silent;
171 	int err_ret;
172 	int ret;
173 
174 	silent = nlctx->is_dump || nlctx->is_monitor;
175 	err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
176 	ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
177 	if (ret < 0)
178 		return err_ret;
179 	nlctx->devname = get_dev_name(tb[ETHTOOL_A_PAUSE_HEADER]);
180 	if (!dev_ok(nlctx))
181 		return err_ret;
182 
183 	if (silent)
184 		print_nl();
185 
186 	open_json_object(NULL);
187 
188 	print_string(PRINT_ANY, "ifname", "Pause parameters for %s:\n",
189 		     nlctx->devname);
190 
191 	show_bool("autonegotiate", "Autonegotiate:\t%s\n",
192 		  tb[ETHTOOL_A_PAUSE_AUTONEG]);
193 	show_bool("rx", "RX:\t\t%s\n", tb[ETHTOOL_A_PAUSE_RX]);
194 	show_bool("tx", "TX:\t\t%s\n", tb[ETHTOOL_A_PAUSE_TX]);
195 
196 	if (!nlctx->is_monitor && tb[ETHTOOL_A_PAUSE_AUTONEG] &&
197 	    mnl_attr_get_u8(tb[ETHTOOL_A_PAUSE_AUTONEG])) {
198 		ret = show_pause_autoneg_status(nlctx);
199 		if (ret < 0)
200 			goto err_close_dev;
201 	}
202 	if (tb[ETHTOOL_A_PAUSE_STATS]) {
203 		ret = show_pause_stats(tb[ETHTOOL_A_PAUSE_STATS]);
204 		if (ret < 0)
205 			goto err_close_dev;
206 	}
207 	if (!silent)
208 		print_nl();
209 
210 	close_json_object();
211 
212 	return MNL_CB_OK;
213 
214 err_close_dev:
215 	close_json_object();
216 	return err_ret;
217 }
218 
219 static const struct lookup_entry_u32 stats_src_values[] = {
220 	{ .arg = "aggregate",	.val = ETHTOOL_MAC_STATS_SRC_AGGREGATE },
221 	{ .arg = "emac",	.val = ETHTOOL_MAC_STATS_SRC_EMAC },
222 	{ .arg = "pmac",	.val = ETHTOOL_MAC_STATS_SRC_PMAC },
223 	{}
224 };
225 
226 static const struct param_parser gpause_params[] = {
227 	{
228 		.arg		= "--src",
229 		.type		= ETHTOOL_A_PAUSE_STATS_SRC,
230 		.handler	= nl_parse_lookup_u32,
231 		.handler_data	= stats_src_values,
232 		.min_argc	= 1,
233 	},
234 	{}
235 };
236 
nl_gpause(struct cmd_context * ctx)237 int nl_gpause(struct cmd_context *ctx)
238 {
239 	struct nl_context *nlctx = ctx->nlctx;
240 	struct nl_socket *nlsk = nlctx->ethnl_socket;
241 	u32 flags;
242 	int ret;
243 
244 	if (netlink_cmd_check(ctx, ETHTOOL_MSG_PAUSE_GET, true))
245 		return -EOPNOTSUPP;
246 
247 	flags = get_stats_flag(nlctx, ETHTOOL_MSG_PAUSE_GET,
248 			       ETHTOOL_A_PAUSE_HEADER);
249 	ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_PAUSE_GET,
250 				      ETHTOOL_A_PAUSE_HEADER, flags);
251 	if (ret < 0)
252 		return ret;
253 
254 	nlctx->cmd = "-a";
255 	nlctx->argp = ctx->argp;
256 	nlctx->argc = ctx->argc;
257 	nlctx->devname = ctx->devname;
258 	nlsk = nlctx->ethnl_socket;
259 
260 	ret = nl_parser(nlctx, gpause_params, NULL, PARSER_GROUP_NONE, NULL);
261 	if (ret < 0)
262 		return 1;
263 
264 	new_json_obj(ctx->json);
265 	ret = nlsock_send_get_request(nlsk, pause_reply_cb);
266 	delete_json_obj();
267 	return ret;
268 }
269 
270 /* PAUSE_SET */
271 
272 static const struct param_parser spause_params[] = {
273 	{
274 		.arg		= "autoneg",
275 		.type		= ETHTOOL_A_PAUSE_AUTONEG,
276 		.handler	= nl_parse_u8bool,
277 		.min_argc	= 1,
278 	},
279 	{
280 		.arg		= "rx",
281 		.type		= ETHTOOL_A_PAUSE_RX,
282 		.handler	= nl_parse_u8bool,
283 		.min_argc	= 1,
284 	},
285 	{
286 		.arg		= "tx",
287 		.type		= ETHTOOL_A_PAUSE_TX,
288 		.handler	= nl_parse_u8bool,
289 		.min_argc	= 1,
290 	},
291 	{}
292 };
293 
nl_spause(struct cmd_context * ctx)294 int nl_spause(struct cmd_context *ctx)
295 {
296 	struct nl_context *nlctx = ctx->nlctx;
297 	struct nl_msg_buff *msgbuff;
298 	struct nl_socket *nlsk;
299 	int ret;
300 
301 	if (netlink_cmd_check(ctx, ETHTOOL_MSG_PAUSE_SET, false))
302 		return -EOPNOTSUPP;
303 
304 	nlctx->cmd = "-A";
305 	nlctx->argp = ctx->argp;
306 	nlctx->argc = ctx->argc;
307 	nlctx->devname = ctx->devname;
308 	nlsk = nlctx->ethnl_socket;
309 	msgbuff = &nlsk->msgbuff;
310 
311 	ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_PAUSE_SET,
312 		       NLM_F_REQUEST | NLM_F_ACK);
313 	if (ret < 0)
314 		return 2;
315 	if (ethnla_fill_header(msgbuff, ETHTOOL_A_PAUSE_HEADER,
316 			       ctx->devname, 0))
317 		return -EMSGSIZE;
318 
319 	ret = nl_parser(nlctx, spause_params, NULL, PARSER_GROUP_NONE, NULL);
320 	if (ret < 0)
321 		return 1;
322 
323 	ret = nlsock_sendmsg(nlsk, NULL);
324 	if (ret < 0)
325 		return 76;
326 	ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx);
327 	if (ret == 0)
328 		return 0;
329 	else
330 		return nlctx->exit_code ?: 76;
331 }
332