xref: /OK3568_Linux_fs/app/forlinx/quectelCM/libmnl/nlmsg.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /*
2  * (C) 2008-2010 by Pablo Neira Ayuso <pablo@netfilter.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published
6  * by the Free Software Foundation; either version 2.1 of the License, or
7  * (at your option) any later version.
8  */
9 #include <stdbool.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <ctype.h>
13 #include <errno.h>
14 #include <string.h>
15 
16 #include "libmnl.h"
17 
18 /**
19  * \defgroup nlmsg Netlink message helpers
20  *
21  * Netlink message:
22  * \verbatim
23 	|<----------------- 4 bytes ------------------->|
24 	|<----- 2 bytes ------>|<------- 2 bytes ------>|
25 	|-----------------------------------------------|
26 	|      Message length (including header)        |
27 	|-----------------------------------------------|
28 	|     Message type     |     Message flags      |
29 	|-----------------------------------------------|
30 	|           Message sequence number             |
31 	|-----------------------------------------------|
32 	|                 Netlink PortID                |
33 	|-----------------------------------------------|
34 	|                                               |
35 	.                   Payload                     .
36 	|_______________________________________________|
37 \endverbatim
38  *
39  * There is usually an extra header after the the Netlink header (at the
40  * beginning of the payload). This extra header is specific of the Netlink
41  * subsystem. After this extra header, it comes the sequence of attributes
42  * that are expressed in Type-Length-Value (TLV) format.
43  *
44  * @{
45  */
46 
47 /**
48  * mnl_nlmsg_size - calculate the size of Netlink message (without alignment)
49  * \param len length of the Netlink payload
50  *
51  * This function returns the size of a netlink message (header plus payload)
52  * without alignment.
53  */
mnl_nlmsg_size(size_t len)54 size_t mnl_nlmsg_size(size_t len)
55 {
56 	return len + MNL_NLMSG_HDRLEN;
57 }
58 
59 /**
60  * mnl_nlmsg_get_payload_len - get the length of the Netlink payload
61  * \param nlh pointer to the header of the Netlink message
62  *
63  * This function returns the Length of the netlink payload, ie. the length
64  * of the full message minus the size of the Netlink header.
65  */
mnl_nlmsg_get_payload_len(const struct nlmsghdr * nlh)66 size_t mnl_nlmsg_get_payload_len(const struct nlmsghdr *nlh)
67 {
68 	return nlh->nlmsg_len - MNL_NLMSG_HDRLEN;
69 }
70 
71 /**
72  * mnl_nlmsg_put_header - reserve and prepare room for Netlink header
73  * \param buf memory already allocated to store the Netlink header
74  *
75  * This function sets to zero the room that is required to put the Netlink
76  * header in the memory buffer passed as parameter. This function also
77  * initializes the nlmsg_len field to the size of the Netlink header. This
78  * function returns a pointer to the Netlink header structure.
79  */
mnl_nlmsg_put_header(void * buf)80 struct nlmsghdr *mnl_nlmsg_put_header(void *buf)
81 {
82 	int len = MNL_ALIGN(sizeof(struct nlmsghdr));
83 	struct nlmsghdr *nlh = buf;
84 
85 	memset(buf, 0, len);
86 	nlh->nlmsg_len = len;
87 	return nlh;
88 }
89 
90 /**
91  * mnl_nlmsg_put_extra_header - reserve and prepare room for an extra header
92  * \param nlh pointer to Netlink header
93  * \param size size of the extra header that we want to put
94  *
95  * This function sets to zero the room that is required to put the extra
96  * header after the initial Netlink header. This function also increases
97  * the nlmsg_len field. You have to invoke mnl_nlmsg_put_header() before
98  * you call this function. This function returns a pointer to the extra
99  * header.
100  */
mnl_nlmsg_put_extra_header(struct nlmsghdr * nlh,size_t size)101 void *mnl_nlmsg_put_extra_header(struct nlmsghdr *nlh,
102 					       size_t size)
103 {
104 	char *ptr = (char *)nlh + nlh->nlmsg_len;
105 	size_t len = MNL_ALIGN(size);
106 	nlh->nlmsg_len += len;
107 	memset(ptr, 0, len);
108 	return ptr;
109 }
110 
111 /**
112  * mnl_nlmsg_get_payload - get a pointer to the payload of the netlink message
113  * \param nlh pointer to a netlink header
114  *
115  * This function returns a pointer to the payload of the netlink message.
116  */
mnl_nlmsg_get_payload(const struct nlmsghdr * nlh)117 void *mnl_nlmsg_get_payload(const struct nlmsghdr *nlh)
118 {
119 	return (void *)nlh + MNL_NLMSG_HDRLEN;
120 }
121 
122 /**
123  * mnl_nlmsg_get_payload_offset - get a pointer to the payload of the message
124  * \param nlh pointer to a netlink header
125  * \param offset offset to the payload of the attributes TLV set
126  *
127  * This function returns a pointer to the payload of the netlink message plus
128  * a given offset.
129  */
mnl_nlmsg_get_payload_offset(const struct nlmsghdr * nlh,size_t offset)130 void *mnl_nlmsg_get_payload_offset(const struct nlmsghdr *nlh,
131 						 size_t offset)
132 {
133 	return (void *)nlh + MNL_NLMSG_HDRLEN + MNL_ALIGN(offset);
134 }
135 
136 /**
137  * mnl_nlmsg_ok - check a there is room for netlink message
138  * \param nlh netlink message that we want to check
139  * \param len remaining bytes in a buffer that contains the netlink message
140  *
141  * This function is used to check that a buffer that contains a netlink
142  * message has enough room for the netlink message that it stores, ie. this
143  * function can be used to verify that a netlink message is not malformed nor
144  * truncated.
145  *
146  * This function does not set errno in case of error since it is intended
147  * for iterations. Thus, it returns true on success and false on error.
148  *
149  * The len parameter may become negative in malformed messages during message
150  * iteration, that is why we use a signed integer.
151  */
mnl_nlmsg_ok(const struct nlmsghdr * nlh,int len)152 bool mnl_nlmsg_ok(const struct nlmsghdr *nlh, int len)
153 {
154 	return len >= (int)sizeof(struct nlmsghdr) &&
155 	       nlh->nlmsg_len >= sizeof(struct nlmsghdr) &&
156 	       (int)nlh->nlmsg_len <= len;
157 }
158 
159 /**
160  * mnl_nlmsg_next - get the next netlink message in a multipart message
161  * \param nlh current netlink message that we are handling
162  * \param len length of the remaining bytes in the buffer (passed by reference).
163  *
164  * This function returns a pointer to the next netlink message that is part
165  * of a multi-part netlink message. Netlink can batch several messages into
166  * one buffer so that the receiver has to iterate over the whole set of
167  * Netlink messages.
168  *
169  * You have to use mnl_nlmsg_ok() to check if the next Netlink message is
170  * valid.
171  */
mnl_nlmsg_next(const struct nlmsghdr * nlh,int * len)172 struct nlmsghdr *mnl_nlmsg_next(const struct nlmsghdr *nlh,
173 					      int *len)
174 {
175 	*len -= MNL_ALIGN(nlh->nlmsg_len);
176 	return (struct nlmsghdr *)((void *)nlh + MNL_ALIGN(nlh->nlmsg_len));
177 }
178 
179 /**
180  * mnl_nlmsg_get_payload_tail - get the ending of the netlink message
181  * \param nlh pointer to netlink message
182  *
183  * This function returns a pointer to the netlink message tail. This is useful
184  * to build a message since we continue adding attributes at the end of the
185  * message.
186  */
mnl_nlmsg_get_payload_tail(const struct nlmsghdr * nlh)187 void *mnl_nlmsg_get_payload_tail(const struct nlmsghdr *nlh)
188 {
189 	return (void *)nlh + MNL_ALIGN(nlh->nlmsg_len);
190 }
191 
192 /**
193  * mnl_nlmsg_seq_ok - perform sequence tracking
194  * \param nlh current netlink message that we are handling
195  * \param seq last sequence number used to send a message
196  *
197  * This functions returns true if the sequence tracking is fulfilled, otherwise
198  * false is returned. We skip the tracking for netlink messages whose sequence
199  * number is zero since it is usually reserved for event-based kernel
200  * notifications. On the other hand, if seq is set but the message sequence
201  * number is not set (i.e. this is an event message coming from kernel-space),
202  * then we also skip the tracking. This approach is good if we use the same
203  * socket to send commands to kernel-space (that we want to track) and to
204  * listen to events (that we do not track).
205  */
mnl_nlmsg_seq_ok(const struct nlmsghdr * nlh,unsigned int seq)206 bool mnl_nlmsg_seq_ok(const struct nlmsghdr *nlh,
207 				    unsigned int seq)
208 {
209 	return nlh->nlmsg_seq && seq ? nlh->nlmsg_seq == seq : true;
210 }
211 
212 /**
213  * mnl_nlmsg_portid_ok - perform portID origin check
214  * \param nlh current netlink message that we are handling
215  * \param portid netlink portid that we want to check
216  *
217  * This functions returns true if the origin is fulfilled, otherwise
218  * false is returned. We skip the tracking for netlink message whose portID
219  * is zero since it is reserved for event-based kernel notifications. On the
220  * other hand, if portid is set but the message PortID is not (i.e. this
221  * is an event message coming from kernel-space), then we also skip the
222  * tracking. This approach is good if we use the same socket to send commands
223  * to kernel-space (that we want to track) and to listen to events (that we
224  * do not track).
225  */
mnl_nlmsg_portid_ok(const struct nlmsghdr * nlh,unsigned int portid)226 bool mnl_nlmsg_portid_ok(const struct nlmsghdr *nlh,
227 				       unsigned int portid)
228 {
229 	return nlh->nlmsg_pid && portid ? nlh->nlmsg_pid == portid : true;
230 }
231 
mnl_nlmsg_fprintf_header(FILE * fd,const struct nlmsghdr * nlh)232 static void mnl_nlmsg_fprintf_header(FILE *fd, const struct nlmsghdr *nlh)
233 {
234 	fprintf(fd, "----------------\t------------------\n");
235 	fprintf(fd, "|  %.010u  |\t| message length |\n", nlh->nlmsg_len);
236 	fprintf(fd, "| %.05u | %c%c%c%c |\t|  type | flags  |\n",
237 		nlh->nlmsg_type,
238 		nlh->nlmsg_flags & NLM_F_REQUEST ? 'R' : '-',
239 		nlh->nlmsg_flags & NLM_F_MULTI ? 'M' : '-',
240 		nlh->nlmsg_flags & NLM_F_ACK ? 'A' : '-',
241 		nlh->nlmsg_flags & NLM_F_ECHO ? 'E' : '-');
242 	fprintf(fd, "|  %.010u  |\t| sequence number|\n", nlh->nlmsg_seq);
243 	fprintf(fd, "|  %.010u  |\t|     port ID    |\n", nlh->nlmsg_pid);
244 	fprintf(fd, "----------------\t------------------\n");
245 }
246 
mnl_nlmsg_fprintf_payload(FILE * fd,const struct nlmsghdr * nlh,size_t extra_header_size)247 static void mnl_nlmsg_fprintf_payload(FILE *fd, const struct nlmsghdr *nlh,
248 				      size_t extra_header_size)
249 {
250 	int rem = 0;
251 	unsigned int i;
252 
253 	for (i=sizeof(struct nlmsghdr); i<nlh->nlmsg_len; i+=4) {
254 		char *b = (char *) nlh;
255 		struct nlattr *attr = (struct nlattr *) (b+i);
256 
257 		/* netlink control message. */
258 		if (nlh->nlmsg_type < NLMSG_MIN_TYPE) {
259 			fprintf(fd, "| %.2x %.2x %.2x %.2x  |\t",
260 				0xff & b[i],	0xff & b[i+1],
261 				0xff & b[i+2],	0xff & b[i+3]);
262 			fprintf(fd, "|                |\n");
263 		/* special handling for the extra header. */
264 		} else if (extra_header_size > 0) {
265 			extra_header_size -= 4;
266 			fprintf(fd, "| %.2x %.2x %.2x %.2x  |\t",
267 				0xff & b[i],	0xff & b[i+1],
268 				0xff & b[i+2],	0xff & b[i+3]);
269 			fprintf(fd, "|  extra header  |\n");
270 		/* this seems like an attribute header. */
271 		} else if (rem == 0 && (attr->nla_type & NLA_TYPE_MASK) != 0) {
272 			fprintf(fd, "|%c[%d;%dm"
273 				    "%.5u"
274 				    "%c[%dm"
275 				    "|"
276 				    "%c[%d;%dm"
277 				    "%c%c"
278 				    "%c[%dm"
279 				    "|"
280 				    "%c[%d;%dm"
281 				    "%.5u"
282 				    "%c[%dm|\t",
283 				27, 1, 31,
284 				attr->nla_len,
285 				27, 0,
286 				27, 1, 32,
287 				attr->nla_type & NLA_F_NESTED ? 'N' : '-',
288 				attr->nla_type &
289 					NLA_F_NET_BYTEORDER ? 'B' : '-',
290 				27, 0,
291 				27, 1, 34,
292 				attr->nla_type & NLA_TYPE_MASK,
293 				27, 0);
294 			fprintf(fd, "|len |flags| type|\n");
295 
296 			if (!(attr->nla_type & NLA_F_NESTED)) {
297 				rem = NLA_ALIGN(attr->nla_len) -
298 					sizeof(struct nlattr);
299 			}
300 		/* this is the attribute payload. */
301 		} else if (rem > 0) {
302 			rem -= 4;
303 			fprintf(fd, "| %.2x %.2x %.2x %.2x  |\t",
304 				0xff & b[i],	0xff & b[i+1],
305 				0xff & b[i+2],	0xff & b[i+3]);
306 			fprintf(fd, "|      data      |");
307 			fprintf(fd, "\t %c %c %c %c\n",
308 				isprint(b[i]) ? b[i] : ' ',
309 				isprint(b[i+1]) ? b[i+1] : ' ',
310 				isprint(b[i+2]) ? b[i+2] : ' ',
311 				isprint(b[i+3]) ? b[i+3] : ' ');
312 		}
313 	}
314 	fprintf(fd, "----------------\t------------------\n");
315 }
316 
317 /**
318  * mnl_nlmsg_fprintf - print netlink message to file
319  * \param fd pointer to file type
320  * \param data pointer to the buffer that contains messages to be printed
321  * \param datalen length of data stored in the buffer
322  * \param extra_header_size size of the extra header (if any)
323  *
324  * This function prints the netlink header to a file handle.
325  * It may be useful for debugging purposes. One example of the output
326  * is the following:
327  *
328  *\verbatim
329 ----------------        ------------------
330 |  0000000040  |        | message length |
331 | 00016 | R-A- |        |  type | flags  |
332 |  1289148991  |        | sequence number|
333 |  0000000000  |        |     port ID    |
334 ----------------        ------------------
335 | 00 00 00 00  |        |  extra header  |
336 | 00 00 00 00  |        |  extra header  |
337 | 01 00 00 00  |        |  extra header  |
338 | 01 00 00 00  |        |  extra header  |
339 |00008|--|00003|        |len |flags| type|
340 | 65 74 68 30  |        |      data      |       e t h 0
341 ----------------        ------------------
342 \endverbatim
343  *
344  * This example above shows the netlink message that is send to kernel-space
345  * to set up the link interface eth0. The netlink and attribute header data
346  * are displayed in base 10 whereas the extra header and the attribute payload
347  * are expressed in base 16. The possible flags in the netlink header are:
348  *
349  * - R, that indicates that NLM_F_REQUEST is set.
350  * - M, that indicates that NLM_F_MULTI is set.
351  * - A, that indicates that NLM_F_ACK is set.
352  * - E, that indicates that NLM_F_ECHO is set.
353  *
354  * The lack of one flag is displayed with '-'. On the other hand, the possible
355  * attribute flags available are:
356  *
357  * - N, that indicates that NLA_F_NESTED is set.
358  * - B, that indicates that NLA_F_NET_BYTEORDER is set.
359  */
mnl_nlmsg_fprintf(FILE * fd,const void * data,size_t datalen,size_t extra_header_size)360 void mnl_nlmsg_fprintf(FILE *fd, const void *data, size_t datalen,
361 				     size_t extra_header_size)
362 {
363 	const struct nlmsghdr *nlh = data;
364 	int len = datalen;
365 
366 	while (mnl_nlmsg_ok(nlh, len)) {
367 		mnl_nlmsg_fprintf_header(fd, nlh);
368 		mnl_nlmsg_fprintf_payload(fd, nlh, extra_header_size);
369 		nlh = mnl_nlmsg_next(nlh, &len);
370 	}
371 }
372 
373 /**
374  * @}
375  */
376 
377 /**
378  * \defgroup batch Netlink message batch helpers
379  *
380  * This library provides helpers to batch several messages into one single
381  * datagram. These helpers do not perform strict memory boundary checkings.
382  *
383  * The following figure represents a Netlink message batch:
384  *
385  *   |<-------------- MNL_SOCKET_BUFFER_SIZE ------------->|
386  *   |<-------------------- batch ------------------>|     |
387  *   |-----------|-----------|-----------|-----------|-----------|
388  *   |<- nlmsg ->|<- nlmsg ->|<- nlmsg ->|<- nlmsg ->|<- nlmsg ->|
389  *   |-----------|-----------|-----------|-----------|-----------|
390  *                                             ^           ^
391  *                                             |           |
392  *                                        message N   message N+1
393  *
394  * To start the batch, you have to call mnl_nlmsg_batch_start() and you can
395  * use mnl_nlmsg_batch_stop() to release it.
396  *
397  * You have to invoke mnl_nlmsg_batch_next() to get room for a new message
398  * in the batch. If this function returns NULL, it means that the last
399  * message that was added (message N+1 in the figure above) does not fit the
400  * batch. Thus, you have to send the batch (which includes until message N)
401  * and, then, you have to call mnl_nlmsg_batch_reset() to re-initialize
402  * the batch (this moves message N+1 to the head of the buffer). For that
403  * reason, the buffer that you have to use to store the batch must be double
404  * of MNL_SOCKET_BUFFER_SIZE to ensure that the last message (message N+1)
405  * that did not fit into the batch is written inside valid memory boundaries.
406  *
407  * @{
408  */
409 
410 struct mnl_nlmsg_batch {
411 	/* the buffer that is used to store the batch. */
412 	void *buf;
413 	size_t limit;
414 	size_t buflen;
415 	/* the current netlink message in the batch. */
416 	void *cur;
417 	bool overflow;
418 };
419 
420 /**
421  * mnl_nlmsg_batch_start - initialize a batch
422  * \param buf pointer to the buffer that will store this batch
423  * \param limit maximum size of the batch (should be MNL_SOCKET_BUFFER_SIZE).
424  *
425  * The buffer that you pass must be double of MNL_SOCKET_BUFFER_SIZE. The
426  * limit must be half of the buffer size, otherwise expect funny memory
427  * corruptions 8-).
428  *
429  * You can allocate the buffer that you use to store the batch in the stack or
430  * the heap, no restrictions in this regard. This function returns NULL on
431  * error.
432  */
mnl_nlmsg_batch_start(void * buf,size_t limit)433 struct mnl_nlmsg_batch *mnl_nlmsg_batch_start(void *buf,
434 							    size_t limit)
435 {
436 	struct mnl_nlmsg_batch *b;
437 
438 	b = malloc(sizeof(struct mnl_nlmsg_batch));
439 	if (b == NULL)
440 		return NULL;
441 
442 	b->buf = buf;
443 	b->limit = limit;
444 	b->buflen = 0;
445 	b->cur = buf;
446 	b->overflow = false;
447 
448 	return b;
449 }
450 
451 /**
452  * mnl_nlmsg_batch_stop - release a batch
453  * \param b pointer to batch
454  *
455  * This function releases the batch allocated by mnl_nlmsg_batch_start().
456  */
mnl_nlmsg_batch_stop(struct mnl_nlmsg_batch * b)457 void mnl_nlmsg_batch_stop(struct mnl_nlmsg_batch *b)
458 {
459 	free(b);
460 }
461 
462 /**
463  * mnl_nlmsg_batch_next - get room for the next message in the batch
464  * \param b pointer to batch
465  *
466  * This function returns false if the last message did not fit into the
467  * batch. Otherwise, it prepares the batch to provide room for the new
468  * Netlink message in the batch and returns true.
469  *
470  * You have to put at least one message in the batch before calling this
471  * function, otherwise your application is likely to crash.
472  */
mnl_nlmsg_batch_next(struct mnl_nlmsg_batch * b)473 bool mnl_nlmsg_batch_next(struct mnl_nlmsg_batch *b)
474 {
475 	struct nlmsghdr *nlh = b->cur;
476 
477 	if (b->buflen + nlh->nlmsg_len > b->limit) {
478 		b->overflow = true;
479 		return false;
480 	}
481 	b->cur = b->buf + b->buflen + nlh->nlmsg_len;
482 	b->buflen += nlh->nlmsg_len;
483 	return true;
484 }
485 
486 /**
487  * mnl_nlmsg_batch_reset - reset the batch
488  * \param b pointer to batch
489  *
490  * This function allows to reset a batch, so you can reuse it to create a
491  * new one. This function moves the last message which does not fit the
492  * batch to the head of the buffer, if any.
493  */
mnl_nlmsg_batch_reset(struct mnl_nlmsg_batch * b)494 void mnl_nlmsg_batch_reset(struct mnl_nlmsg_batch *b)
495 {
496 	if (b->overflow) {
497 		struct nlmsghdr *nlh = b->cur;
498 		memcpy(b->buf, b->cur, nlh->nlmsg_len);
499 		b->buflen = nlh->nlmsg_len;
500 		b->cur = b->buf + b->buflen;
501 		b->overflow = false;
502 	} else {
503 		b->buflen = 0;
504 		b->cur = b->buf;
505 	}
506 }
507 
508 /**
509  * mnl_nlmsg_batch_size - get current size of the batch
510  * \param b pointer to batch
511  *
512  * This function returns the current size of the batch.
513  */
mnl_nlmsg_batch_size(struct mnl_nlmsg_batch * b)514 size_t mnl_nlmsg_batch_size(struct mnl_nlmsg_batch *b)
515 {
516 	return b->buflen;
517 }
518 
519 /**
520  * mnl_nlmsg_batch_head - get head of this batch
521  * \param b pointer to batch
522  *
523  * This function returns a pointer to the head of the batch, which is the
524  * beginning of the buffer that is used.
525  */
mnl_nlmsg_batch_head(struct mnl_nlmsg_batch * b)526 void *mnl_nlmsg_batch_head(struct mnl_nlmsg_batch *b)
527 {
528 	return b->buf;
529 }
530 
531 /**
532  * mnl_nlmsg_batch_current - returns current position in the batch
533  * \param b pointer to batch
534  *
535  * This function returns a pointer to the current position in the buffer
536  * that is used to store the batch.
537  */
mnl_nlmsg_batch_current(struct mnl_nlmsg_batch * b)538 void *mnl_nlmsg_batch_current(struct mnl_nlmsg_batch *b)
539 {
540 	return b->cur;
541 }
542 
543 /**
544  * mnl_nlmsg_batch_is_empty - check if there is any message in the batch
545  * \param b pointer to batch
546  *
547  * This function returns true if the batch is empty.
548  */
mnl_nlmsg_batch_is_empty(struct mnl_nlmsg_batch * b)549 bool mnl_nlmsg_batch_is_empty(struct mnl_nlmsg_batch *b)
550 {
551 	return b->buflen == 0;
552 }
553 
554 /**
555  * @}
556  */
557