1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 #include <sys/param.h> 31 #include <sys/malloc.h> 32 #include <sys/lock.h> 33 #include <sys/rmlock.h> 34 #include <sys/mbuf.h> 35 #include <sys/ck.h> 36 #include <sys/socket.h> 37 #include <sys/socketvar.h> 38 #include <sys/syslog.h> 39 40 #include <netinet/in.h> 41 42 #include <netlink/netlink.h> 43 #include <netlink/netlink_ctl.h> 44 #include <netlink/netlink_linux.h> 45 #include <netlink/netlink_var.h> 46 47 #define DEBUG_MOD_NAME nl_writer 48 #define DEBUG_MAX_LEVEL LOG_DEBUG3 49 #include <netlink/netlink_debug.h> 50 _DECLARE_DEBUG(LOG_DEBUG); 51 52 /* 53 * The goal of this file is to provide convenient message writing KPI on top of 54 * different storage methods (mbufs, uio, temporary memory chunks). 55 * 56 * The main KPI guarantee is the the (last) message always resides in the contiguous 57 * memory buffer, so one is able to update the header after writing the entire message. 58 * 59 * This guarantee comes with a side effect of potentially reallocating underlying 60 * buffer, so one needs to update the desired pointers after something is added 61 * to the header. 62 * 63 * Messaging layer contains hooks performing transparent Linux translation for the messages. 64 * 65 * There are 3 types of supported targets: 66 * * socket (adds mbufs to the socket buffer, used for message replies) 67 * * group (sends mbuf/chain to the specified groups, used for the notifications) 68 * * chain (returns mbuf chain, used in Linux message translation code) 69 * 70 * There are 3 types of storage: 71 * * NS_WRITER_TYPE_MBUF (mbuf-based, most efficient, used when a single message 72 * fits in MCLBYTES) 73 * * NS_WRITER_TYPE_BUF (fallback, malloc-based, used when a single message needs 74 * to be larger than one supported by NS_WRITER_TYPE_MBUF) 75 * * NS_WRITER_TYPE_LBUF (malloc-based, similar to NS_WRITER_TYPE_BUF, used for 76 * Linux sockets, calls translation hook prior to sending messages to the socket). 77 * 78 * Internally, KPI switches between different types of storage when memory requirements 79 * change. It happens transparently to the caller. 80 */ 81 82 83 typedef bool nlwriter_op_init(struct nl_writer *nw, int size, bool waitok); 84 typedef bool nlwriter_op_write(struct nl_writer *nw, void *buf, int buflen, int cnt); 85 86 struct nlwriter_ops { 87 nlwriter_op_init *init; 88 nlwriter_op_write *write_socket; 89 nlwriter_op_write *write_group; 90 nlwriter_op_write *write_chain; 91 }; 92 93 /* 94 * NS_WRITER_TYPE_BUF 95 * Writes message to a temporary memory buffer, 96 * flushing to the socket/group when buffer size limit is reached 97 */ 98 static bool 99 nlmsg_get_ns_buf(struct nl_writer *nw, int size, bool waitok) 100 { 101 int mflag = waitok ? M_WAITOK : M_NOWAIT; 102 nw->_storage = malloc(size, M_NETLINK, mflag | M_ZERO); 103 if (__predict_false(nw->_storage == NULL)) 104 return (false); 105 nw->alloc_len = size; 106 nw->offset = 0; 107 nw->hdr = NULL; 108 nw->data = nw->_storage; 109 nw->writer_type = NS_WRITER_TYPE_BUF; 110 nw->malloc_flag = mflag; 111 nw->num_messages = 0; 112 nw->enomem = false; 113 return (true); 114 } 115 116 static bool 117 nlmsg_write_socket_buf(struct nl_writer *nw, void *buf, int datalen, int cnt) 118 { 119 NL_LOG(LOG_DEBUG2, "IN: ptr: %p len: %d arg: %p", buf, datalen, nw); 120 if (__predict_false(datalen == 0)) { 121 free(buf, M_NETLINK); 122 return (true); 123 } 124 125 struct mbuf *m = m_getm2(NULL, datalen, nw->malloc_flag, MT_DATA, M_PKTHDR); 126 if (__predict_false(m == NULL)) { 127 /* XXX: should we set sorcverr? */ 128 free(buf, M_NETLINK); 129 return (false); 130 } 131 m_append(m, datalen, buf); 132 free(buf, M_NETLINK); 133 134 int io_flags = (nw->ignore_limit) ? NL_IOF_IGNORE_LIMIT : 0; 135 return (nl_send_one(m, (struct nlpcb *)(nw->arg_ptr), cnt, io_flags)); 136 } 137 138 static bool 139 nlmsg_write_group_buf(struct nl_writer *nw, void *buf, int datalen, int cnt) 140 { 141 NL_LOG(LOG_DEBUG2, "IN: ptr: %p len: %d arg: %p", buf, datalen, nw->arg_ptr); 142 if (__predict_false(datalen == 0)) { 143 free(buf, M_NETLINK); 144 return (true); 145 } 146 147 struct mbuf *m = m_getm2(NULL, datalen, nw->malloc_flag, MT_DATA, M_PKTHDR); 148 if (__predict_false(m == NULL)) { 149 free(buf, M_NETLINK); 150 return (false); 151 } 152 bool success = m_append(m, datalen, buf) != 0; 153 free(buf, M_NETLINK); 154 155 if (!success) 156 return (false); 157 158 nl_send_group(m, cnt, nw->arg_uint >> 16, nw->arg_uint & 0xFFFF); 159 return (true); 160 } 161 162 static bool 163 nlmsg_write_chain_buf(struct nl_writer *nw, void *buf, int datalen, int cnt) 164 { 165 struct mbuf **m0 = (struct mbuf **)(nw->arg_ptr); 166 NL_LOG(LOG_DEBUG2, "IN: ptr: %p len: %d arg: %p", buf, datalen, nw->arg_ptr); 167 168 if (__predict_false(datalen == 0)) { 169 free(buf, M_NETLINK); 170 return (true); 171 } 172 173 if (*m0 == NULL) { 174 struct mbuf *m; 175 176 m = m_getm2(NULL, datalen, nw->malloc_flag, MT_DATA, M_PKTHDR); 177 if (__predict_false(m == NULL)) { 178 free(buf, M_NETLINK); 179 return (false); 180 } 181 *m0 = m; 182 } 183 if (__predict_false(m_append(*m0, datalen, buf) == 0)) { 184 free(buf, M_NETLINK); 185 return (false); 186 } 187 return (true); 188 } 189 190 191 /* 192 * NS_WRITER_TYPE_MBUF 193 * Writes message to the allocated mbuf, 194 * flushing to socket/group when mbuf size limit is reached. 195 * This is the most efficient mechanism as it avoids double-copying. 196 * 197 * Allocates a single mbuf suitable to store up to @size bytes of data. 198 * If size < MHLEN (around 160 bytes), allocates mbuf with pkghdr 199 * If size <= MCLBYTES (2k), allocate a single mbuf cluster 200 * Otherwise, return NULL. 201 */ 202 static bool 203 nlmsg_get_ns_mbuf(struct nl_writer *nw, int size, bool waitok) 204 { 205 struct mbuf *m; 206 207 int mflag = waitok ? M_WAITOK : M_NOWAIT; 208 m = m_get2(size, mflag, MT_DATA, M_PKTHDR); 209 if (__predict_false(m == NULL)) 210 return (false); 211 nw->alloc_len = M_TRAILINGSPACE(m); 212 nw->offset = 0; 213 nw->hdr = NULL; 214 nw->_storage = (void *)m; 215 nw->data = mtod(m, void *); 216 nw->writer_type = NS_WRITER_TYPE_MBUF; 217 nw->malloc_flag = mflag; 218 nw->num_messages = 0; 219 nw->enomem = false; 220 memset(nw->data, 0, size); 221 NL_LOG(LOG_DEBUG2, "alloc mbuf %p req_len %d alloc_len %d data_ptr %p", 222 m, size, nw->alloc_len, nw->data); 223 return (true); 224 } 225 226 static bool 227 nlmsg_write_socket_mbuf(struct nl_writer *nw, void *buf, int datalen, int cnt) 228 { 229 struct mbuf *m = (struct mbuf *)buf; 230 NL_LOG(LOG_DEBUG2, "IN: ptr: %p len: %d arg: %p", buf, datalen, nw->arg_ptr); 231 232 if (__predict_false(datalen == 0)) { 233 m_freem(m); 234 return (true); 235 } 236 237 m->m_pkthdr.len = datalen; 238 m->m_len = datalen; 239 int io_flags = (nw->ignore_limit) ? NL_IOF_IGNORE_LIMIT : 0; 240 return (nl_send_one(m, (struct nlpcb *)(nw->arg_ptr), cnt, io_flags)); 241 } 242 243 static bool 244 nlmsg_write_group_mbuf(struct nl_writer *nw, void *buf, int datalen, int cnt) 245 { 246 struct mbuf *m = (struct mbuf *)buf; 247 NL_LOG(LOG_DEBUG2, "IN: ptr: %p len: %d arg: %p", buf, datalen, nw->arg_ptr); 248 249 if (__predict_false(datalen == 0)) { 250 m_freem(m); 251 return (true); 252 } 253 254 m->m_pkthdr.len = datalen; 255 m->m_len = datalen; 256 nl_send_group(m, cnt, nw->arg_uint >> 16, nw->arg_uint & 0xFFFF); 257 return (true); 258 } 259 260 static bool 261 nlmsg_write_chain_mbuf(struct nl_writer *nw, void *buf, int datalen, int cnt) 262 { 263 struct mbuf *m_new = (struct mbuf *)buf; 264 struct mbuf **m0 = (struct mbuf **)(nw->arg_ptr); 265 266 NL_LOG(LOG_DEBUG2, "IN: ptr: %p len: %d arg: %p", buf, datalen, nw->arg_ptr); 267 268 if (__predict_false(datalen == 0)) { 269 m_freem(m_new); 270 return (true); 271 } 272 273 m_new->m_pkthdr.len = datalen; 274 m_new->m_len = datalen; 275 276 if (*m0 == NULL) { 277 *m0 = m_new; 278 } else { 279 struct mbuf *m_last; 280 for (m_last = *m0; m_last->m_next != NULL; m_last = m_last->m_next) 281 ; 282 m_last->m_next = m_new; 283 (*m0)->m_pkthdr.len += datalen; 284 } 285 286 return (true); 287 } 288 289 /* 290 * NS_WRITER_TYPE_LBUF 291 * Writes message to the allocated memory buffer, 292 * flushing to socket/group when mbuf size limit is reached. 293 * Calls linux handler to rewrite messages before sending to the socket. 294 */ 295 static bool 296 nlmsg_get_ns_lbuf(struct nl_writer *nw, int size, bool waitok) 297 { 298 int mflag = waitok ? M_WAITOK : M_NOWAIT; 299 size = roundup2(size, sizeof(void *)); 300 int add_size = sizeof(struct linear_buffer) + SCRATCH_BUFFER_SIZE; 301 char *buf = malloc(add_size + size * 2, M_NETLINK, mflag | M_ZERO); 302 if (__predict_false(buf == NULL)) 303 return (false); 304 305 /* Fill buffer header first */ 306 struct linear_buffer *lb = (struct linear_buffer *)buf; 307 lb->base = &buf[sizeof(struct linear_buffer) + size]; 308 lb->size = size + SCRATCH_BUFFER_SIZE; 309 310 nw->alloc_len = size; 311 nw->offset = 0; 312 nw->hdr = NULL; 313 nw->_storage = buf; 314 nw->data = (char *)(lb + 1); 315 nw->malloc_flag = mflag; 316 nw->writer_type = NS_WRITER_TYPE_LBUF; 317 nw->num_messages = 0; 318 nw->enomem = false; 319 return (true); 320 } 321 322 static bool 323 nlmsg_write_socket_lbuf(struct nl_writer *nw, void *buf, int datalen, int cnt) 324 { 325 struct linear_buffer *lb = (struct linear_buffer *)buf; 326 char *data = (char *)(lb + 1); 327 struct nlpcb *nlp = (struct nlpcb *)(nw->arg_ptr); 328 329 if (__predict_false(datalen == 0)) { 330 free(buf, M_NETLINK); 331 return (true); 332 } 333 334 struct mbuf *m = NULL; 335 if (linux_netlink_p != NULL) 336 m = linux_netlink_p->msgs_to_linux(nlp->nl_proto, data, datalen, nlp); 337 free(buf, M_NETLINK); 338 339 if (__predict_false(m == NULL)) { 340 /* XXX: should we set sorcverr? */ 341 return (false); 342 } 343 344 int io_flags = (nw->ignore_limit) ? NL_IOF_IGNORE_LIMIT : 0; 345 return (nl_send_one(m, nlp, cnt, io_flags)); 346 } 347 348 /* Shouldn't be called (maybe except Linux code originating message) */ 349 static bool 350 nlmsg_write_group_lbuf(struct nl_writer *nw, void *buf, int datalen, int cnt) 351 { 352 struct linear_buffer *lb = (struct linear_buffer *)buf; 353 char *data = (char *)(lb + 1); 354 355 if (__predict_false(datalen == 0)) { 356 free(buf, M_NETLINK); 357 return (true); 358 } 359 360 struct mbuf *m = m_getm2(NULL, datalen, nw->malloc_flag, MT_DATA, M_PKTHDR); 361 if (__predict_false(m == NULL)) { 362 free(buf, M_NETLINK); 363 return (false); 364 } 365 m_append(m, datalen, data); 366 free(buf, M_NETLINK); 367 368 nl_send_group(m, cnt, nw->arg_uint >> 16, nw->arg_uint & 0xFFFF); 369 return (true); 370 } 371 372 static const struct nlwriter_ops nlmsg_writers[] = { 373 /* NS_WRITER_TYPE_MBUF */ 374 { 375 .init = nlmsg_get_ns_mbuf, 376 .write_socket = nlmsg_write_socket_mbuf, 377 .write_group = nlmsg_write_group_mbuf, 378 .write_chain = nlmsg_write_chain_mbuf, 379 }, 380 /* NS_WRITER_TYPE_BUF */ 381 { 382 .init = nlmsg_get_ns_buf, 383 .write_socket = nlmsg_write_socket_buf, 384 .write_group = nlmsg_write_group_buf, 385 .write_chain = nlmsg_write_chain_buf, 386 }, 387 /* NS_WRITER_TYPE_LBUF */ 388 { 389 .init = nlmsg_get_ns_lbuf, 390 .write_socket = nlmsg_write_socket_lbuf, 391 .write_group = nlmsg_write_group_lbuf, 392 }, 393 }; 394 395 static void 396 nlmsg_set_callback(struct nl_writer *nw) 397 { 398 const struct nlwriter_ops *pops = &nlmsg_writers[nw->writer_type]; 399 400 switch (nw->writer_target) { 401 case NS_WRITER_TARGET_SOCKET: 402 nw->cb = pops->write_socket; 403 break; 404 case NS_WRITER_TARGET_GROUP: 405 nw->cb = pops->write_group; 406 break; 407 case NS_WRITER_TARGET_CHAIN: 408 nw->cb = pops->write_chain; 409 break; 410 default: 411 panic("not implemented"); 412 } 413 } 414 415 static bool 416 nlmsg_get_buf_type(struct nl_writer *nw, int size, int type, bool waitok) 417 { 418 MPASS(type + 1 <= sizeof(nlmsg_writers) / sizeof(nlmsg_writers[0])); 419 NL_LOG(LOG_DEBUG3, "Setting up nw %p size %d type %d", nw, size, type); 420 return (nlmsg_writers[type].init(nw, size, waitok)); 421 } 422 423 static bool 424 nlmsg_get_buf(struct nl_writer *nw, int size, bool waitok, bool is_linux) 425 { 426 int type; 427 428 if (!is_linux) { 429 if (__predict_true(size <= MCLBYTES)) 430 type = NS_WRITER_TYPE_MBUF; 431 else 432 type = NS_WRITER_TYPE_BUF; 433 } else 434 type = NS_WRITER_TYPE_LBUF; 435 return (nlmsg_get_buf_type(nw, size, type, waitok)); 436 } 437 438 bool 439 nlmsg_get_unicast_writer(struct nl_writer *nw, int size, struct nlpcb *nlp) 440 { 441 if (!nlmsg_get_buf(nw, size, false, nlp->nl_linux)) 442 return (false); 443 nw->arg_ptr = (void *)nlp; 444 nw->writer_target = NS_WRITER_TARGET_SOCKET; 445 nlmsg_set_callback(nw); 446 return (true); 447 } 448 449 bool 450 nlmsg_get_group_writer(struct nl_writer *nw, int size, int protocol, int group_id) 451 { 452 if (!nlmsg_get_buf(nw, size, false, false)) 453 return (false); 454 nw->arg_uint = (uint64_t)protocol << 16 | (uint64_t)group_id; 455 nw->writer_target = NS_WRITER_TARGET_GROUP; 456 nlmsg_set_callback(nw); 457 return (true); 458 } 459 460 bool 461 nlmsg_get_chain_writer(struct nl_writer *nw, int size, struct mbuf **pm) 462 { 463 if (!nlmsg_get_buf(nw, size, false, false)) 464 return (false); 465 *pm = NULL; 466 nw->arg_ptr = (void *)pm; 467 nw->writer_target = NS_WRITER_TARGET_CHAIN; 468 nlmsg_set_callback(nw); 469 NL_LOG(LOG_DEBUG3, "setup cb %p (need %p)", nw->cb, &nlmsg_write_chain_mbuf); 470 return (true); 471 } 472 473 void 474 nlmsg_ignore_limit(struct nl_writer *nw) 475 { 476 nw->ignore_limit = true; 477 } 478 479 bool 480 nlmsg_flush(struct nl_writer *nw) 481 { 482 483 if (__predict_false(nw->hdr != NULL)) { 484 /* Last message has not been completed, skip it. */ 485 int completed_len = (char *)nw->hdr - nw->data; 486 /* Send completed messages */ 487 nw->offset -= nw->offset - completed_len; 488 nw->hdr = NULL; 489 } 490 491 NL_LOG(LOG_DEBUG2, "OUT"); 492 bool result = nw->cb(nw, nw->_storage, nw->offset, nw->num_messages); 493 nw->_storage = NULL; 494 495 if (!result) { 496 NL_LOG(LOG_DEBUG, "nw %p offset %d: flush with %p() failed", nw, nw->offset, nw->cb); 497 } 498 499 return (result); 500 } 501 502 /* 503 * Flushes previous data and allocates new underlying storage 504 * sufficient for holding at least @required_len bytes. 505 * Return true on success. 506 */ 507 bool 508 nlmsg_refill_buffer(struct nl_writer *nw, int required_len) 509 { 510 struct nl_writer ns_new = {}; 511 int completed_len, new_len; 512 513 if (nw->enomem) 514 return (false); 515 516 NL_LOG(LOG_DEBUG3, "no space at offset %d/%d (want %d), trying to reclaim", 517 nw->offset, nw->alloc_len, required_len); 518 519 /* Calculated new buffer size and allocate it s*/ 520 completed_len = (nw->hdr != NULL) ? (char *)nw->hdr - nw->data : nw->offset; 521 if (completed_len > 0 && required_len < MCLBYTES) { 522 /* We already ran out of space, use the largest effective size */ 523 new_len = max(nw->alloc_len, MCLBYTES); 524 } else { 525 if (nw->alloc_len < MCLBYTES) 526 new_len = MCLBYTES; 527 else 528 new_len = nw->alloc_len * 2; 529 while (new_len < required_len) 530 new_len *= 2; 531 } 532 bool waitok = (nw->malloc_flag == M_WAITOK); 533 bool is_linux = (nw->writer_type == NS_WRITER_TYPE_LBUF); 534 if (!nlmsg_get_buf(&ns_new, new_len, waitok, is_linux)) { 535 nw->enomem = true; 536 NL_LOG(LOG_DEBUG, "getting new buf failed, setting ENOMEM"); 537 return (false); 538 } 539 if (nw->ignore_limit) 540 nlmsg_ignore_limit(&ns_new); 541 542 /* Update callback data */ 543 ns_new.writer_target = nw->writer_target; 544 nlmsg_set_callback(&ns_new); 545 ns_new.arg_uint = nw->arg_uint; 546 547 /* Copy last (unfinished) header to the new storage */ 548 int last_len = nw->offset - completed_len; 549 if (last_len > 0) { 550 memcpy(ns_new.data, nw->hdr, last_len); 551 ns_new.hdr = (struct nlmsghdr *)ns_new.data; 552 ns_new.offset = last_len; 553 } 554 555 NL_LOG(LOG_DEBUG2, "completed: %d bytes, copied: %d bytes", completed_len, last_len); 556 557 /* Flush completed headers & switch to the new nw */ 558 nlmsg_flush(nw); 559 memcpy(nw, &ns_new, sizeof(struct nl_writer)); 560 NL_LOG(LOG_DEBUG2, "switched buffer: used %d/%d bytes", nw->offset, nw->alloc_len); 561 562 return (true); 563 } 564 565 bool 566 nlmsg_add(struct nl_writer *nw, uint32_t portid, uint32_t seq, uint16_t type, 567 uint16_t flags, uint32_t len) 568 { 569 struct nlmsghdr *hdr; 570 571 MPASS(nw->hdr == NULL); 572 573 int required_len = NETLINK_ALIGN(len + sizeof(struct nlmsghdr)); 574 if (__predict_false(nw->offset + required_len > nw->alloc_len)) { 575 if (!nlmsg_refill_buffer(nw, required_len)) 576 return (false); 577 } 578 579 hdr = (struct nlmsghdr *)(&nw->data[nw->offset]); 580 581 hdr->nlmsg_len = len; 582 hdr->nlmsg_type = type; 583 hdr->nlmsg_flags = flags; 584 hdr->nlmsg_seq = seq; 585 hdr->nlmsg_pid = portid; 586 587 nw->hdr = hdr; 588 nw->offset += sizeof(struct nlmsghdr); 589 590 return (true); 591 } 592 593 bool 594 nlmsg_end(struct nl_writer *nw) 595 { 596 MPASS(nw->hdr != NULL); 597 598 if (nw->enomem) { 599 NL_LOG(LOG_DEBUG, "ENOMEM when dumping message"); 600 nlmsg_abort(nw); 601 return (false); 602 } 603 604 nw->hdr->nlmsg_len = (uint32_t)(nw->data + nw->offset - (char *)nw->hdr); 605 NL_LOG(LOG_DEBUG2, "wrote msg len: %u type: %d: flags: 0x%X seq: %u pid: %u", 606 nw->hdr->nlmsg_len, nw->hdr->nlmsg_type, nw->hdr->nlmsg_flags, 607 nw->hdr->nlmsg_seq, nw->hdr->nlmsg_pid); 608 nw->hdr = NULL; 609 nw->num_messages++; 610 return (true); 611 } 612 613 void 614 nlmsg_abort(struct nl_writer *nw) 615 { 616 if (nw->hdr != NULL) { 617 nw->offset = (uint32_t)((char *)nw->hdr - nw->data); 618 nw->hdr = NULL; 619 } 620 } 621 622 void 623 nlmsg_ack(struct nlpcb *nlp, int error, struct nlmsghdr *hdr, 624 struct nl_pstate *npt) 625 { 626 struct nlmsgerr *errmsg; 627 int payload_len; 628 uint32_t flags = nlp->nl_flags; 629 struct nl_writer *nw = npt->nw; 630 bool cap_ack; 631 632 payload_len = sizeof(struct nlmsgerr); 633 634 /* 635 * The only case when we send the full message in the 636 * reply is when there is an error and NETLINK_CAP_ACK 637 * is not set. 638 */ 639 cap_ack = (error == 0) || (flags & NLF_CAP_ACK); 640 if (!cap_ack) 641 payload_len += hdr->nlmsg_len - sizeof(struct nlmsghdr); 642 payload_len = NETLINK_ALIGN(payload_len); 643 644 uint16_t nl_flags = cap_ack ? NLM_F_CAPPED : 0; 645 if ((npt->err_msg || npt->err_off) && nlp->nl_flags & NLF_EXT_ACK) 646 nl_flags |= NLM_F_ACK_TLVS; 647 648 NL_LOG(LOG_DEBUG3, "acknowledging message type %d seq %d", 649 hdr->nlmsg_type, hdr->nlmsg_seq); 650 651 if (!nlmsg_add(nw, nlp->nl_port, hdr->nlmsg_seq, NLMSG_ERROR, nl_flags, payload_len)) 652 goto enomem; 653 654 errmsg = nlmsg_reserve_data(nw, payload_len, struct nlmsgerr); 655 errmsg->error = error; 656 /* In case of error copy the whole message, else just the header */ 657 memcpy(&errmsg->msg, hdr, cap_ack ? sizeof(*hdr) : hdr->nlmsg_len); 658 659 if (npt->err_msg != NULL && nlp->nl_flags & NLF_EXT_ACK) 660 nlattr_add_string(nw, NLMSGERR_ATTR_MSG, npt->err_msg); 661 if (npt->err_off != 0 && nlp->nl_flags & NLF_EXT_ACK) 662 nlattr_add_u32(nw, NLMSGERR_ATTR_OFFS, npt->err_off); 663 if (npt->cookie != NULL) 664 nlattr_add_raw(nw, npt->cookie); 665 666 if (nlmsg_end(nw)) 667 return; 668 enomem: 669 NLP_LOG(LOG_DEBUG, nlp, "error allocating ack data for message %d seq %u", 670 hdr->nlmsg_type, hdr->nlmsg_seq); 671 nlmsg_abort(nw); 672 } 673 674 bool 675 nlmsg_end_dump(struct nl_writer *nw, int error, struct nlmsghdr *hdr) 676 { 677 if (!nlmsg_add(nw, hdr->nlmsg_pid, hdr->nlmsg_seq, NLMSG_DONE, 0, sizeof(int))) { 678 NL_LOG(LOG_DEBUG, "Error finalizing table dump"); 679 return (false); 680 } 681 /* Save operation result */ 682 int *perror = nlmsg_reserve_object(nw, int); 683 NL_LOG(LOG_DEBUG2, "record error=%d at off %d (%p)", error, 684 nw->offset, perror); 685 *perror = error; 686 nlmsg_end(nw); 687 nw->suppress_ack = true; 688 689 return (true); 690 } 691 692 bool 693 nlattr_add_in_addr(struct nl_writer *nw, int attrtype, const struct in_addr *in) 694 { 695 return (nlattr_add(nw, attrtype, sizeof(*in), in)); 696 } 697 698 bool 699 nlattr_add_in6_addr(struct nl_writer *nw, int attrtype, const struct in6_addr *in6) 700 { 701 return (nlattr_add(nw, attrtype, sizeof(*in6), in6)); 702 } 703