1 #include <sys/param.h> 2 #include <sys/module.h> 3 #include <sys/file.h> 4 #include <sys/ioctl.h> 5 #include <sys/linker.h> 6 #include <sys/socket.h> 7 #include <sys/sysctl.h> 8 #include <sys/time.h> 9 #include <sys/queue.h> 10 11 #include <net/if.h> 12 #include <net/if_dl.h> 13 #include <net/if_types.h> 14 15 #include <netinet/in.h> 16 #include <netinet/if_ether.h> 17 18 #include <netinet/icmp6.h> 19 #include <netinet6/in6_var.h> 20 #include <netinet6/nd6.h> 21 22 #include <arpa/inet.h> 23 24 #include <ctype.h> 25 #include <netdb.h> 26 #include <errno.h> 27 #include <nlist.h> 28 #include <stdio.h> 29 #include <string.h> 30 #include <paths.h> 31 #include <stdlib.h> 32 #include <fcntl.h> 33 #include <unistd.h> 34 #include <libxo/xo.h> 35 36 37 #include <netlink/netlink.h> 38 #include <netlink/netlink_route.h> 39 #include <netlink/netlink_snl.h> 40 #include <netlink/netlink_snl_route.h> 41 #include <netlink/netlink_snl_route_compat.h> 42 #include <netlink/netlink_snl_route_parsers.h> 43 44 #include "ndp.h" 45 46 #define RTF_ANNOUNCE RTF_PROTO2 47 48 static void 49 nl_init_socket(struct snl_state *ss) 50 { 51 if (snl_init(ss, NETLINK_ROUTE)) 52 return; 53 54 if (modfind("netlink") == -1 && errno == ENOENT) { 55 /* Try to load */ 56 if (kldload("netlink") == -1) 57 xo_err(1, "netlink is not loaded and load attempt failed"); 58 if (snl_init(ss, NETLINK_ROUTE)) 59 return; 60 } 61 62 xo_err(1, "unable to open netlink socket"); 63 } 64 65 static bool 66 get_link_info(struct snl_state *ss, uint32_t ifindex, 67 struct snl_parsed_link_simple *link) 68 { 69 struct snl_writer nw; 70 71 snl_init_writer(ss, &nw); 72 73 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK); 74 struct ifinfomsg *ifmsg = snl_reserve_msg_object(&nw, struct ifinfomsg); 75 if (ifmsg != NULL) 76 ifmsg->ifi_index = ifindex; 77 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) 78 return (false); 79 80 hdr = snl_read_reply(ss, hdr->nlmsg_seq); 81 82 if (hdr == NULL || hdr->nlmsg_type != RTM_NEWLINK) 83 return (false); 84 85 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, link)) 86 return (false); 87 88 return (true); 89 } 90 91 92 93 static bool 94 has_l2(struct snl_state *ss, uint32_t ifindex) 95 { 96 struct snl_parsed_link_simple link = {}; 97 98 if (!get_link_info(ss, ifindex, &link)) 99 return (false); 100 101 return (valid_type(link.ifi_type) != 0); 102 } 103 104 static uint32_t 105 get_myfib(void) 106 { 107 uint32_t fibnum = 0; 108 size_t len = sizeof(fibnum); 109 110 sysctlbyname("net.my_fibnum", (void *)&fibnum, &len, NULL, 0); 111 112 return (fibnum); 113 } 114 115 static void 116 ip6_writemask(struct in6_addr *addr6, uint8_t mask) 117 { 118 uint32_t *cp; 119 120 for (cp = (uint32_t *)addr6; mask >= 32; mask -= 32) 121 *cp++ = 0xFFFFFFFF; 122 if (mask > 0) 123 *cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0); 124 } 125 #define s6_addr32 __u6_addr.__u6_addr32 126 #define IN6_MASK_ADDR(a, m) do { \ 127 (a)->s6_addr32[0] &= (m)->s6_addr32[0]; \ 128 (a)->s6_addr32[1] &= (m)->s6_addr32[1]; \ 129 (a)->s6_addr32[2] &= (m)->s6_addr32[2]; \ 130 (a)->s6_addr32[3] &= (m)->s6_addr32[3]; \ 131 } while (0) 132 133 static int 134 guess_ifindex(struct snl_state *ss, uint32_t fibnum, const struct sockaddr_in6 *dst) 135 { 136 struct snl_writer nw; 137 138 if (IN6_IS_ADDR_LINKLOCAL(&dst->sin6_addr)) 139 return (dst->sin6_scope_id); 140 else if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr)) 141 return (0); 142 143 144 snl_init_writer(ss, &nw); 145 146 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETROUTE); 147 struct rtmsg *rtm = snl_reserve_msg_object(&nw, struct rtmsg); 148 rtm->rtm_family = AF_INET6; 149 150 snl_add_msg_attr_ip(&nw, RTA_DST, (struct sockaddr *)dst); 151 snl_add_msg_attr_u32(&nw, RTA_TABLE, fibnum); 152 153 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) 154 return (0); 155 156 hdr = snl_read_reply(ss, hdr->nlmsg_seq); 157 158 if (hdr->nlmsg_type != NL_RTM_NEWROUTE) { 159 /* No route found, unable to guess ifindex */ 160 return (0); 161 } 162 163 struct snl_parsed_route r = {}; 164 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r)) 165 return (0); 166 167 if (r.rta_multipath.num_nhops > 0 || (r.rta_rtflags & RTF_GATEWAY)) 168 return (0); 169 170 /* Check if the interface is of supported type */ 171 if (has_l2(ss, r.rta_oif)) 172 return (r.rta_oif); 173 174 /* Check the case when we matched the loopback route for P2P */ 175 snl_init_writer(ss, &nw); 176 hdr = snl_create_msg_request(&nw, RTM_GETNEXTHOP); 177 snl_reserve_msg_object(&nw, struct nhmsg); 178 179 int off = snl_add_msg_attr_nested(&nw, NHA_FREEBSD); 180 snl_add_msg_attr_u32(&nw, NHAF_KID, r.rta_knh_id); 181 snl_add_msg_attr_u8(&nw, NHAF_FAMILY, AF_INET6); 182 snl_add_msg_attr_u32(&nw, NHAF_TABLE, fibnum); 183 snl_end_attr_nested(&nw, off); 184 185 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr)) 186 return (0); 187 188 hdr = snl_read_reply(ss, hdr->nlmsg_seq); 189 190 if (hdr->nlmsg_type != NL_RTM_NEWNEXTHOP) { 191 /* No nexthop found, unable to guess ifindex */ 192 return (0); 193 } 194 195 struct snl_parsed_nhop nh = {}; 196 if (!snl_parse_nlmsg(ss, hdr, &snl_nhmsg_parser, &nh)) 197 return (0); 198 199 return (nh.nhaf_aif); 200 } 201 202 static uint32_t 203 fix_ifindex(struct snl_state *ss, uint32_t ifindex, const struct sockaddr_in6 *sa) 204 { 205 if (ifindex == 0) 206 ifindex = guess_ifindex(ss, get_myfib(), sa); 207 return (ifindex); 208 } 209 210 static void 211 print_entry(struct snl_parsed_neigh *neigh, struct snl_parsed_link_simple *link) 212 { 213 struct timeval now; 214 char host_buf[NI_MAXHOST]; 215 int addrwidth; 216 int llwidth; 217 int ifwidth; 218 char *ifname; 219 220 getnameinfo(neigh->nda_dst, sizeof(struct sockaddr_in6), host_buf, 221 sizeof(host_buf), NULL, 0, (opts.nflag ? NI_NUMERICHOST : 0)); 222 223 gettimeofday(&now, 0); 224 if (opts.tflag) 225 ts_print(&now); 226 227 struct sockaddr_dl sdl = { 228 .sdl_family = AF_LINK, 229 .sdl_type = link->ifi_type, 230 .sdl_len = sizeof(struct sockaddr_dl), 231 }; 232 233 if (neigh->nda_lladdr) { 234 sdl.sdl_alen = NLA_DATA_LEN(neigh->nda_lladdr), 235 memcpy(sdl.sdl_data, NLA_DATA(neigh->nda_lladdr), sdl.sdl_alen); 236 } 237 238 addrwidth = strlen(host_buf); 239 if (addrwidth < W_ADDR) 240 addrwidth = W_ADDR; 241 llwidth = strlen(ether_str(&sdl)); 242 if (W_ADDR + W_LL - addrwidth > llwidth) 243 llwidth = W_ADDR + W_LL - addrwidth; 244 ifname = link->ifla_ifname; 245 ifwidth = strlen(ifname); 246 if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth) 247 ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth; 248 249 xo_open_instance("neighbor-cache"); 250 /* Compose format string for libxo, as it doesn't support *.* */ 251 char xobuf[200]; 252 snprintf(xobuf, sizeof(xobuf), 253 "{:address/%%-%d.%ds/%%s} {:mac-address/%%-%d.%ds/%%s} {:interface/%%%d.%ds/%%s}", 254 addrwidth, addrwidth, llwidth, llwidth, ifwidth, ifwidth); 255 xo_emit(xobuf, host_buf, ether_str(&sdl), ifname); 256 257 /* Print neighbor discovery specific information */ 258 time_t expire = (time_t)neigh->ndaf_next_ts; 259 int expire_in = expire - now.tv_sec; 260 if (expire > now.tv_sec) 261 xo_emit("{d:/ %-9.9s}{e:expires_sec/%d}", sec2str(expire_in), expire_in); 262 else if (expire == 0) 263 xo_emit("{d:/ %-9.9s}{en:permanent/true}", "permanent"); 264 else 265 xo_emit("{d:/ %-9.9s}{e:expires_sec/%d}", "expired", expire_in); 266 267 const char *lle_state = ""; 268 switch (neigh->ndm_state) { 269 case NUD_INCOMPLETE: 270 lle_state = "I"; 271 break; 272 case NUD_REACHABLE: 273 lle_state = "R"; 274 break; 275 case NUD_STALE: 276 lle_state = "S"; 277 break; 278 case NUD_DELAY: 279 lle_state = "D"; 280 break; 281 case NUD_PROBE: 282 lle_state = "P"; 283 break; 284 case NUD_FAILED: 285 lle_state = "F"; 286 break; 287 default: 288 lle_state = "N"; 289 break; 290 } 291 xo_emit(" {:neighbor-state/%s}", lle_state); 292 293 bool isrouter = neigh->ndm_flags & NTF_ROUTER; 294 295 /* 296 * other flags. R: router, P: proxy, W: ?? 297 */ 298 char flgbuf[8]; 299 snprintf(flgbuf, sizeof(flgbuf), "%s%s", 300 isrouter ? "R" : "", 301 (neigh->ndm_flags & NTF_PROXY) ? "p" : ""); 302 xo_emit(" {:nd-flags/%s}", flgbuf); 303 304 if (neigh->nda_probes != 0) 305 xo_emit("{u:/ %d}", neigh->nda_probes); 306 307 xo_emit("\n"); 308 xo_close_instance("neighbor-cache"); 309 } 310 311 int 312 print_entries_nl(uint32_t ifindex, struct sockaddr_in6 *addr, bool cflag) 313 { 314 struct snl_state ss_req = {}, ss_cmd = {}; 315 struct snl_parsed_link_simple link = {}; 316 struct snl_writer nw; 317 struct nlmsghdr *hdr; 318 struct ndmsg *ndmsg; 319 320 nl_init_socket(&ss_req); 321 snl_init_writer(&ss_req, &nw); 322 323 /* Print header */ 324 if (!opts.tflag && !cflag) { 325 char xobuf[200]; 326 snprintf(xobuf, sizeof(xobuf), 327 "{T:/%%-%d.%ds} {T:/%%-%d.%ds} {T:/%%%d.%ds} {T:/%%-9.9s} {T:/%%1s} {T:/%%5s}\n", 328 W_ADDR, W_ADDR, W_LL, W_LL, W_IF, W_IF); 329 xo_emit(xobuf, "Neighbor", "Linklayer Address", "Netif", "Expire", "S", "Flags"); 330 } 331 332 again: 333 hdr = snl_create_msg_request(&nw, RTM_GETNEIGH); 334 ndmsg = snl_reserve_msg_object(&nw, struct ndmsg); 335 if (ndmsg != NULL) { 336 ndmsg->ndm_family = AF_INET6; 337 ndmsg->ndm_ifindex = ifindex; 338 } 339 340 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss_req, hdr)) { 341 snl_free(&ss_req); 342 return (0); 343 } 344 345 uint32_t nlmsg_seq = hdr->nlmsg_seq; 346 struct snl_errmsg_data e = {}; 347 int count = 0; 348 nl_init_socket(&ss_cmd); 349 350 xo_open_list("neighbor-cache"); 351 352 while ((hdr = snl_read_reply_multi(&ss_req, nlmsg_seq, &e)) != NULL) { 353 struct snl_parsed_neigh neigh = {}; 354 355 if (!snl_parse_nlmsg(&ss_req, hdr, &snl_rtm_neigh_parser, &neigh)) 356 continue; 357 358 if (neigh.nda_ifindex != link.ifi_index) { 359 snl_clear_lb(&ss_cmd); 360 memset(&link, 0, sizeof(link)); 361 if (!get_link_info(&ss_cmd, neigh.nda_ifindex, &link)) 362 continue; 363 } 364 365 /* TODO: embed LL in the parser */ 366 struct sockaddr_in6 *dst = (struct sockaddr_in6 *)neigh.nda_dst; 367 if (IN6_IS_ADDR_LINKLOCAL(&dst->sin6_addr)) 368 dst->sin6_scope_id = neigh.nda_ifindex; 369 370 if (addr != NULL) { 371 if (IN6_ARE_ADDR_EQUAL(&addr->sin6_addr, 372 &dst->sin6_addr) == 0 || 373 addr->sin6_scope_id != dst->sin6_scope_id) 374 continue; 375 } 376 377 if (cflag) { 378 char dst_str[INET6_ADDRSTRLEN]; 379 380 inet_ntop(AF_INET6, &dst->sin6_addr, dst_str, sizeof(dst_str)); 381 delete_nl(neigh.nda_ifindex, dst_str, false); /* no warn */ 382 } else 383 print_entry(&neigh, &link); 384 385 count++; 386 snl_clear_lb(&ss_req); 387 } 388 if (opts.repeat) { 389 xo_emit("\n"); 390 xo_flush(); 391 sleep(opts.repeat); 392 goto again; 393 } 394 xo_close_list("neighbor-cache"); 395 396 snl_free(&ss_req); 397 snl_free(&ss_cmd); 398 399 return (count); 400 } 401 402 int 403 delete_nl(uint32_t ifindex, char *host, bool warn) 404 { 405 #define xo_warnx(...) do { if (warn) { xo_warnx(__VA_ARGS__); } } while(0) 406 struct snl_state ss = {}; 407 struct snl_writer nw; 408 struct sockaddr_in6 dst; 409 410 int gai_error = getaddr(host, &dst); 411 if (gai_error) { 412 xo_warnx("%s: %s", host, gai_strerror(gai_error)); 413 return 1; 414 } 415 416 nl_init_socket(&ss); 417 418 ifindex = fix_ifindex(&ss, ifindex, &dst); 419 if (ifindex == 0) { 420 xo_warnx("delete: cannot locate %s", host); 421 snl_free(&ss); 422 return (0); 423 } 424 425 snl_init_writer(&ss, &nw); 426 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_DELNEIGH); 427 struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg); 428 if (ndmsg != NULL) { 429 ndmsg->ndm_family = AF_INET6; 430 ndmsg->ndm_ifindex = ifindex; 431 } 432 snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)&dst); 433 434 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss, hdr)) { 435 snl_free(&ss); 436 return (1); 437 } 438 439 struct snl_errmsg_data e = {}; 440 snl_read_reply_code(&ss, hdr->nlmsg_seq, &e); 441 if (e.error != 0) { 442 if (e.error_str != NULL) 443 xo_warnx("delete %s: %s (%s)", host, strerror(e.error), e.error_str); 444 else 445 xo_warnx("delete %s: %s", host, strerror(e.error)); 446 } else { 447 char host_buf[NI_MAXHOST]; 448 char ifix_buf[IFNAMSIZ]; 449 450 getnameinfo((struct sockaddr *)&dst, 451 dst.sin6_len, host_buf, 452 sizeof(host_buf), NULL, 0, 453 (opts.nflag ? NI_NUMERICHOST : 0)); 454 455 char *ifname = if_indextoname(ifindex, ifix_buf); 456 if (ifname == NULL) { 457 strlcpy(ifix_buf, "?", sizeof(ifix_buf)); 458 ifname = ifix_buf; 459 } 460 char abuf[INET6_ADDRSTRLEN]; 461 inet_ntop(AF_INET6, &dst.sin6_addr, abuf, sizeof(abuf)); 462 463 xo_open_instance("neighbor-cache"); 464 xo_emit("{:hostname/%s}{d:/ (%s) deleted\n}", host, host_buf); 465 xo_emit("{e:address/%s}{e:interface/%s}", abuf, ifname); 466 xo_close_instance("neighbor-cache"); 467 } 468 snl_free(&ss); 469 470 return (e.error != 0); 471 #undef xo_warnx /* see above */ 472 } 473 474 int 475 set_nl(uint32_t ifindex, struct sockaddr_in6 *dst, struct sockaddr_dl *sdl, char *host) 476 { 477 struct snl_state ss = {}; 478 struct snl_writer nw; 479 480 nl_init_socket(&ss); 481 482 ifindex = fix_ifindex(&ss, ifindex, dst); 483 if (ifindex == 0) { 484 xo_warnx("delete: cannot locate %s", host); 485 snl_free(&ss); 486 return (0); 487 } 488 489 snl_init_writer(&ss, &nw); 490 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_NEWNEIGH); 491 hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE; 492 struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg); 493 if (ndmsg != NULL) { 494 uint8_t nl_flags = NTF_STICKY; 495 496 ndmsg->ndm_family = AF_INET6; 497 ndmsg->ndm_ifindex = ifindex; 498 ndmsg->ndm_state = NUD_PERMANENT; 499 500 if (opts.flags & RTF_ANNOUNCE) 501 nl_flags |= NTF_PROXY; 502 ndmsg->ndm_flags = nl_flags; 503 } 504 snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)dst); 505 snl_add_msg_attr(&nw, NDA_LLADDR, sdl->sdl_alen, LLADDR(sdl)); 506 507 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss, hdr)) { 508 snl_free(&ss); 509 return (1); 510 } 511 512 struct snl_errmsg_data e = {}; 513 snl_read_reply_code(&ss, hdr->nlmsg_seq, &e); 514 if (e.error != 0) { 515 if (e.error_str != NULL) 516 xo_warnx("set: %s: %s (%s)", host, strerror(e.error), e.error_str); 517 else 518 xo_warnx("set %s: %s", host, strerror(e.error)); 519 } 520 snl_free(&ss); 521 522 return (e.error != 0); 523 } 524 525