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