17e5bf684SAlexander V. Chernikov /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
37e5bf684SAlexander V. Chernikov  *
47e5bf684SAlexander V. Chernikov  * Copyright (c) 2022 Alexander V. Chernikov <melifaro@FreeBSD.org>
57e5bf684SAlexander V. Chernikov  *
67e5bf684SAlexander V. Chernikov  * Redistribution and use in source and binary forms, with or without
77e5bf684SAlexander V. Chernikov  * modification, are permitted provided that the following conditions
87e5bf684SAlexander V. Chernikov  * are met:
97e5bf684SAlexander V. Chernikov  * 1. Redistributions of source code must retain the above copyright
107e5bf684SAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer.
117e5bf684SAlexander V. Chernikov  * 2. Redistributions in binary form must reproduce the above copyright
127e5bf684SAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer in the
137e5bf684SAlexander V. Chernikov  *    documentation and/or other materials provided with the distribution.
147e5bf684SAlexander V. Chernikov  *
157e5bf684SAlexander V. Chernikov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
167e5bf684SAlexander V. Chernikov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
177e5bf684SAlexander V. Chernikov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
187e5bf684SAlexander V. Chernikov  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
197e5bf684SAlexander V. Chernikov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
207e5bf684SAlexander V. Chernikov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
217e5bf684SAlexander V. Chernikov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
227e5bf684SAlexander V. Chernikov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
237e5bf684SAlexander V. Chernikov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
247e5bf684SAlexander V. Chernikov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
257e5bf684SAlexander V. Chernikov  * SUCH DAMAGE.
267e5bf684SAlexander V. Chernikov  */
277e5bf684SAlexander V. Chernikov 
287e5bf684SAlexander V. Chernikov #include <sys/param.h>
297e5bf684SAlexander V. Chernikov #include <sys/malloc.h>
307e5bf684SAlexander V. Chernikov #include <sys/lock.h>
317e5bf684SAlexander V. Chernikov #include <sys/rmlock.h>
327e5bf684SAlexander V. Chernikov #include <sys/mbuf.h>
337e5bf684SAlexander V. Chernikov #include <sys/socket.h>
347e5bf684SAlexander V. Chernikov #include <sys/socketvar.h>
357e5bf684SAlexander V. Chernikov #include <sys/syslog.h>
367e5bf684SAlexander V. Chernikov 
377e5bf684SAlexander V. Chernikov #include <netlink/netlink.h>
387e5bf684SAlexander V. Chernikov #include <netlink/netlink_ctl.h>
397e5bf684SAlexander V. Chernikov #include <netlink/netlink_linux.h>
407e5bf684SAlexander V. Chernikov #include <netlink/netlink_var.h>
417e5bf684SAlexander V. Chernikov 
427e5bf684SAlexander V. Chernikov #define	DEBUG_MOD_NAME	nl_writer
437e5bf684SAlexander V. Chernikov #define	DEBUG_MAX_LEVEL	LOG_DEBUG3
447e5bf684SAlexander V. Chernikov #include <netlink/netlink_debug.h>
45fa554de7SKristof Provost _DECLARE_DEBUG(LOG_INFO);
467e5bf684SAlexander V. Chernikov 
477e5bf684SAlexander V. Chernikov static bool
nlmsg_get_buf(struct nl_writer * nw,u_int len,bool waitok)4817083b94SGleb Smirnoff nlmsg_get_buf(struct nl_writer *nw, u_int len, bool waitok)
497e5bf684SAlexander V. Chernikov {
5017083b94SGleb Smirnoff 	const int mflag = waitok ? M_WAITOK : M_NOWAIT;
5117083b94SGleb Smirnoff 
5217083b94SGleb Smirnoff 	MPASS(nw->buf == NULL);
5317083b94SGleb Smirnoff 
5417083b94SGleb Smirnoff 	NL_LOG(LOG_DEBUG3, "Setting up nw %p len %u %s", nw, len,
5517083b94SGleb Smirnoff 	    waitok ? "wait" : "nowait");
5617083b94SGleb Smirnoff 
5717083b94SGleb Smirnoff 	nw->buf = nl_buf_alloc(len, mflag);
5817083b94SGleb Smirnoff 	if (__predict_false(nw->buf == NULL))
597e5bf684SAlexander V. Chernikov 		return (false);
607e5bf684SAlexander V. Chernikov 	nw->hdr = NULL;
617e5bf684SAlexander V. Chernikov 	nw->malloc_flag = mflag;
627e5bf684SAlexander V. Chernikov 	nw->num_messages = 0;
637e5bf684SAlexander V. Chernikov 	nw->enomem = false;
647e5bf684SAlexander V. Chernikov 
657e5bf684SAlexander V. Chernikov 	return (true);
667e5bf684SAlexander V. Chernikov }
677e5bf684SAlexander V. Chernikov 
6809fa78d4SGleb Smirnoff static bool
nl_send_one(struct nl_writer * nw)6909fa78d4SGleb Smirnoff nl_send_one(struct nl_writer *nw)
7009fa78d4SGleb Smirnoff {
7109fa78d4SGleb Smirnoff 
7209fa78d4SGleb Smirnoff 	return (nl_send(nw, nw->nlp));
7309fa78d4SGleb Smirnoff }
7409fa78d4SGleb Smirnoff 
757e5bf684SAlexander V. Chernikov bool
_nlmsg_get_unicast_writer(struct nl_writer * nw,int size,struct nlpcb * nlp)7619e43c16SAlexander V. Chernikov _nlmsg_get_unicast_writer(struct nl_writer *nw, int size, struct nlpcb *nlp)
777e5bf684SAlexander V. Chernikov {
7817083b94SGleb Smirnoff 	nw->nlp = nlp;
7917083b94SGleb Smirnoff 	nw->cb = nl_send_one;
8017083b94SGleb Smirnoff 
8117083b94SGleb Smirnoff 	return (nlmsg_get_buf(nw, size, false));
827e5bf684SAlexander V. Chernikov }
837e5bf684SAlexander V. Chernikov 
847e5bf684SAlexander V. Chernikov bool
_nlmsg_get_group_writer(struct nl_writer * nw,int size,int protocol,int group_id)8519e43c16SAlexander V. Chernikov _nlmsg_get_group_writer(struct nl_writer *nw, int size, int protocol, int group_id)
867e5bf684SAlexander V. Chernikov {
8717083b94SGleb Smirnoff 	nw->group.proto = protocol;
8817083b94SGleb Smirnoff 	nw->group.id = group_id;
8917083b94SGleb Smirnoff 	nw->cb = nl_send_group;
907e5bf684SAlexander V. Chernikov 
9117083b94SGleb Smirnoff 	return (nlmsg_get_buf(nw, size, false));
927e5bf684SAlexander V. Chernikov }
937e5bf684SAlexander V. Chernikov 
947e5bf684SAlexander V. Chernikov void
_nlmsg_ignore_limit(struct nl_writer * nw)9519e43c16SAlexander V. Chernikov _nlmsg_ignore_limit(struct nl_writer *nw)
967e5bf684SAlexander V. Chernikov {
977e5bf684SAlexander V. Chernikov 	nw->ignore_limit = true;
987e5bf684SAlexander V. Chernikov }
997e5bf684SAlexander V. Chernikov 
1007e5bf684SAlexander V. Chernikov bool
_nlmsg_flush(struct nl_writer * nw)10119e43c16SAlexander V. Chernikov _nlmsg_flush(struct nl_writer *nw)
1027e5bf684SAlexander V. Chernikov {
103f75d7facSGleb Smirnoff 	bool result;
1047e5bf684SAlexander V. Chernikov 
1057e5bf684SAlexander V. Chernikov 	if (__predict_false(nw->hdr != NULL)) {
1067e5bf684SAlexander V. Chernikov 		/* Last message has not been completed, skip it. */
10717083b94SGleb Smirnoff 		int completed_len = (char *)nw->hdr - nw->buf->data;
1087e5bf684SAlexander V. Chernikov 		/* Send completed messages */
10917083b94SGleb Smirnoff 		nw->buf->datalen -= nw->buf->datalen - completed_len;
1107e5bf684SAlexander V. Chernikov 		nw->hdr = NULL;
1117e5bf684SAlexander V. Chernikov         }
1127e5bf684SAlexander V. Chernikov 
113f75d7facSGleb Smirnoff 	if (nw->buf->datalen == 0) {
114f75d7facSGleb Smirnoff 		MPASS(nw->num_messages == 0);
115f75d7facSGleb Smirnoff 		nl_buf_free(nw->buf);
116f75d7facSGleb Smirnoff 		nw->buf = NULL;
117f75d7facSGleb Smirnoff 		return (true);
118f75d7facSGleb Smirnoff 	}
119f75d7facSGleb Smirnoff 
120f75d7facSGleb Smirnoff 	result = nw->cb(nw);
12117083b94SGleb Smirnoff 	nw->num_messages = 0;
1227e5bf684SAlexander V. Chernikov 
1237e5bf684SAlexander V. Chernikov 	if (!result) {
12417083b94SGleb Smirnoff 		NL_LOG(LOG_DEBUG, "nw %p flush with %p() failed", nw, nw->cb);
1257e5bf684SAlexander V. Chernikov 	}
1267e5bf684SAlexander V. Chernikov 
1277e5bf684SAlexander V. Chernikov 	return (result);
1287e5bf684SAlexander V. Chernikov }
1297e5bf684SAlexander V. Chernikov 
1307e5bf684SAlexander V. Chernikov /*
1317e5bf684SAlexander V. Chernikov  * Flushes previous data and allocates new underlying storage
1327e5bf684SAlexander V. Chernikov  *  sufficient for holding at least @required_len bytes.
1337e5bf684SAlexander V. Chernikov  * Return true on success.
1347e5bf684SAlexander V. Chernikov  */
1357e5bf684SAlexander V. Chernikov bool
_nlmsg_refill_buffer(struct nl_writer * nw,u_int required_len)13617083b94SGleb Smirnoff _nlmsg_refill_buffer(struct nl_writer *nw, u_int required_len)
1377e5bf684SAlexander V. Chernikov {
13817083b94SGleb Smirnoff 	struct nl_buf *new;
13917083b94SGleb Smirnoff 	u_int completed_len, new_len, last_len;
14017083b94SGleb Smirnoff 
14117083b94SGleb Smirnoff 	MPASS(nw->buf != NULL);
1427e5bf684SAlexander V. Chernikov 
1437e5bf684SAlexander V. Chernikov 	if (nw->enomem)
1447e5bf684SAlexander V. Chernikov 		return (false);
1457e5bf684SAlexander V. Chernikov 
14617083b94SGleb Smirnoff 	NL_LOG(LOG_DEBUG3, "no space at offset %u/%u (want %u), trying to "
14717083b94SGleb Smirnoff 	    "reclaim", nw->buf->datalen, nw->buf->buflen, required_len);
1487e5bf684SAlexander V. Chernikov 
14917083b94SGleb Smirnoff 	/* Calculate new buffer size and allocate it. */
15017083b94SGleb Smirnoff 	completed_len = (nw->hdr != NULL) ?
15117083b94SGleb Smirnoff 	    (char *)nw->hdr - nw->buf->data : nw->buf->datalen;
152c1839039SAlexander V. Chernikov 	if (completed_len > 0 && required_len < NLMBUFSIZE) {
15317083b94SGleb Smirnoff 		/* We already ran out of space, use largest effective size. */
15417083b94SGleb Smirnoff 		new_len = max(nw->buf->buflen, NLMBUFSIZE);
1557e5bf684SAlexander V. Chernikov 	} else {
15617083b94SGleb Smirnoff 		if (nw->buf->buflen < NLMBUFSIZE)
15717083b94SGleb Smirnoff 			/* XXXGL: does this happen? */
158c1839039SAlexander V. Chernikov 			new_len = NLMBUFSIZE;
1597e5bf684SAlexander V. Chernikov 		else
16017083b94SGleb Smirnoff 			new_len = nw->buf->buflen * 2;
1617e5bf684SAlexander V. Chernikov 		while (new_len < required_len)
1627e5bf684SAlexander V. Chernikov 			new_len *= 2;
1637e5bf684SAlexander V. Chernikov 	}
16417083b94SGleb Smirnoff 
16517083b94SGleb Smirnoff 	new = nl_buf_alloc(new_len, nw->malloc_flag | M_ZERO);
16617083b94SGleb Smirnoff 	if (__predict_false(new == NULL)) {
1677e5bf684SAlexander V. Chernikov 		nw->enomem = true;
1687e5bf684SAlexander V. Chernikov 		NL_LOG(LOG_DEBUG, "getting new buf failed, setting ENOMEM");
1697e5bf684SAlexander V. Chernikov 		return (false);
1707e5bf684SAlexander V. Chernikov 	}
1717e5bf684SAlexander V. Chernikov 
17217083b94SGleb Smirnoff 	/* Copy last (unfinished) header to the new storage. */
17317083b94SGleb Smirnoff 	last_len = nw->buf->datalen - completed_len;
1747e5bf684SAlexander V. Chernikov 	if (last_len > 0) {
17517083b94SGleb Smirnoff 		memcpy(new->data, nw->hdr, last_len);
17617083b94SGleb Smirnoff 		new->datalen = last_len;
1777e5bf684SAlexander V. Chernikov 	}
1787e5bf684SAlexander V. Chernikov 
17917083b94SGleb Smirnoff 	NL_LOG(LOG_DEBUG2, "completed: %u bytes, copied: %u bytes",
18017083b94SGleb Smirnoff 	    completed_len, last_len);
1817e5bf684SAlexander V. Chernikov 
18217083b94SGleb Smirnoff 	if (completed_len > 0) {
1837e5bf684SAlexander V. Chernikov 		nlmsg_flush(nw);
18417083b94SGleb Smirnoff 		MPASS(nw->buf == NULL);
18517083b94SGleb Smirnoff 	} else
18617083b94SGleb Smirnoff 		nl_buf_free(nw->buf);
18717083b94SGleb Smirnoff 	nw->buf = new;
18817083b94SGleb Smirnoff 	nw->hdr = (last_len > 0) ? (struct nlmsghdr *)new->data : NULL;
18917083b94SGleb Smirnoff 	NL_LOG(LOG_DEBUG2, "switched buffer: used %u/%u bytes",
19017083b94SGleb Smirnoff 	    new->datalen, new->buflen);
1917e5bf684SAlexander V. Chernikov 
1927e5bf684SAlexander V. Chernikov 	return (true);
1937e5bf684SAlexander V. Chernikov }
1947e5bf684SAlexander V. Chernikov 
1957e5bf684SAlexander V. Chernikov bool
_nlmsg_add(struct nl_writer * nw,uint32_t portid,uint32_t seq,uint16_t type,uint16_t flags,uint32_t len)19619e43c16SAlexander V. Chernikov _nlmsg_add(struct nl_writer *nw, uint32_t portid, uint32_t seq, uint16_t type,
1977e5bf684SAlexander V. Chernikov     uint16_t flags, uint32_t len)
1987e5bf684SAlexander V. Chernikov {
19917083b94SGleb Smirnoff 	struct nl_buf *nb = nw->buf;
2007e5bf684SAlexander V. Chernikov 	struct nlmsghdr *hdr;
20117083b94SGleb Smirnoff 	u_int required_len;
2027e5bf684SAlexander V. Chernikov 
2037e5bf684SAlexander V. Chernikov 	MPASS(nw->hdr == NULL);
2047e5bf684SAlexander V. Chernikov 
20517083b94SGleb Smirnoff 	required_len = NETLINK_ALIGN(len + sizeof(struct nlmsghdr));
20617083b94SGleb Smirnoff 	if (__predict_false(nb->datalen + required_len > nb->buflen)) {
2077e5bf684SAlexander V. Chernikov 		if (!nlmsg_refill_buffer(nw, required_len))
2087e5bf684SAlexander V. Chernikov 			return (false);
20917083b94SGleb Smirnoff 		nb = nw->buf;
2107e5bf684SAlexander V. Chernikov 	}
2117e5bf684SAlexander V. Chernikov 
21217083b94SGleb Smirnoff 	hdr = (struct nlmsghdr *)(&nb->data[nb->datalen]);
2137e5bf684SAlexander V. Chernikov 
2147e5bf684SAlexander V. Chernikov 	hdr->nlmsg_len = len;
2157e5bf684SAlexander V. Chernikov 	hdr->nlmsg_type = type;
2167e5bf684SAlexander V. Chernikov 	hdr->nlmsg_flags = flags;
2177e5bf684SAlexander V. Chernikov 	hdr->nlmsg_seq = seq;
2187e5bf684SAlexander V. Chernikov 	hdr->nlmsg_pid = portid;
2197e5bf684SAlexander V. Chernikov 
2207e5bf684SAlexander V. Chernikov 	nw->hdr = hdr;
22117083b94SGleb Smirnoff 	nb->datalen += sizeof(struct nlmsghdr);
2227e5bf684SAlexander V. Chernikov 
2237e5bf684SAlexander V. Chernikov 	return (true);
2247e5bf684SAlexander V. Chernikov }
2257e5bf684SAlexander V. Chernikov 
2267e5bf684SAlexander V. Chernikov bool
_nlmsg_end(struct nl_writer * nw)22719e43c16SAlexander V. Chernikov _nlmsg_end(struct nl_writer *nw)
2287e5bf684SAlexander V. Chernikov {
22917083b94SGleb Smirnoff 	struct nl_buf *nb = nw->buf;
23017083b94SGleb Smirnoff 
2317e5bf684SAlexander V. Chernikov 	MPASS(nw->hdr != NULL);
2327e5bf684SAlexander V. Chernikov 
2337e5bf684SAlexander V. Chernikov 	if (nw->enomem) {
2347e5bf684SAlexander V. Chernikov 		NL_LOG(LOG_DEBUG, "ENOMEM when dumping message");
2357e5bf684SAlexander V. Chernikov 		nlmsg_abort(nw);
2367e5bf684SAlexander V. Chernikov 		return (false);
2377e5bf684SAlexander V. Chernikov 	}
2387e5bf684SAlexander V. Chernikov 
23917083b94SGleb Smirnoff 	nw->hdr->nlmsg_len = nb->data + nb->datalen - (char *)nw->hdr;
240f4d3aa74SAlexander V. Chernikov 	NL_LOG(LOG_DEBUG2, "wrote msg len: %u type: %d: flags: 0x%X seq: %u pid: %u",
241f4d3aa74SAlexander V. Chernikov 	    nw->hdr->nlmsg_len, nw->hdr->nlmsg_type, nw->hdr->nlmsg_flags,
242f4d3aa74SAlexander V. Chernikov 	    nw->hdr->nlmsg_seq, nw->hdr->nlmsg_pid);
2437e5bf684SAlexander V. Chernikov 	nw->hdr = NULL;
2447e5bf684SAlexander V. Chernikov 	nw->num_messages++;
2457e5bf684SAlexander V. Chernikov 	return (true);
2467e5bf684SAlexander V. Chernikov }
2477e5bf684SAlexander V. Chernikov 
2487e5bf684SAlexander V. Chernikov void
_nlmsg_abort(struct nl_writer * nw)24919e43c16SAlexander V. Chernikov _nlmsg_abort(struct nl_writer *nw)
2507e5bf684SAlexander V. Chernikov {
25117083b94SGleb Smirnoff 	struct nl_buf *nb = nw->buf;
25217083b94SGleb Smirnoff 
2537e5bf684SAlexander V. Chernikov 	if (nw->hdr != NULL) {
25417083b94SGleb Smirnoff 		nb->datalen = (char *)nw->hdr - nb->data;
2557e5bf684SAlexander V. Chernikov 		nw->hdr = NULL;
2567e5bf684SAlexander V. Chernikov 	}
2577e5bf684SAlexander V. Chernikov }
2587e5bf684SAlexander V. Chernikov 
2597e5bf684SAlexander V. Chernikov void
nlmsg_ack(struct nlpcb * nlp,int error,struct nlmsghdr * hdr,struct nl_pstate * npt)2607e5bf684SAlexander V. Chernikov nlmsg_ack(struct nlpcb *nlp, int error, struct nlmsghdr *hdr,
2617e5bf684SAlexander V. Chernikov     struct nl_pstate *npt)
2627e5bf684SAlexander V. Chernikov {
2637e5bf684SAlexander V. Chernikov 	struct nlmsgerr *errmsg;
2647e5bf684SAlexander V. Chernikov 	int payload_len;
2657e5bf684SAlexander V. Chernikov 	uint32_t flags = nlp->nl_flags;
2667e5bf684SAlexander V. Chernikov 	struct nl_writer *nw = npt->nw;
2677e5bf684SAlexander V. Chernikov 	bool cap_ack;
2687e5bf684SAlexander V. Chernikov 
2697e5bf684SAlexander V. Chernikov 	payload_len = sizeof(struct nlmsgerr);
2707e5bf684SAlexander V. Chernikov 
2717e5bf684SAlexander V. Chernikov 	/*
2727e5bf684SAlexander V. Chernikov 	 * The only case when we send the full message in the
2737e5bf684SAlexander V. Chernikov 	 * reply is when there is an error and NETLINK_CAP_ACK
2747e5bf684SAlexander V. Chernikov 	 * is not set.
2757e5bf684SAlexander V. Chernikov 	 */
2767e5bf684SAlexander V. Chernikov 	cap_ack = (error == 0) || (flags & NLF_CAP_ACK);
2777e5bf684SAlexander V. Chernikov 	if (!cap_ack)
2787e5bf684SAlexander V. Chernikov 		payload_len += hdr->nlmsg_len - sizeof(struct nlmsghdr);
2797e5bf684SAlexander V. Chernikov 	payload_len = NETLINK_ALIGN(payload_len);
2807e5bf684SAlexander V. Chernikov 
2817e5bf684SAlexander V. Chernikov 	uint16_t nl_flags = cap_ack ? NLM_F_CAPPED : 0;
2827e5bf684SAlexander V. Chernikov 	if ((npt->err_msg || npt->err_off) && nlp->nl_flags & NLF_EXT_ACK)
2837e5bf684SAlexander V. Chernikov 		nl_flags |= NLM_F_ACK_TLVS;
2847e5bf684SAlexander V. Chernikov 
2857e5bf684SAlexander V. Chernikov 	NL_LOG(LOG_DEBUG3, "acknowledging message type %d seq %d",
2867e5bf684SAlexander V. Chernikov 	    hdr->nlmsg_type, hdr->nlmsg_seq);
2877e5bf684SAlexander V. Chernikov 
2887e5bf684SAlexander V. Chernikov 	if (!nlmsg_add(nw, nlp->nl_port, hdr->nlmsg_seq, NLMSG_ERROR, nl_flags, payload_len))
2897e5bf684SAlexander V. Chernikov 		goto enomem;
2907e5bf684SAlexander V. Chernikov 
2917e5bf684SAlexander V. Chernikov 	errmsg = nlmsg_reserve_data(nw, payload_len, struct nlmsgerr);
2927e5bf684SAlexander V. Chernikov 	errmsg->error = error;
2937e5bf684SAlexander V. Chernikov 	/* In case of error copy the whole message, else just the header */
2947e5bf684SAlexander V. Chernikov 	memcpy(&errmsg->msg, hdr, cap_ack ? sizeof(*hdr) : hdr->nlmsg_len);
2957e5bf684SAlexander V. Chernikov 
2967e5bf684SAlexander V. Chernikov 	if (npt->err_msg != NULL && nlp->nl_flags & NLF_EXT_ACK)
2977e5bf684SAlexander V. Chernikov 		nlattr_add_string(nw, NLMSGERR_ATTR_MSG, npt->err_msg);
2987e5bf684SAlexander V. Chernikov 	if (npt->err_off != 0 && nlp->nl_flags & NLF_EXT_ACK)
2997e5bf684SAlexander V. Chernikov 		nlattr_add_u32(nw, NLMSGERR_ATTR_OFFS, npt->err_off);
30025c2dd2fSAlexander V. Chernikov 	if (npt->cookie != NULL)
30125c2dd2fSAlexander V. Chernikov 		nlattr_add_raw(nw, npt->cookie);
3027e5bf684SAlexander V. Chernikov 
3037e5bf684SAlexander V. Chernikov 	if (nlmsg_end(nw))
3047e5bf684SAlexander V. Chernikov 		return;
3057e5bf684SAlexander V. Chernikov enomem:
3067e5bf684SAlexander V. Chernikov 	NLP_LOG(LOG_DEBUG, nlp, "error allocating ack data for message %d seq %u",
3077e5bf684SAlexander V. Chernikov 	    hdr->nlmsg_type, hdr->nlmsg_seq);
3087e5bf684SAlexander V. Chernikov 	nlmsg_abort(nw);
3097e5bf684SAlexander V. Chernikov }
3107e5bf684SAlexander V. Chernikov 
3117e5bf684SAlexander V. Chernikov bool
_nlmsg_end_dump(struct nl_writer * nw,int error,struct nlmsghdr * hdr)31219e43c16SAlexander V. Chernikov _nlmsg_end_dump(struct nl_writer *nw, int error, struct nlmsghdr *hdr)
3137e5bf684SAlexander V. Chernikov {
3147e5bf684SAlexander V. Chernikov 	if (!nlmsg_add(nw, hdr->nlmsg_pid, hdr->nlmsg_seq, NLMSG_DONE, 0, sizeof(int))) {
3157e5bf684SAlexander V. Chernikov 		NL_LOG(LOG_DEBUG, "Error finalizing table dump");
3167e5bf684SAlexander V. Chernikov 		return (false);
3177e5bf684SAlexander V. Chernikov 	}
3187e5bf684SAlexander V. Chernikov 	/* Save operation result */
3197e5bf684SAlexander V. Chernikov 	int *perror = nlmsg_reserve_object(nw, int);
3207e5bf684SAlexander V. Chernikov 	NL_LOG(LOG_DEBUG2, "record error=%d at off %d (%p)", error,
32117083b94SGleb Smirnoff 	    nw->buf->datalen, perror);
3227e5bf684SAlexander V. Chernikov 	*perror = error;
3237e5bf684SAlexander V. Chernikov 	nlmsg_end(nw);
324f4d3aa74SAlexander V. Chernikov 	nw->suppress_ack = true;
3257e5bf684SAlexander V. Chernikov 
3267e5bf684SAlexander V. Chernikov 	return (true);
3277e5bf684SAlexander V. Chernikov }
328c1839039SAlexander V. Chernikov 
32967d9023fSGleb Smirnoff /*
33067d9023fSGleb Smirnoff  * KPI functions.
33167d9023fSGleb Smirnoff  */
33267d9023fSGleb Smirnoff 
33317083b94SGleb Smirnoff u_int
nlattr_save_offset(const struct nl_writer * nw)33467d9023fSGleb Smirnoff nlattr_save_offset(const struct nl_writer *nw)
33567d9023fSGleb Smirnoff {
33617083b94SGleb Smirnoff 	return (nw->buf->datalen - ((char *)nw->hdr - nw->buf->data));
33767d9023fSGleb Smirnoff }
33867d9023fSGleb Smirnoff 
33967d9023fSGleb Smirnoff void *
nlmsg_reserve_data_raw(struct nl_writer * nw,size_t sz)34067d9023fSGleb Smirnoff nlmsg_reserve_data_raw(struct nl_writer *nw, size_t sz)
34167d9023fSGleb Smirnoff {
34217083b94SGleb Smirnoff 	struct nl_buf *nb = nw->buf;
34317083b94SGleb Smirnoff 	void *data;
34467d9023fSGleb Smirnoff 
34517083b94SGleb Smirnoff 	sz = NETLINK_ALIGN(sz);
34617083b94SGleb Smirnoff 	if (__predict_false(nb->datalen + sz > nb->buflen)) {
34767d9023fSGleb Smirnoff 		if (!nlmsg_refill_buffer(nw, sz))
34867d9023fSGleb Smirnoff 			return (NULL);
34917083b94SGleb Smirnoff 		nb = nw->buf;
35067d9023fSGleb Smirnoff 	}
35167d9023fSGleb Smirnoff 
35217083b94SGleb Smirnoff 	data = &nb->data[nb->datalen];
35317083b94SGleb Smirnoff 	bzero(data, sz);
35417083b94SGleb Smirnoff 	nb->datalen += sz;
35567d9023fSGleb Smirnoff 
35617083b94SGleb Smirnoff 	return (data);
35767d9023fSGleb Smirnoff }
35867d9023fSGleb Smirnoff 
35967d9023fSGleb Smirnoff bool
nlattr_add(struct nl_writer * nw,int attr_type,int attr_len,const void * data)36067d9023fSGleb Smirnoff nlattr_add(struct nl_writer *nw, int attr_type, int attr_len, const void *data)
36167d9023fSGleb Smirnoff {
36217083b94SGleb Smirnoff 	struct nl_buf *nb = nw->buf;
36317083b94SGleb Smirnoff 	struct nlattr *nla;
36417083b94SGleb Smirnoff 	u_int required_len;
36567d9023fSGleb Smirnoff 
36617083b94SGleb Smirnoff 	required_len = NLA_ALIGN(attr_len + sizeof(struct nlattr));
36717083b94SGleb Smirnoff 	if (__predict_false(nb->datalen + required_len > nb->buflen)) {
36867d9023fSGleb Smirnoff 		if (!nlmsg_refill_buffer(nw, required_len))
36967d9023fSGleb Smirnoff 			return (false);
37017083b94SGleb Smirnoff 		nb = nw->buf;
37167d9023fSGleb Smirnoff 	}
37267d9023fSGleb Smirnoff 
37317083b94SGleb Smirnoff 	nla = (struct nlattr *)(&nb->data[nb->datalen]);
37467d9023fSGleb Smirnoff 
37567d9023fSGleb Smirnoff 	nla->nla_len = attr_len + sizeof(struct nlattr);
37667d9023fSGleb Smirnoff 	nla->nla_type = attr_type;
37767d9023fSGleb Smirnoff 	if (attr_len > 0) {
37867d9023fSGleb Smirnoff 		if ((attr_len % 4) != 0) {
37967d9023fSGleb Smirnoff 			/* clear padding bytes */
38067d9023fSGleb Smirnoff 			bzero((char *)nla + required_len - 4, 4);
38167d9023fSGleb Smirnoff 		}
38267d9023fSGleb Smirnoff 		memcpy((nla + 1), data, attr_len);
38367d9023fSGleb Smirnoff 	}
38417083b94SGleb Smirnoff 	nb->datalen += required_len;
38567d9023fSGleb Smirnoff 	return (true);
38667d9023fSGleb Smirnoff }
38767d9023fSGleb Smirnoff 
388c1839039SAlexander V. Chernikov #include <netlink/ktest_netlink_message_writer.h>
389