xref: /aosp_15_r20/external/ethtool/netlink/nlsock.c (revision 1b481fc3bb1b45d4cf28d1ec12969dc1055f555d)
1 /*
2  * nlsock.c - netlink socket
3  *
4  * Data structure and code for netlink socket abstraction.
5  */
6 
7 #include <stdint.h>
8 #include <errno.h>
9 
10 #include "../internal.h"
11 #include "nlsock.h"
12 #include "netlink.h"
13 #include "prettymsg.h"
14 
15 #define NLSOCK_RECV_BUFFSIZE 65536
16 
ctrl_msg_summary(const struct nlmsghdr * nlhdr)17 static void ctrl_msg_summary(const struct nlmsghdr *nlhdr)
18 {
19 	const struct nlmsgerr *nlerr;
20 
21 	switch (nlhdr->nlmsg_type) {
22 	case NLMSG_NOOP:
23 		printf(" noop\n");
24 		break;
25 	case NLMSG_ERROR:
26 		printf(" error");
27 		if (nlhdr->nlmsg_len < NLMSG_HDRLEN + sizeof(*nlerr)) {
28 			printf(" malformed\n");
29 			break;
30 		}
31 		nlerr = mnl_nlmsg_get_payload(nlhdr);
32 		printf(" errno=%d\n", nlerr->error);
33 		break;
34 	case NLMSG_DONE:
35 		printf(" done\n");
36 		break;
37 	case NLMSG_OVERRUN:
38 		printf(" overrun\n");
39 		break;
40 	default:
41 		printf(" type %u\n", nlhdr->nlmsg_type);
42 		break;
43 	}
44 }
45 
genl_msg_summary(const struct nlmsghdr * nlhdr,int ethnl_fam,bool outgoing,bool pretty)46 static void genl_msg_summary(const struct nlmsghdr *nlhdr, int ethnl_fam,
47 			     bool outgoing, bool pretty)
48 {
49 	if (nlhdr->nlmsg_type == ethnl_fam) {
50 		const struct pretty_nlmsg_desc *msg_desc;
51 		const struct genlmsghdr *ghdr;
52 		unsigned int n_desc;
53 
54 		printf(" ethool");
55 		if (nlhdr->nlmsg_len < NLMSG_HDRLEN + GENL_HDRLEN) {
56 			printf(" malformed\n");
57 			return;
58 		}
59 		ghdr = mnl_nlmsg_get_payload(nlhdr);
60 
61 		msg_desc = outgoing ? ethnl_umsg_desc : ethnl_kmsg_desc;
62 		n_desc = outgoing ? ethnl_umsg_n_desc : ethnl_kmsg_n_desc;
63 		if (ghdr->cmd < n_desc && msg_desc[ghdr->cmd].name)
64 			printf(" %s", msg_desc[ghdr->cmd].name);
65 		else
66 			printf(" cmd %u", ghdr->cmd);
67 		fputc('\n', stdout);
68 
69 		if (pretty)
70 			pretty_print_genlmsg(nlhdr, msg_desc, n_desc, 0);
71 		return;
72 	}
73 
74 	if (nlhdr->nlmsg_type == GENL_ID_CTRL) {
75 		printf(" genl-ctrl\n");
76 		if (pretty)
77 			pretty_print_genlmsg(nlhdr, genlctrl_msg_desc,
78 					     genlctrl_msg_n_desc, 0);
79 	} else {
80 		fputc('\n', stdout);
81 		if (pretty)
82 			pretty_print_genlmsg(nlhdr, NULL, 0, 0);
83 	}
84 }
85 
rtnl_msg_summary(const struct nlmsghdr * nlhdr,bool pretty)86 static void rtnl_msg_summary(const struct nlmsghdr *nlhdr, bool pretty)
87 {
88 	unsigned int type = nlhdr->nlmsg_type;
89 
90 	if (type < rtnl_msg_n_desc && rtnl_msg_desc[type].name)
91 		printf(" %s\n", rtnl_msg_desc[type].name);
92 	else
93 		printf(" type %u\n", type);
94 
95 	if (pretty)
96 		pretty_print_rtnlmsg(nlhdr, 0);
97 }
98 
debug_msg_summary(const struct nlmsghdr * nlhdr,int ethnl_fam,int nl_fam,bool outgoing,bool pretty)99 static void debug_msg_summary(const struct nlmsghdr *nlhdr, int ethnl_fam,
100 			      int nl_fam, bool outgoing, bool pretty)
101 {
102 	printf("    msg length %u", nlhdr->nlmsg_len);
103 
104 	if (nlhdr->nlmsg_type < NLMSG_MIN_TYPE) {
105 		ctrl_msg_summary(nlhdr);
106 		return;
107 	}
108 
109 	switch(nl_fam) {
110 	case NETLINK_GENERIC:
111 		genl_msg_summary(nlhdr, ethnl_fam, outgoing, pretty);
112 		break;
113 	case NETLINK_ROUTE:
114 		rtnl_msg_summary(nlhdr, pretty);
115 		break;
116 	default:
117 		fputc('\n', stdout);
118 		break;
119 	}
120 }
121 
debug_msg(struct nl_socket * nlsk,const void * msg,unsigned int len,bool outgoing)122 static void debug_msg(struct nl_socket *nlsk, const void *msg, unsigned int len,
123 		      bool outgoing)
124 {
125 	const char *dirlabel = outgoing ? "sending" : "received";
126 	uint32_t debug = nlsk->nlctx->ctx->debug;
127 	const struct nlmsghdr *nlhdr = msg;
128 	bool summary, dump, pretty;
129 	const char *nl_fam_label;
130 	int left = len;
131 
132 	summary = debug_on(debug, DEBUG_NL_MSGS);
133 	dump = debug_on(debug,
134 			outgoing ? DEBUG_NL_DUMP_SND : DEBUG_NL_DUMP_RCV);
135 	pretty = debug_on(debug, DEBUG_NL_PRETTY_MSG);
136 	if (!summary && !dump)
137 		return;
138 	switch(nlsk->nl_fam) {
139 	case NETLINK_GENERIC:
140 		nl_fam_label = "genetlink";
141 		break;
142 	case NETLINK_ROUTE:
143 		nl_fam_label = "rtnetlink";
144 		break;
145 	default:
146 		nl_fam_label = "netlink";
147 		break;
148 	}
149 	printf("%s %s packet (%u bytes):\n", dirlabel, nl_fam_label, len);
150 
151 	while (nlhdr && left > 0 && mnl_nlmsg_ok(nlhdr, left)) {
152 		if (summary)
153 			debug_msg_summary(nlhdr, nlsk->nlctx->ethnl_fam,
154 					  nlsk->nl_fam, outgoing, pretty);
155 		if (dump)
156 			mnl_nlmsg_fprintf(stdout, nlhdr, nlhdr->nlmsg_len,
157 					  GENL_HDRLEN);
158 
159 		nlhdr = mnl_nlmsg_next(nlhdr, &left);
160 	}
161 }
162 
163 /**
164  * nlsock_process_ack() - process NLMSG_ERROR message from kernel
165  * @nlhdr:          pointer to netlink header
166  * @len:            length of received data (from nlhdr to end of buffer)
167  * @suppress_nlerr: 0 show all errors, 1 silence -EOPNOTSUPP, 2 silence all
168  *
169  * Return: error code extracted from the message
170  */
nlsock_process_ack(struct nlmsghdr * nlhdr,unsigned long len,unsigned int suppress_nlerr,bool pretty)171 static int nlsock_process_ack(struct nlmsghdr *nlhdr, unsigned long len,
172 			      unsigned int suppress_nlerr, bool pretty)
173 {
174 	const struct nlattr *tb[NLMSGERR_ATTR_MAX + 1] = {};
175 	DECLARE_ATTR_TB_INFO(tb);
176 	unsigned int err_offset = 0;
177 	unsigned int tlv_offset;
178 	struct nlmsgerr *nlerr;
179 	bool silent;
180 
181 	if ((len < NLMSG_HDRLEN + sizeof(*nlerr)) || (len < nlhdr->nlmsg_len))
182 		return -EFAULT;
183 	nlerr = mnl_nlmsg_get_payload(nlhdr);
184 	silent = suppress_nlerr >= 2 ||
185 		(suppress_nlerr && nlerr->error == -EOPNOTSUPP);
186 	if (silent || !(nlhdr->nlmsg_flags & NLM_F_ACK_TLVS))
187 		goto tlv_done;
188 
189 	tlv_offset = sizeof(*nlerr);
190 	if (!(nlhdr->nlmsg_flags & NLM_F_CAPPED))
191 		tlv_offset += MNL_ALIGN(mnl_nlmsg_get_payload_len(&nlerr->msg));
192 	if (mnl_attr_parse(nlhdr, tlv_offset, attr_cb, &tb_info) < 0)
193 		goto tlv_done;
194 
195 	if (tb[NLMSGERR_ATTR_MSG]) {
196 		const char *msg = mnl_attr_get_str(tb[NLMSGERR_ATTR_MSG]);
197 
198 		fprintf(stderr, "netlink %s: %s",
199 			nlerr->error ? "error" : "warning", msg);
200 		if (!pretty && tb[NLMSGERR_ATTR_OFFS])
201 			fprintf(stderr, " (offset %u)",
202 				mnl_attr_get_u32(tb[NLMSGERR_ATTR_OFFS]));
203 		fputc('\n', stderr);
204 	}
205 	if (tb[NLMSGERR_ATTR_OFFS])
206 		err_offset = mnl_attr_get_u32(tb[NLMSGERR_ATTR_OFFS]);
207 
208 tlv_done:
209 	if (nlerr->error && !silent) {
210 		errno = -nlerr->error;
211 		perror("netlink error");
212 	}
213 	if (pretty && !(nlhdr->nlmsg_flags & NLM_F_CAPPED) &&
214 	    nlhdr->nlmsg_len >= NLMSG_HDRLEN + nlerr->msg.nlmsg_len) {
215 		fprintf(stderr, "offending message%s:\n",
216 			err_offset ? " and attribute" : "");
217 		pretty_print_genlmsg(&nlerr->msg, ethnl_umsg_desc,
218 				     ethnl_umsg_n_desc, err_offset);
219 	}
220 	return nlerr->error;
221 }
222 
223 /**
224  * nlsock_process_reply() - process reply packet(s) from kernel
225  * @nlsk:     netlink socket to read from
226  * @reply_cb: callback to process each message
227  * @data:     pointer passed as argument to @reply_cb callback
228  *
229  * Read packets from kernel and pass reply messages to @reply_cb callback
230  * until an error is encountered or NLMSG_ERR message is received. In the
231  * latter case, return value is the error code extracted from it.
232  *
233  * Return: 0 on success or negative error code
234  */
nlsock_process_reply(struct nl_socket * nlsk,mnl_cb_t reply_cb,void * data)235 int nlsock_process_reply(struct nl_socket *nlsk, mnl_cb_t reply_cb, void *data)
236 {
237 	struct nl_msg_buff *msgbuff = &nlsk->msgbuff;
238 	struct nlmsghdr *nlhdr;
239 	ssize_t len;
240 	char *buff;
241 	int ret;
242 
243 	ret = msgbuff_realloc(msgbuff, NLSOCK_RECV_BUFFSIZE);
244 	if (ret < 0)
245 		return ret;
246 	buff = msgbuff->buff;
247 
248 	do {
249 		len = mnl_socket_recvfrom(nlsk->sk, buff, msgbuff->size);
250 		if (len <= 0)
251 			return (len ? -EFAULT : 0);
252 		debug_msg(nlsk, buff, len, false);
253 		if (len < NLMSG_HDRLEN)
254 			return -EFAULT;
255 
256 		nlhdr = (struct nlmsghdr *)buff;
257 		if (nlhdr->nlmsg_type == NLMSG_ERROR) {
258 			unsigned int suppress = nlsk->nlctx->suppress_nlerr;
259 			bool pretty;
260 
261 			pretty = debug_on(nlsk->nlctx->ctx->debug,
262 					  DEBUG_NL_PRETTY_MSG);
263 			return nlsock_process_ack(nlhdr, len, suppress, pretty);
264 		}
265 
266 		msgbuff->nlhdr = nlhdr;
267 		msgbuff->genlhdr = mnl_nlmsg_get_payload(nlhdr);
268 		msgbuff->payload =
269 			mnl_nlmsg_get_payload_offset(nlhdr, GENL_HDRLEN);
270 		ret = mnl_cb_run(buff, len, nlsk->seq, nlsk->port, reply_cb,
271 				 data);
272 	} while (ret > 0);
273 
274 	return ret;
275 }
276 
nlsock_prep_get_request(struct nl_socket * nlsk,unsigned int nlcmd,uint16_t hdr_attrtype,u32 flags)277 int nlsock_prep_get_request(struct nl_socket *nlsk, unsigned int nlcmd,
278 			    uint16_t hdr_attrtype, u32 flags)
279 {
280 	unsigned int nlm_flags = NLM_F_REQUEST | NLM_F_ACK;
281 	struct nl_context *nlctx = nlsk->nlctx;
282 	const char *devname = nlctx->ctx->devname;
283 	int ret;
284 
285 	if (devname && !strcmp(devname, WILDCARD_DEVNAME)) {
286 		devname = NULL;
287 		nlm_flags |= NLM_F_DUMP;
288 	}
289 	nlctx->is_dump = !devname;
290 
291 	ret = msg_init(nlctx, &nlsk->msgbuff, nlcmd, nlm_flags);
292 	if (ret < 0)
293 		return ret;
294 	if (ethnla_fill_header(&nlsk->msgbuff, hdr_attrtype, devname, flags))
295 		return -EMSGSIZE;
296 
297 	return 0;
298 }
299 
300 #ifndef TEST_ETHTOOL
301 /**
302  * nlsock_sendmsg() - send a netlink message to kernel
303  * @nlsk:    netlink socket
304  * @altbuff: alternative message buffer; if null, use buffer embedded in @nlsk
305  *
306  * Return: sent size or negative error code
307  */
nlsock_sendmsg(struct nl_socket * nlsk,struct nl_msg_buff * altbuff)308 ssize_t nlsock_sendmsg(struct nl_socket *nlsk, struct nl_msg_buff *altbuff)
309 {
310 	struct nl_msg_buff *msgbuff = altbuff ?: &nlsk->msgbuff;
311 	struct nlmsghdr *nlhdr = msgbuff->nlhdr;
312 
313 	nlhdr->nlmsg_seq = ++nlsk->seq;
314 	debug_msg(nlsk, msgbuff->buff, nlhdr->nlmsg_len, true);
315 	return mnl_socket_sendto(nlsk->sk, nlhdr, nlhdr->nlmsg_len);
316 }
317 #endif
318 
319 /**
320  * nlsock_send_get_request() - send request and process reply
321  * @nlsk: netlink socket
322  * @cb:   callback to process reply message(s)
323  *
324  * This simple helper only handles the most common case when the embedded
325  * message buffer is sent and @cb takes netlink context (struct nl_context)
326  * as last argument.
327  */
nlsock_send_get_request(struct nl_socket * nlsk,mnl_cb_t cb)328 int nlsock_send_get_request(struct nl_socket *nlsk, mnl_cb_t cb)
329 {
330 	int ret;
331 
332 	ret = nlsock_sendmsg(nlsk, NULL);
333 	if (ret < 0)
334 		goto err;
335 	ret = nlsock_process_reply(nlsk, cb, nlsk->nlctx);
336 	if (ret == 0)
337 		return 0;
338 err:
339 	return nlsk->nlctx->exit_code ?: 1;
340 }
341 
342 /**
343  * nlsock_init() - allocate and initialize netlink socket
344  * @nlctx:  netlink context
345  * @__nlsk: store pointer to the allocated socket here
346  * @nl_fam: netlink family (e.g. NETLINK_GENERIC or NETLINK_ROUTE)
347  *
348  * Allocate and initialize netlink socket and its embedded message buffer.
349  * Cleans up on error, caller is responsible for destroying the socket with
350  * nlsock_done() on success.
351  *
352  * Return: 0 on success or negative error code
353  */
nlsock_init(struct nl_context * nlctx,struct nl_socket ** __nlsk,int nl_fam)354 int nlsock_init(struct nl_context *nlctx, struct nl_socket **__nlsk, int nl_fam)
355 {
356 	struct nl_socket *nlsk;
357 	int val;
358 	int ret;
359 
360 	nlsk = calloc(1, sizeof(*nlsk));
361 	if (!nlsk)
362 		return -ENOMEM;
363 	nlsk->nlctx = nlctx;
364 	msgbuff_init(&nlsk->msgbuff);
365 
366 	ret = -ECONNREFUSED;
367 	nlsk->sk = mnl_socket_open(nl_fam);
368 	if (!nlsk->sk)
369 		goto out_msgbuff;
370 	val = 1;
371 	mnl_socket_setsockopt(nlsk->sk, NETLINK_EXT_ACK, &val, sizeof(val));
372 	ret = mnl_socket_bind(nlsk->sk, 0, MNL_SOCKET_AUTOPID);
373 	if (ret < 0)
374 		goto out_close;
375 	nlsk->port = mnl_socket_get_portid(nlsk->sk);
376 	nlsk->nl_fam = nl_fam;
377 
378 	*__nlsk = nlsk;
379 	return 0;
380 
381 out_close:
382 	if (nlsk->sk)
383 		mnl_socket_close(nlsk->sk);
384 out_msgbuff:
385 	msgbuff_done(&nlsk->msgbuff);
386 	free(nlsk);
387 	return ret;
388 }
389 
390 /**
391  * nlsock_done() - destroy a netlink socket
392  * @nlsk: netlink socket
393  *
394  * Close the socket and free the structure and related data.
395  */
nlsock_done(struct nl_socket * nlsk)396 void nlsock_done(struct nl_socket *nlsk)
397 {
398 	if (!nlsk)
399 		return;
400 	if (nlsk->sk)
401 		mnl_socket_close(nlsk->sk);
402 	msgbuff_done(&nlsk->msgbuff);
403 	memset(nlsk, '\0', sizeof(*nlsk));
404 	free(nlsk);
405 }
406