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