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