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 "opt_netlink.h" 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 #include "opt_inet.h" 33 #include "opt_inet6.h" 34 #include <sys/types.h> 35 #include <sys/eventhandler.h> 36 #include <sys/malloc.h> 37 #include <sys/socket.h> 38 #include <sys/syslog.h> 39 40 #include <net/if.h> 41 #include <net/if_llatbl.h> 42 #include <netlink/netlink.h> 43 #include <netlink/netlink_ctl.h> 44 #include <netlink/netlink_route.h> 45 #include <netlink/route/route_var.h> 46 47 #include <netinet6/in6_var.h> /* nd6.h requires this */ 48 #include <netinet6/nd6.h> /* nd6 state machine */ 49 #include <netinet6/scope6_var.h> /* scope deembedding */ 50 51 #define DEBUG_MOD_NAME nl_neigh 52 #define DEBUG_MAX_LEVEL LOG_DEBUG3 53 #include <netlink/netlink_debug.h> 54 _DECLARE_DEBUG(LOG_DEBUG); 55 56 static int lle_families[] = { AF_INET, AF_INET6 }; 57 58 static eventhandler_tag lle_event_p; 59 60 struct netlink_walkargs { 61 struct nl_writer *nw; 62 struct nlmsghdr hdr; 63 struct nlpcb *so; 64 struct ifnet *ifp; 65 int family; 66 int error; 67 int count; 68 int dumped; 69 }; 70 71 static int 72 lle_state_to_nl_state(int family, struct llentry *lle) 73 { 74 int state = lle->ln_state; 75 76 switch (family) { 77 case AF_INET: 78 if (lle->la_flags & (LLE_STATIC | LLE_IFADDR)) 79 state = 1; 80 switch (state) { 81 case 0: /* ARP_LLINFO_INCOMPLETE */ 82 return (NUD_INCOMPLETE); 83 case 1: /* ARP_LLINFO_REACHABLE */ 84 return (NUD_REACHABLE); 85 case 2: /* ARP_LLINFO_VERIFY */ 86 return (NUD_PROBE); 87 } 88 break; 89 case AF_INET6: 90 switch (state) { 91 case ND6_LLINFO_INCOMPLETE: 92 return (NUD_INCOMPLETE); 93 case ND6_LLINFO_REACHABLE: 94 return (NUD_REACHABLE); 95 case ND6_LLINFO_STALE: 96 return (NUD_STALE); 97 case ND6_LLINFO_DELAY: 98 return (NUD_DELAY); 99 case ND6_LLINFO_PROBE: 100 return (NUD_PROBE); 101 } 102 break; 103 } 104 105 return (NUD_NONE); 106 } 107 108 static uint32_t 109 lle_flags_to_nl_flags(const struct llentry *lle) 110 { 111 uint32_t nl_flags = 0; 112 113 if (lle->la_flags & LLE_IFADDR) 114 nl_flags |= NTF_SELF; 115 if (lle->la_flags & LLE_PUB) 116 nl_flags |= NTF_PROXY; 117 if (lle->la_flags & LLE_STATIC) 118 nl_flags |= NTF_STICKY; 119 if (lle->ln_router != 0) 120 nl_flags |= NTF_ROUTER; 121 122 return (nl_flags); 123 } 124 125 static int 126 dump_lle_locked(struct llentry *lle, void *arg) 127 { 128 struct netlink_walkargs *wa = (struct netlink_walkargs *)arg; 129 struct nlmsghdr *hdr = &wa->hdr; 130 struct nl_writer *nw = wa->nw; 131 struct ndmsg *ndm; 132 #if defined(INET) || defined(INET6) 133 union { 134 struct in_addr in; 135 struct in6_addr in6; 136 } addr; 137 #endif 138 139 IF_DEBUG_LEVEL(LOG_DEBUG2) { 140 char llebuf[NHOP_PRINT_BUFSIZE]; 141 llentry_print_buf_lltable(lle, llebuf, sizeof(llebuf)); 142 NL_LOG(LOG_DEBUG2, "dumping %s", llebuf); 143 } 144 145 if (!nlmsg_reply(nw, hdr, sizeof(struct ndmsg))) 146 goto enomem; 147 148 ndm = nlmsg_reserve_object(nw, struct ndmsg); 149 ndm->ndm_family = wa->family; 150 ndm->ndm_ifindex = wa->ifp->if_index; 151 ndm->ndm_state = lle_state_to_nl_state(wa->family, lle); 152 ndm->ndm_flags = lle_flags_to_nl_flags(lle); 153 154 switch (wa->family) { 155 #ifdef INET 156 case AF_INET: 157 addr.in = lle->r_l3addr.addr4; 158 nlattr_add(nw, NDA_DST, 4, &addr); 159 break; 160 #endif 161 #ifdef INET6 162 case AF_INET6: 163 addr.in6 = lle->r_l3addr.addr6; 164 in6_clearscope(&addr.in6); 165 nlattr_add(nw, NDA_DST, 16, &addr); 166 break; 167 #endif 168 } 169 170 if (lle->r_flags & RLLE_VALID) { 171 /* Has L2 */ 172 int addrlen = wa->ifp->if_addrlen; 173 nlattr_add(nw, NDA_LLADDR, addrlen, lle->ll_addr); 174 } 175 176 nlattr_add_u32(nw, NDA_PROBES, lle->la_asked); 177 178 struct nda_cacheinfo *cache; 179 cache = nlmsg_reserve_attr(nw, NDA_CACHEINFO, struct nda_cacheinfo); 180 if (cache == NULL) 181 goto enomem; 182 /* TODO: provide confirmed/updated */ 183 cache->ndm_refcnt = lle->lle_refcnt; 184 185 if (nlmsg_end(nw)) 186 return (0); 187 enomem: 188 NL_LOG(LOG_DEBUG, "unable to dump lle state (ENOMEM)"); 189 nlmsg_abort(nw); 190 return (ENOMEM); 191 } 192 193 static int 194 dump_lle(struct lltable *llt, struct llentry *lle, void *arg) 195 { 196 int error; 197 198 LLE_RLOCK(lle); 199 error = dump_lle_locked(lle, arg); 200 LLE_RUNLOCK(lle); 201 return (error); 202 } 203 204 static bool 205 dump_llt(struct lltable *llt, struct netlink_walkargs *wa) 206 { 207 lltable_foreach_lle(llt, dump_lle, wa); 208 209 return (true); 210 } 211 212 static int 213 dump_llts_iface(struct netlink_walkargs *wa, struct ifnet *ifp, int family) 214 { 215 int error = 0; 216 217 wa->ifp = ifp; 218 for (int i = 0; i < sizeof(lle_families) / sizeof(int); i++) { 219 int fam = lle_families[i]; 220 struct lltable *llt = lltable_get(ifp, fam); 221 if (llt != NULL && (family == 0 || family == fam)) { 222 wa->count++; 223 wa->family = fam; 224 if (!dump_llt(llt, wa)) { 225 error = ENOMEM; 226 break; 227 } 228 wa->dumped++; 229 } 230 } 231 return (error); 232 } 233 234 static int 235 dump_llts(struct netlink_walkargs *wa, struct ifnet *ifp, int family) 236 { 237 NL_LOG(LOG_DEBUG, "Start dump ifp=%s family=%d", ifp ? if_name(ifp) : "NULL", family); 238 239 wa->hdr.nlmsg_flags |= NLM_F_MULTI; 240 241 if (ifp != NULL) { 242 dump_llts_iface(wa, ifp, family); 243 } else { 244 CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) { 245 dump_llts_iface(wa, ifp, family); 246 } 247 } 248 249 NL_LOG(LOG_DEBUG, "End dump, iterated %d dumped %d", wa->count, wa->dumped); 250 251 if (!nlmsg_end_dump(wa->nw, wa->error, &wa->hdr)) { 252 NL_LOG(LOG_DEBUG, "Unable to add new message"); 253 return (ENOMEM); 254 } 255 256 return (0); 257 } 258 259 static int 260 get_lle(struct netlink_walkargs *wa, struct ifnet *ifp, int family, struct sockaddr *dst) 261 { 262 struct lltable *llt = lltable_get(ifp, family); 263 if (llt == NULL) 264 return (ESRCH); 265 266 #ifdef INET6 267 if (dst->sa_family == AF_INET6) { 268 struct sockaddr_in6 *dst6 = (struct sockaddr_in6 *)dst; 269 270 if (IN6_IS_SCOPE_LINKLOCAL(&dst6->sin6_addr)) 271 in6_set_unicast_scopeid(&dst6->sin6_addr, ifp->if_index); 272 } 273 #endif 274 struct llentry *lle = lla_lookup(llt, LLE_UNLOCKED, dst); 275 if (lle == NULL) 276 return (ESRCH); 277 278 wa->ifp = ifp; 279 wa->family = family; 280 281 return (dump_lle(llt, lle, wa)); 282 } 283 284 struct nl_parsed_neigh { 285 struct sockaddr *nda_dst; 286 struct ifnet *nda_ifp; 287 struct nlattr *nda_lladdr; 288 uint32_t ndm_flags; 289 uint16_t ndm_state; 290 uint8_t ndm_family; 291 }; 292 293 #define _IN(_field) offsetof(struct ndmsg, _field) 294 #define _OUT(_field) offsetof(struct nl_parsed_neigh, _field) 295 static struct nlfield_parser nlf_p_neigh[] = { 296 { .off_in = _IN(ndm_family), .off_out = _OUT(ndm_family), .cb = nlf_get_u8 }, 297 { .off_in = _IN(ndm_flags), .off_out = _OUT(ndm_flags), .cb = nlf_get_u8_u32 }, 298 { .off_in = _IN(ndm_state), .off_out = _OUT(ndm_state), .cb = nlf_get_u16 }, 299 { .off_in = _IN(ndm_ifindex), .off_out = _OUT(nda_ifp), .cb = nlf_get_ifpz }, 300 }; 301 302 static struct nlattr_parser nla_p_neigh[] = { 303 { .type = NDA_DST, .off = _OUT(nda_dst), .cb = nlattr_get_ip }, 304 { .type = NDA_LLADDR, .off = _OUT(nda_lladdr), .cb = nlattr_get_nla }, 305 { .type = NDA_IFINDEX, .off = _OUT(nda_ifp), .cb = nlattr_get_ifp }, 306 { .type = NDA_FLAGS_EXT, .off = _OUT(ndm_flags), .cb = nlattr_get_uint32 }, 307 }; 308 #undef _IN 309 #undef _OUT 310 NL_DECLARE_PARSER(ndmsg_parser, struct ndmsg, nlf_p_neigh, nla_p_neigh); 311 312 313 /* 314 * type=RTM_NEWNEIGH, flags=NLM_F_REQUEST|NLM_F_ACK|NLM_F_EXCL|NLM_F_CREATE, seq=1661941473, pid=0}, 315 * {ndm_family=AF_INET6, ndm_ifindex=if_nametoindex("enp0s31f6"), ndm_state=NUD_PERMANENT, ndm_flags=0, ndm_type=RTN_UNSPEC}, 316 * [ 317 * {{nla_len=20, nla_type=NDA_DST}, inet_pton(AF_INET6, "2a01:4f8:13a:70c::3")}, 318 * {{nla_len=10, nla_type=NDA_LLADDR}, 20:4e:71:62:ae:f2}]}, iov_len=60} 319 */ 320 321 static int 322 rtnl_handle_newneigh(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_pstate *npt) 323 { 324 int error; 325 326 struct nl_parsed_neigh attrs = {}; 327 error = nl_parse_nlmsg(hdr, &ndmsg_parser, npt, &attrs); 328 if (error != 0) 329 return (error); 330 331 if (attrs.nda_ifp == NULL || attrs.nda_dst == NULL || attrs.nda_lladdr == NULL) { 332 if (attrs.nda_ifp == NULL) 333 NLMSG_REPORT_ERR_MSG(npt, "NDA_IFINDEX / ndm_ifindex not set"); 334 if (attrs.nda_dst == NULL) 335 NLMSG_REPORT_ERR_MSG(npt, "NDA_DST not set"); 336 if (attrs.nda_lladdr == NULL) 337 NLMSG_REPORT_ERR_MSG(npt, "NDA_LLADDR not set"); 338 return (EINVAL); 339 } 340 341 if (attrs.nda_dst->sa_family != attrs.ndm_family) { 342 NLMSG_REPORT_ERR_MSG(npt, 343 "NDA_DST family (%d) is different from ndm_family (%d)", 344 attrs.nda_dst->sa_family, attrs.ndm_family); 345 return (EINVAL); 346 } 347 348 int addrlen = attrs.nda_ifp->if_addrlen; 349 if (attrs.nda_lladdr->nla_len != sizeof(struct nlattr) + addrlen) { 350 NLMSG_REPORT_ERR_MSG(npt, 351 "NDA_LLADDR address length (%d) is different from expected (%d)", 352 (int)attrs.nda_lladdr->nla_len - (int)sizeof(struct nlattr), addrlen); 353 return (EINVAL); 354 } 355 356 if (attrs.ndm_state != NUD_PERMANENT) { 357 NLMSG_REPORT_ERR_MSG(npt, "ndm_state %d not supported", attrs.ndm_state); 358 return (ENOTSUP); 359 } 360 361 const uint16_t supported_flags = NTF_PROXY | NTF_STICKY; 362 if ((attrs.ndm_flags & supported_flags) != attrs.ndm_flags) { 363 NLMSG_REPORT_ERR_MSG(npt, "ndm_flags %X not supported", 364 attrs.ndm_flags &~ supported_flags); 365 return (ENOTSUP); 366 } 367 368 /* Replacement requires new entry creation anyway */ 369 if ((hdr->nlmsg_flags & (NLM_F_CREATE | NLM_F_REPLACE)) == 0) 370 return (ENOTSUP); 371 372 struct lltable *llt = lltable_get(attrs.nda_ifp, attrs.ndm_family); 373 if (llt == NULL) 374 return (EAFNOSUPPORT); 375 376 377 uint8_t linkhdr[LLE_MAX_LINKHDR]; 378 size_t linkhdrsize = sizeof(linkhdr); 379 int lladdr_off = 0; 380 if (lltable_calc_llheader(attrs.nda_ifp, attrs.ndm_family, 381 (char *)(attrs.nda_lladdr + 1), linkhdr, &linkhdrsize, &lladdr_off) != 0) { 382 NLMSG_REPORT_ERR_MSG(npt, "unable to calculate lle prepend data"); 383 return (EINVAL); 384 } 385 386 int lle_flags = LLE_STATIC | ((attrs.ndm_flags & NTF_PROXY) ? LLE_PUB : 0); 387 struct llentry *lle = lltable_alloc_entry(llt, lle_flags, attrs.nda_dst); 388 if (lle == NULL) 389 return (ENOMEM); 390 lltable_set_entry_addr(attrs.nda_ifp, lle, linkhdr, linkhdrsize, lladdr_off); 391 392 /* llentry created, try to insert or update :*/ 393 IF_AFDATA_WLOCK(attrs.nda_ifp); 394 LLE_WLOCK(lle); 395 struct llentry *lle_tmp = lla_lookup(llt, LLE_EXCLUSIVE, attrs.nda_dst); 396 if (lle_tmp != NULL) { 397 if (hdr->nlmsg_flags & NLM_F_EXCL) { 398 LLE_WUNLOCK(lle_tmp); 399 lle_tmp = NULL; 400 error = EEXIST; 401 } else if (hdr->nlmsg_flags & NLM_F_REPLACE) { 402 lltable_unlink_entry(llt, lle_tmp); 403 lltable_link_entry(llt, lle); 404 } else 405 error = EEXIST; 406 } else { 407 if (hdr->nlmsg_flags & NLM_F_CREATE) 408 lltable_link_entry(llt, lle); 409 else 410 error = ENOENT; 411 } 412 IF_AFDATA_WUNLOCK(attrs.nda_ifp); 413 414 if (error != 0) { 415 if (lle != NULL) 416 llentry_free(lle); 417 return (error); 418 } 419 420 if (lle_tmp != NULL) 421 llentry_free(lle_tmp); 422 423 /* XXX: We're inside epoch */ 424 EVENTHANDLER_INVOKE(lle_event, lle, LLENTRY_RESOLVED); 425 LLE_WUNLOCK(lle); 426 427 return (0); 428 } 429 430 static int 431 rtnl_handle_delneigh(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_pstate *npt) 432 { 433 int error; 434 435 struct nl_parsed_neigh attrs = {}; 436 error = nl_parse_nlmsg(hdr, &ndmsg_parser, npt, &attrs); 437 if (error != 0) 438 return (error); 439 440 if (attrs.nda_dst == NULL) { 441 NLMSG_REPORT_ERR_MSG(npt, "NDA_DST not set"); 442 return (EINVAL); 443 } 444 445 if (attrs.nda_ifp == NULL) { 446 NLMSG_REPORT_ERR_MSG(npt, "no ifindex provided"); 447 return (EINVAL); 448 } 449 450 struct lltable *llt = lltable_get(attrs.nda_ifp, attrs.ndm_family); 451 if (llt == NULL) 452 return (EAFNOSUPPORT); 453 454 IF_AFDATA_WLOCK(attrs.nda_ifp); 455 struct llentry *lle = lla_lookup(llt, LLE_EXCLUSIVE, attrs.nda_dst); 456 if (lle != NULL) { 457 if ((lle->la_flags & LLE_IFADDR) != 0) { 458 LLE_WUNLOCK(lle); 459 lle = NULL; 460 error = EPERM; 461 } else 462 lltable_unlink_entry(llt, lle); 463 } else 464 error = ENOENT; 465 IF_AFDATA_WUNLOCK(attrs.nda_ifp); 466 467 if (error == 0 && lle != NULL) 468 EVENTHANDLER_INVOKE(lle_event, lle, LLENTRY_DELETED); 469 470 if (lle != NULL) 471 llentry_free(lle); 472 473 return (error); 474 } 475 476 static int 477 rtnl_handle_getneigh(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_pstate *npt) 478 { 479 int error; 480 481 struct nl_parsed_neigh attrs = {}; 482 error = nl_parse_nlmsg(hdr, &ndmsg_parser, npt, &attrs); 483 if (error != 0) 484 return (error); 485 486 if (attrs.nda_dst != NULL && attrs.nda_ifp == NULL) { 487 NLMSG_REPORT_ERR_MSG(npt, "has NDA_DST but no ifindex provided"); 488 return (EINVAL); 489 } 490 491 struct netlink_walkargs wa = { 492 .so = nlp, 493 .nw = npt->nw, 494 .hdr.nlmsg_pid = hdr->nlmsg_pid, 495 .hdr.nlmsg_seq = hdr->nlmsg_seq, 496 .hdr.nlmsg_flags = hdr->nlmsg_flags, 497 .hdr.nlmsg_type = NL_RTM_NEWNEIGH, 498 }; 499 500 if (attrs.nda_dst == NULL) 501 error = dump_llts(&wa, attrs.nda_ifp, attrs.ndm_family); 502 else 503 error = get_lle(&wa, attrs.nda_ifp, attrs.ndm_family, attrs.nda_dst); 504 505 return (error); 506 } 507 508 static const struct rtnl_cmd_handler cmd_handlers[] = { 509 { 510 .cmd = NL_RTM_NEWNEIGH, 511 .name = "RTM_NEWNEIGH", 512 .cb = &rtnl_handle_newneigh, 513 }, 514 { 515 .cmd = NL_RTM_DELNEIGH, 516 .name = "RTM_DELNEIGH", 517 .cb = &rtnl_handle_delneigh, 518 .priv = PRIV_NET_ROUTE, 519 }, 520 { 521 .cmd = NL_RTM_GETNEIGH, 522 .name = "RTM_GETNEIGH", 523 .cb = &rtnl_handle_getneigh, 524 } 525 }; 526 527 static void 528 rtnl_lle_event(void *arg __unused, struct llentry *lle, int evt) 529 { 530 struct ifnet *ifp; 531 int family; 532 533 LLE_WLOCK_ASSERT(lle); 534 535 ifp = lltable_get_ifp(lle->lle_tbl); 536 family = lltable_get_af(lle->lle_tbl); 537 538 if (family != AF_INET && family != AF_INET6) 539 return; 540 541 int nlmsgs_type = evt == LLENTRY_RESOLVED ? NL_RTM_NEWNEIGH : NL_RTM_DELNEIGH; 542 543 struct nl_writer nw = {}; 544 if (!nlmsg_get_group_writer(&nw, NLMSG_SMALL, NETLINK_ROUTE, RTNLGRP_NEIGH)) { 545 NL_LOG(LOG_DEBUG, "error allocating group writer"); 546 return; 547 } 548 549 struct netlink_walkargs wa = { 550 .hdr.nlmsg_type = nlmsgs_type, 551 .nw = &nw, 552 .ifp = ifp, 553 .family = family, 554 }; 555 556 dump_lle_locked(lle, &wa); 557 nlmsg_flush(&nw); 558 } 559 560 static const struct nlhdr_parser *all_parsers[] = { &ndmsg_parser }; 561 562 void 563 rtnl_neighs_init(void) 564 { 565 NL_VERIFY_PARSERS(all_parsers); 566 rtnl_register_messages(cmd_handlers, NL_ARRAY_LEN(cmd_handlers)); 567 lle_event_p = EVENTHANDLER_REGISTER(lle_event, rtnl_lle_event, NULL, 568 EVENTHANDLER_PRI_ANY); 569 } 570 571 void 572 rtnl_neighs_destroy(void) 573 { 574 EVENTHANDLER_DEREGISTER(lle_event, lle_event_p); 575 } 576