1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 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 #include "opt_inet.h" 30 #include "opt_inet6.h" 31 #include <sys/types.h> 32 #include <sys/malloc.h> 33 #include <sys/rmlock.h> 34 #include <sys/socket.h> 35 #include <sys/stdarg.h> 36 37 #include <net/if.h> 38 #include <net/route.h> 39 #include <net/route/nhop.h> 40 41 #include <net/route/route_ctl.h> 42 #include <netinet/in.h> 43 #include <netlink/netlink.h> 44 #include <netlink/netlink_ctl.h> 45 #include <netlink/netlink_var.h> 46 #include <netlink/netlink_route.h> 47 48 #define DEBUG_MOD_NAME nl_parser 49 #define DEBUG_MAX_LEVEL LOG_DEBUG3 50 #include <netlink/netlink_debug.h> 51 _DECLARE_DEBUG(LOG_INFO); 52 53 /* 54 * Some applications try to provide only the non-zero part of the required 55 * message header instead of a full one. It happens when fetching routes or 56 * interface addresses, where the first header byte is the family. 57 * This behavior is "illegal" under the "strict" Netlink socket option, however 58 * there are many applications out there doing things in the "old" way. 59 * Support this usecase by copying the provided bytes into the temporary 60 * zero-filled header and running the parser on this header instead. 61 */ 62 struct nlmsghdr * 63 nl_alloc_compat_hdr(struct nlmsghdr *hdr, uint32_t len, struct nl_pstate *npt) 64 { 65 struct nlmsghdr *tmp; 66 67 MPASS(hdr->nlmsg_len < sizeof(struct nlmsghdr) + len); 68 69 len += sizeof(struct nlmsghdr); 70 if (npt->strict) { 71 nlmsg_report_err_msg(npt, 72 "header too short: expected %d, got %d", 73 len, hdr->nlmsg_len); 74 return (NULL); 75 } 76 tmp = npt_alloc(npt, len); 77 if (tmp == NULL) 78 return (NULL); 79 memcpy(tmp, hdr, hdr->nlmsg_len); 80 tmp->nlmsg_len = len; 81 82 return (tmp); 83 } 84 85 bool 86 nlmsg_report_err_msg(struct nl_pstate *npt, const char *fmt, ...) 87 { 88 va_list ap; 89 90 if (npt->err_msg != NULL) 91 return (false); 92 char *buf = npt_alloc(npt, NL_MAX_ERROR_BUF); 93 if (buf == NULL) 94 return (false); 95 va_start(ap, fmt); 96 vsnprintf(buf, NL_MAX_ERROR_BUF, fmt, ap); 97 va_end(ap); 98 99 npt->err_msg = buf; 100 return (true); 101 } 102 103 bool 104 nlmsg_report_err_offset(struct nl_pstate *npt, uint32_t off) 105 { 106 if (npt->err_off != 0) 107 return (false); 108 npt->err_off = off; 109 return (true); 110 } 111 112 void 113 nlmsg_report_cookie(struct nl_pstate *npt, struct nlattr *nla) 114 { 115 MPASS(nla->nla_type == NLMSGERR_ATTR_COOKIE); 116 MPASS(nla->nla_len >= sizeof(struct nlattr)); 117 npt->cookie = nla; 118 } 119 120 void 121 nlmsg_report_cookie_u32(struct nl_pstate *npt, uint32_t val) 122 { 123 struct nlattr *nla = npt_alloc(npt, sizeof(*nla) + sizeof(uint32_t)); 124 125 nla->nla_type = NLMSGERR_ATTR_COOKIE; 126 nla->nla_len = sizeof(*nla) + sizeof(uint32_t); 127 memcpy(nla + 1, &val, sizeof(uint32_t)); 128 nlmsg_report_cookie(npt, nla); 129 } 130 131 static const struct nlattr_parser * 132 search_states(const struct nlattr_parser *ps, u_int pslen, int key) 133 { 134 int left_i = 0, right_i = pslen - 1; 135 136 if (key < ps[0].type || key > ps[pslen - 1].type) 137 return (NULL); 138 139 while (left_i + 1 < right_i) { 140 int mid_i = (left_i + right_i) / 2; 141 if (key < ps[mid_i].type) 142 right_i = mid_i; 143 else if (key > ps[mid_i].type) 144 left_i = mid_i + 1; 145 else 146 return (&ps[mid_i]); 147 } 148 if (ps[left_i].type == key) 149 return (&ps[left_i]); 150 else if (ps[right_i].type == key) 151 return (&ps[right_i]); 152 return (NULL); 153 } 154 155 int 156 nl_parse_attrs_raw(struct nlattr *nla_head, uint16_t len, 157 const struct nlattr_parser *ps, u_int pslen, struct nl_pstate *npt, 158 void *target) 159 { 160 const struct nlattr_parser *s; 161 struct nlattr *nla; 162 uint16_t orig_len, off; 163 int error = 0; 164 165 NL_LOG(LOG_DEBUG3, "parse %p remaining_len %d", nla_head, len); 166 orig_len = len; 167 NLA_FOREACH(nla, nla_head, len) { 168 NL_LOG(LOG_DEBUG3, ">> parsing %p attr_type %u len %u (rem %u)", 169 nla, nla->nla_type, nla->nla_len, len); 170 if (nla->nla_len < sizeof(struct nlattr)) { 171 NLMSG_REPORT_ERR_MSG(npt, 172 "Invalid attr %p type %u len: %u", 173 nla, nla->nla_type, nla->nla_len); 174 off = (char *)nla - (char *)npt->hdr; 175 nlmsg_report_err_offset(npt, off); 176 return (EINVAL); 177 } 178 179 s = search_states(ps, pslen, nla->nla_type & NLA_TYPE_MASK); 180 if (s != NULL) { 181 void *ptr; 182 183 ptr = (void *)((char *)target + s->off); 184 error = s->cb(nla, npt, s->arg, ptr); 185 if (error != 0) { 186 off = (char *)nla - (char *)npt->hdr; 187 nlmsg_report_err_offset(npt, off); 188 NL_LOG(LOG_DEBUG3, 189 "parse failed at offset %u", off); 190 return (error); 191 } 192 } else { 193 /* Ignore non-specified attributes */ 194 NL_LOG(LOG_DEBUG3, "ignoring attr %u", nla->nla_type); 195 } 196 } 197 if (len >= sizeof(struct nlattr)) { 198 nla = (struct nlattr *)((char *)nla_head + (orig_len - len)); 199 NL_LOG(LOG_DEBUG3, " >>> end %p attr_type %u len %u", nla, 200 nla->nla_type, nla->nla_len); 201 } 202 NL_LOG(LOG_DEBUG3, "end parse: %p remaining_len %u", nla, len); 203 204 return (0); 205 } 206 207 void 208 nl_get_attrs_bmask_raw(struct nlattr *nla_head, uint32_t len, 209 struct nlattr_bmask *bm) 210 { 211 struct nlattr *nla = NULL; 212 uint16_t nla_type; 213 214 BIT_ZERO(NL_ATTR_BMASK_SIZE, bm); 215 216 NLA_FOREACH(nla, nla_head, len) { 217 if (nla->nla_len < sizeof(struct nlattr)) 218 return; 219 nla_type = nla->nla_type & NLA_TYPE_MASK; 220 if (nla_type < NL_ATTR_BMASK_SIZE) 221 BIT_SET(NL_ATTR_BMASK_SIZE, nla_type, bm); 222 else 223 NL_LOG(LOG_DEBUG2, 224 "Skipping type %u in the mask: too short", 225 nla_type); 226 } 227 } 228 229 bool 230 nl_has_attr(const struct nlattr_bmask *bm, uint16_t nla_type) 231 { 232 MPASS(nla_type < NL_ATTR_BMASK_SIZE); 233 234 return (BIT_ISSET(NL_ATTR_BMASK_SIZE, nla_type, bm)); 235 } 236 237 int 238 nlattr_get_flag(struct nlattr *nla, struct nl_pstate *npt, const void *arg, 239 void *target) 240 { 241 if (__predict_false(NLA_DATA_LEN(nla) != 0)) { 242 NLMSG_REPORT_ERR_MSG(npt, "nla type %d size(%u) is not a flag", 243 nla->nla_type, NLA_DATA_LEN(nla)); 244 return (EINVAL); 245 } 246 247 *((uint8_t *)target) = 1; 248 return (0); 249 } 250 251 static struct sockaddr * 252 parse_rta_ip4(void *rta_data, struct nl_pstate *npt, int *perror) 253 { 254 struct sockaddr_in *sin; 255 256 sin = (struct sockaddr_in *)npt_alloc_sockaddr(npt, 257 sizeof(struct sockaddr_in)); 258 if (__predict_false(sin == NULL)) { 259 *perror = ENOBUFS; 260 return (NULL); 261 } 262 sin->sin_len = sizeof(struct sockaddr_in); 263 sin->sin_family = AF_INET; 264 memcpy(&sin->sin_addr, rta_data, sizeof(struct in_addr)); 265 return ((struct sockaddr *)sin); 266 } 267 268 static struct sockaddr * 269 parse_rta_ip6(void *rta_data, struct nl_pstate *npt, int *perror) 270 { 271 struct sockaddr_in6 *sin6; 272 273 sin6 = (struct sockaddr_in6 *)npt_alloc_sockaddr(npt, 274 sizeof(struct sockaddr_in6)); 275 if (__predict_false(sin6 == NULL)) { 276 *perror = ENOBUFS; 277 return (NULL); 278 } 279 sin6->sin6_len = sizeof(struct sockaddr_in6); 280 sin6->sin6_family = AF_INET6; 281 memcpy(&sin6->sin6_addr, rta_data, sizeof(struct in6_addr)); 282 return ((struct sockaddr *)sin6); 283 } 284 285 static struct sockaddr * 286 parse_rta_ip(struct rtattr *rta, struct nl_pstate *npt, int *perror) 287 { 288 void *rta_data = NL_RTA_DATA(rta); 289 int rta_len = NL_RTA_DATA_LEN(rta); 290 291 if (rta_len == sizeof(struct in_addr)) { 292 return (parse_rta_ip4(rta_data, npt, perror)); 293 } else if (rta_len == sizeof(struct in6_addr)) { 294 return (parse_rta_ip6(rta_data, npt, perror)); 295 } else { 296 NLMSG_REPORT_ERR_MSG(npt, "unknown IP len: %d for rta type %d", 297 rta_len, rta->rta_type); 298 *perror = ENOTSUP; 299 return (NULL); 300 } 301 return (NULL); 302 } 303 304 int 305 nlattr_get_ip(struct nlattr *nla, struct nl_pstate *npt, const void *arg, 306 void *target) 307 { 308 int error = 0; 309 310 struct sockaddr *sa = parse_rta_ip((struct rtattr *)nla, npt, &error); 311 312 *((struct sockaddr **)target) = sa; 313 return (error); 314 } 315 316 static struct sockaddr * 317 parse_rta_via(struct rtattr *rta, struct nl_pstate *npt, int *perror) 318 { 319 struct rtvia *via = NL_RTA_DATA(rta); 320 int data_len = NL_RTA_DATA_LEN(rta); 321 322 if (__predict_false(data_len) < sizeof(struct rtvia)) { 323 NLMSG_REPORT_ERR_MSG(npt, "undersized RTA_VIA(%d) attr: len %d", 324 rta->rta_type, data_len); 325 *perror = EINVAL; 326 return (NULL); 327 } 328 data_len -= offsetof(struct rtvia, rtvia_addr); 329 330 switch (via->rtvia_family) { 331 case AF_INET: 332 if (__predict_false(data_len < sizeof(struct in_addr))) { 333 *perror = EINVAL; 334 return (NULL); 335 } 336 return (parse_rta_ip4(via->rtvia_addr, npt, perror)); 337 case AF_INET6: 338 if (__predict_false(data_len < sizeof(struct in6_addr))) { 339 *perror = EINVAL; 340 return (NULL); 341 } 342 return (parse_rta_ip6(via->rtvia_addr, npt, perror)); 343 default: 344 *perror = ENOTSUP; 345 return (NULL); 346 } 347 } 348 349 int 350 nlattr_get_ipvia(struct nlattr *nla, struct nl_pstate *npt, const void *arg, 351 void *target) 352 { 353 int error = 0; 354 355 struct sockaddr *sa = parse_rta_via((struct rtattr *)nla, npt, &error); 356 357 *((struct sockaddr **)target) = sa; 358 return (error); 359 } 360 361 int 362 nlattr_get_bool(struct nlattr *nla, struct nl_pstate *npt, const void *arg, 363 void *target) 364 { 365 if (__predict_false(NLA_DATA_LEN(nla) != sizeof(bool))) { 366 NLMSG_REPORT_ERR_MSG(npt, "nla type %d size(%u) is not bool", 367 nla->nla_type, NLA_DATA_LEN(nla)); 368 return (EINVAL); 369 } 370 *((bool *)target) = *((const bool *)NL_RTA_DATA_CONST(nla)); 371 return (0); 372 } 373 374 int 375 nlattr_get_uint8(struct nlattr *nla, struct nl_pstate *npt, const void *arg, 376 void *target) 377 { 378 if (__predict_false(NLA_DATA_LEN(nla) != sizeof(uint8_t))) { 379 NLMSG_REPORT_ERR_MSG(npt, "nla type %d size(%u) is not uint8", 380 nla->nla_type, NLA_DATA_LEN(nla)); 381 return (EINVAL); 382 } 383 *((uint8_t *)target) = *((const uint8_t *)NL_RTA_DATA_CONST(nla)); 384 return (0); 385 } 386 387 int 388 nlattr_get_uint16(struct nlattr *nla, struct nl_pstate *npt, const void *arg, 389 void *target) 390 { 391 if (__predict_false(NLA_DATA_LEN(nla) != sizeof(uint16_t))) { 392 NLMSG_REPORT_ERR_MSG(npt, "nla type %d size(%u) is not uint16", 393 nla->nla_type, NLA_DATA_LEN(nla)); 394 return (EINVAL); 395 } 396 *((uint16_t *)target) = *((const uint16_t *)NL_RTA_DATA_CONST(nla)); 397 return (0); 398 } 399 400 int 401 nlattr_get_uint32(struct nlattr *nla, struct nl_pstate *npt, const void *arg, 402 void *target) 403 { 404 if (__predict_false(NLA_DATA_LEN(nla) != sizeof(uint32_t))) { 405 NLMSG_REPORT_ERR_MSG(npt, "nla type %d size(%u) is not uint32", 406 nla->nla_type, NLA_DATA_LEN(nla)); 407 return (EINVAL); 408 } 409 *((uint32_t *)target) = *((const uint32_t *)NL_RTA_DATA_CONST(nla)); 410 return (0); 411 } 412 413 int 414 nlattr_get_uint64(struct nlattr *nla, struct nl_pstate *npt, const void *arg, 415 void *target) 416 { 417 if (__predict_false(NLA_DATA_LEN(nla) != sizeof(uint64_t))) { 418 NLMSG_REPORT_ERR_MSG(npt, "nla type %d size(%u) is not uint64", 419 nla->nla_type, NLA_DATA_LEN(nla)); 420 return (EINVAL); 421 } 422 memcpy(target, NL_RTA_DATA_CONST(nla), sizeof(uint64_t)); 423 return (0); 424 } 425 426 int 427 nlattr_get_in_addr(struct nlattr *nla, struct nl_pstate *npt, const void *arg, 428 void *target) 429 { 430 if (__predict_false(NLA_DATA_LEN(nla) != sizeof(in_addr_t))) { 431 NLMSG_REPORT_ERR_MSG(npt, 432 "nla type %d size(%u) is not in_addr_t", 433 nla->nla_type, NLA_DATA_LEN(nla)); 434 return (EINVAL); 435 } 436 memcpy(target, NLA_DATA_CONST(nla), sizeof(in_addr_t)); 437 return (0); 438 } 439 440 int 441 nlattr_get_in6_addr(struct nlattr *nla, struct nl_pstate *npt, const void *arg, 442 void *target) 443 { 444 if (__predict_false(NLA_DATA_LEN(nla) != sizeof(struct in6_addr))) { 445 NLMSG_REPORT_ERR_MSG(npt, 446 "nla type %d size(%u) is not struct in6_addr", 447 nla->nla_type, NLA_DATA_LEN(nla)); 448 return (EINVAL); 449 } 450 memcpy(target, NLA_DATA_CONST(nla), sizeof(struct in6_addr)); 451 return (0); 452 } 453 454 static int 455 nlattr_get_ifp_internal(struct nlattr *nla, struct nl_pstate *npt, 456 void *target, bool zero_ok) 457 { 458 struct ifnet *ifp; 459 u_int ifindex; 460 461 if (__predict_false(NLA_DATA_LEN(nla) != sizeof(uint32_t))) { 462 NLMSG_REPORT_ERR_MSG(npt, "nla type %d size(%u) is not uint32", 463 nla->nla_type, NLA_DATA_LEN(nla)); 464 return (EINVAL); 465 } 466 ifindex = *((const u_int *)NLA_DATA_CONST(nla)); 467 468 if (ifindex == 0 && zero_ok) { 469 *((struct ifnet **)target) = NULL; 470 return (0); 471 } 472 473 NET_EPOCH_ASSERT(); 474 475 ifp = ifnet_byindex(ifindex); 476 if (__predict_false(ifp == NULL)) { 477 NLMSG_REPORT_ERR_MSG(npt, "nla type %d: ifindex %u invalid", 478 nla->nla_type, ifindex); 479 return (ENOENT); 480 } 481 *((struct ifnet **)target) = ifp; 482 NL_LOG(LOG_DEBUG3, "nla type %d: ifindex %u -> %s", nla->nla_type, 483 ifindex, if_name(ifp)); 484 485 return (0); 486 } 487 488 int 489 nlattr_get_ifp(struct nlattr *nla, struct nl_pstate *npt, const void *arg, 490 void *target) 491 { 492 return (nlattr_get_ifp_internal(nla, npt, target, false)); 493 } 494 495 int 496 nlattr_get_ifpz(struct nlattr *nla, struct nl_pstate *npt, const void *arg, 497 void *target) 498 { 499 return (nlattr_get_ifp_internal(nla, npt, target, true)); 500 } 501 502 int 503 nlattr_get_chara(struct nlattr *nla, struct nl_pstate *npt, const void *arg, 504 void *target) 505 { 506 int maxlen = NLA_DATA_LEN(nla); 507 int target_size = (size_t)arg; 508 int len = strnlen((char *)NLA_DATA(nla), maxlen); 509 510 if (__predict_false(len >= maxlen) || 511 __predict_false(len >= target_size)) { 512 NLMSG_REPORT_ERR_MSG(npt, "nla type %d size(%u) is not " 513 "NULL-terminated or longer than %u", 514 nla->nla_type, maxlen, target_size); 515 return (EINVAL); 516 } 517 518 strncpy((char *)target, (char *)NLA_DATA(nla), target_size); 519 return (0); 520 } 521 522 int 523 nlattr_get_string(struct nlattr *nla, struct nl_pstate *npt, const void *arg, 524 void *target) 525 { 526 int maxlen = NLA_DATA_LEN(nla); 527 528 if (__predict_false(strnlen((char *)NLA_DATA(nla), maxlen) >= maxlen)) { 529 NLMSG_REPORT_ERR_MSG(npt, 530 "nla type %d size(%u) is not NULL-terminated", 531 nla->nla_type, maxlen); 532 return (EINVAL); 533 } 534 535 *((char **)target) = (char *)NLA_DATA(nla); 536 return (0); 537 } 538 539 int 540 nlattr_get_stringn(struct nlattr *nla, struct nl_pstate *npt, const void *arg, 541 void *target) 542 { 543 int maxlen = NLA_DATA_LEN(nla); 544 545 char *buf = npt_alloc(npt, maxlen + 1); 546 if (buf == NULL) 547 return (ENOMEM); 548 buf[maxlen] = '\0'; 549 memcpy(buf, NLA_DATA(nla), maxlen); 550 551 *((char **)target) = buf; 552 return (0); 553 } 554 555 int 556 nlattr_get_bytes(struct nlattr *nla, struct nl_pstate *npt, const void *arg, 557 void *target) 558 { 559 size_t size = (size_t)arg; 560 561 if (NLA_DATA_LEN(nla) != size) 562 return (EINVAL); 563 564 memcpy(target, NLA_DATA(nla), size); 565 566 return (0); 567 } 568 569 int 570 nlattr_get_nla(struct nlattr *nla, struct nl_pstate *npt, const void *arg, 571 void *target) 572 { 573 NL_LOG(LOG_DEBUG3, "STORING %p len %d", nla, nla->nla_len); 574 *((struct nlattr **)target) = nla; 575 return (0); 576 } 577 578 int 579 nlattr_get_nested(struct nlattr *nla, struct nl_pstate *npt, const void *arg, 580 void *target) 581 { 582 const struct nlhdr_parser *p = (const struct nlhdr_parser *)arg; 583 584 /* Assumes target points to the beginning of the structure. */ 585 return (nl_parse_header(NLA_DATA(nla), NLA_DATA_LEN(nla), p, npt, 586 target)); 587 } 588 589 int 590 nlattr_get_nested_ptr(struct nlattr *nla, struct nl_pstate *npt, 591 const void *arg, void *target) 592 { 593 const struct nlhdr_parser *p = (const struct nlhdr_parser *)arg; 594 595 /* Assumes target points to the beginning of the structure. */ 596 return (nl_parse_header(NLA_DATA(nla), NLA_DATA_LEN(nla), p, npt, 597 *(void **)target)); 598 } 599 600 int 601 nlf_get_ifp(void *src, struct nl_pstate *npt, void *target) 602 { 603 struct ifnet *ifp; 604 u_int ifindex; 605 606 NET_EPOCH_ASSERT(); 607 608 ifindex = *((const u_int *)src); 609 ifp = ifnet_byindex(ifindex); 610 if (ifp == NULL) { 611 NL_LOG(LOG_DEBUG, "ifindex %u invalid", ifindex); 612 return (ENOENT); 613 } 614 *((struct ifnet **)target) = ifp; 615 616 return (0); 617 } 618 619 int 620 nlf_get_ifpz(void *src, struct nl_pstate *npt, void *target) 621 { 622 struct ifnet *ifp; 623 u_int ifindex; 624 625 NET_EPOCH_ASSERT(); 626 627 ifindex = *((const u_int *)src); 628 ifp = ifnet_byindex(ifindex); 629 if (ifindex != 0 && ifp == NULL) { 630 NL_LOG(LOG_DEBUG, "ifindex %u invalid", ifindex); 631 return (ENOENT); 632 } 633 *((struct ifnet **)target) = ifp; 634 635 return (0); 636 } 637 638 int 639 nlf_get_u8(void *src, struct nl_pstate *npt, void *target) 640 { 641 uint8_t val = *((const uint8_t *)src); 642 643 *((uint8_t *)target) = val; 644 645 return (0); 646 } 647 648 int 649 nlf_get_u8_u32(void *src, struct nl_pstate *npt, void *target) 650 { 651 *((uint32_t *)target) = *((const uint8_t *)src); 652 return (0); 653 } 654 655 int 656 nlf_get_u16(void *src, struct nl_pstate *npt, void *target) 657 { 658 *((uint16_t *)target) = *((const uint16_t *)src); 659 return (0); 660 } 661 662 int 663 nlf_get_u32(void *src, struct nl_pstate *npt, void *target) 664 { 665 *((uint32_t *)target) = *((const uint32_t *)src); 666 return (0); 667 } 668