xref: /aosp_15_r20/external/ethtool/netlink/monitor.c (revision 1b481fc3bb1b45d4cf28d1ec12969dc1055f555d)
1 /*
2  * monitor.c - netlink notification monitor
3  *
4  * Implementation of "ethtool --monitor" for watching netlink notifications.
5  */
6 
7 #include <errno.h>
8 
9 #include "../internal.h"
10 #include "netlink.h"
11 #include "nlsock.h"
12 #include "strset.h"
13 
14 static struct {
15 	uint8_t		cmd;
16 	mnl_cb_t	cb;
17 } monitor_callbacks[] = {
18 	{
19 		.cmd	= ETHTOOL_MSG_LINKMODES_NTF,
20 		.cb	= linkmodes_reply_cb,
21 	},
22 	{
23 		.cmd	= ETHTOOL_MSG_LINKINFO_NTF,
24 		.cb	= linkinfo_reply_cb,
25 	},
26 	{
27 		.cmd	= ETHTOOL_MSG_WOL_NTF,
28 		.cb	= wol_reply_cb,
29 	},
30 	{
31 		.cmd	= ETHTOOL_MSG_DEBUG_NTF,
32 		.cb	= debug_reply_cb,
33 	},
34 	{
35 		.cmd	= ETHTOOL_MSG_FEATURES_NTF,
36 		.cb	= features_reply_cb,
37 	},
38 	{
39 		.cmd	= ETHTOOL_MSG_PRIVFLAGS_NTF,
40 		.cb	= privflags_reply_cb,
41 	},
42 	{
43 		.cmd	= ETHTOOL_MSG_RINGS_NTF,
44 		.cb	= rings_reply_cb,
45 	},
46 	{
47 		.cmd	= ETHTOOL_MSG_CHANNELS_NTF,
48 		.cb	= channels_reply_cb,
49 	},
50 	{
51 		.cmd	= ETHTOOL_MSG_COALESCE_NTF,
52 		.cb	= coalesce_reply_cb,
53 	},
54 	{
55 		.cmd	= ETHTOOL_MSG_PAUSE_NTF,
56 		.cb	= pause_reply_cb,
57 	},
58 	{
59 		.cmd	= ETHTOOL_MSG_EEE_NTF,
60 		.cb	= eee_reply_cb,
61 	},
62 	{
63 		.cmd	= ETHTOOL_MSG_CABLE_TEST_NTF,
64 		.cb	= cable_test_ntf_cb,
65 	},
66 	{
67 		.cmd	= ETHTOOL_MSG_CABLE_TEST_TDR_NTF,
68 		.cb	= cable_test_tdr_ntf_cb,
69 	},
70 	{
71 		.cmd	= ETHTOOL_MSG_FEC_NTF,
72 		.cb	= fec_reply_cb,
73 	},
74 	{
75 		.cmd	= ETHTOOL_MSG_MODULE_NTF,
76 		.cb	= module_reply_cb,
77 	},
78 };
79 
clear_filter(struct nl_context * nlctx)80 static void clear_filter(struct nl_context *nlctx)
81 {
82 	unsigned int i;
83 
84 	for (i = 0; i < CMDMASK_WORDS; i++)
85 		nlctx->filter_cmds[i] = 0;
86 }
87 
test_filter_cmd(const struct nl_context * nlctx,unsigned int cmd)88 static bool test_filter_cmd(const struct nl_context *nlctx, unsigned int cmd)
89 {
90 	return nlctx->filter_cmds[cmd / 32] & (1U << (cmd % 32));
91 }
92 
set_filter_cmd(struct nl_context * nlctx,unsigned int cmd)93 static void set_filter_cmd(struct nl_context *nlctx, unsigned int cmd)
94 {
95 	nlctx->filter_cmds[cmd / 32] |= (1U << (cmd % 32));
96 }
97 
set_filter_all(struct nl_context * nlctx)98 static void set_filter_all(struct nl_context *nlctx)
99 {
100 	unsigned int i;
101 
102 	for (i = 0; i < ARRAY_SIZE(monitor_callbacks); i++)
103 		set_filter_cmd(nlctx, monitor_callbacks[i].cmd);
104 }
105 
monitor_any_cb(const struct nlmsghdr * nlhdr,void * data)106 static int monitor_any_cb(const struct nlmsghdr *nlhdr, void *data)
107 {
108 	const struct genlmsghdr *ghdr = (const struct genlmsghdr *)(nlhdr + 1);
109 	struct nl_context *nlctx = data;
110 	unsigned int i;
111 
112 	if (!test_filter_cmd(nlctx, ghdr->cmd))
113 		return MNL_CB_OK;
114 
115 	for (i = 0; i < MNL_ARRAY_SIZE(monitor_callbacks); i++)
116 		if (monitor_callbacks[i].cmd == ghdr->cmd)
117 			return monitor_callbacks[i].cb(nlhdr, data);
118 
119 	return MNL_CB_OK;
120 }
121 
122 struct monitor_option {
123 	const char	*pattern;
124 	uint8_t		cmd;
125 	uint32_t	info_mask;
126 };
127 
128 static struct monitor_option monitor_opts[] = {
129 	{
130 		.pattern	= "|--all",
131 		.cmd		= 0,
132 	},
133 	{
134 		.pattern	= "-s|--change",
135 		.cmd		= ETHTOOL_MSG_LINKINFO_NTF,
136 	},
137 	{
138 		.pattern	= "-s|--change",
139 		.cmd		= ETHTOOL_MSG_LINKMODES_NTF,
140 	},
141 	{
142 		.pattern	= "-s|--change",
143 		.cmd		= ETHTOOL_MSG_WOL_NTF,
144 	},
145 	{
146 		.pattern	= "-s|--change",
147 		.cmd		= ETHTOOL_MSG_DEBUG_NTF,
148 	},
149 	{
150 		.pattern	= "-k|--show-features|--show-offload|-K|--features|--offload",
151 		.cmd		= ETHTOOL_MSG_FEATURES_NTF,
152 	},
153 	{
154 		.pattern	= "--show-priv-flags|--set-priv-flags",
155 		.cmd		= ETHTOOL_MSG_PRIVFLAGS_NTF,
156 	},
157 	{
158 		.pattern	= "-g|--show-ring|-G|--set-ring",
159 		.cmd		= ETHTOOL_MSG_RINGS_NTF,
160 	},
161 	{
162 		.pattern	= "-l|--show-channels|-L|--set-channels",
163 		.cmd		= ETHTOOL_MSG_CHANNELS_NTF,
164 	},
165 	{
166 		.pattern	= "-c|--show-coalesce|-C|--coalesce",
167 		.cmd		= ETHTOOL_MSG_COALESCE_NTF,
168 	},
169 	{
170 		.pattern	= "-a|--show-pause|-A|--pause",
171 		.cmd		= ETHTOOL_MSG_PAUSE_NTF,
172 	},
173 	{
174 		.pattern	= "--show-eee|--set-eee",
175 		.cmd		= ETHTOOL_MSG_EEE_NTF,
176 	},
177 	{
178 		.pattern	= "--cable-test",
179 		.cmd		= ETHTOOL_MSG_CABLE_TEST_NTF,
180 	},
181 	{
182 		.pattern	= "--cable-test-tdr",
183 		.cmd		= ETHTOOL_MSG_CABLE_TEST_TDR_NTF,
184 	},
185 	{
186 		.pattern	= "--show-module|--set-module",
187 		.cmd		= ETHTOOL_MSG_MODULE_NTF,
188 	},
189 };
190 
pattern_match(const char * s,const char * pattern)191 static bool pattern_match(const char *s, const char *pattern)
192 {
193 	const char *opt = pattern;
194 	const char *next;
195 	int slen = strlen(s);
196 	int optlen;
197 
198 	do {
199 		next = opt;
200 		while (*next && *next != '|')
201 			next++;
202 		optlen = next - opt;
203 		if (slen == optlen && !strncmp(s, opt, optlen))
204 			return true;
205 
206 		opt = next;
207 		if (*opt == '|')
208 			opt++;
209 	} while (*opt);
210 
211 	return false;
212 }
213 
parse_monitor(struct cmd_context * ctx)214 static int parse_monitor(struct cmd_context *ctx)
215 {
216 	struct nl_context *nlctx = ctx->nlctx;
217 	char **argp = ctx->argp;
218 	int argc = ctx->argc;
219 	const char *opt = "";
220 	bool opt_found;
221 	unsigned int i;
222 
223 	if (*argp && argp[0][0] == '-') {
224 		opt = *argp;
225 		argp++;
226 		argc--;
227 	}
228 	opt_found = false;
229 	clear_filter(nlctx);
230 	for (i = 0; i < MNL_ARRAY_SIZE(monitor_opts); i++) {
231 		if (pattern_match(opt, monitor_opts[i].pattern)) {
232 			unsigned int cmd = monitor_opts[i].cmd;
233 
234 			if (!cmd)
235 				set_filter_all(nlctx);
236 			else
237 				set_filter_cmd(nlctx, cmd);
238 			opt_found = true;
239 		}
240 	}
241 	if (!opt_found) {
242 		fprintf(stderr, "monitoring for option '%s' not supported\n",
243 			*argp);
244 		return -1;
245 	}
246 
247 	if (*argp && strcmp(*argp, WILDCARD_DEVNAME))
248 		ctx->devname = *argp;
249 	return 0;
250 }
251 
nl_monitor(struct cmd_context * ctx)252 int nl_monitor(struct cmd_context *ctx)
253 {
254 	struct nl_context *nlctx;
255 	struct nl_socket *nlsk;
256 	uint32_t grpid;
257 	bool is_dev;
258 	int ret;
259 
260 	ret = netlink_init(ctx);
261 	if (ret < 0) {
262 		fprintf(stderr, "Netlink interface initialization failed, option --monitor not supported.\n");
263 		return ret;
264 	}
265 	nlctx = ctx->nlctx;
266 	nlsk = nlctx->ethnl_socket;
267 	grpid = nlctx->ethnl_mongrp;
268 	if (!grpid) {
269 		fprintf(stderr, "multicast group 'monitor' not found\n");
270 		return -EOPNOTSUPP;
271 	}
272 
273 	if (parse_monitor(ctx) < 0)
274 		return 1;
275 	is_dev = ctx->devname && strcmp(ctx->devname, WILDCARD_DEVNAME);
276 
277 	ret = preload_global_strings(nlsk);
278 	if (ret < 0)
279 		return ret;
280 	ret = mnl_socket_setsockopt(nlsk->sk, NETLINK_ADD_MEMBERSHIP,
281 				    &grpid, sizeof(grpid));
282 	if (ret < 0)
283 		return ret;
284 	if (is_dev) {
285 		ret = preload_perdev_strings(nlsk, ctx->devname);
286 		if (ret < 0)
287 			goto out_strings;
288 	}
289 
290 	nlctx->filter_devname = ctx->devname;
291 	nlctx->is_monitor = true;
292 	nlsk->port = 0;
293 	nlsk->seq = 0;
294 
295 	fputs("listening...\n", stdout);
296 	fflush(stdout);
297 	ret = nlsock_process_reply(nlsk, monitor_any_cb, nlctx);
298 
299 out_strings:
300 	cleanup_all_strings();
301 	return ret;
302 }
303 
nl_monitor_usage(void)304 void nl_monitor_usage(void)
305 {
306 	unsigned int i;
307 	const char *p;
308 
309 	fputs("        ethtool --monitor               Show kernel notifications\n",
310 	      stdout);
311 	fputs("                ( [ --all ]", stdout);
312 	for (i = 1; i < MNL_ARRAY_SIZE(monitor_opts); i++) {
313 		if (!strcmp(monitor_opts[i].pattern, monitor_opts[i - 1].pattern))
314 			continue;
315 		fputs("\n                  | ", stdout);
316 		for (p = monitor_opts[i].pattern; *p; p++)
317 			if (*p == '|')
318 				fputs(" | ", stdout);
319 			else
320 				fputc(*p, stdout);
321 	}
322 	fputs(" )\n", stdout);
323 	fputs("                [ DEVNAME | * ]\n", stdout);
324 }
325