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