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