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 /* Ensure the receive buffer can hold the next complete Netlink message. */
329 static inline int
snl_grow_rxbuf_to_next_message(struct snl_state * ss)330 snl_grow_rxbuf_to_next_message(struct snl_state *ss)
331 {
332 char *buf;
333 ssize_t len;
334
335 if (ss->off != ss->datalen)
336 return (0);
337 do {
338 len = recv(ss->fd, NULL, 0, MSG_PEEK | MSG_TRUNC);
339 } while (len < 0 && errno == EINTR);
340 if (len < 0)
341 return (errno);
342 if (len == 0)
343 return (EIO);
344 if ((size_t)len <= ss->bufsize)
345 return (0);
346 buf = realloc(ss->buf, (size_t)len);
347 if (buf == NULL)
348 return (ENOMEM);
349 ss->buf = buf;
350 ss->bufsize = (size_t)len;
351 return (0);
352 }
353
354 static inline uint32_t
snl_get_seq(struct snl_state * ss)355 snl_get_seq(struct snl_state *ss)
356 {
357 return (++ss->seq);
358 }
359
360 struct snl_msg_info {
361 int cmsg_type;
362 int cmsg_level;
363 uint32_t process_id;
364 uint8_t port_id;
365 uint8_t seq_id;
366 };
367 static inline bool parse_cmsg(struct snl_state *ss, const struct msghdr *msg,
368 struct snl_msg_info *attrs);
369
370 static inline struct nlmsghdr *
snl_read_message_dbg(struct snl_state * ss,struct snl_msg_info * cinfo)371 snl_read_message_dbg(struct snl_state *ss, struct snl_msg_info *cinfo)
372 {
373 memset(cinfo, 0, sizeof(*cinfo));
374
375 if (ss->off == ss->datalen) {
376 struct sockaddr_nl nladdr;
377 char cbuf[64];
378
379 struct iovec iov = {
380 .iov_base = ss->buf,
381 .iov_len = ss->bufsize,
382 };
383 struct msghdr msg = {
384 .msg_name = &nladdr,
385 .msg_namelen = sizeof(nladdr),
386 .msg_iov = &iov,
387 .msg_iovlen = 1,
388 .msg_control = cbuf,
389 .msg_controllen = sizeof(cbuf),
390 };
391 ss->off = 0;
392 ss->datalen = 0;
393 for (;;) {
394 ssize_t datalen = recvmsg(ss->fd, &msg, 0);
395 if (datalen > 0) {
396 ss->datalen = datalen;
397 parse_cmsg(ss, &msg, cinfo);
398 break;
399 } else if (errno != EINTR)
400 return (NULL);
401 }
402 }
403 struct nlmsghdr *hdr = (struct nlmsghdr *)(void *)&ss->buf[ss->off];
404 ss->off += NLMSG_ALIGN(hdr->nlmsg_len);
405 return (hdr);
406 }
407
408
409 static inline struct nlmsghdr *
snl_read_message(struct snl_state * ss)410 snl_read_message(struct snl_state *ss)
411 {
412 if (ss->off == ss->datalen) {
413 struct sockaddr_nl nladdr;
414 struct iovec iov = {
415 .iov_base = ss->buf,
416 .iov_len = ss->bufsize,
417 };
418 struct msghdr msg = {
419 .msg_name = &nladdr,
420 .msg_namelen = sizeof(nladdr),
421 .msg_iov = &iov,
422 .msg_iovlen = 1,
423 };
424 ss->off = 0;
425 ss->datalen = 0;
426 for (;;) {
427 ssize_t datalen = recvmsg(ss->fd, &msg, 0);
428 if (datalen > 0) {
429 ss->datalen = datalen;
430 break;
431 } else if (errno != EINTR)
432 return (NULL);
433 }
434 }
435 struct nlmsghdr *hdr = (struct nlmsghdr *)(void *)&ss->buf[ss->off];
436 ss->off += NLMSG_ALIGN(hdr->nlmsg_len);
437 return (hdr);
438 }
439
440 static inline struct nlmsghdr *
snl_read_reply(struct snl_state * ss,uint32_t nlmsg_seq)441 snl_read_reply(struct snl_state *ss, uint32_t nlmsg_seq)
442 {
443 struct nlmsghdr *hdr;
444
445 while ((hdr = snl_read_message(ss)) != NULL) {
446 if (hdr->nlmsg_seq == nlmsg_seq)
447 return (hdr);
448 }
449
450 return (NULL);
451 }
452
453 /*
454 * Checks that attributes are sorted by attribute type.
455 */
456 static inline void
snl_verify_parsers(const struct snl_hdr_parser ** parser,int count)457 snl_verify_parsers(const struct snl_hdr_parser **parser, int count)
458 {
459 for (int i = 0; i < count; i++) {
460 const struct snl_hdr_parser *p = parser[i];
461 int attr_type = 0;
462 for (int j = 0; j < p->np_size; j++) {
463 assert(p->np[j].type > attr_type);
464 attr_type = p->np[j].type;
465 }
466 }
467 }
468 #define SNL_VERIFY_PARSERS(_p) snl_verify_parsers((_p), nitems(_p))
469
470 static const struct snl_attr_parser *
find_parser(const struct snl_attr_parser * ps,int pslen,int key)471 find_parser(const struct snl_attr_parser *ps, int pslen, int key)
472 {
473 int left_i = 0, right_i = pslen - 1;
474
475 if (key < ps[0].type || key > ps[pslen - 1].type)
476 return (NULL);
477
478 while (left_i + 1 < right_i) {
479 int mid_i = (left_i + right_i) / 2;
480 if (key < ps[mid_i].type)
481 right_i = mid_i;
482 else if (key > ps[mid_i].type)
483 left_i = mid_i + 1;
484 else
485 return (&ps[mid_i]);
486 }
487 if (ps[left_i].type == key)
488 return (&ps[left_i]);
489 else if (ps[right_i].type == key)
490 return (&ps[right_i]);
491 return (NULL);
492 }
493
494 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)495 snl_parse_attrs_raw(struct snl_state *ss, struct nlattr *nla_head, int len,
496 const struct snl_attr_parser *ps, int pslen, void *target)
497 {
498 struct nlattr *nla;
499
500 NLA_FOREACH(nla, nla_head, len) {
501 if (nla->nla_len < sizeof(struct nlattr))
502 return (false);
503 int nla_type = nla->nla_type & NLA_TYPE_MASK;
504 const struct snl_attr_parser *s = find_parser(ps, pslen, nla_type);
505 if (s != NULL) {
506 void *ptr = (void *)((char *)target + s->off);
507 if (!s->cb(ss, nla, s->arg, ptr))
508 return (false);
509 }
510 }
511 return (true);
512 }
513
514 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)515 snl_parse_attrs(struct snl_state *ss, struct nlmsghdr *hdr, int hdrlen,
516 const struct snl_attr_parser *ps, int pslen, void *target)
517 {
518 int off = NLMSG_HDRLEN + NETLINK_ALIGN(hdrlen);
519 int len = hdr->nlmsg_len - off;
520 struct nlattr *nla_head = (struct nlattr *)(void *)((char *)hdr + off);
521
522 return (snl_parse_attrs_raw(ss, nla_head, len, ps, pslen, target));
523 }
524
525 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)526 snl_parse_fields(struct snl_state *ss, struct nlmsghdr *hdr, int hdrlen __unused,
527 const struct snl_field_parser *ps, int pslen, void *target)
528 {
529 for (int i = 0; i < pslen; i++) {
530 const struct snl_field_parser *fp = &ps[i];
531 void *src = (char *)hdr + fp->off_in;
532 void *dst = (char *)target + fp->off_out;
533
534 fp->cb(ss, src, dst);
535 }
536 }
537
538 static inline bool
snl_parse_header(struct snl_state * ss,void * hdr,int len,const struct snl_hdr_parser * parser,void * target)539 snl_parse_header(struct snl_state *ss, void *hdr, int len,
540 const struct snl_hdr_parser *parser, void *target)
541 {
542 struct nlattr *nla_head;
543
544 /* Extract fields first (if any) */
545 snl_parse_fields(ss, (struct nlmsghdr *)hdr, parser->in_hdr_size,
546 parser->fp, parser->fp_size, target);
547
548 nla_head = (struct nlattr *)(void *)((char *)hdr + parser->in_hdr_size);
549 bool result = snl_parse_attrs_raw(ss, nla_head, len - parser->in_hdr_size,
550 parser->np, parser->np_size, target);
551
552 if (result && parser->cb_post != NULL)
553 result = parser->cb_post(ss, target);
554
555 return (result);
556 }
557
558 static inline bool
snl_parse_nlmsg(struct snl_state * ss,struct nlmsghdr * hdr,const struct snl_hdr_parser * parser,void * target)559 snl_parse_nlmsg(struct snl_state *ss, struct nlmsghdr *hdr,
560 const struct snl_hdr_parser *parser, void *target)
561 {
562 return (snl_parse_header(ss, hdr + 1, hdr->nlmsg_len - sizeof(*hdr), parser, target));
563 }
564
565 static inline bool
snl_attr_get_flag(struct snl_state * ss __unused,struct nlattr * nla,const void * arg __unused,void * target)566 snl_attr_get_flag(struct snl_state *ss __unused, struct nlattr *nla, const void *arg __unused,
567 void *target)
568 {
569 if (NLA_DATA_LEN(nla) == 0) {
570 *((uint8_t *)target) = 1;
571 return (true);
572 }
573 return (false);
574 }
575
576 static inline bool
snl_attr_get_bytes(struct snl_state * ss __unused,struct nlattr * nla,const void * arg,void * target)577 snl_attr_get_bytes(struct snl_state *ss __unused, struct nlattr *nla, const void *arg,
578 void *target)
579 {
580 if ((size_t)NLA_DATA_LEN(nla) != (size_t)arg)
581 return (false);
582
583 memcpy(target, NLA_DATA_CONST(nla), (size_t)arg);
584
585 return (true);
586 }
587
588 static inline bool
snl_attr_get_bool(struct snl_state * ss __unused,struct nlattr * nla,const void * arg __unused,void * target)589 snl_attr_get_bool(struct snl_state *ss __unused, struct nlattr *nla,
590 const void *arg __unused, void *target)
591 {
592 if (NLA_DATA_LEN(nla) == sizeof(bool)) {
593 *((bool *)target) = *((const bool *)NLA_DATA_CONST(nla));
594 return (true);
595 }
596 return (false);
597 }
598
599 static inline bool
snl_attr_get_uint8(struct snl_state * ss __unused,struct nlattr * nla,const void * arg __unused,void * target)600 snl_attr_get_uint8(struct snl_state *ss __unused, struct nlattr *nla,
601 const void *arg __unused, void *target)
602 {
603 if (NLA_DATA_LEN(nla) == sizeof(uint8_t)) {
604 *((uint8_t *)target) = *((const uint8_t *)NLA_DATA_CONST(nla));
605 return (true);
606 }
607 return (false);
608 }
609
610 static inline bool
snl_attr_get_uint16(struct snl_state * ss __unused,struct nlattr * nla,const void * arg __unused,void * target)611 snl_attr_get_uint16(struct snl_state *ss __unused, struct nlattr *nla,
612 const void *arg __unused, void *target)
613 {
614 if (NLA_DATA_LEN(nla) == sizeof(uint16_t)) {
615 *((uint16_t *)target) = *((const uint16_t *)NLA_DATA_CONST(nla));
616 return (true);
617 }
618 return (false);
619 }
620
621 static inline bool
snl_attr_get_uint32(struct snl_state * ss __unused,struct nlattr * nla,const void * arg __unused,void * target)622 snl_attr_get_uint32(struct snl_state *ss __unused, struct nlattr *nla,
623 const void *arg __unused, void *target)
624 {
625 if (NLA_DATA_LEN(nla) == sizeof(uint32_t)) {
626 *((uint32_t *)target) = *((const uint32_t *)NLA_DATA_CONST(nla));
627 return (true);
628 }
629 return (false);
630 }
631
632 static inline bool
snl_attr_get_uint64(struct snl_state * ss __unused,struct nlattr * nla,const void * arg __unused,void * target)633 snl_attr_get_uint64(struct snl_state *ss __unused, struct nlattr *nla,
634 const void *arg __unused, void *target)
635 {
636 if (NLA_DATA_LEN(nla) == sizeof(uint64_t)) {
637 memcpy(target, NLA_DATA_CONST(nla), sizeof(uint64_t));
638 return (true);
639 }
640 return (false);
641 }
642
643 static inline bool
snl_attr_get_int8(struct snl_state * ss,struct nlattr * nla,const void * arg,void * target)644 snl_attr_get_int8(struct snl_state *ss, struct nlattr *nla, const void *arg,
645 void *target)
646 {
647 return (snl_attr_get_uint8(ss, nla, arg, target));
648 }
649
650 static inline bool
snl_attr_get_int16(struct snl_state * ss,struct nlattr * nla,const void * arg,void * target)651 snl_attr_get_int16(struct snl_state *ss, struct nlattr *nla, const void *arg,
652 void *target)
653 {
654 return (snl_attr_get_uint16(ss, nla, arg, target));
655 }
656
657 static inline bool
snl_attr_get_int32(struct snl_state * ss,struct nlattr * nla,const void * arg,void * target)658 snl_attr_get_int32(struct snl_state *ss, struct nlattr *nla, const void *arg,
659 void *target)
660 {
661 return (snl_attr_get_uint32(ss, nla, arg, target));
662 }
663
664 static inline bool
snl_attr_get_int64(struct snl_state * ss,struct nlattr * nla,const void * arg,void * target)665 snl_attr_get_int64(struct snl_state *ss, struct nlattr *nla, const void *arg,
666 void *target)
667 {
668 return (snl_attr_get_uint64(ss, nla, arg, target));
669 }
670
671 static inline bool
snl_attr_get_time_t(struct snl_state * ss __unused,struct nlattr * nla,const void * arg __unused,void * target)672 snl_attr_get_time_t(struct snl_state *ss __unused, struct nlattr *nla,
673 const void *arg __unused, void *target)
674 {
675 if (NLA_DATA_LEN(nla) == sizeof(time_t)) {
676 memcpy(target, NLA_DATA_CONST(nla), sizeof(time_t));
677 return (true);
678 }
679 return (false);
680 }
681
682 static inline bool
snl_attr_get_string(struct snl_state * ss __unused,struct nlattr * nla,const void * arg __unused,void * target)683 snl_attr_get_string(struct snl_state *ss __unused, struct nlattr *nla,
684 const void *arg __unused, void *target)
685 {
686 size_t maxlen = NLA_DATA_LEN(nla);
687
688 if (strnlen((char *)NLA_DATA(nla), maxlen) < maxlen) {
689 *((char **)target) = (char *)NLA_DATA(nla);
690 return (true);
691 }
692 return (false);
693 }
694
695 static inline bool
snl_attr_get_stringn(struct snl_state * ss,struct nlattr * nla,const void * arg __unused,void * target)696 snl_attr_get_stringn(struct snl_state *ss, struct nlattr *nla,
697 const void *arg __unused, void *target)
698 {
699 int maxlen = NLA_DATA_LEN(nla);
700
701 char *buf = (char *)snl_allocz(ss, maxlen + 1);
702 if (buf == NULL)
703 return (false);
704 buf[maxlen] = '\0';
705 memcpy(buf, NLA_DATA(nla), maxlen);
706
707 *((char **)target) = buf;
708 return (true);
709 }
710
711 static inline bool
snl_attr_copy_string(struct snl_state * ss,struct nlattr * nla,const void * arg,void * target)712 snl_attr_copy_string(struct snl_state *ss, struct nlattr *nla,
713 const void *arg, void *target)
714 {
715 char *tmp;
716
717 if (snl_attr_get_string(ss, nla, NULL, &tmp)) {
718 strlcpy((char *)target, tmp, (size_t)arg);
719 return (true);
720 }
721 return (false);
722 }
723
724 static inline bool
snl_attr_dup_string(struct snl_state * ss __unused,struct nlattr * nla,const void * arg __unused,void * target)725 snl_attr_dup_string(struct snl_state *ss __unused, struct nlattr *nla,
726 const void *arg __unused, void *target)
727 {
728 size_t maxlen = NLA_DATA_LEN(nla);
729
730 if (strnlen((char *)NLA_DATA(nla), maxlen) < maxlen) {
731 char *buf = (char *)snl_allocz(ss, maxlen);
732 if (buf == NULL)
733 return (false);
734 memcpy(buf, NLA_DATA(nla), maxlen);
735 *((char **)target) = buf;
736 return (true);
737 }
738 return (false);
739 }
740
741 static inline bool
snl_attr_get_nested(struct snl_state * ss,struct nlattr * nla,const void * arg,void * target)742 snl_attr_get_nested(struct snl_state *ss, struct nlattr *nla, const void *arg, void *target)
743 {
744 const struct snl_hdr_parser *p = (const struct snl_hdr_parser *)arg;
745
746 /* Assumes target points to the beginning of the structure */
747 return (snl_parse_header(ss, NLA_DATA(nla), NLA_DATA_LEN(nla), p, target));
748 }
749
750 struct snl_parray {
751 uint32_t count;
752 void **items;
753 uint32_t _capacity;
754 };
755
756 static inline bool
snl_parray_append(struct snl_state * ss,struct snl_parray * array,void * item,uint32_t start_size)757 snl_parray_append(struct snl_state *ss, struct snl_parray *array, void *item,
758 uint32_t start_size)
759 {
760 void **new_items;
761 size_t alloc_size;
762 uint32_t new_size;
763
764 if (start_size == 0)
765 return (false);
766 if (array->_capacity == 0) {
767 new_size = start_size;
768 if (__builtin_mul_overflow((size_t)new_size,
769 sizeof(*new_items), &alloc_size))
770 return (false);
771 array->items = (void **)snl_allocz(ss, alloc_size);
772 if (array->items == NULL)
773 return (false);
774 array->_capacity = new_size;
775 } else if (array->count == array->_capacity) {
776 if (array->_capacity > UINT32_MAX / 2)
777 return (false);
778 new_size = array->_capacity * 2;
779 if (__builtin_mul_overflow((size_t)new_size,
780 sizeof(*new_items), &alloc_size))
781 return (false);
782 new_items = (void **)snl_allocz(ss, alloc_size);
783 if (new_items == NULL)
784 return (false);
785 memcpy(new_items, array->items,
786 array->count * sizeof(*new_items));
787 array->items = new_items;
788 array->_capacity = new_size;
789 }
790 array->items[array->count++] = item;
791 return (true);
792 }
793
794 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)795 snl_attr_get_parray_sz(struct snl_state *ss, struct nlattr *container_nla,
796 uint32_t start_size, const void *arg, struct snl_parray *array)
797 {
798 const struct snl_hdr_parser *p = (const struct snl_hdr_parser *)arg;
799 struct nlattr *nla;
800
801 if (p->out_size == 0 || start_size == 0)
802 return (false);
803 /* A container attribute replaces, rather than extends, its output. */
804 array->count = 0;
805 array->items = NULL;
806 array->_capacity = 0;
807
808 /*
809 * If the provided parser is an attribute parser, assume that each
810 * nla in the container nla is the container nla itself and parse
811 * the contents of this nla.
812 * Otherwise, run the parser on raw data, assuming the header of this
813 * data has u16 field with total size in the beginning.
814 */
815 uint32_t data_off = 0;
816
817 if (p->in_hdr_size == 0)
818 data_off = sizeof(struct nlattr);
819
820 NLA_FOREACH(nla, NLA_DATA(container_nla), NLA_DATA_LEN(container_nla)) {
821 void *item = snl_allocz(ss, p->out_size);
822
823 if (item == NULL)
824 return (false);
825
826 void *data = (char *)(void *)nla + data_off;
827 int data_len = nla->nla_len - data_off;
828
829 if (!(snl_parse_header(ss, data, data_len, p, item)))
830 return (false);
831
832 if (!snl_parray_append(ss, array, item, start_size))
833 return (false);
834 }
835
836 return (true);
837 }
838
839 /*
840 * Parses and stores the unknown-size array.
841 * Assumes each array item is a container and the NLAs in the container are
842 * parsable by the parser provided in @arg.
843 * @target must point to an actual struct snl_parray. Do not substitute a
844 * layout-compatible structure: the parser array includes private growth state.
845 */
846 static inline bool
snl_attr_get_parray(struct snl_state * ss,struct nlattr * nla,const void * arg,void * target)847 snl_attr_get_parray(struct snl_state *ss, struct nlattr *nla, const void *arg, void *target)
848 {
849 return (snl_attr_get_parray_sz(ss, nla, 8, arg,
850 (struct snl_parray *)target));
851 }
852
853 /*
854 * Append one repeated nested attribute to an array. Unlike
855 * snl_attr_get_parray(), the attribute itself is one array member rather
856 * than a container holding an entire array. Use this for modern Netlink
857 * multi-attributes. @target must point to an actual struct snl_parray.
858 */
859 static inline bool
snl_attr_get_multi(struct snl_state * ss,struct nlattr * nla,const void * arg,void * target)860 snl_attr_get_multi(struct snl_state *ss, struct nlattr *nla, const void *arg,
861 void *target)
862 {
863 const struct snl_hdr_parser *p = arg;
864 struct snl_parray *array = target;
865 void *item;
866
867 if (p->out_size == 0)
868 return (false);
869 item = snl_allocz(ss, p->out_size);
870 if (item == NULL)
871 return (false);
872 if (!snl_parse_header(ss, NLA_DATA(nla), NLA_DATA_LEN(nla), p, item))
873 return (false);
874 return (snl_parray_append(ss, array, item, 8));
875 }
876
877 static inline bool
snl_attr_get_nla(struct snl_state * ss __unused,struct nlattr * nla,const void * arg __unused,void * target)878 snl_attr_get_nla(struct snl_state *ss __unused, struct nlattr *nla,
879 const void *arg __unused, void *target)
880 {
881 *((struct nlattr **)target) = nla;
882 return (true);
883 }
884
885 static inline bool
snl_attr_dup_nla(struct snl_state * ss,struct nlattr * nla,const void * arg __unused,void * target)886 snl_attr_dup_nla(struct snl_state *ss, struct nlattr *nla,
887 const void *arg __unused, void *target)
888 {
889 void *ptr = snl_allocz(ss, nla->nla_len);
890
891 if (ptr != NULL) {
892 memcpy(ptr, nla, nla->nla_len);
893 *((void **)target) = ptr;
894 return (true);
895 }
896 return (false);
897 }
898
899 static inline bool
snl_attr_copy_struct(struct snl_state * ss,struct nlattr * nla,const void * arg __unused,void * target)900 snl_attr_copy_struct(struct snl_state *ss, struct nlattr *nla,
901 const void *arg __unused, void *target)
902 {
903 void *ptr = snl_allocz(ss, NLA_DATA_LEN(nla));
904
905 if (ptr != NULL) {
906 memcpy(ptr, NLA_DATA(nla), NLA_DATA_LEN(nla));
907 *((void **)target) = ptr;
908 return (true);
909 }
910 return (false);
911 }
912
913 static inline bool
snl_attr_dup_struct(struct snl_state * ss,struct nlattr * nla,const void * arg __unused,void * target)914 snl_attr_dup_struct(struct snl_state *ss, struct nlattr *nla,
915 const void *arg __unused, void *target)
916 {
917 void *ptr = snl_allocz(ss, NLA_DATA_LEN(nla));
918
919 if (ptr != NULL) {
920 memcpy(ptr, NLA_DATA(nla), NLA_DATA_LEN(nla));
921 *((void **)target) = ptr;
922 return (true);
923 }
924 return (false);
925 }
926
927 struct snl_attr_bit {
928 uint32_t bit_index;
929 char *bit_name;
930 int bit_value;
931 };
932
933 #define _OUT(_field) offsetof(struct snl_attr_bit, _field)
934 static const struct snl_attr_parser _nla_p_bit[] = {
935 { .type = NLA_BITSET_BIT_INDEX, .off = _OUT(bit_index), .cb = snl_attr_get_uint32 },
936 { .type = NLA_BITSET_BIT_NAME, .off = _OUT(bit_name), .cb = snl_attr_dup_string },
937 { .type = NLA_BITSET_BIT_VALUE, .off = _OUT(bit_value), .cb = snl_attr_get_flag },
938 };
939 #undef _OUT
940 SNL_DECLARE_ATTR_PARSER_EXT(_nla_bit_parser, sizeof(struct snl_attr_bit), _nla_p_bit, NULL);
941
942 struct snl_attr_bitset {
943 uint32_t nla_bitset_size;
944 uint32_t *nla_bitset_mask;
945 uint32_t *nla_bitset_value;
946 struct snl_parray bits;
947 };
948
949 #define _OUT(_field) offsetof(struct snl_attr_bitset, _field)
950 static const struct snl_attr_parser _nla_p_bitset[] = {
951 { .type = NLA_BITSET_SIZE, .off = _OUT(nla_bitset_size), .cb = snl_attr_get_uint32 },
952 { .type = NLA_BITSET_BITS, .off = _OUT(bits), .cb = snl_attr_get_parray, .arg = &_nla_bit_parser },
953 { .type = NLA_BITSET_VALUE, .off = _OUT(nla_bitset_mask), .cb = snl_attr_dup_nla },
954 { .type = NLA_BITSET_MASK, .off = _OUT(nla_bitset_value), .cb = snl_attr_dup_nla },
955 };
956
957 static inline bool
_cb_p_bitset(struct snl_state * ss __unused,void * _target)958 _cb_p_bitset(struct snl_state *ss __unused, void *_target)
959 {
960 struct snl_attr_bitset *target = (struct snl_attr_bitset *)_target;
961
962 uint32_t sz_bytes = _roundup2(target->nla_bitset_size, 32) / 8;
963
964 if (target->nla_bitset_mask != NULL) {
965 struct nlattr *nla = (struct nlattr *)target->nla_bitset_mask;
966 uint32_t data_len = NLA_DATA_LEN(nla);
967
968 if (data_len != sz_bytes || _roundup2(data_len, 4) != data_len)
969 return (false);
970 target->nla_bitset_mask = (uint32_t *)NLA_DATA(nla);
971 }
972
973 if (target->nla_bitset_value != NULL) {
974 struct nlattr *nla = (struct nlattr *)target->nla_bitset_value;
975 uint32_t data_len = NLA_DATA_LEN(nla);
976
977 if (data_len != sz_bytes || _roundup2(data_len, 4) != data_len)
978 return (false);
979 target->nla_bitset_value = (uint32_t *)NLA_DATA(nla);
980 }
981 return (true);
982 }
983 #undef _OUT
984 SNL_DECLARE_ATTR_PARSER_EXT(_nla_bitset_parser,
985 sizeof(struct snl_attr_bitset),
986 _nla_p_bitset, _cb_p_bitset);
987
988 /*
989 * Parses the compact bitset representation.
990 */
991 static inline bool
snl_attr_get_bitset_c(struct snl_state * ss,struct nlattr * nla,const void * arg __unused,void * _target)992 snl_attr_get_bitset_c(struct snl_state *ss, struct nlattr *nla,
993 const void *arg __unused, void *_target)
994 {
995 const struct snl_hdr_parser *p = &_nla_bitset_parser;
996 struct snl_attr_bitset *target = (struct snl_attr_bitset *)_target;
997
998 /* Assumes target points to the beginning of the structure */
999 if (!snl_parse_header(ss, NLA_DATA(nla), NLA_DATA_LEN(nla), p, _target))
1000 return (false);
1001 if (target->nla_bitset_mask == NULL || target->nla_bitset_value == NULL)
1002 return (false);
1003 return (true);
1004 }
1005
1006 static inline void
snl_field_get_uint8(struct snl_state * ss __unused,void * src,void * target)1007 snl_field_get_uint8(struct snl_state *ss __unused, void *src, void *target)
1008 {
1009 *((uint8_t *)target) = *((uint8_t *)src);
1010 }
1011
1012 static inline void
snl_field_get_uint16(struct snl_state * ss __unused,void * src,void * target)1013 snl_field_get_uint16(struct snl_state *ss __unused, void *src, void *target)
1014 {
1015 *((uint16_t *)target) = *((uint16_t *)src);
1016 }
1017
1018 static inline void
snl_field_get_uint32(struct snl_state * ss __unused,void * src,void * target)1019 snl_field_get_uint32(struct snl_state *ss __unused, void *src, void *target)
1020 {
1021 *((uint32_t *)target) = *((uint32_t *)src);
1022 }
1023
1024 static inline void
snl_field_get_ptr(struct snl_state * ss __unused,void * src,void * target)1025 snl_field_get_ptr(struct snl_state *ss __unused, void *src, void *target)
1026 {
1027 *((void **)target) = src;
1028 }
1029
1030 struct snl_errmsg_data {
1031 struct nlmsghdr *orig_hdr;
1032 int error;
1033 uint32_t error_offs;
1034 char *error_str;
1035 struct nlattr *cookie;
1036 };
1037
1038 #define _IN(_field) offsetof(struct nlmsgerr, _field)
1039 #define _OUT(_field) offsetof(struct snl_errmsg_data, _field)
1040 static const struct snl_attr_parser nla_p_errmsg[] = {
1041 { .type = NLMSGERR_ATTR_MSG, .off = _OUT(error_str), .cb = snl_attr_get_string },
1042 { .type = NLMSGERR_ATTR_OFFS, .off = _OUT(error_offs), .cb = snl_attr_get_uint32 },
1043 { .type = NLMSGERR_ATTR_COOKIE, .off = _OUT(cookie), .cb = snl_attr_get_nla },
1044 };
1045
1046 static const struct snl_field_parser nlf_p_errmsg[] = {
1047 { .off_in = _IN(error), .off_out = _OUT(error), .cb = snl_field_get_uint32 },
1048 { .off_in = _IN(msg), .off_out = _OUT(orig_hdr), .cb = snl_field_get_ptr },
1049 };
1050 #undef _IN
1051 #undef _OUT
1052 SNL_DECLARE_PARSER(snl_errmsg_parser, struct nlmsgerr, nlf_p_errmsg, nla_p_errmsg);
1053
1054 #define _IN(_field) offsetof(struct nlmsgerr, _field)
1055 #define _OUT(_field) offsetof(struct snl_errmsg_data, _field)
1056 static const struct snl_field_parser nlf_p_donemsg[] = {
1057 { .off_in = _IN(error), .off_out = _OUT(error), .cb = snl_field_get_uint32 },
1058 };
1059 #undef _IN
1060 #undef _OUT
1061 SNL_DECLARE_FIELD_PARSER(snl_donemsg_parser, struct nlmsgerr, nlf_p_donemsg);
1062
1063 static inline bool
snl_parse_errmsg(struct snl_state * ss,struct nlmsghdr * hdr,struct snl_errmsg_data * e)1064 snl_parse_errmsg(struct snl_state *ss, struct nlmsghdr *hdr, struct snl_errmsg_data *e)
1065 {
1066 if ((hdr->nlmsg_flags & NLM_F_CAPPED) != 0)
1067 return (snl_parse_nlmsg(ss, hdr, &snl_errmsg_parser, e));
1068
1069 const struct snl_hdr_parser *ps = &snl_errmsg_parser;
1070 struct nlmsgerr *errmsg = (struct nlmsgerr *)(hdr + 1);
1071 int hdrlen = sizeof(int) + NLMSG_ALIGN(errmsg->msg.nlmsg_len);
1072 struct nlattr *attr_head = (struct nlattr *)(void *)((char *)errmsg + hdrlen);
1073 int attr_len = hdr->nlmsg_len - sizeof(struct nlmsghdr) - hdrlen;
1074
1075 snl_parse_fields(ss, (struct nlmsghdr *)errmsg, hdrlen, ps->fp, ps->fp_size, e);
1076 return (snl_parse_attrs_raw(ss, attr_head, attr_len, ps->np, ps->np_size, e));
1077 }
1078
1079 static inline bool
snl_read_reply_code(struct snl_state * ss,uint32_t nlmsg_seq,struct snl_errmsg_data * e)1080 snl_read_reply_code(struct snl_state *ss, uint32_t nlmsg_seq, struct snl_errmsg_data *e)
1081 {
1082 struct nlmsghdr *hdr = snl_read_reply(ss, nlmsg_seq);
1083
1084 if (hdr == NULL) {
1085 e->error = EINVAL;
1086 } else if (hdr->nlmsg_type == NLMSG_ERROR) {
1087 if (!snl_parse_errmsg(ss, hdr, e))
1088 e->error = EINVAL;
1089 return (e->error == 0);
1090 }
1091
1092 return (false);
1093 }
1094
1095 #define _OUT(_field) offsetof(struct snl_msg_info, _field)
1096 static const struct snl_attr_parser _nla_p_cinfo[] = {
1097 { .type = NLMSGINFO_ATTR_PROCESS_ID, .off = _OUT(process_id), .cb = snl_attr_get_uint32 },
1098 { .type = NLMSGINFO_ATTR_PORT_ID, .off = _OUT(port_id), .cb = snl_attr_get_uint32 },
1099 { .type = NLMSGINFO_ATTR_SEQ_ID, .off = _OUT(seq_id), .cb = snl_attr_get_uint32 },
1100 };
1101 #undef _OUT
1102 SNL_DECLARE_ATTR_PARSER(snl_msg_info_parser, _nla_p_cinfo);
1103
1104 static inline bool
parse_cmsg(struct snl_state * ss,const struct msghdr * msg,struct snl_msg_info * attrs)1105 parse_cmsg(struct snl_state *ss, const struct msghdr *msg, struct snl_msg_info *attrs)
1106 {
1107 for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
1108 cmsg = CMSG_NXTHDR(msg, cmsg)) {
1109 if (cmsg->cmsg_level != SOL_NETLINK || cmsg->cmsg_type != NETLINK_MSG_INFO)
1110 continue;
1111
1112 void *data = CMSG_DATA(cmsg);
1113 int len = cmsg->cmsg_len - ((char *)data - (char *)cmsg);
1114 const struct snl_hdr_parser *ps = &snl_msg_info_parser;
1115
1116 return (snl_parse_attrs_raw(ss, (struct nlattr *)data, len, ps->np, ps->np_size, attrs));
1117 }
1118
1119 return (false);
1120 }
1121
1122 /*
1123 * Assumes e is zeroed
1124 */
1125 static inline struct nlmsghdr *
snl_read_reply_multi(struct snl_state * ss,uint32_t nlmsg_seq,struct snl_errmsg_data * e)1126 snl_read_reply_multi(struct snl_state *ss, uint32_t nlmsg_seq, struct snl_errmsg_data *e)
1127 {
1128 struct nlmsghdr *hdr = snl_read_reply(ss, nlmsg_seq);
1129
1130 if (hdr == NULL) {
1131 e->error = EINVAL;
1132 } else if (hdr->nlmsg_type == NLMSG_ERROR) {
1133 if (!snl_parse_errmsg(ss, hdr, e))
1134 e->error = EINVAL;
1135 } else if (hdr->nlmsg_type == NLMSG_DONE) {
1136 snl_parse_nlmsg(ss, hdr, &snl_donemsg_parser, e);
1137 } else
1138 return (hdr);
1139
1140 return (NULL);
1141 }
1142
1143
1144 /* writer logic */
1145 struct snl_writer {
1146 char *base;
1147 uint32_t offset;
1148 uint32_t size;
1149 struct nlmsghdr *hdr;
1150 struct snl_state *ss;
1151 bool error;
1152 };
1153
1154 static inline void
snl_init_writer(struct snl_state * ss,struct snl_writer * nw)1155 snl_init_writer(struct snl_state *ss, struct snl_writer *nw)
1156 {
1157 nw->size = SNL_WRITER_BUFFER_SIZE;
1158 nw->base = (char *)snl_allocz(ss, nw->size);
1159 if (__predict_false(nw->base == NULL)) {
1160 nw->error = true;
1161 nw->size = 0;
1162 } else
1163 nw->error = false;
1164
1165 nw->offset = 0;
1166 nw->hdr = NULL;
1167 nw->ss = ss;
1168 }
1169
1170 static inline bool
snl_realloc_msg_buffer(struct snl_writer * nw,size_t sz)1171 snl_realloc_msg_buffer(struct snl_writer *nw, size_t sz)
1172 {
1173 uint32_t new_size = nw->size * 2;
1174 char *new_base;
1175
1176 while (new_size < nw->size + sz)
1177 new_size *= 2;
1178
1179 if (nw->error)
1180 return (false);
1181
1182 new_base = snl_allocz(nw->ss, new_size);
1183 if (new_base == NULL) {
1184 nw->error = true;
1185 return (false);
1186 }
1187
1188 memcpy(new_base, nw->base, nw->offset);
1189 if (nw->hdr != NULL) {
1190 int hdr_off = (char *)(nw->hdr) - nw->base;
1191
1192 nw->hdr = (struct nlmsghdr *)(void *)(new_base + hdr_off);
1193 }
1194 nw->base = new_base;
1195 nw->size = new_size;
1196
1197 return (true);
1198 }
1199
1200 static inline void *
snl_reserve_msg_data_raw(struct snl_writer * nw,size_t sz)1201 snl_reserve_msg_data_raw(struct snl_writer *nw, size_t sz)
1202 {
1203 sz = NETLINK_ALIGN(sz);
1204
1205 if (__predict_false(nw->offset + sz > nw->size)) {
1206 if (!snl_realloc_msg_buffer(nw, sz))
1207 return (NULL);
1208 }
1209
1210 void *data_ptr = &nw->base[nw->offset];
1211 nw->offset += sz;
1212
1213 return (data_ptr);
1214 }
1215 #define snl_reserve_msg_object(_ns, _t) ((_t *)snl_reserve_msg_data_raw(_ns, sizeof(_t)))
1216 #define snl_reserve_msg_data(_ns, _sz, _t) ((_t *)snl_reserve_msg_data_raw(_ns, _sz))
1217
1218 static inline struct nlattr *
snl_reserve_msg_attr_raw(struct snl_writer * nw,uint16_t nla_type,uint16_t sz)1219 snl_reserve_msg_attr_raw(struct snl_writer *nw, uint16_t nla_type, uint16_t sz)
1220 {
1221 struct nlattr *nla;
1222
1223 sz += sizeof(struct nlattr);
1224 nla = snl_reserve_msg_data(nw, sz, struct nlattr);
1225 if (__predict_false(nla == NULL))
1226 return (NULL);
1227 nla->nla_type = nla_type;
1228 nla->nla_len = sz;
1229
1230 return (nla);
1231 }
1232 #define snl_reserve_msg_attr(_ns, _at, _t) \
1233 ((_t *)(snl_reserve_msg_attr_raw(_ns, _at, sizeof(_t)) + 1))
1234
1235 static inline bool
snl_add_msg_attr(struct snl_writer * nw,int attr_type,int attr_len,const void * data)1236 snl_add_msg_attr(struct snl_writer *nw, int attr_type, int attr_len, const void *data)
1237 {
1238 int required_len = NLA_ALIGN(attr_len + sizeof(struct nlattr));
1239
1240 if (__predict_false(nw->offset + required_len > nw->size)) {
1241 if (!snl_realloc_msg_buffer(nw, required_len))
1242 return (false);
1243 }
1244
1245 struct nlattr *nla = (struct nlattr *)(void *)(&nw->base[nw->offset]);
1246
1247 nla->nla_len = attr_len + sizeof(struct nlattr);
1248 nla->nla_type = attr_type;
1249 if (attr_len > 0) {
1250 if ((attr_len % 4) != 0) {
1251 /* clear padding bytes */
1252 bzero((char *)nla + required_len - 4, 4);
1253 }
1254 memcpy((nla + 1), data, attr_len);
1255 }
1256 nw->offset += required_len;
1257 return (true);
1258 }
1259
1260 static inline bool
snl_add_msg_attr_raw(struct snl_writer * nw,const struct nlattr * nla_src)1261 snl_add_msg_attr_raw(struct snl_writer *nw, const struct nlattr *nla_src)
1262 {
1263 int attr_len = nla_src->nla_len - sizeof(struct nlattr);
1264
1265 assert(attr_len >= 0);
1266
1267 return (snl_add_msg_attr(nw, nla_src->nla_type, attr_len, (const void *)(nla_src + 1)));
1268 }
1269
1270 static inline bool
snl_add_msg_attr_bool(struct snl_writer * nw,int attrtype,bool value)1271 snl_add_msg_attr_bool(struct snl_writer *nw, int attrtype, bool value)
1272 {
1273 return (snl_add_msg_attr(nw, attrtype, sizeof(bool), &value));
1274 }
1275
1276 static inline bool
snl_add_msg_attr_u8(struct snl_writer * nw,int attrtype,uint8_t value)1277 snl_add_msg_attr_u8(struct snl_writer *nw, int attrtype, uint8_t value)
1278 {
1279 return (snl_add_msg_attr(nw, attrtype, sizeof(uint8_t), &value));
1280 }
1281
1282 static inline bool
snl_add_msg_attr_u16(struct snl_writer * nw,int attrtype,uint16_t value)1283 snl_add_msg_attr_u16(struct snl_writer *nw, int attrtype, uint16_t value)
1284 {
1285 return (snl_add_msg_attr(nw, attrtype, sizeof(uint16_t), &value));
1286 }
1287
1288 static inline bool
snl_add_msg_attr_u32(struct snl_writer * nw,int attrtype,uint32_t value)1289 snl_add_msg_attr_u32(struct snl_writer *nw, int attrtype, uint32_t value)
1290 {
1291 return (snl_add_msg_attr(nw, attrtype, sizeof(uint32_t), &value));
1292 }
1293
1294 static inline bool
snl_add_msg_attr_u64(struct snl_writer * nw,int attrtype,uint64_t value)1295 snl_add_msg_attr_u64(struct snl_writer *nw, int attrtype, uint64_t value)
1296 {
1297 return (snl_add_msg_attr(nw, attrtype, sizeof(uint64_t), &value));
1298 }
1299
1300 static inline bool
snl_add_msg_attr_s8(struct snl_writer * nw,int attrtype,int8_t value)1301 snl_add_msg_attr_s8(struct snl_writer *nw, int attrtype, int8_t value)
1302 {
1303 return (snl_add_msg_attr(nw, attrtype, sizeof(int8_t), &value));
1304 }
1305
1306 static inline bool
snl_add_msg_attr_s16(struct snl_writer * nw,int attrtype,int16_t value)1307 snl_add_msg_attr_s16(struct snl_writer *nw, int attrtype, int16_t value)
1308 {
1309 return (snl_add_msg_attr(nw, attrtype, sizeof(int16_t), &value));
1310 }
1311
1312 static inline bool
snl_add_msg_attr_s32(struct snl_writer * nw,int attrtype,int32_t value)1313 snl_add_msg_attr_s32(struct snl_writer *nw, int attrtype, int32_t value)
1314 {
1315 return (snl_add_msg_attr(nw, attrtype, sizeof(int32_t), &value));
1316 }
1317
1318 static inline bool
snl_add_msg_attr_s64(struct snl_writer * nw,int attrtype,int64_t value)1319 snl_add_msg_attr_s64(struct snl_writer *nw, int attrtype, int64_t value)
1320 {
1321 return (snl_add_msg_attr(nw, attrtype, sizeof(int64_t), &value));
1322 }
1323
1324 static inline bool
snl_add_msg_attr_flag(struct snl_writer * nw,int attrtype)1325 snl_add_msg_attr_flag(struct snl_writer *nw, int attrtype)
1326 {
1327 return (snl_add_msg_attr(nw, attrtype, 0, NULL));
1328 }
1329
1330 static inline bool
snl_add_msg_attr_string(struct snl_writer * nw,int attrtype,const char * str)1331 snl_add_msg_attr_string(struct snl_writer *nw, int attrtype, const char *str)
1332 {
1333 return (snl_add_msg_attr(nw, attrtype, strlen(str) + 1, str));
1334 }
1335
1336
1337 static inline int
snl_get_msg_offset(const struct snl_writer * nw)1338 snl_get_msg_offset(const struct snl_writer *nw)
1339 {
1340 return (nw->offset - ((char *)nw->hdr - nw->base));
1341 }
1342
1343 static inline void *
_snl_restore_msg_offset(const struct snl_writer * nw,int off)1344 _snl_restore_msg_offset(const struct snl_writer *nw, int off)
1345 {
1346 return ((void *)((char *)nw->hdr + off));
1347 }
1348 #define snl_restore_msg_offset(_ns, _off, _t) ((_t *)_snl_restore_msg_offset(_ns, _off))
1349
1350 static inline int
snl_add_msg_attr_nested(struct snl_writer * nw,int attrtype)1351 snl_add_msg_attr_nested(struct snl_writer *nw, int attrtype)
1352 {
1353 int off = snl_get_msg_offset(nw);
1354 struct nlattr *nla = snl_reserve_msg_data(nw, sizeof(struct nlattr), struct nlattr);
1355 if (__predict_false(nla == NULL))
1356 return (0);
1357 nla->nla_type = attrtype;
1358 return (off);
1359 }
1360
1361 static inline void
snl_end_attr_nested(const struct snl_writer * nw,int off)1362 snl_end_attr_nested(const struct snl_writer *nw, int off)
1363 {
1364 if (!nw->error) {
1365 struct nlattr *nla = snl_restore_msg_offset(nw, off, struct nlattr);
1366 nla->nla_len = NETLINK_ALIGN(snl_get_msg_offset(nw) - off);
1367 }
1368 }
1369
1370 static inline struct nlmsghdr *
snl_create_msg_request(struct snl_writer * nw,int nlmsg_type)1371 snl_create_msg_request(struct snl_writer *nw, int nlmsg_type)
1372 {
1373 struct nlmsghdr *hdr;
1374
1375 assert(nw->hdr == NULL);
1376
1377 if (__predict_false((hdr =
1378 snl_reserve_msg_object(nw, struct nlmsghdr)) == NULL))
1379 return (NULL);
1380 hdr->nlmsg_type = nlmsg_type;
1381 hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
1382 nw->hdr = hdr;
1383
1384 return (hdr);
1385 }
1386
1387 static void
snl_abort_msg(struct snl_writer * nw)1388 snl_abort_msg(struct snl_writer *nw)
1389 {
1390 if (nw->hdr != NULL) {
1391 int offset = (char *)(&nw->base[nw->offset]) - (char *)(nw->hdr);
1392
1393 nw->offset -= offset;
1394 nw->hdr = NULL;
1395 }
1396 }
1397
1398 static inline struct nlmsghdr *
snl_finalize_msg(struct snl_writer * nw)1399 snl_finalize_msg(struct snl_writer *nw)
1400 {
1401 if (nw->error)
1402 snl_abort_msg(nw);
1403 if (nw->hdr != NULL) {
1404 struct nlmsghdr *hdr = nw->hdr;
1405
1406 int offset = (char *)(&nw->base[nw->offset]) - (char *)(nw->hdr);
1407 hdr->nlmsg_len = offset;
1408 hdr->nlmsg_seq = snl_get_seq(nw->ss);
1409 nw->hdr = NULL;
1410
1411 return (hdr);
1412 }
1413 return (NULL);
1414 }
1415
1416 static inline bool
snl_send_msgs(struct snl_writer * nw)1417 snl_send_msgs(struct snl_writer *nw)
1418 {
1419 int offset = nw->offset;
1420
1421 assert(nw->hdr == NULL);
1422 nw->offset = 0;
1423
1424 return (snl_send(nw->ss, nw->base, offset));
1425 }
1426
1427 #endif
1428