xref: /freebsd/sys/netlink/netlink_io.c (revision b977dd1ea5fbc2df3f1279330be4d089322eb2cf)
17e5bf684SAlexander V. Chernikov /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
37e5bf684SAlexander V. Chernikov  *
47e5bf684SAlexander V. Chernikov  * Copyright (c) 2021 Ng Peng Nam Sean
57e5bf684SAlexander V. Chernikov  * Copyright (c) 2022 Alexander V. Chernikov <melifaro@FreeBSD.org>
67e5bf684SAlexander V. Chernikov  *
77e5bf684SAlexander V. Chernikov  * Redistribution and use in source and binary forms, with or without
87e5bf684SAlexander V. Chernikov  * modification, are permitted provided that the following conditions
97e5bf684SAlexander V. Chernikov  * are met:
107e5bf684SAlexander V. Chernikov  * 1. Redistributions of source code must retain the above copyright
117e5bf684SAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer.
127e5bf684SAlexander V. Chernikov  * 2. Redistributions in binary form must reproduce the above copyright
137e5bf684SAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer in the
147e5bf684SAlexander V. Chernikov  *    documentation and/or other materials provided with the distribution.
157e5bf684SAlexander V. Chernikov  *
167e5bf684SAlexander V. Chernikov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
177e5bf684SAlexander V. Chernikov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
187e5bf684SAlexander V. Chernikov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
197e5bf684SAlexander V. Chernikov  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
207e5bf684SAlexander V. Chernikov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
217e5bf684SAlexander V. Chernikov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
227e5bf684SAlexander V. Chernikov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
237e5bf684SAlexander V. Chernikov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
247e5bf684SAlexander V. Chernikov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
257e5bf684SAlexander V. Chernikov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
267e5bf684SAlexander V. Chernikov  * SUCH DAMAGE.
277e5bf684SAlexander V. Chernikov  */
287e5bf684SAlexander V. Chernikov 
297e5bf684SAlexander V. Chernikov #include <sys/param.h>
307e5bf684SAlexander V. Chernikov #include <sys/ck.h>
31fc083c3eSJung-uk Kim #include <sys/lock.h>
32fc083c3eSJung-uk Kim #include <sys/malloc.h>
33fc083c3eSJung-uk Kim #include <sys/mbuf.h>
34fc083c3eSJung-uk Kim #include <sys/mutex.h>
357e5bf684SAlexander V. Chernikov #include <sys/socket.h>
367e5bf684SAlexander V. Chernikov #include <sys/socketvar.h>
377e5bf684SAlexander V. Chernikov #include <sys/syslog.h>
387e5bf684SAlexander V. Chernikov 
397e5bf684SAlexander V. Chernikov #include <netlink/netlink.h>
407e5bf684SAlexander V. Chernikov #include <netlink/netlink_ctl.h>
417e5bf684SAlexander V. Chernikov #include <netlink/netlink_linux.h>
427e5bf684SAlexander V. Chernikov #include <netlink/netlink_var.h>
437e5bf684SAlexander V. Chernikov 
447e5bf684SAlexander V. Chernikov #define	DEBUG_MOD_NAME	nl_io
457e5bf684SAlexander V. Chernikov #define	DEBUG_MAX_LEVEL	LOG_DEBUG3
467e5bf684SAlexander V. Chernikov #include <netlink/netlink_debug.h>
47fa554de7SKristof Provost _DECLARE_DEBUG(LOG_INFO);
487e5bf684SAlexander V. Chernikov 
497e5bf684SAlexander V. Chernikov /*
507e5bf684SAlexander V. Chernikov  * The logic below provide a p2p interface for receiving and
517e5bf684SAlexander V. Chernikov  * sending netlink data between the kernel and userland.
527e5bf684SAlexander V. Chernikov  */
537e5bf684SAlexander V. Chernikov 
54660bd40aSGleb Smirnoff static bool nl_process_nbuf(struct nl_buf *nb, struct nlpcb *nlp);
557e5bf684SAlexander V. Chernikov 
5617083b94SGleb Smirnoff struct nl_buf *
nl_buf_alloc(size_t len,int mflag)5717083b94SGleb Smirnoff nl_buf_alloc(size_t len, int mflag)
587e5bf684SAlexander V. Chernikov {
5917083b94SGleb Smirnoff 	struct nl_buf *nb;
607e5bf684SAlexander V. Chernikov 
6117083b94SGleb Smirnoff 	nb = malloc(sizeof(struct nl_buf) + len, M_NETLINK, mflag);
6217083b94SGleb Smirnoff 	if (__predict_true(nb != NULL)) {
6317083b94SGleb Smirnoff 		nb->buflen = len;
6417083b94SGleb Smirnoff 		nb->datalen = nb->offset = 0;
657e5bf684SAlexander V. Chernikov 	}
667e5bf684SAlexander V. Chernikov 
6717083b94SGleb Smirnoff 	return (nb);
687e5bf684SAlexander V. Chernikov }
697e5bf684SAlexander V. Chernikov 
7030d7e724SAlexander V. Chernikov void
nl_buf_free(struct nl_buf * nb)7117083b94SGleb Smirnoff nl_buf_free(struct nl_buf *nb)
7230d7e724SAlexander V. Chernikov {
7317083b94SGleb Smirnoff 
7417083b94SGleb Smirnoff 	free(nb, M_NETLINK);
7517083b94SGleb Smirnoff }
7617083b94SGleb Smirnoff 
7717083b94SGleb Smirnoff void
nl_schedule_taskqueue(struct nlpcb * nlp)787e5bf684SAlexander V. Chernikov nl_schedule_taskqueue(struct nlpcb *nlp)
797e5bf684SAlexander V. Chernikov {
807e5bf684SAlexander V. Chernikov 	if (!nlp->nl_task_pending) {
817e5bf684SAlexander V. Chernikov 		nlp->nl_task_pending = true;
827e5bf684SAlexander V. Chernikov 		taskqueue_enqueue(nlp->nl_taskqueue, &nlp->nl_task);
837e5bf684SAlexander V. Chernikov 		NL_LOG(LOG_DEBUG3, "taskqueue scheduled");
847e5bf684SAlexander V. Chernikov 	} else {
857e5bf684SAlexander V. Chernikov 		NL_LOG(LOG_DEBUG3, "taskqueue schedule skipped");
867e5bf684SAlexander V. Chernikov 	}
877e5bf684SAlexander V. Chernikov }
887e5bf684SAlexander V. Chernikov 
897e5bf684SAlexander V. Chernikov static bool
nl_process_received_one(struct nlpcb * nlp)907e5bf684SAlexander V. Chernikov nl_process_received_one(struct nlpcb *nlp)
917e5bf684SAlexander V. Chernikov {
92660bd40aSGleb Smirnoff 	struct socket *so = nlp->nl_socket;
9317083b94SGleb Smirnoff 	struct sockbuf *sb;
94660bd40aSGleb Smirnoff 	struct nl_buf *nb;
957e5bf684SAlexander V. Chernikov 	bool reschedule = false;
967e5bf684SAlexander V. Chernikov 
977e5bf684SAlexander V. Chernikov 	NLP_LOCK(nlp);
987e5bf684SAlexander V. Chernikov 	nlp->nl_task_pending = false;
997e5bf684SAlexander V. Chernikov 	NLP_UNLOCK(nlp);
10017083b94SGleb Smirnoff 
10117083b94SGleb Smirnoff 	/*
10217083b94SGleb Smirnoff 	 * Do not process queued up requests if there is no space to queue
10317083b94SGleb Smirnoff 	 * replies.
10417083b94SGleb Smirnoff 	 */
10517083b94SGleb Smirnoff 	sb = &so->so_rcv;
10617083b94SGleb Smirnoff 	SOCK_RECVBUF_LOCK(so);
10717083b94SGleb Smirnoff 	if (sb->sb_hiwat <= sb->sb_ccc) {
10817083b94SGleb Smirnoff 		SOCK_RECVBUF_UNLOCK(so);
1097e5bf684SAlexander V. Chernikov 		return (false);
1107e5bf684SAlexander V. Chernikov 	}
11117083b94SGleb Smirnoff 	SOCK_RECVBUF_UNLOCK(so);
1127e5bf684SAlexander V. Chernikov 
11317083b94SGleb Smirnoff 	sb = &so->so_snd;
114660bd40aSGleb Smirnoff 	SOCK_SENDBUF_LOCK(so);
115660bd40aSGleb Smirnoff 	while ((nb = TAILQ_FIRST(&sb->nl_queue)) != NULL) {
116660bd40aSGleb Smirnoff 		TAILQ_REMOVE(&sb->nl_queue, nb, tailq);
117660bd40aSGleb Smirnoff 		SOCK_SENDBUF_UNLOCK(so);
118660bd40aSGleb Smirnoff 		reschedule = nl_process_nbuf(nb, nlp);
119660bd40aSGleb Smirnoff 		SOCK_SENDBUF_LOCK(so);
120660bd40aSGleb Smirnoff 		if (reschedule) {
121660bd40aSGleb Smirnoff 			sb->sb_acc -= nb->datalen;
122660bd40aSGleb Smirnoff 			sb->sb_ccc -= nb->datalen;
123660bd40aSGleb Smirnoff 			/* XXXGL: potentially can reduce lock&unlock count. */
124660bd40aSGleb Smirnoff 			sowwakeup_locked(so);
12517083b94SGleb Smirnoff 			nl_buf_free(nb);
126660bd40aSGleb Smirnoff 			SOCK_SENDBUF_LOCK(so);
127660bd40aSGleb Smirnoff 		} else {
128660bd40aSGleb Smirnoff 			TAILQ_INSERT_HEAD(&sb->nl_queue, nb, tailq);
1297e5bf684SAlexander V. Chernikov 			break;
1307e5bf684SAlexander V. Chernikov 		}
1317e5bf684SAlexander V. Chernikov 	}
132660bd40aSGleb Smirnoff 	SOCK_SENDBUF_UNLOCK(so);
1337e5bf684SAlexander V. Chernikov 
1347e5bf684SAlexander V. Chernikov 	return (reschedule);
1357e5bf684SAlexander V. Chernikov }
1367e5bf684SAlexander V. Chernikov 
1377e5bf684SAlexander V. Chernikov static void
nl_process_received(struct nlpcb * nlp)1387e5bf684SAlexander V. Chernikov nl_process_received(struct nlpcb *nlp)
1397e5bf684SAlexander V. Chernikov {
1407e5bf684SAlexander V. Chernikov 	NL_LOG(LOG_DEBUG3, "taskqueue called");
1417e5bf684SAlexander V. Chernikov 
14230d7e724SAlexander V. Chernikov 	if (__predict_false(nlp->nl_need_thread_setup)) {
14330d7e724SAlexander V. Chernikov 		nl_set_thread_nlp(curthread, nlp);
14430d7e724SAlexander V. Chernikov 		NLP_LOCK(nlp);
14530d7e724SAlexander V. Chernikov 		nlp->nl_need_thread_setup = false;
14630d7e724SAlexander V. Chernikov 		NLP_UNLOCK(nlp);
14730d7e724SAlexander V. Chernikov 	}
14830d7e724SAlexander V. Chernikov 
1497e5bf684SAlexander V. Chernikov 	while (nl_process_received_one(nlp))
1507e5bf684SAlexander V. Chernikov 		;
1517e5bf684SAlexander V. Chernikov }
1527e5bf684SAlexander V. Chernikov 
1537e5bf684SAlexander V. Chernikov /*
1547e5bf684SAlexander V. Chernikov  * Called after some data have been read from the socket.
1557e5bf684SAlexander V. Chernikov  */
1567e5bf684SAlexander V. Chernikov void
nl_on_transmit(struct nlpcb * nlp)1577e5bf684SAlexander V. Chernikov nl_on_transmit(struct nlpcb *nlp)
1587e5bf684SAlexander V. Chernikov {
1597e5bf684SAlexander V. Chernikov 	NLP_LOCK(nlp);
1607e5bf684SAlexander V. Chernikov 
1617e5bf684SAlexander V. Chernikov 	struct socket *so = nlp->nl_socket;
1627e5bf684SAlexander V. Chernikov 	if (__predict_false(nlp->nl_dropped_bytes > 0 && so != NULL)) {
1638d9f3e05SAlexander V. Chernikov 		unsigned long dropped_bytes = nlp->nl_dropped_bytes;
1648d9f3e05SAlexander V. Chernikov 		unsigned long dropped_messages = nlp->nl_dropped_messages;
1657e5bf684SAlexander V. Chernikov 		nlp->nl_dropped_bytes = 0;
1667e5bf684SAlexander V. Chernikov 		nlp->nl_dropped_messages = 0;
1677e5bf684SAlexander V. Chernikov 
1687e5bf684SAlexander V. Chernikov 		struct sockbuf *sb = &so->so_rcv;
1697e5bf684SAlexander V. Chernikov 		NLP_LOG(LOG_DEBUG, nlp,
1707e5bf684SAlexander V. Chernikov 		    "socket RX overflowed, %lu messages (%lu bytes) dropped. "
17117083b94SGleb Smirnoff 		    "bytes: [%u/%u]", dropped_messages, dropped_bytes,
17217083b94SGleb Smirnoff 		    sb->sb_ccc, sb->sb_hiwat);
1737e5bf684SAlexander V. Chernikov 		/* TODO: send netlink message */
1747e5bf684SAlexander V. Chernikov 	}
1757e5bf684SAlexander V. Chernikov 
1767e5bf684SAlexander V. Chernikov 	nl_schedule_taskqueue(nlp);
1777e5bf684SAlexander V. Chernikov 	NLP_UNLOCK(nlp);
1787e5bf684SAlexander V. Chernikov }
1797e5bf684SAlexander V. Chernikov 
1807e5bf684SAlexander V. Chernikov void
nl_taskqueue_handler(void * _arg,int pending)1817e5bf684SAlexander V. Chernikov nl_taskqueue_handler(void *_arg, int pending)
1827e5bf684SAlexander V. Chernikov {
1837e5bf684SAlexander V. Chernikov 	struct nlpcb *nlp = (struct nlpcb *)_arg;
1847e5bf684SAlexander V. Chernikov 
1857e5bf684SAlexander V. Chernikov 	CURVNET_SET(nlp->nl_socket->so_vnet);
1867e5bf684SAlexander V. Chernikov 	nl_process_received(nlp);
1877e5bf684SAlexander V. Chernikov 	CURVNET_RESTORE();
1887e5bf684SAlexander V. Chernikov }
1897e5bf684SAlexander V. Chernikov 
1907e5bf684SAlexander V. Chernikov /*
19117083b94SGleb Smirnoff  * Tries to send current data buffer from writer.
1927e5bf684SAlexander V. Chernikov  *
1937e5bf684SAlexander V. Chernikov  * Returns true on success.
1947e5bf684SAlexander V. Chernikov  * If no queue overrunes happened, wakes up socket owner.
1957e5bf684SAlexander V. Chernikov  */
1967e5bf684SAlexander V. Chernikov bool
nl_send(struct nl_writer * nw,struct nlpcb * nlp)19709fa78d4SGleb Smirnoff nl_send(struct nl_writer *nw, struct nlpcb *nlp)
1987e5bf684SAlexander V. Chernikov {
19917083b94SGleb Smirnoff 	struct socket *so = nlp->nl_socket;
20017083b94SGleb Smirnoff 	struct sockbuf *sb = &so->so_rcv;
20117083b94SGleb Smirnoff 	struct nl_buf *nb;
20217083b94SGleb Smirnoff 
20317083b94SGleb Smirnoff 	MPASS(nw->hdr == NULL);
204f75d7facSGleb Smirnoff 	MPASS(nw->buf != NULL);
205f75d7facSGleb Smirnoff 	MPASS(nw->buf->datalen > 0);
2067e5bf684SAlexander V. Chernikov 
2077e5bf684SAlexander V. Chernikov 	IF_DEBUG_LEVEL(LOG_DEBUG2) {
20817083b94SGleb Smirnoff 		struct nlmsghdr *hdr = (struct nlmsghdr *)nw->buf->data;
2097e5bf684SAlexander V. Chernikov 		NLP_LOG(LOG_DEBUG2, nlp,
21017083b94SGleb Smirnoff 		    "TX len %u msgs %u msg type %d first hdrlen %u",
21117083b94SGleb Smirnoff 		    nw->buf->datalen, nw->num_messages, hdr->nlmsg_type,
21217083b94SGleb Smirnoff 		    hdr->nlmsg_len);
2137e5bf684SAlexander V. Chernikov 	}
2147e5bf684SAlexander V. Chernikov 
21517083b94SGleb Smirnoff 	if (nlp->nl_linux && linux_netlink_p != NULL &&
21617083b94SGleb Smirnoff 	    __predict_false(!linux_netlink_p->msgs_to_linux(nw, nlp))) {
21717083b94SGleb Smirnoff 		nl_buf_free(nw->buf);
21817083b94SGleb Smirnoff 		nw->buf = NULL;
2197e5bf684SAlexander V. Chernikov 		return (false);
2207e5bf684SAlexander V. Chernikov 	}
2217e5bf684SAlexander V. Chernikov 
22217083b94SGleb Smirnoff 	nb = nw->buf;
22317083b94SGleb Smirnoff 	nw->buf = NULL;
22417083b94SGleb Smirnoff 
22517083b94SGleb Smirnoff 	SOCK_RECVBUF_LOCK(so);
22617083b94SGleb Smirnoff 	if (!nw->ignore_limit && __predict_false(sb->sb_hiwat <= sb->sb_ccc)) {
22717083b94SGleb Smirnoff 		SOCK_RECVBUF_UNLOCK(so);
2287e5bf684SAlexander V. Chernikov 		NLP_LOCK(nlp);
22917083b94SGleb Smirnoff 		nlp->nl_dropped_bytes += nb->datalen;
23017083b94SGleb Smirnoff 		nlp->nl_dropped_messages += nw->num_messages;
2317e5bf684SAlexander V. Chernikov 		NLP_LOG(LOG_DEBUG2, nlp, "RX oveflow: %lu m (+%d), %lu b (+%d)",
23217083b94SGleb Smirnoff 		    (unsigned long)nlp->nl_dropped_messages, nw->num_messages,
23317083b94SGleb Smirnoff 		    (unsigned long)nlp->nl_dropped_bytes, nb->datalen);
2347e5bf684SAlexander V. Chernikov 		NLP_UNLOCK(nlp);
23517083b94SGleb Smirnoff 		nl_buf_free(nb);
23617083b94SGleb Smirnoff 		return (false);
23717083b94SGleb Smirnoff 	} else {
23817083b94SGleb Smirnoff 		bool full;
2397e5bf684SAlexander V. Chernikov 
24017083b94SGleb Smirnoff 		TAILQ_INSERT_TAIL(&sb->nl_queue, nb, tailq);
24117083b94SGleb Smirnoff 		sb->sb_acc += nb->datalen;
24217083b94SGleb Smirnoff 		sb->sb_ccc += nb->datalen;
24317083b94SGleb Smirnoff 		full = sb->sb_hiwat <= sb->sb_ccc;
24417083b94SGleb Smirnoff 		sorwakeup_locked(so);
24517083b94SGleb Smirnoff 		if (full) {
24617083b94SGleb Smirnoff 			NLP_LOCK(nlp);
24717083b94SGleb Smirnoff 			nlp->nl_tx_blocked = true;
24817083b94SGleb Smirnoff 			NLP_UNLOCK(nlp);
24917083b94SGleb Smirnoff 		}
25017083b94SGleb Smirnoff 		return (true);
25117083b94SGleb Smirnoff 	}
2527e5bf684SAlexander V. Chernikov }
2537e5bf684SAlexander V. Chernikov 
2547e5bf684SAlexander V. Chernikov static int
nl_receive_message(struct nlmsghdr * hdr,int remaining_length,struct nlpcb * nlp,struct nl_pstate * npt)2557e5bf684SAlexander V. Chernikov nl_receive_message(struct nlmsghdr *hdr, int remaining_length,
2567e5bf684SAlexander V. Chernikov     struct nlpcb *nlp, struct nl_pstate *npt)
2577e5bf684SAlexander V. Chernikov {
2587e5bf684SAlexander V. Chernikov 	nl_handler_f handler = nl_handlers[nlp->nl_proto].cb;
2597e5bf684SAlexander V. Chernikov 	int error = 0;
2607e5bf684SAlexander V. Chernikov 
261f4d3aa74SAlexander V. Chernikov 	NLP_LOG(LOG_DEBUG2, nlp, "msg len: %u type: %d: flags: 0x%X seq: %u pid: %u",
262f4d3aa74SAlexander V. Chernikov 	    hdr->nlmsg_len, hdr->nlmsg_type, hdr->nlmsg_flags, hdr->nlmsg_seq,
263f4d3aa74SAlexander V. Chernikov 	    hdr->nlmsg_pid);
2647e5bf684SAlexander V. Chernikov 
2657e5bf684SAlexander V. Chernikov 	if (__predict_false(hdr->nlmsg_len > remaining_length)) {
2667e5bf684SAlexander V. Chernikov 		NLP_LOG(LOG_DEBUG, nlp, "message is not entirely present: want %d got %d",
2677e5bf684SAlexander V. Chernikov 		    hdr->nlmsg_len, remaining_length);
2687e5bf684SAlexander V. Chernikov 		return (EINVAL);
2697e5bf684SAlexander V. Chernikov 	} else if (__predict_false(hdr->nlmsg_len < sizeof(*hdr))) {
2707e5bf684SAlexander V. Chernikov 		NL_LOG(LOG_DEBUG, "message too short: %d", hdr->nlmsg_len);
2717e5bf684SAlexander V. Chernikov 		return (EINVAL);
2727e5bf684SAlexander V. Chernikov 	}
2737e5bf684SAlexander V. Chernikov 	/* Stamp each message with sender pid */
2747e5bf684SAlexander V. Chernikov 	hdr->nlmsg_pid = nlp->nl_port;
2757e5bf684SAlexander V. Chernikov 
2767e5bf684SAlexander V. Chernikov 	npt->hdr = hdr;
2777e5bf684SAlexander V. Chernikov 
278*b977dd1eSGleb Smirnoff 	if (hdr->nlmsg_flags & NLM_F_REQUEST &&
279*b977dd1eSGleb Smirnoff 	    hdr->nlmsg_type >= NLMSG_MIN_TYPE) {
2807e5bf684SAlexander V. Chernikov 		NL_LOG(LOG_DEBUG2, "handling message with msg type: %d",
2817e5bf684SAlexander V. Chernikov 		   hdr->nlmsg_type);
282*b977dd1eSGleb Smirnoff 		if (nlp->nl_linux) {
283*b977dd1eSGleb Smirnoff 			MPASS(linux_netlink_p != NULL);
284*b977dd1eSGleb Smirnoff 			error = linux_netlink_p->msg_from_linux(nlp->nl_proto,
285*b977dd1eSGleb Smirnoff 			    &hdr, npt);
286*b977dd1eSGleb Smirnoff 			if (error)
287*b977dd1eSGleb Smirnoff 				goto ack;
2887e5bf684SAlexander V. Chernikov 		}
2897e5bf684SAlexander V. Chernikov 		error = handler(hdr, npt);
2907e5bf684SAlexander V. Chernikov 		NL_LOG(LOG_DEBUG2, "retcode: %d", error);
2917e5bf684SAlexander V. Chernikov 	}
292*b977dd1eSGleb Smirnoff ack:
2937e5bf684SAlexander V. Chernikov 	if ((hdr->nlmsg_flags & NLM_F_ACK) || (error != 0 && error != EINTR)) {
294f4d3aa74SAlexander V. Chernikov 		if (!npt->nw->suppress_ack) {
2957e5bf684SAlexander V. Chernikov 			NL_LOG(LOG_DEBUG3, "ack");
2967e5bf684SAlexander V. Chernikov 			nlmsg_ack(nlp, error, hdr, npt);
297f4d3aa74SAlexander V. Chernikov 		}
2987e5bf684SAlexander V. Chernikov 	}
2997e5bf684SAlexander V. Chernikov 
3007e5bf684SAlexander V. Chernikov 	return (0);
3017e5bf684SAlexander V. Chernikov }
3027e5bf684SAlexander V. Chernikov 
3037e5bf684SAlexander V. Chernikov static void
npt_clear(struct nl_pstate * npt)3047e5bf684SAlexander V. Chernikov npt_clear(struct nl_pstate *npt)
3057e5bf684SAlexander V. Chernikov {
3067e5bf684SAlexander V. Chernikov 	lb_clear(&npt->lb);
3077e5bf684SAlexander V. Chernikov 	npt->error = 0;
3087e5bf684SAlexander V. Chernikov 	npt->err_msg = NULL;
3097e5bf684SAlexander V. Chernikov 	npt->err_off = 0;
3107e5bf684SAlexander V. Chernikov 	npt->hdr = NULL;
311f4d3aa74SAlexander V. Chernikov 	npt->nw->suppress_ack = false;
3127e5bf684SAlexander V. Chernikov }
3137e5bf684SAlexander V. Chernikov 
3147e5bf684SAlexander V. Chernikov /*
3157e5bf684SAlexander V. Chernikov  * Processes an incoming packet, which can contain multiple netlink messages
3167e5bf684SAlexander V. Chernikov  */
317660bd40aSGleb Smirnoff static bool
nl_process_nbuf(struct nl_buf * nb,struct nlpcb * nlp)318660bd40aSGleb Smirnoff nl_process_nbuf(struct nl_buf *nb, struct nlpcb *nlp)
3197e5bf684SAlexander V. Chernikov {
3207e5bf684SAlexander V. Chernikov 	struct nlmsghdr *hdr;
3217e5bf684SAlexander V. Chernikov 	int error;
3227e5bf684SAlexander V. Chernikov 
323660bd40aSGleb Smirnoff 	NL_LOG(LOG_DEBUG3, "RX netlink buf %p on %p", nb, nlp->nl_socket);
3247e5bf684SAlexander V. Chernikov 
3257e5bf684SAlexander V. Chernikov 	struct nl_writer nw = {};
3267e5bf684SAlexander V. Chernikov 	if (!nlmsg_get_unicast_writer(&nw, NLMSG_SMALL, nlp)) {
3277e5bf684SAlexander V. Chernikov 		NL_LOG(LOG_DEBUG, "error allocating socket writer");
328660bd40aSGleb Smirnoff 		return (true);
3297e5bf684SAlexander V. Chernikov 	}
3307e5bf684SAlexander V. Chernikov 
3317e5bf684SAlexander V. Chernikov 	nlmsg_ignore_limit(&nw);
3327e5bf684SAlexander V. Chernikov 
3337e5bf684SAlexander V. Chernikov 	struct nl_pstate npt = {
3347e5bf684SAlexander V. Chernikov 		.nlp = nlp,
335660bd40aSGleb Smirnoff 		.lb.base = &nb->data[roundup2(nb->datalen, 8)],
336660bd40aSGleb Smirnoff 		.lb.size = nb->buflen - roundup2(nb->datalen, 8),
3377e5bf684SAlexander V. Chernikov 		.nw = &nw,
3387e5bf684SAlexander V. Chernikov 		.strict = nlp->nl_flags & NLF_STRICT,
3397e5bf684SAlexander V. Chernikov 	};
3407e5bf684SAlexander V. Chernikov 
341660bd40aSGleb Smirnoff 	for (; nb->offset + sizeof(struct nlmsghdr) <= nb->datalen;) {
342660bd40aSGleb Smirnoff 		hdr = (struct nlmsghdr *)&nb->data[nb->offset];
3437e5bf684SAlexander V. Chernikov 		/* Save length prior to calling handler */
3447e5bf684SAlexander V. Chernikov 		int msglen = NLMSG_ALIGN(hdr->nlmsg_len);
345660bd40aSGleb Smirnoff 		NL_LOG(LOG_DEBUG3, "parsing offset %d/%d",
346660bd40aSGleb Smirnoff 		    nb->offset, nb->datalen);
3477e5bf684SAlexander V. Chernikov 		npt_clear(&npt);
348660bd40aSGleb Smirnoff 		error = nl_receive_message(hdr, nb->datalen - nb->offset, nlp,
349660bd40aSGleb Smirnoff 		    &npt);
350660bd40aSGleb Smirnoff 		nb->offset += msglen;
3517e5bf684SAlexander V. Chernikov 		if (__predict_false(error != 0 || nlp->nl_tx_blocked))
3527e5bf684SAlexander V. Chernikov 			break;
3537e5bf684SAlexander V. Chernikov 	}
3547e5bf684SAlexander V. Chernikov 	NL_LOG(LOG_DEBUG3, "packet parsing done");
3557e5bf684SAlexander V. Chernikov 	nlmsg_flush(&nw);
3567e5bf684SAlexander V. Chernikov 
3577e5bf684SAlexander V. Chernikov 	if (nlp->nl_tx_blocked) {
3587e5bf684SAlexander V. Chernikov 		NLP_LOCK(nlp);
3597e5bf684SAlexander V. Chernikov 		nlp->nl_tx_blocked = false;
3607e5bf684SAlexander V. Chernikov 		NLP_UNLOCK(nlp);
361660bd40aSGleb Smirnoff 		return (false);
362660bd40aSGleb Smirnoff 	} else
363660bd40aSGleb Smirnoff 		return (true);
3647e5bf684SAlexander V. Chernikov }
365