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