1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2022 Alexander V. Chernikov <melifaro@FreeBSD.org>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27 #ifndef _NETLINK_NETLINK_SNL_H_
28 #define _NETLINK_NETLINK_SNL_H_
29
30 /*
31 * Simple Netlink Library
32 */
33
34 #include <sys/param.h>
35 #include <sys/socket.h>
36
37 #include <netlink/netlink.h>
38 #include <netlink/netlink_bitset.h>
39
40 #include <assert.h>
41 #include <errno.h>
42 #include <stdalign.h>
43 #include <stddef.h>
44 #include <stdbool.h>
45 #include <stdint.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49
50 #define _roundup2(x, y) (((x)+((y)-1))&(~((y)-1)))
51
52 #define NETLINK_ALIGN_SIZE sizeof(uint32_t)
53 #define NETLINK_ALIGN(_len) _roundup2(_len, NETLINK_ALIGN_SIZE)
54
55 #define NLA_ALIGN_SIZE sizeof(uint32_t)
56 #define NLA_HDRLEN ((int)sizeof(struct nlattr))
57 #define NLA_DATA_LEN(_nla) ((int)((_nla)->nla_len - NLA_HDRLEN))
58 #define NLA_DATA(_nla) NL_ITEM_DATA(_nla, NLA_HDRLEN)
59 #define NLA_DATA_CONST(_nla) NL_ITEM_DATA_CONST(_nla, NLA_HDRLEN)
60
61 #define NLA_TYPE(_nla) ((_nla)->nla_type & 0x3FFF)
62
63 #define NLA_NEXT(_attr) (struct nlattr *)(void *)((char *)_attr + NLA_ALIGN(_attr->nla_len))
64
65 #define _NLA_END(_start, _len) ((char *)(_start) + (_len))
66 #define NLA_FOREACH(_attr, _start, _len) \
67 for (_attr = (struct nlattr *)(_start); \
68 ((char *)_attr < _NLA_END(_start, _len)) && \
69 ((char *)NLA_NEXT(_attr) <= _NLA_END(_start, _len)); \
70 _attr = NLA_NEXT(_attr))
71
72 #define NLA_NEXT_CONST(_attr) (const struct nlattr *)(const void *) \
73 ((const char *)_attr + NLA_ALIGN(_attr->nla_len))
74
75 #define _NLA_END_CONST(_start, _len) \
76 ((const char *)(_start) + (_len))
77 #define NLA_FOREACH_CONST(_attr, _start, _len) \
78 for (_attr = (const struct nlattr *)(_start); \
79 ((const char *)_attr < _NLA_END_CONST(_start, _len)) && \
80 ((const char *)NLA_NEXT_CONST(_attr) <= \
81 _NLA_END_CONST(_start, _len)); \
82 _attr = NLA_NEXT_CONST(_attr))
83
84 struct linear_buffer {
85 char *base; /* Base allocated memory pointer */
86 uint32_t offset; /* Currently used offset */
87 uint32_t size; /* Total buffer size */
88 struct linear_buffer *next; /* Buffer chaining */
89 } __aligned(alignof(__max_align_t));
90
91 static inline struct linear_buffer *
lb_init(uint32_t size)92 lb_init(uint32_t size)
93 {
94 struct linear_buffer *lb = (struct linear_buffer *)calloc(1, size);
95
96 if (lb != NULL) {
97 lb->base = (char *)(lb + 1);
98 lb->size = size - sizeof(*lb);
99 }
100
101 return (lb);
102 }
103
104 static inline void
lb_free(struct linear_buffer * lb)105 lb_free(struct linear_buffer *lb)
106 {
107 free(lb);
108 }
109
110 static inline char *
lb_allocz(struct linear_buffer * lb,int len)111 lb_allocz(struct linear_buffer *lb, int len)
112 {
113 len = roundup2(len, alignof(__max_align_t));
114 if (lb->offset + len > lb->size)
115 return (NULL);
116 char *data = (lb->base + lb->offset);
117 lb->offset += len;
118 return (data);
119 }
120
121 static inline void
lb_clear(struct linear_buffer * lb)122 lb_clear(struct linear_buffer *lb)
123 {
124 memset(lb->base, 0, lb->offset);
125 lb->offset = 0;
126 }
127
128 struct snl_state {
129 int fd;
130 char *buf;
131 size_t off;
132 size_t bufsize;
133 size_t datalen;
134 uint32_t seq;
135 bool init_done;
136 struct linear_buffer *lb;
137 };
138 #define SCRATCH_BUFFER_SIZE 1024
139 #define SNL_WRITER_BUFFER_SIZE 256
140
141 typedef void snl_parse_field_f(struct snl_state *ss, void *hdr, void *target);
142 struct snl_field_parser {
143 uint16_t off_in;
144 uint16_t off_out;
145 snl_parse_field_f *cb;
146 };
147 static const struct snl_field_parser snl_f_p_empty[] = {};
148
149 typedef bool snl_parse_attr_f(struct snl_state *ss, struct nlattr *attr,
150 const void *arg, void *target);
151 struct snl_attr_parser {
152 uint16_t type; /* Attribute type */
153 uint16_t off; /* field offset in the target structure */
154 snl_parse_attr_f *cb; /* parser function to call */
155
156 /* Optional parser argument */
157 union {
158 const void *arg;
159 const uint32_t arg_u32;
160 };
161 };
162
163 typedef bool snl_parse_post_f(struct snl_state *ss, void *target);
164
165 struct snl_hdr_parser {
166 uint16_t in_hdr_size; /* Input header size */
167 uint16_t out_size; /* Output structure size */
168 uint16_t fp_size; /* Number of items in field parser */
169 uint16_t np_size; /* Number of items in attribute parser */
170 const struct snl_field_parser *fp; /* array of header field parsers */
171 const struct snl_attr_parser *np; /* array of attribute parsers */
172 snl_parse_post_f *cb_post; /* post-parse callback */
173 };
174
175 #define SNL_DECLARE_PARSER_EXT(_name, _sz_h_in, _sz_out, _fp, _np, _cb) \
176 static const struct snl_hdr_parser _name = { \
177 .in_hdr_size = _sz_h_in, \
178 .out_size = _sz_out, \
179 .fp = &((_fp)[0]), \
180 .np = &((_np)[0]), \
181 .fp_size = nitems(_fp), \
182 .np_size = nitems(_np), \
183 .cb_post = _cb, \
184 }
185
186 #define SNL_DECLARE_PARSER(_name, _t, _fp, _np) \
187 SNL_DECLARE_PARSER_EXT(_name, sizeof(_t), 0, _fp, _np, NULL)
188
189 #define SNL_DECLARE_FIELD_PARSER_EXT(_name, _sz_h_in, _sz_out, _fp, _cb) \
190 static const struct snl_hdr_parser _name = { \
191 .in_hdr_size = _sz_h_in, \
192 .out_size = _sz_out, \
193 .fp = &((_fp)[0]), \
194 .fp_size = nitems(_fp), \
195 .cb_post = _cb, \
196 }
197
198 #define SNL_DECLARE_FIELD_PARSER(_name, _t, _fp) \
199 SNL_DECLARE_FIELD_PARSER_EXT(_name, sizeof(_t), 0, _fp, NULL)
200
201 #define SNL_DECLARE_ATTR_PARSER_EXT(_name, _sz_out, _np, _cb) \
202 static const struct snl_hdr_parser _name = { \
203 .out_size = _sz_out, \
204 .np = &((_np)[0]), \
205 .np_size = nitems(_np), \
206 .cb_post = _cb, \
207 }
208
209 #define SNL_DECLARE_ATTR_PARSER(_name, _np) \
210 SNL_DECLARE_ATTR_PARSER_EXT(_name, 0, _np, NULL)
211
212
213 static inline void *
snl_allocz(struct snl_state * ss,int len)214 snl_allocz(struct snl_state *ss, int len)
215 {
216 void *data = lb_allocz(ss->lb, len);
217
218 if (data == NULL) {
219 uint32_t size = ss->lb->size * 2;
220
221 while (size < len + sizeof(struct linear_buffer))
222 size *= 2;
223
224 struct linear_buffer *lb = lb_init(size);
225
226 if (lb != NULL) {
227 lb->next = ss->lb;
228 ss->lb = lb;
229 data = lb_allocz(ss->lb, len);
230 }
231 }
232
233 return (data);
234 }
235
236 static inline void
snl_clear_lb(struct snl_state * ss)237 snl_clear_lb(struct snl_state *ss)
238 {
239 struct linear_buffer *lb = ss->lb;
240
241 lb_clear(lb);
242 lb = lb->next;
243 ss->lb->next = NULL;
244 /* Remove all linear bufs except the largest one */
245 while (lb != NULL) {
246 struct linear_buffer *lb_next = lb->next;
247 lb_free(lb);
248 lb = lb_next;
249 }
250 }
251
252 static void
snl_free(struct snl_state * ss)253 snl_free(struct snl_state *ss)
254 {
255 if (ss->init_done)
256 close(ss->fd);
257 if (ss->buf != NULL)
258 free(ss->buf);
259 if (ss->lb != NULL) {
260 snl_clear_lb(ss);
261 lb_free(ss->lb);
262 }
263 }
264
265 static inline bool
snl_init(struct snl_state * ss,int netlink_family)266 snl_init(struct snl_state *ss, int netlink_family)
267 {
268 memset(ss, 0, sizeof(*ss));
269
270 ss->fd = socket(AF_NETLINK, SOCK_RAW, netlink_family);
271 if (ss->fd == -1)
272 return (false);
273 ss->init_done = true;
274
275 int val = 1;
276 socklen_t optlen = sizeof(val);
277 if (setsockopt(ss->fd, SOL_NETLINK, NETLINK_EXT_ACK, &val, optlen) == -1) {
278 snl_free(ss);
279 return (false);
280 }
281
282 int rcvbuf;
283 if (getsockopt(ss->fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &optlen) == -1) {
284 snl_free(ss);
285 return (false);
286 }
287
288 ss->bufsize = rcvbuf;
289 ss->buf = (char *)malloc(ss->bufsize);
290 if (ss->buf == NULL) {
291 snl_free(ss);
292 return (false);
293 }
294
295 ss->lb = lb_init(SCRATCH_BUFFER_SIZE);
296 if (ss->lb == NULL) {
297 snl_free(ss);
298 return (false);
299 }
300
301 return (true);
302 }
303
304 static inline bool
snl_clone(struct snl_state * ss,const struct snl_state * orig)305 snl_clone(struct snl_state *ss, const struct snl_state *orig)
306 {
307 *ss = (struct snl_state){
308 .fd = orig->fd,
309 .init_done = false,
310 };
311 return ((ss->lb = lb_init(SCRATCH_BUFFER_SIZE)) != NULL);
312 }
313
314 static inline bool
snl_send(struct snl_state * ss,void * data,int sz)315 snl_send(struct snl_state *ss, void *data, int sz)
316 {
317 return (send(ss->fd, data, sz, 0) == sz);
318 }
319
320 static inline bool
snl_send_message(struct snl_state * ss,struct nlmsghdr * hdr)321 snl_send_message(struct snl_state *ss, struct nlmsghdr *hdr)
322 {
323 ssize_t sz = NLMSG_ALIGN(hdr->nlmsg_len);
324
325 return (send(ss->fd, hdr, sz, 0) == sz);
326 }
327
328 static inline uint32_t
snl_get_seq(struct snl_state * ss)329 snl_get_seq(struct snl_state *ss)
330 {
331 return (++ss->seq);
332 }
333
334 struct snl_msg_info {
335 int cmsg_type;
336 int cmsg_level;
337 uint32_t process_id;
338 uint8_t port_id;
339 uint8_t seq_id;
340 };
341 static inline bool parse_cmsg(struct snl_state *ss, const struct msghdr *msg,
342 struct snl_msg_info *attrs);
343
344 static inline struct nlmsghdr *
snl_read_message_dbg(struct snl_state * ss,struct snl_msg_info * cinfo)345 snl_read_message_dbg(struct snl_state *ss, struct snl_msg_info *cinfo)
346 {
347 memset(cinfo, 0, sizeof(*cinfo));
348
349 if (ss->off == ss->datalen) {
350 struct sockaddr_nl nladdr;
351 char cbuf[64];
352
353 struct iovec iov = {
354 .iov_base = ss->buf,
355 .iov_len = ss->bufsize,
356 };
357 struct msghdr msg = {
358 .msg_name = &nladdr,
359 .msg_namelen = sizeof(nladdr),
360 .msg_iov = &iov,
361 .msg_iovlen = 1,
362 .msg_control = cbuf,
363 .msg_controllen = sizeof(cbuf),
364 };
365 ss->off = 0;
366 ss->datalen = 0;
367 for (;;) {
368 ssize_t datalen = recvmsg(ss->fd, &msg, 0);
369 if (datalen > 0) {
370 ss->datalen = datalen;
371 parse_cmsg(ss, &msg, cinfo);
372 break;
373 } else if (errno != EINTR)
374 return (NULL);
375 }
376 }
377 struct nlmsghdr *hdr = (struct nlmsghdr *)(void *)&ss->buf[ss->off];
378 ss->off += NLMSG_ALIGN(hdr->nlmsg_len);
379 return (hdr);
380 }
381
382
383 static inline struct nlmsghdr *
snl_read_message(struct snl_state * ss)384 snl_read_message(struct snl_state *ss)
385 {
386 if (ss->off == ss->datalen) {
387 struct sockaddr_nl nladdr;
388 struct iovec iov = {
389 .iov_base = ss->buf,
390 .iov_len = ss->bufsize,
391 };
392 struct msghdr msg = {
393 .msg_name = &nladdr,
394 .msg_namelen = sizeof(nladdr),
395 .msg_iov = &iov,
396 .msg_iovlen = 1,
397 };
398 ss->off = 0;
399 ss->datalen = 0;
400 for (;;) {
401 ssize_t datalen = recvmsg(ss->fd, &msg, 0);
402 if (datalen > 0) {
403 ss->datalen = datalen;
404 break;
405 } else if (errno != EINTR)
406 return (NULL);
407 }
408 }
409 struct nlmsghdr *hdr = (struct nlmsghdr *)(void *)&ss->buf[ss->off];
410 ss->off += NLMSG_ALIGN(hdr->nlmsg_len);
411 return (hdr);
412 }
413
414 static inline struct nlmsghdr *
snl_read_reply(struct snl_state * ss,uint32_t nlmsg_seq)415 snl_read_reply(struct snl_state *ss, uint32_t nlmsg_seq)
416 {
417 struct nlmsghdr *hdr;
418
419 while ((hdr = snl_read_message(ss)) != NULL) {
420 if (hdr->nlmsg_seq == nlmsg_seq)
421 return (hdr);
422 }
423
424 return (NULL);
425 }
426
427 /*
428 * Checks that attributes are sorted by attribute type.
429 */
430 static inline void
snl_verify_parsers(const struct snl_hdr_parser ** parser,int count)431 snl_verify_parsers(const struct snl_hdr_parser **parser, int count)
432 {
433 for (int i = 0; i < count; i++) {
434 const struct snl_hdr_parser *p = parser[i];
435 int attr_type = 0;
436 for (int j = 0; j < p->np_size; j++) {
437 assert(p->np[j].type > attr_type);
438 attr_type = p->np[j].type;
439 }
440 }
441 }
442 #define SNL_VERIFY_PARSERS(_p) snl_verify_parsers((_p), nitems(_p))
443
444 static const struct snl_attr_parser *
find_parser(const struct snl_attr_parser * ps,int pslen,int key)445 find_parser(const struct snl_attr_parser *ps, int pslen, int key)
446 {
447 int left_i = 0, right_i = pslen - 1;
448
449 if (key < ps[0].type || key > ps[pslen - 1].type)
450 return (NULL);
451
452 while (left_i + 1 < right_i) {
453 int mid_i = (left_i + right_i) / 2;
454 if (key < ps[mid_i].type)
455 right_i = mid_i;
456 else if (key > ps[mid_i].type)
457 left_i = mid_i + 1;
458 else
459 return (&ps[mid_i]);
460 }
461 if (ps[left_i].type == key)
462 return (&ps[left_i]);
463 else if (ps[right_i].type == key)
464 return (&ps[right_i]);
465 return (NULL);
466 }
467
468 static inline bool
snl_parse_attrs_raw(struct snl_state * ss,struct nlattr * nla_head,int len,const struct snl_attr_parser * ps,int pslen,void * target)469 snl_parse_attrs_raw(struct snl_state *ss, struct nlattr *nla_head, int len,
470 const struct snl_attr_parser *ps, int pslen, void *target)
471 {
472 struct nlattr *nla;
473
474 NLA_FOREACH(nla, nla_head, len) {
475 if (nla->nla_len < sizeof(struct nlattr))
476 return (false);
477 int nla_type = nla->nla_type & NLA_TYPE_MASK;
478 const struct snl_attr_parser *s = find_parser(ps, pslen, nla_type);
479 if (s != NULL) {
480 void *ptr = (void *)((char *)target + s->off);
481 if (!s->cb(ss, nla, s->arg, ptr))
482 return (false);
483 }
484 }
485 return (true);
486 }
487
488 static inline bool
snl_parse_attrs(struct snl_state * ss,struct nlmsghdr * hdr,int hdrlen,const struct snl_attr_parser * ps,int pslen,void * target)489 snl_parse_attrs(struct snl_state *ss, struct nlmsghdr *hdr, int hdrlen,
490 const struct snl_attr_parser *ps, int pslen, void *target)
491 {
492 int off = NLMSG_HDRLEN + NETLINK_ALIGN(hdrlen);
493 int len = hdr->nlmsg_len - off;
494 struct nlattr *nla_head = (struct nlattr *)(void *)((char *)hdr + off);
495
496 return (snl_parse_attrs_raw(ss, nla_head, len, ps, pslen, target));
497 }
498
499 static inline void
snl_parse_fields(struct snl_state * ss,struct nlmsghdr * hdr,int hdrlen __unused,const struct snl_field_parser * ps,int pslen,void * target)500 snl_parse_fields(struct snl_state *ss, struct nlmsghdr *hdr, int hdrlen __unused,
501 const struct snl_field_parser *ps, int pslen, void *target)
502 {
503 for (int i = 0; i < pslen; i++) {
504 const struct snl_field_parser *fp = &ps[i];
505 void *src = (char *)hdr + fp->off_in;
506 void *dst = (char *)target + fp->off_out;
507
508 fp->cb(ss, src, dst);
509 }
510 }
511
512 static inline bool
snl_parse_header(struct snl_state * ss,void * hdr,int len,const struct snl_hdr_parser * parser,void * target)513 snl_parse_header(struct snl_state *ss, void *hdr, int len,
514 const struct snl_hdr_parser *parser, void *target)
515 {
516 struct nlattr *nla_head;
517
518 /* Extract fields first (if any) */
519 snl_parse_fields(ss, (struct nlmsghdr *)hdr, parser->in_hdr_size,
520 parser->fp, parser->fp_size, target);
521
522 nla_head = (struct nlattr *)(void *)((char *)hdr + parser->in_hdr_size);
523 bool result = snl_parse_attrs_raw(ss, nla_head, len - parser->in_hdr_size,
524 parser->np, parser->np_size, target);
525
526 if (result && parser->cb_post != NULL)
527 result = parser->cb_post(ss, target);
528
529 return (result);
530 }
531
532 static inline bool
snl_parse_nlmsg(struct snl_state * ss,struct nlmsghdr * hdr,const struct snl_hdr_parser * parser,void * target)533 snl_parse_nlmsg(struct snl_state *ss, struct nlmsghdr *hdr,
534 const struct snl_hdr_parser *parser, void *target)
535 {
536 return (snl_parse_header(ss, hdr + 1, hdr->nlmsg_len - sizeof(*hdr), parser, target));
537 }
538
539 static inline bool
snl_attr_get_flag(struct snl_state * ss __unused,struct nlattr * nla,const void * arg __unused,void * target)540 snl_attr_get_flag(struct snl_state *ss __unused, struct nlattr *nla, const void *arg __unused,
541 void *target)
542 {
543 if (NLA_DATA_LEN(nla) == 0) {
544 *((uint8_t *)target) = 1;
545 return (true);
546 }
547 return (false);
548 }
549
550 static inline bool
snl_attr_get_bytes(struct snl_state * ss __unused,struct nlattr * nla,const void * arg,void * target)551 snl_attr_get_bytes(struct snl_state *ss __unused, struct nlattr *nla, const void *arg,
552 void *target)
553 {
554 if ((size_t)NLA_DATA_LEN(nla) != (size_t)arg)
555 return (false);
556
557 memcpy(target, NLA_DATA_CONST(nla), (size_t)arg);
558
559 return (true);
560 }
561
562 static inline bool
snl_attr_get_bool(struct snl_state * ss __unused,struct nlattr * nla,const void * arg __unused,void * target)563 snl_attr_get_bool(struct snl_state *ss __unused, struct nlattr *nla,
564 const void *arg __unused, void *target)
565 {
566 if (NLA_DATA_LEN(nla) == sizeof(bool)) {
567 *((bool *)target) = *((const bool *)NLA_DATA_CONST(nla));
568 return (true);
569 }
570 return (false);
571 }
572
573 static inline bool
snl_attr_get_uint8(struct snl_state * ss __unused,struct nlattr * nla,const void * arg __unused,void * target)574 snl_attr_get_uint8(struct snl_state *ss __unused, struct nlattr *nla,
575 const void *arg __unused, void *target)
576 {
577 if (NLA_DATA_LEN(nla) == sizeof(uint8_t)) {
578 *((uint8_t *)target) = *((const uint8_t *)NLA_DATA_CONST(nla));
579 return (true);
580 }
581 return (false);
582 }
583
584 static inline bool
snl_attr_get_uint16(struct snl_state * ss __unused,struct nlattr * nla,const void * arg __unused,void * target)585 snl_attr_get_uint16(struct snl_state *ss __unused, struct nlattr *nla,
586 const void *arg __unused, void *target)
587 {
588 if (NLA_DATA_LEN(nla) == sizeof(uint16_t)) {
589 *((uint16_t *)target) = *((const uint16_t *)NLA_DATA_CONST(nla));
590 return (true);
591 }
592 return (false);
593 }
594
595 static inline bool
snl_attr_get_uint32(struct snl_state * ss __unused,struct nlattr * nla,const void * arg __unused,void * target)596 snl_attr_get_uint32(struct snl_state *ss __unused, struct nlattr *nla,
597 const void *arg __unused, void *target)
598 {
599 if (NLA_DATA_LEN(nla) == sizeof(uint32_t)) {
600 *((uint32_t *)target) = *((const uint32_t *)NLA_DATA_CONST(nla));
601 return (true);
602 }
603 return (false);
604 }
605
606 static inline bool
snl_attr_get_uint64(struct snl_state * ss __unused,struct nlattr * nla,const void * arg __unused,void * target)607 snl_attr_get_uint64(struct snl_state *ss __unused, struct nlattr *nla,
608 const void *arg __unused, void *target)
609 {
610 if (NLA_DATA_LEN(nla) == sizeof(uint64_t)) {
611 memcpy(target, NLA_DATA_CONST(nla), sizeof(uint64_t));
612 return (true);
613 }
614 return (false);
615 }
616
617 static inline bool
snl_attr_get_int8(struct snl_state * ss,struct nlattr * nla,const void * arg,void * target)618 snl_attr_get_int8(struct snl_state *ss, struct nlattr *nla, const void *arg,
619 void *target)
620 {
621 return (snl_attr_get_uint8(ss, nla, arg, target));
622 }
623
624 static inline bool
snl_attr_get_int16(struct snl_state * ss,struct nlattr * nla,const void * arg,void * target)625 snl_attr_get_int16(struct snl_state *ss, struct nlattr *nla, const void *arg,
626 void *target)
627 {
628 return (snl_attr_get_uint16(ss, nla, arg, target));
629 }
630
631 static inline bool
snl_attr_get_int32(struct snl_state * ss,struct nlattr * nla,const void * arg,void * target)632 snl_attr_get_int32(struct snl_state *ss, struct nlattr *nla, const void *arg,
633 void *target)
634 {
635 return (snl_attr_get_uint32(ss, nla, arg, target));
636 }
637
638 static inline bool
snl_attr_get_int64(struct snl_state * ss,struct nlattr * nla,const void * arg,void * target)639 snl_attr_get_int64(struct snl_state *ss, struct nlattr *nla, const void *arg,
640 void *target)
641 {
642 return (snl_attr_get_uint64(ss, nla, arg, target));
643 }
644
645 static inline bool
snl_attr_get_time_t(struct snl_state * ss __unused,struct nlattr * nla,const void * arg __unused,void * target)646 snl_attr_get_time_t(struct snl_state *ss __unused, struct nlattr *nla,
647 const void *arg __unused, void *target)
648 {
649 if (NLA_DATA_LEN(nla) == sizeof(time_t)) {
650 memcpy(target, NLA_DATA_CONST(nla), sizeof(time_t));
651 return (true);
652 }
653 return (false);
654 }
655
656 static inline bool
snl_attr_get_string(struct snl_state * ss __unused,struct nlattr * nla,const void * arg __unused,void * target)657 snl_attr_get_string(struct snl_state *ss __unused, struct nlattr *nla,
658 const void *arg __unused, void *target)
659 {
660 size_t maxlen = NLA_DATA_LEN(nla);
661
662 if (strnlen((char *)NLA_DATA(nla), maxlen) < maxlen) {
663 *((char **)target) = (char *)NLA_DATA(nla);
664 return (true);
665 }
666 return (false);
667 }
668
669 static inline bool
snl_attr_get_stringn(struct snl_state * ss,struct nlattr * nla,const void * arg __unused,void * target)670 snl_attr_get_stringn(struct snl_state *ss, struct nlattr *nla,
671 const void *arg __unused, void *target)
672 {
673 int maxlen = NLA_DATA_LEN(nla);
674
675 char *buf = (char *)snl_allocz(ss, maxlen + 1);
676 if (buf == NULL)
677 return (false);
678 buf[maxlen] = '\0';
679 memcpy(buf, NLA_DATA(nla), maxlen);
680
681 *((char **)target) = buf;
682 return (true);
683 }
684
685 static inline bool
snl_attr_copy_string(struct snl_state * ss,struct nlattr * nla,const void * arg,void * target)686 snl_attr_copy_string(struct snl_state *ss, struct nlattr *nla,
687 const void *arg, void *target)
688 {
689 char *tmp;
690
691 if (snl_attr_get_string(ss, nla, NULL, &tmp)) {
692 strlcpy((char *)target, tmp, (size_t)arg);
693 return (true);
694 }
695 return (false);
696 }
697
698 static inline bool
snl_attr_dup_string(struct snl_state * ss __unused,struct nlattr * nla,const void * arg __unused,void * target)699 snl_attr_dup_string(struct snl_state *ss __unused, struct nlattr *nla,
700 const void *arg __unused, void *target)
701 {
702 size_t maxlen = NLA_DATA_LEN(nla);
703
704 if (strnlen((char *)NLA_DATA(nla), maxlen) < maxlen) {
705 char *buf = (char *)snl_allocz(ss, maxlen);
706 if (buf == NULL)
707 return (false);
708 memcpy(buf, NLA_DATA(nla), maxlen);
709 *((char **)target) = buf;
710 return (true);
711 }
712 return (false);
713 }
714
715 static inline bool
snl_attr_get_nested(struct snl_state * ss,struct nlattr * nla,const void * arg,void * target)716 snl_attr_get_nested(struct snl_state *ss, struct nlattr *nla, const void *arg, void *target)
717 {
718 const struct snl_hdr_parser *p = (const struct snl_hdr_parser *)arg;
719
720 /* Assumes target points to the beginning of the structure */
721 return (snl_parse_header(ss, NLA_DATA(nla), NLA_DATA_LEN(nla), p, target));
722 }
723
724 struct snl_parray {
725 uint32_t count;
726 void **items;
727 uint32_t _capacity;
728 };
729
730 static inline bool
snl_parray_append(struct snl_state * ss,struct snl_parray * array,void * item,uint32_t start_size)731 snl_parray_append(struct snl_state *ss, struct snl_parray *array, void *item,
732 uint32_t start_size)
733 {
734 void **new_items;
735 size_t alloc_size;
736 uint32_t new_size;
737
738 if (start_size == 0)
739 return (false);
740 if (array->_capacity == 0) {
741 new_size = start_size;
742 if (__builtin_mul_overflow((size_t)new_size,
743 sizeof(*new_items), &alloc_size))
744 return (false);
745 array->items = (void **)snl_allocz(ss, alloc_size);
746 if (array->items == NULL)
747 return (false);
748 array->_capacity = new_size;
749 } else if (array->count == array->_capacity) {
750 if (array->_capacity > UINT32_MAX / 2)
751 return (false);
752 new_size = array->_capacity * 2;
753 if (__builtin_mul_overflow((size_t)new_size,
754 sizeof(*new_items), &alloc_size))
755 return (false);
756 new_items = (void **)snl_allocz(ss, alloc_size);
757 if (new_items == NULL)
758 return (false);
759 memcpy(new_items, array->items,
760 array->count * sizeof(*new_items));
761 array->items = new_items;
762 array->_capacity = new_size;
763 }
764 array->items[array->count++] = item;
765 return (true);
766 }
767
768 static inline bool
snl_attr_get_parray_sz(struct snl_state * ss,struct nlattr * container_nla,uint32_t start_size,const void * arg,struct snl_parray * array)769 snl_attr_get_parray_sz(struct snl_state *ss, struct nlattr *container_nla,
770 uint32_t start_size, const void *arg, struct snl_parray *array)
771 {
772 const struct snl_hdr_parser *p = (const struct snl_hdr_parser *)arg;
773 struct nlattr *nla;
774
775 if (p->out_size == 0 || start_size == 0)
776 return (false);
777 /* A container attribute replaces, rather than extends, its output. */
778 array->count = 0;
779 array->items = NULL;
780 array->_capacity = 0;
781
782 /*
783 * If the provided parser is an attribute parser, assume that each
784 * nla in the container nla is the container nla itself and parse
785 * the contents of this nla.
786 * Otherwise, run the parser on raw data, assuming the header of this
787 * data has u16 field with total size in the beginning.
788 */
789 uint32_t data_off = 0;
790
791 if (p->in_hdr_size == 0)
792 data_off = sizeof(struct nlattr);
793
794 NLA_FOREACH(nla, NLA_DATA(container_nla), NLA_DATA_LEN(container_nla)) {
795 void *item = snl_allocz(ss, p->out_size);
796
797 if (item == NULL)
798 return (false);
799
800 void *data = (char *)(void *)nla + data_off;
801 int data_len = nla->nla_len - data_off;
802
803 if (!(snl_parse_header(ss, data, data_len, p, item)))
804 return (false);
805
806 if (!snl_parray_append(ss, array, item, start_size))
807 return (false);
808 }
809
810 return (true);
811 }
812
813 /*
814 * Parses and stores the unknown-size array.
815 * Assumes each array item is a container and the NLAs in the container are
816 * parsable by the parser provided in @arg.
817 * @target must point to an actual struct snl_parray. Do not substitute a
818 * layout-compatible structure: the parser array includes private growth state.
819 */
820 static inline bool
snl_attr_get_parray(struct snl_state * ss,struct nlattr * nla,const void * arg,void * target)821 snl_attr_get_parray(struct snl_state *ss, struct nlattr *nla, const void *arg, void *target)
822 {
823 return (snl_attr_get_parray_sz(ss, nla, 8, arg,
824 (struct snl_parray *)target));
825 }
826
827 /*
828 * Append one repeated nested attribute to an array. Unlike
829 * snl_attr_get_parray(), the attribute itself is one array member rather
830 * than a container holding an entire array. Use this for modern Netlink
831 * multi-attributes. @target must point to an actual struct snl_parray.
832 */
833 static inline bool
snl_attr_get_multi(struct snl_state * ss,struct nlattr * nla,const void * arg,void * target)834 snl_attr_get_multi(struct snl_state *ss, struct nlattr *nla, const void *arg,
835 void *target)
836 {
837 const struct snl_hdr_parser *p = arg;
838 struct snl_parray *array = target;
839 void *item;
840
841 if (p->out_size == 0)
842 return (false);
843 item = snl_allocz(ss, p->out_size);
844 if (item == NULL)
845 return (false);
846 if (!snl_parse_header(ss, NLA_DATA(nla), NLA_DATA_LEN(nla), p, item))
847 return (false);
848 return (snl_parray_append(ss, array, item, 8));
849 }
850
851 static inline bool
snl_attr_get_nla(struct snl_state * ss __unused,struct nlattr * nla,const void * arg __unused,void * target)852 snl_attr_get_nla(struct snl_state *ss __unused, struct nlattr *nla,
853 const void *arg __unused, void *target)
854 {
855 *((struct nlattr **)target) = nla;
856 return (true);
857 }
858
859 static inline bool
snl_attr_dup_nla(struct snl_state * ss,struct nlattr * nla,const void * arg __unused,void * target)860 snl_attr_dup_nla(struct snl_state *ss, struct nlattr *nla,
861 const void *arg __unused, void *target)
862 {
863 void *ptr = snl_allocz(ss, nla->nla_len);
864
865 if (ptr != NULL) {
866 memcpy(ptr, nla, nla->nla_len);
867 *((void **)target) = ptr;
868 return (true);
869 }
870 return (false);
871 }
872
873 static inline bool
snl_attr_copy_struct(struct snl_state * ss,struct nlattr * nla,const void * arg __unused,void * target)874 snl_attr_copy_struct(struct snl_state *ss, struct nlattr *nla,
875 const void *arg __unused, void *target)
876 {
877 void *ptr = snl_allocz(ss, NLA_DATA_LEN(nla));
878
879 if (ptr != NULL) {
880 memcpy(ptr, NLA_DATA(nla), NLA_DATA_LEN(nla));
881 *((void **)target) = ptr;
882 return (true);
883 }
884 return (false);
885 }
886
887 static inline bool
snl_attr_dup_struct(struct snl_state * ss,struct nlattr * nla,const void * arg __unused,void * target)888 snl_attr_dup_struct(struct snl_state *ss, struct nlattr *nla,
889 const void *arg __unused, void *target)
890 {
891 void *ptr = snl_allocz(ss, NLA_DATA_LEN(nla));
892
893 if (ptr != NULL) {
894 memcpy(ptr, NLA_DATA(nla), NLA_DATA_LEN(nla));
895 *((void **)target) = ptr;
896 return (true);
897 }
898 return (false);
899 }
900
901 struct snl_attr_bit {
902 uint32_t bit_index;
903 char *bit_name;
904 int bit_value;
905 };
906
907 #define _OUT(_field) offsetof(struct snl_attr_bit, _field)
908 static const struct snl_attr_parser _nla_p_bit[] = {
909 { .type = NLA_BITSET_BIT_INDEX, .off = _OUT(bit_index), .cb = snl_attr_get_uint32 },
910 { .type = NLA_BITSET_BIT_NAME, .off = _OUT(bit_name), .cb = snl_attr_dup_string },
911 { .type = NLA_BITSET_BIT_VALUE, .off = _OUT(bit_value), .cb = snl_attr_get_flag },
912 };
913 #undef _OUT
914 SNL_DECLARE_ATTR_PARSER_EXT(_nla_bit_parser, sizeof(struct snl_attr_bit), _nla_p_bit, NULL);
915
916 struct snl_attr_bitset {
917 uint32_t nla_bitset_size;
918 uint32_t *nla_bitset_mask;
919 uint32_t *nla_bitset_value;
920 struct snl_parray bits;
921 };
922
923 #define _OUT(_field) offsetof(struct snl_attr_bitset, _field)
924 static const struct snl_attr_parser _nla_p_bitset[] = {
925 { .type = NLA_BITSET_SIZE, .off = _OUT(nla_bitset_size), .cb = snl_attr_get_uint32 },
926 { .type = NLA_BITSET_BITS, .off = _OUT(bits), .cb = snl_attr_get_parray, .arg = &_nla_bit_parser },
927 { .type = NLA_BITSET_VALUE, .off = _OUT(nla_bitset_mask), .cb = snl_attr_dup_nla },
928 { .type = NLA_BITSET_MASK, .off = _OUT(nla_bitset_value), .cb = snl_attr_dup_nla },
929 };
930
931 static inline bool
_cb_p_bitset(struct snl_state * ss __unused,void * _target)932 _cb_p_bitset(struct snl_state *ss __unused, void *_target)
933 {
934 struct snl_attr_bitset *target = (struct snl_attr_bitset *)_target;
935
936 uint32_t sz_bytes = _roundup2(target->nla_bitset_size, 32) / 8;
937
938 if (target->nla_bitset_mask != NULL) {
939 struct nlattr *nla = (struct nlattr *)target->nla_bitset_mask;
940 uint32_t data_len = NLA_DATA_LEN(nla);
941
942 if (data_len != sz_bytes || _roundup2(data_len, 4) != data_len)
943 return (false);
944 target->nla_bitset_mask = (uint32_t *)NLA_DATA(nla);
945 }
946
947 if (target->nla_bitset_value != NULL) {
948 struct nlattr *nla = (struct nlattr *)target->nla_bitset_value;
949 uint32_t data_len = NLA_DATA_LEN(nla);
950
951 if (data_len != sz_bytes || _roundup2(data_len, 4) != data_len)
952 return (false);
953 target->nla_bitset_value = (uint32_t *)NLA_DATA(nla);
954 }
955 return (true);
956 }
957 #undef _OUT
958 SNL_DECLARE_ATTR_PARSER_EXT(_nla_bitset_parser,
959 sizeof(struct snl_attr_bitset),
960 _nla_p_bitset, _cb_p_bitset);
961
962 /*
963 * Parses the compact bitset representation.
964 */
965 static inline bool
snl_attr_get_bitset_c(struct snl_state * ss,struct nlattr * nla,const void * arg __unused,void * _target)966 snl_attr_get_bitset_c(struct snl_state *ss, struct nlattr *nla,
967 const void *arg __unused, void *_target)
968 {
969 const struct snl_hdr_parser *p = &_nla_bitset_parser;
970 struct snl_attr_bitset *target = (struct snl_attr_bitset *)_target;
971
972 /* Assumes target points to the beginning of the structure */
973 if (!snl_parse_header(ss, NLA_DATA(nla), NLA_DATA_LEN(nla), p, _target))
974 return (false);
975 if (target->nla_bitset_mask == NULL || target->nla_bitset_value == NULL)
976 return (false);
977 return (true);
978 }
979
980 static inline void
snl_field_get_uint8(struct snl_state * ss __unused,void * src,void * target)981 snl_field_get_uint8(struct snl_state *ss __unused, void *src, void *target)
982 {
983 *((uint8_t *)target) = *((uint8_t *)src);
984 }
985
986 static inline void
snl_field_get_uint16(struct snl_state * ss __unused,void * src,void * target)987 snl_field_get_uint16(struct snl_state *ss __unused, void *src, void *target)
988 {
989 *((uint16_t *)target) = *((uint16_t *)src);
990 }
991
992 static inline void
snl_field_get_uint32(struct snl_state * ss __unused,void * src,void * target)993 snl_field_get_uint32(struct snl_state *ss __unused, void *src, void *target)
994 {
995 *((uint32_t *)target) = *((uint32_t *)src);
996 }
997
998 static inline void
snl_field_get_ptr(struct snl_state * ss __unused,void * src,void * target)999 snl_field_get_ptr(struct snl_state *ss __unused, void *src, void *target)
1000 {
1001 *((void **)target) = src;
1002 }
1003
1004 struct snl_errmsg_data {
1005 struct nlmsghdr *orig_hdr;
1006 int error;
1007 uint32_t error_offs;
1008 char *error_str;
1009 struct nlattr *cookie;
1010 };
1011
1012 #define _IN(_field) offsetof(struct nlmsgerr, _field)
1013 #define _OUT(_field) offsetof(struct snl_errmsg_data, _field)
1014 static const struct snl_attr_parser nla_p_errmsg[] = {
1015 { .type = NLMSGERR_ATTR_MSG, .off = _OUT(error_str), .cb = snl_attr_get_string },
1016 { .type = NLMSGERR_ATTR_OFFS, .off = _OUT(error_offs), .cb = snl_attr_get_uint32 },
1017 { .type = NLMSGERR_ATTR_COOKIE, .off = _OUT(cookie), .cb = snl_attr_get_nla },
1018 };
1019
1020 static const struct snl_field_parser nlf_p_errmsg[] = {
1021 { .off_in = _IN(error), .off_out = _OUT(error), .cb = snl_field_get_uint32 },
1022 { .off_in = _IN(msg), .off_out = _OUT(orig_hdr), .cb = snl_field_get_ptr },
1023 };
1024 #undef _IN
1025 #undef _OUT
1026 SNL_DECLARE_PARSER(snl_errmsg_parser, struct nlmsgerr, nlf_p_errmsg, nla_p_errmsg);
1027
1028 #define _IN(_field) offsetof(struct nlmsgerr, _field)
1029 #define _OUT(_field) offsetof(struct snl_errmsg_data, _field)
1030 static const struct snl_field_parser nlf_p_donemsg[] = {
1031 { .off_in = _IN(error), .off_out = _OUT(error), .cb = snl_field_get_uint32 },
1032 };
1033 #undef _IN
1034 #undef _OUT
1035 SNL_DECLARE_FIELD_PARSER(snl_donemsg_parser, struct nlmsgerr, nlf_p_donemsg);
1036
1037 static inline bool
snl_parse_errmsg(struct snl_state * ss,struct nlmsghdr * hdr,struct snl_errmsg_data * e)1038 snl_parse_errmsg(struct snl_state *ss, struct nlmsghdr *hdr, struct snl_errmsg_data *e)
1039 {
1040 if ((hdr->nlmsg_flags & NLM_F_CAPPED) != 0)
1041 return (snl_parse_nlmsg(ss, hdr, &snl_errmsg_parser, e));
1042
1043 const struct snl_hdr_parser *ps = &snl_errmsg_parser;
1044 struct nlmsgerr *errmsg = (struct nlmsgerr *)(hdr + 1);
1045 int hdrlen = sizeof(int) + NLMSG_ALIGN(errmsg->msg.nlmsg_len);
1046 struct nlattr *attr_head = (struct nlattr *)(void *)((char *)errmsg + hdrlen);
1047 int attr_len = hdr->nlmsg_len - sizeof(struct nlmsghdr) - hdrlen;
1048
1049 snl_parse_fields(ss, (struct nlmsghdr *)errmsg, hdrlen, ps->fp, ps->fp_size, e);
1050 return (snl_parse_attrs_raw(ss, attr_head, attr_len, ps->np, ps->np_size, e));
1051 }
1052
1053 static inline bool
snl_read_reply_code(struct snl_state * ss,uint32_t nlmsg_seq,struct snl_errmsg_data * e)1054 snl_read_reply_code(struct snl_state *ss, uint32_t nlmsg_seq, struct snl_errmsg_data *e)
1055 {
1056 struct nlmsghdr *hdr = snl_read_reply(ss, nlmsg_seq);
1057
1058 if (hdr == NULL) {
1059 e->error = EINVAL;
1060 } else if (hdr->nlmsg_type == NLMSG_ERROR) {
1061 if (!snl_parse_errmsg(ss, hdr, e))
1062 e->error = EINVAL;
1063 return (e->error == 0);
1064 }
1065
1066 return (false);
1067 }
1068
1069 #define _OUT(_field) offsetof(struct snl_msg_info, _field)
1070 static const struct snl_attr_parser _nla_p_cinfo[] = {
1071 { .type = NLMSGINFO_ATTR_PROCESS_ID, .off = _OUT(process_id), .cb = snl_attr_get_uint32 },
1072 { .type = NLMSGINFO_ATTR_PORT_ID, .off = _OUT(port_id), .cb = snl_attr_get_uint32 },
1073 { .type = NLMSGINFO_ATTR_SEQ_ID, .off = _OUT(seq_id), .cb = snl_attr_get_uint32 },
1074 };
1075 #undef _OUT
1076 SNL_DECLARE_ATTR_PARSER(snl_msg_info_parser, _nla_p_cinfo);
1077
1078 static inline bool
parse_cmsg(struct snl_state * ss,const struct msghdr * msg,struct snl_msg_info * attrs)1079 parse_cmsg(struct snl_state *ss, const struct msghdr *msg, struct snl_msg_info *attrs)
1080 {
1081 for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
1082 cmsg = CMSG_NXTHDR(msg, cmsg)) {
1083 if (cmsg->cmsg_level != SOL_NETLINK || cmsg->cmsg_type != NETLINK_MSG_INFO)
1084 continue;
1085
1086 void *data = CMSG_DATA(cmsg);
1087 int len = cmsg->cmsg_len - ((char *)data - (char *)cmsg);
1088 const struct snl_hdr_parser *ps = &snl_msg_info_parser;
1089
1090 return (snl_parse_attrs_raw(ss, (struct nlattr *)data, len, ps->np, ps->np_size, attrs));
1091 }
1092
1093 return (false);
1094 }
1095
1096 /*
1097 * Assumes e is zeroed
1098 */
1099 static inline struct nlmsghdr *
snl_read_reply_multi(struct snl_state * ss,uint32_t nlmsg_seq,struct snl_errmsg_data * e)1100 snl_read_reply_multi(struct snl_state *ss, uint32_t nlmsg_seq, struct snl_errmsg_data *e)
1101 {
1102 struct nlmsghdr *hdr = snl_read_reply(ss, nlmsg_seq);
1103
1104 if (hdr == NULL) {
1105 e->error = EINVAL;
1106 } else if (hdr->nlmsg_type == NLMSG_ERROR) {
1107 if (!snl_parse_errmsg(ss, hdr, e))
1108 e->error = EINVAL;
1109 } else if (hdr->nlmsg_type == NLMSG_DONE) {
1110 snl_parse_nlmsg(ss, hdr, &snl_donemsg_parser, e);
1111 } else
1112 return (hdr);
1113
1114 return (NULL);
1115 }
1116
1117
1118 /* writer logic */
1119 struct snl_writer {
1120 char *base;
1121 uint32_t offset;
1122 uint32_t size;
1123 struct nlmsghdr *hdr;
1124 struct snl_state *ss;
1125 bool error;
1126 };
1127
1128 static inline void
snl_init_writer(struct snl_state * ss,struct snl_writer * nw)1129 snl_init_writer(struct snl_state *ss, struct snl_writer *nw)
1130 {
1131 nw->size = SNL_WRITER_BUFFER_SIZE;
1132 nw->base = (char *)snl_allocz(ss, nw->size);
1133 if (__predict_false(nw->base == NULL)) {
1134 nw->error = true;
1135 nw->size = 0;
1136 } else
1137 nw->error = false;
1138
1139 nw->offset = 0;
1140 nw->hdr = NULL;
1141 nw->ss = ss;
1142 }
1143
1144 static inline bool
snl_realloc_msg_buffer(struct snl_writer * nw,size_t sz)1145 snl_realloc_msg_buffer(struct snl_writer *nw, size_t sz)
1146 {
1147 uint32_t new_size = nw->size * 2;
1148 char *new_base;
1149
1150 while (new_size < nw->size + sz)
1151 new_size *= 2;
1152
1153 if (nw->error)
1154 return (false);
1155
1156 new_base = snl_allocz(nw->ss, new_size);
1157 if (new_base == NULL) {
1158 nw->error = true;
1159 return (false);
1160 }
1161
1162 memcpy(new_base, nw->base, nw->offset);
1163 if (nw->hdr != NULL) {
1164 int hdr_off = (char *)(nw->hdr) - nw->base;
1165
1166 nw->hdr = (struct nlmsghdr *)(void *)(new_base + hdr_off);
1167 }
1168 nw->base = new_base;
1169 nw->size = new_size;
1170
1171 return (true);
1172 }
1173
1174 static inline void *
snl_reserve_msg_data_raw(struct snl_writer * nw,size_t sz)1175 snl_reserve_msg_data_raw(struct snl_writer *nw, size_t sz)
1176 {
1177 sz = NETLINK_ALIGN(sz);
1178
1179 if (__predict_false(nw->offset + sz > nw->size)) {
1180 if (!snl_realloc_msg_buffer(nw, sz))
1181 return (NULL);
1182 }
1183
1184 void *data_ptr = &nw->base[nw->offset];
1185 nw->offset += sz;
1186
1187 return (data_ptr);
1188 }
1189 #define snl_reserve_msg_object(_ns, _t) ((_t *)snl_reserve_msg_data_raw(_ns, sizeof(_t)))
1190 #define snl_reserve_msg_data(_ns, _sz, _t) ((_t *)snl_reserve_msg_data_raw(_ns, _sz))
1191
1192 static inline struct nlattr *
snl_reserve_msg_attr_raw(struct snl_writer * nw,uint16_t nla_type,uint16_t sz)1193 snl_reserve_msg_attr_raw(struct snl_writer *nw, uint16_t nla_type, uint16_t sz)
1194 {
1195 struct nlattr *nla;
1196
1197 sz += sizeof(struct nlattr);
1198 nla = snl_reserve_msg_data(nw, sz, struct nlattr);
1199 if (__predict_false(nla == NULL))
1200 return (NULL);
1201 nla->nla_type = nla_type;
1202 nla->nla_len = sz;
1203
1204 return (nla);
1205 }
1206 #define snl_reserve_msg_attr(_ns, _at, _t) \
1207 ((_t *)(snl_reserve_msg_attr_raw(_ns, _at, sizeof(_t)) + 1))
1208
1209 static inline bool
snl_add_msg_attr(struct snl_writer * nw,int attr_type,int attr_len,const void * data)1210 snl_add_msg_attr(struct snl_writer *nw, int attr_type, int attr_len, const void *data)
1211 {
1212 int required_len = NLA_ALIGN(attr_len + sizeof(struct nlattr));
1213
1214 if (__predict_false(nw->offset + required_len > nw->size)) {
1215 if (!snl_realloc_msg_buffer(nw, required_len))
1216 return (false);
1217 }
1218
1219 struct nlattr *nla = (struct nlattr *)(void *)(&nw->base[nw->offset]);
1220
1221 nla->nla_len = attr_len + sizeof(struct nlattr);
1222 nla->nla_type = attr_type;
1223 if (attr_len > 0) {
1224 if ((attr_len % 4) != 0) {
1225 /* clear padding bytes */
1226 bzero((char *)nla + required_len - 4, 4);
1227 }
1228 memcpy((nla + 1), data, attr_len);
1229 }
1230 nw->offset += required_len;
1231 return (true);
1232 }
1233
1234 static inline bool
snl_add_msg_attr_raw(struct snl_writer * nw,const struct nlattr * nla_src)1235 snl_add_msg_attr_raw(struct snl_writer *nw, const struct nlattr *nla_src)
1236 {
1237 int attr_len = nla_src->nla_len - sizeof(struct nlattr);
1238
1239 assert(attr_len >= 0);
1240
1241 return (snl_add_msg_attr(nw, nla_src->nla_type, attr_len, (const void *)(nla_src + 1)));
1242 }
1243
1244 static inline bool
snl_add_msg_attr_bool(struct snl_writer * nw,int attrtype,bool value)1245 snl_add_msg_attr_bool(struct snl_writer *nw, int attrtype, bool value)
1246 {
1247 return (snl_add_msg_attr(nw, attrtype, sizeof(bool), &value));
1248 }
1249
1250 static inline bool
snl_add_msg_attr_u8(struct snl_writer * nw,int attrtype,uint8_t value)1251 snl_add_msg_attr_u8(struct snl_writer *nw, int attrtype, uint8_t value)
1252 {
1253 return (snl_add_msg_attr(nw, attrtype, sizeof(uint8_t), &value));
1254 }
1255
1256 static inline bool
snl_add_msg_attr_u16(struct snl_writer * nw,int attrtype,uint16_t value)1257 snl_add_msg_attr_u16(struct snl_writer *nw, int attrtype, uint16_t value)
1258 {
1259 return (snl_add_msg_attr(nw, attrtype, sizeof(uint16_t), &value));
1260 }
1261
1262 static inline bool
snl_add_msg_attr_u32(struct snl_writer * nw,int attrtype,uint32_t value)1263 snl_add_msg_attr_u32(struct snl_writer *nw, int attrtype, uint32_t value)
1264 {
1265 return (snl_add_msg_attr(nw, attrtype, sizeof(uint32_t), &value));
1266 }
1267
1268 static inline bool
snl_add_msg_attr_u64(struct snl_writer * nw,int attrtype,uint64_t value)1269 snl_add_msg_attr_u64(struct snl_writer *nw, int attrtype, uint64_t value)
1270 {
1271 return (snl_add_msg_attr(nw, attrtype, sizeof(uint64_t), &value));
1272 }
1273
1274 static inline bool
snl_add_msg_attr_s8(struct snl_writer * nw,int attrtype,int8_t value)1275 snl_add_msg_attr_s8(struct snl_writer *nw, int attrtype, int8_t value)
1276 {
1277 return (snl_add_msg_attr(nw, attrtype, sizeof(int8_t), &value));
1278 }
1279
1280 static inline bool
snl_add_msg_attr_s16(struct snl_writer * nw,int attrtype,int16_t value)1281 snl_add_msg_attr_s16(struct snl_writer *nw, int attrtype, int16_t value)
1282 {
1283 return (snl_add_msg_attr(nw, attrtype, sizeof(int16_t), &value));
1284 }
1285
1286 static inline bool
snl_add_msg_attr_s32(struct snl_writer * nw,int attrtype,int32_t value)1287 snl_add_msg_attr_s32(struct snl_writer *nw, int attrtype, int32_t value)
1288 {
1289 return (snl_add_msg_attr(nw, attrtype, sizeof(int32_t), &value));
1290 }
1291
1292 static inline bool
snl_add_msg_attr_s64(struct snl_writer * nw,int attrtype,int64_t value)1293 snl_add_msg_attr_s64(struct snl_writer *nw, int attrtype, int64_t value)
1294 {
1295 return (snl_add_msg_attr(nw, attrtype, sizeof(int64_t), &value));
1296 }
1297
1298 static inline bool
snl_add_msg_attr_flag(struct snl_writer * nw,int attrtype)1299 snl_add_msg_attr_flag(struct snl_writer *nw, int attrtype)
1300 {
1301 return (snl_add_msg_attr(nw, attrtype, 0, NULL));
1302 }
1303
1304 static inline bool
snl_add_msg_attr_string(struct snl_writer * nw,int attrtype,const char * str)1305 snl_add_msg_attr_string(struct snl_writer *nw, int attrtype, const char *str)
1306 {
1307 return (snl_add_msg_attr(nw, attrtype, strlen(str) + 1, str));
1308 }
1309
1310
1311 static inline int
snl_get_msg_offset(const struct snl_writer * nw)1312 snl_get_msg_offset(const struct snl_writer *nw)
1313 {
1314 return (nw->offset - ((char *)nw->hdr - nw->base));
1315 }
1316
1317 static inline void *
_snl_restore_msg_offset(const struct snl_writer * nw,int off)1318 _snl_restore_msg_offset(const struct snl_writer *nw, int off)
1319 {
1320 return ((void *)((char *)nw->hdr + off));
1321 }
1322 #define snl_restore_msg_offset(_ns, _off, _t) ((_t *)_snl_restore_msg_offset(_ns, _off))
1323
1324 static inline int
snl_add_msg_attr_nested(struct snl_writer * nw,int attrtype)1325 snl_add_msg_attr_nested(struct snl_writer *nw, int attrtype)
1326 {
1327 int off = snl_get_msg_offset(nw);
1328 struct nlattr *nla = snl_reserve_msg_data(nw, sizeof(struct nlattr), struct nlattr);
1329 if (__predict_false(nla == NULL))
1330 return (0);
1331 nla->nla_type = attrtype;
1332 return (off);
1333 }
1334
1335 static inline void
snl_end_attr_nested(const struct snl_writer * nw,int off)1336 snl_end_attr_nested(const struct snl_writer *nw, int off)
1337 {
1338 if (!nw->error) {
1339 struct nlattr *nla = snl_restore_msg_offset(nw, off, struct nlattr);
1340 nla->nla_len = NETLINK_ALIGN(snl_get_msg_offset(nw) - off);
1341 }
1342 }
1343
1344 static inline struct nlmsghdr *
snl_create_msg_request(struct snl_writer * nw,int nlmsg_type)1345 snl_create_msg_request(struct snl_writer *nw, int nlmsg_type)
1346 {
1347 struct nlmsghdr *hdr;
1348
1349 assert(nw->hdr == NULL);
1350
1351 if (__predict_false((hdr =
1352 snl_reserve_msg_object(nw, struct nlmsghdr)) == NULL))
1353 return (NULL);
1354 hdr->nlmsg_type = nlmsg_type;
1355 hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
1356 nw->hdr = hdr;
1357
1358 return (hdr);
1359 }
1360
1361 static void
snl_abort_msg(struct snl_writer * nw)1362 snl_abort_msg(struct snl_writer *nw)
1363 {
1364 if (nw->hdr != NULL) {
1365 int offset = (char *)(&nw->base[nw->offset]) - (char *)(nw->hdr);
1366
1367 nw->offset -= offset;
1368 nw->hdr = NULL;
1369 }
1370 }
1371
1372 static inline struct nlmsghdr *
snl_finalize_msg(struct snl_writer * nw)1373 snl_finalize_msg(struct snl_writer *nw)
1374 {
1375 if (nw->error)
1376 snl_abort_msg(nw);
1377 if (nw->hdr != NULL) {
1378 struct nlmsghdr *hdr = nw->hdr;
1379
1380 int offset = (char *)(&nw->base[nw->offset]) - (char *)(nw->hdr);
1381 hdr->nlmsg_len = offset;
1382 hdr->nlmsg_seq = snl_get_seq(nw->ss);
1383 nw->hdr = NULL;
1384
1385 return (hdr);
1386 }
1387 return (NULL);
1388 }
1389
1390 static inline bool
snl_send_msgs(struct snl_writer * nw)1391 snl_send_msgs(struct snl_writer *nw)
1392 {
1393 int offset = nw->offset;
1394
1395 assert(nw->hdr == NULL);
1396 nw->offset = 0;
1397
1398 return (snl_send(nw->ss, nw->base, offset));
1399 }
1400
1401 #endif
1402