2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2022 Alexander V. Chernikov <melifaro@FreeBSD.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
28 #include <sys/param.h>
29 #include <sys/malloc.h>
31 #include <sys/rmlock.h>
33 #include <sys/socket.h>
34 #include <sys/socketvar.h>
35 #include <sys/syslog.h>
37 #include <netlink/netlink.h>
38 #include <netlink/netlink_ctl.h>
39 #include <netlink/netlink_linux.h>
40 #include <netlink/netlink_var.h>
42 #define DEBUG_MOD_NAME nl_writer
43 #define DEBUG_MAX_LEVEL LOG_DEBUG3
44 #include <netlink/netlink_debug.h>
45 _DECLARE_DEBUG(LOG_INFO);
48 nlmsg_get_buf(struct nl_writer *nw, u_int len, bool waitok)
50 const int mflag = waitok ? M_WAITOK : M_NOWAIT;
52 MPASS(nw->buf == NULL);
54 NL_LOG(LOG_DEBUG3, "Setting up nw %p len %u %s", nw, len,
55 waitok ? "wait" : "nowait");
57 nw->buf = nl_buf_alloc(len, mflag);
58 if (__predict_false(nw->buf == NULL))
61 nw->malloc_flag = mflag;
69 nl_send_one(struct nl_writer *nw)
72 return (nl_send(nw, nw->nlp));
76 _nlmsg_get_unicast_writer(struct nl_writer *nw, int size, struct nlpcb *nlp)
81 return (nlmsg_get_buf(nw, size, false));
85 _nlmsg_get_group_writer(struct nl_writer *nw, int size, int protocol, int group_id)
87 nw->group.proto = protocol;
88 nw->group.id = group_id;
89 nw->cb = nl_send_group;
91 return (nlmsg_get_buf(nw, size, false));
95 _nlmsg_ignore_limit(struct nl_writer *nw)
97 nw->ignore_limit = true;
101 _nlmsg_flush(struct nl_writer *nw)
105 if (__predict_false(nw->hdr != NULL)) {
106 /* Last message has not been completed, skip it. */
107 int completed_len = (char *)nw->hdr - nw->buf->data;
108 /* Send completed messages */
109 nw->buf->datalen -= nw->buf->datalen - completed_len;
113 if (nw->buf->datalen == 0) {
114 MPASS(nw->num_messages == 0);
115 nl_buf_free(nw->buf);
121 nw->num_messages = 0;
124 NL_LOG(LOG_DEBUG, "nw %p flush with %p() failed", nw, nw->cb);
131 * Flushes previous data and allocates new underlying storage
132 * sufficient for holding at least @required_len bytes.
133 * Return true on success.
136 _nlmsg_refill_buffer(struct nl_writer *nw, u_int required_len)
139 u_int completed_len, new_len, last_len;
141 MPASS(nw->buf != NULL);
146 NL_LOG(LOG_DEBUG3, "no space at offset %u/%u (want %u), trying to "
147 "reclaim", nw->buf->datalen, nw->buf->buflen, required_len);
149 /* Calculate new buffer size and allocate it. */
150 completed_len = (nw->hdr != NULL) ?
151 (char *)nw->hdr - nw->buf->data : nw->buf->datalen;
152 if (completed_len > 0 && required_len < NLMBUFSIZE) {
153 /* We already ran out of space, use largest effective size. */
154 new_len = max(nw->buf->buflen, NLMBUFSIZE);
156 if (nw->buf->buflen < NLMBUFSIZE)
157 /* XXXGL: does this happen? */
158 new_len = NLMBUFSIZE;
160 new_len = nw->buf->buflen * 2;
161 while (new_len < required_len)
165 new = nl_buf_alloc(new_len, nw->malloc_flag | M_ZERO);
166 if (__predict_false(new == NULL)) {
168 NL_LOG(LOG_DEBUG, "getting new buf failed, setting ENOMEM");
172 /* Copy last (unfinished) header to the new storage. */
173 last_len = nw->buf->datalen - completed_len;
175 memcpy(new->data, nw->hdr, last_len);
176 new->datalen = last_len;
179 NL_LOG(LOG_DEBUG2, "completed: %u bytes, copied: %u bytes",
180 completed_len, last_len);
182 if (completed_len > 0) {
184 MPASS(nw->buf == NULL);
186 nl_buf_free(nw->buf);
188 nw->hdr = (last_len > 0) ? (struct nlmsghdr *)new->data : NULL;
189 NL_LOG(LOG_DEBUG2, "switched buffer: used %u/%u bytes",
190 new->datalen, new->buflen);
196 _nlmsg_add(struct nl_writer *nw, uint32_t portid, uint32_t seq, uint16_t type,
197 uint16_t flags, uint32_t len)
199 struct nl_buf *nb = nw->buf;
200 struct nlmsghdr *hdr;
203 MPASS(nw->hdr == NULL);
205 required_len = NETLINK_ALIGN(len + sizeof(struct nlmsghdr));
206 if (__predict_false(nb->datalen + required_len > nb->buflen)) {
207 if (!nlmsg_refill_buffer(nw, required_len))
212 hdr = (struct nlmsghdr *)(&nb->data[nb->datalen]);
214 hdr->nlmsg_len = len;
215 hdr->nlmsg_type = type;
216 hdr->nlmsg_flags = flags;
217 hdr->nlmsg_seq = seq;
218 hdr->nlmsg_pid = portid;
221 nb->datalen += sizeof(struct nlmsghdr);
227 _nlmsg_end(struct nl_writer *nw)
229 struct nl_buf *nb = nw->buf;
231 MPASS(nw->hdr != NULL);
234 NL_LOG(LOG_DEBUG, "ENOMEM when dumping message");
239 nw->hdr->nlmsg_len = nb->data + nb->datalen - (char *)nw->hdr;
240 NL_LOG(LOG_DEBUG2, "wrote msg len: %u type: %d: flags: 0x%X seq: %u pid: %u",
241 nw->hdr->nlmsg_len, nw->hdr->nlmsg_type, nw->hdr->nlmsg_flags,
242 nw->hdr->nlmsg_seq, nw->hdr->nlmsg_pid);
249 _nlmsg_abort(struct nl_writer *nw)
251 struct nl_buf *nb = nw->buf;
253 if (nw->hdr != NULL) {
254 nb->datalen = (char *)nw->hdr - nb->data;
260 nlmsg_ack(struct nlpcb *nlp, int error, struct nlmsghdr *hdr,
261 struct nl_pstate *npt)
263 struct nlmsgerr *errmsg;
265 uint32_t flags = nlp->nl_flags;
266 struct nl_writer *nw = npt->nw;
269 payload_len = sizeof(struct nlmsgerr);
272 * The only case when we send the full message in the
273 * reply is when there is an error and NETLINK_CAP_ACK
276 cap_ack = (error == 0) || (flags & NLF_CAP_ACK);
278 payload_len += hdr->nlmsg_len - sizeof(struct nlmsghdr);
279 payload_len = NETLINK_ALIGN(payload_len);
281 uint16_t nl_flags = cap_ack ? NLM_F_CAPPED : 0;
282 if ((npt->err_msg || npt->err_off) && nlp->nl_flags & NLF_EXT_ACK)
283 nl_flags |= NLM_F_ACK_TLVS;
285 NL_LOG(LOG_DEBUG3, "acknowledging message type %d seq %d",
286 hdr->nlmsg_type, hdr->nlmsg_seq);
288 if (!nlmsg_add(nw, nlp->nl_port, hdr->nlmsg_seq, NLMSG_ERROR, nl_flags, payload_len))
291 errmsg = nlmsg_reserve_data(nw, payload_len, struct nlmsgerr);
292 errmsg->error = error;
293 /* In case of error copy the whole message, else just the header */
294 memcpy(&errmsg->msg, hdr, cap_ack ? sizeof(*hdr) : hdr->nlmsg_len);
296 if (npt->err_msg != NULL && nlp->nl_flags & NLF_EXT_ACK)
297 nlattr_add_string(nw, NLMSGERR_ATTR_MSG, npt->err_msg);
298 if (npt->err_off != 0 && nlp->nl_flags & NLF_EXT_ACK)
299 nlattr_add_u32(nw, NLMSGERR_ATTR_OFFS, npt->err_off);
300 if (npt->cookie != NULL)
301 nlattr_add_raw(nw, npt->cookie);
306 NLP_LOG(LOG_DEBUG, nlp, "error allocating ack data for message %d seq %u",
307 hdr->nlmsg_type, hdr->nlmsg_seq);
312 _nlmsg_end_dump(struct nl_writer *nw, int error, struct nlmsghdr *hdr)
314 if (!nlmsg_add(nw, hdr->nlmsg_pid, hdr->nlmsg_seq, NLMSG_DONE, 0, sizeof(int))) {
315 NL_LOG(LOG_DEBUG, "Error finalizing table dump");
318 /* Save operation result */
319 int *perror = nlmsg_reserve_object(nw, int);
320 NL_LOG(LOG_DEBUG2, "record error=%d at off %d (%p)", error,
321 nw->buf->datalen, perror);
324 nw->suppress_ack = true;
334 nlattr_save_offset(const struct nl_writer *nw)
336 return (nw->buf->datalen - ((char *)nw->hdr - nw->buf->data));
340 nlmsg_reserve_data_raw(struct nl_writer *nw, size_t sz)
342 struct nl_buf *nb = nw->buf;
345 sz = NETLINK_ALIGN(sz);
346 if (__predict_false(nb->datalen + sz > nb->buflen)) {
347 if (!nlmsg_refill_buffer(nw, sz))
352 data = &nb->data[nb->datalen];
360 nlattr_add(struct nl_writer *nw, int attr_type, int attr_len, const void *data)
362 struct nl_buf *nb = nw->buf;
366 required_len = NLA_ALIGN(attr_len + sizeof(struct nlattr));
367 if (__predict_false(nb->datalen + required_len > nb->buflen)) {
368 if (!nlmsg_refill_buffer(nw, required_len))
373 nla = (struct nlattr *)(&nb->data[nb->datalen]);
375 nla->nla_len = attr_len + sizeof(struct nlattr);
376 nla->nla_type = attr_type;
378 if ((attr_len % 4) != 0) {
379 /* clear padding bytes */
380 bzero((char *)nla + required_len - 4, 4);
382 memcpy((nla + 1), data, attr_len);
384 nb->datalen += required_len;
388 #include <netlink/ktest_netlink_message_writer.h>