xref: /freebsd/sys/netlink/netlink_snl.h (revision 60c0a075472cabd0ac7f54411f6a65626169984a)
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