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 {
103*f75d7facSGleb 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
113*f75d7facSGleb Smirnoff if (nw->buf->datalen == 0) {
114*f75d7facSGleb Smirnoff MPASS(nw->num_messages == 0);
115*f75d7facSGleb Smirnoff nl_buf_free(nw->buf);
116*f75d7facSGleb Smirnoff nw->buf = NULL;
117*f75d7facSGleb Smirnoff return (true);
118*f75d7facSGleb Smirnoff }
119*f75d7facSGleb Smirnoff
120*f75d7facSGleb 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