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 318 nl_init_socket(&ss_req); 319 snl_init_writer(&ss_req, &nw); 320 321 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETNEIGH); 322 struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg); 323 if (ndmsg != NULL) { 324 ndmsg->ndm_family = AF_INET6; 325 ndmsg->ndm_ifindex = ifindex; 326 } 327 328 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss_req, hdr)) { 329 snl_free(&ss_req); 330 return (0); 331 } 332 333 uint32_t nlmsg_seq = hdr->nlmsg_seq; 334 struct snl_errmsg_data e = {}; 335 int count = 0; 336 nl_init_socket(&ss_cmd); 337 338 /* Print header */ 339 if (!opts.tflag && !cflag) { 340 char xobuf[200]; 341 snprintf(xobuf, sizeof(xobuf), 342 "{T:/%%-%d.%ds} {T:/%%-%d.%ds} {T:/%%%d.%ds} {T:/%%-9.9s} {T:/%%1s} {T:/%%5s}\n", 343 W_ADDR, W_ADDR, W_LL, W_LL, W_IF, W_IF); 344 xo_emit(xobuf, "Neighbor", "Linklayer Address", "Netif", "Expire", "S", "Flags"); 345 } 346 xo_open_list("neighbor-cache"); 347 348 while ((hdr = snl_read_reply_multi(&ss_req, nlmsg_seq, &e)) != NULL) { 349 struct snl_parsed_neigh neigh = {}; 350 351 if (!snl_parse_nlmsg(&ss_req, hdr, &snl_rtm_neigh_parser, &neigh)) 352 continue; 353 354 if (neigh.nda_ifindex != link.ifi_index) { 355 snl_clear_lb(&ss_cmd); 356 memset(&link, 0, sizeof(link)); 357 if (!get_link_info(&ss_cmd, neigh.nda_ifindex, &link)) 358 continue; 359 } 360 361 /* TODO: embed LL in the parser */ 362 struct sockaddr_in6 *dst = (struct sockaddr_in6 *)neigh.nda_dst; 363 if (IN6_IS_ADDR_LINKLOCAL(&dst->sin6_addr)) 364 dst->sin6_scope_id = neigh.nda_ifindex; 365 366 if (addr != NULL) { 367 if (IN6_ARE_ADDR_EQUAL(&addr->sin6_addr, 368 &dst->sin6_addr) == 0 || 369 addr->sin6_scope_id != dst->sin6_scope_id) 370 continue; 371 } 372 373 if (cflag) { 374 char dst_str[INET6_ADDRSTRLEN]; 375 376 inet_ntop(AF_INET6, &dst->sin6_addr, dst_str, sizeof(dst_str)); 377 delete_nl(neigh.nda_ifindex, dst_str, false); /* no warn */ 378 } else 379 print_entry(&neigh, &link); 380 381 count++; 382 snl_clear_lb(&ss_req); 383 } 384 xo_close_list("neighbor-cache"); 385 386 snl_free(&ss_req); 387 snl_free(&ss_cmd); 388 389 return (count); 390 } 391 392 int 393 delete_nl(uint32_t ifindex, char *host, bool warn) 394 { 395 #define xo_warnx(...) do { if (warn) { xo_warnx(__VA_ARGS__); } } while(0) 396 struct snl_state ss = {}; 397 struct snl_writer nw; 398 struct sockaddr_in6 dst; 399 400 int gai_error = getaddr(host, &dst); 401 if (gai_error) { 402 xo_warnx("%s: %s", host, gai_strerror(gai_error)); 403 return 1; 404 } 405 406 nl_init_socket(&ss); 407 408 ifindex = fix_ifindex(&ss, ifindex, &dst); 409 if (ifindex == 0) { 410 xo_warnx("delete: cannot locate %s", host); 411 snl_free(&ss); 412 return (0); 413 } 414 415 snl_init_writer(&ss, &nw); 416 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_DELNEIGH); 417 struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg); 418 if (ndmsg != NULL) { 419 ndmsg->ndm_family = AF_INET6; 420 ndmsg->ndm_ifindex = ifindex; 421 } 422 snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)&dst); 423 424 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss, hdr)) { 425 snl_free(&ss); 426 return (1); 427 } 428 429 struct snl_errmsg_data e = {}; 430 snl_read_reply_code(&ss, hdr->nlmsg_seq, &e); 431 if (e.error != 0) { 432 if (e.error_str != NULL) 433 xo_warnx("delete %s: %s (%s)", host, strerror(e.error), e.error_str); 434 else 435 xo_warnx("delete %s: %s", host, strerror(e.error)); 436 } else { 437 char host_buf[NI_MAXHOST]; 438 char ifix_buf[IFNAMSIZ]; 439 440 getnameinfo((struct sockaddr *)&dst, 441 dst.sin6_len, host_buf, 442 sizeof(host_buf), NULL, 0, 443 (opts.nflag ? NI_NUMERICHOST : 0)); 444 445 char *ifname = if_indextoname(ifindex, ifix_buf); 446 if (ifname == NULL) { 447 strlcpy(ifix_buf, "?", sizeof(ifix_buf)); 448 ifname = ifix_buf; 449 } 450 char abuf[INET6_ADDRSTRLEN]; 451 inet_ntop(AF_INET6, &dst.sin6_addr, abuf, sizeof(abuf)); 452 453 xo_open_instance("neighbor-cache"); 454 xo_emit("{:hostname/%s}{d:/ (%s) deleted\n}", host, host_buf); 455 xo_emit("{e:address/%s}{e:interface/%s}", abuf, ifname); 456 xo_close_instance("neighbor-cache"); 457 } 458 snl_free(&ss); 459 460 return (e.error != 0); 461 #undef xo_warnx /* see above */ 462 } 463 464 int 465 set_nl(uint32_t ifindex, struct sockaddr_in6 *dst, struct sockaddr_dl *sdl, char *host) 466 { 467 struct snl_state ss = {}; 468 struct snl_writer nw; 469 470 nl_init_socket(&ss); 471 472 ifindex = fix_ifindex(&ss, ifindex, dst); 473 if (ifindex == 0) { 474 xo_warnx("delete: cannot locate %s", host); 475 snl_free(&ss); 476 return (0); 477 } 478 479 snl_init_writer(&ss, &nw); 480 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_NEWNEIGH); 481 hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE; 482 struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg); 483 if (ndmsg != NULL) { 484 uint8_t nl_flags = NTF_STICKY; 485 486 ndmsg->ndm_family = AF_INET6; 487 ndmsg->ndm_ifindex = ifindex; 488 ndmsg->ndm_state = NUD_PERMANENT; 489 490 if (opts.flags & RTF_ANNOUNCE) 491 nl_flags |= NTF_PROXY; 492 ndmsg->ndm_flags = nl_flags; 493 } 494 snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)dst); 495 snl_add_msg_attr(&nw, NDA_LLADDR, sdl->sdl_alen, LLADDR(sdl)); 496 497 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss, hdr)) { 498 snl_free(&ss); 499 return (1); 500 } 501 502 struct snl_errmsg_data e = {}; 503 snl_read_reply_code(&ss, hdr->nlmsg_seq, &e); 504 if (e.error != 0) { 505 if (e.error_str != NULL) 506 xo_warnx("set: %s: %s (%s)", host, strerror(e.error), e.error_str); 507 else 508 xo_warnx("set %s: %s", host, strerror(e.error)); 509 } 510 snl_free(&ss); 511 512 return (e.error != 0); 513 } 514 515