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 #include "gmt2local.h" 37 38 39 #include <netlink/netlink.h> 40 #include <netlink/netlink_route.h> 41 #include <netlink/netlink_snl.h> 42 #include <netlink/netlink_snl_route.h> 43 #include <netlink/netlink_snl_route_compat.h> 44 #include <netlink/netlink_snl_route_parsers.h> 45 46 #include <libxo/xo.h> 47 #include "ndp.h" 48 49 #define RTF_ANNOUNCE RTF_PROTO2 50 51 static void 52 nl_init_socket(struct snl_state *ss) 53 { 54 if (snl_init(ss, NETLINK_ROUTE)) 55 return; 56 57 if (modfind("netlink") == -1 && errno == ENOENT) { 58 /* Try to load */ 59 if (kldload("netlink") == -1) 60 err(1, "netlink is not loaded and load attempt failed"); 61 if (snl_init(ss, NETLINK_ROUTE)) 62 return; 63 } 64 65 err(1, "unable to open netlink socket"); 66 } 67 68 static bool 69 get_link_info(struct snl_state *ss, uint32_t ifindex, 70 struct snl_parsed_link_simple *link) 71 { 72 struct snl_writer nw; 73 74 snl_init_writer(ss, &nw); 75 76 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK); 77 struct ifinfomsg *ifmsg = snl_reserve_msg_object(&nw, struct ifinfomsg); 78 if (ifmsg != NULL) 79 ifmsg->ifi_index = ifindex; 80 if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) 81 return (false); 82 83 hdr = snl_read_reply(ss, hdr->nlmsg_seq); 84 85 if (hdr == NULL || hdr->nlmsg_type != RTM_NEWLINK) 86 return (false); 87 88 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, link)) 89 return (false); 90 91 return (true); 92 } 93 94 95 96 static bool 97 has_l2(struct snl_state *ss, uint32_t ifindex) 98 { 99 struct snl_parsed_link_simple link = {}; 100 101 if (!get_link_info(ss, ifindex, &link)) 102 return (false); 103 104 return (valid_type(link.ifi_type) != 0); 105 } 106 107 static uint32_t 108 get_myfib(void) 109 { 110 uint32_t fibnum = 0; 111 size_t len = sizeof(fibnum); 112 113 sysctlbyname("net.my_fibnum", (void *)&fibnum, &len, NULL, 0); 114 115 return (fibnum); 116 } 117 118 static void 119 ip6_writemask(struct in6_addr *addr6, uint8_t mask) 120 { 121 uint32_t *cp; 122 123 for (cp = (uint32_t *)addr6; mask >= 32; mask -= 32) 124 *cp++ = 0xFFFFFFFF; 125 if (mask > 0) 126 *cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0); 127 } 128 #define s6_addr32 __u6_addr.__u6_addr32 129 #define IN6_MASK_ADDR(a, m) do { \ 130 (a)->s6_addr32[0] &= (m)->s6_addr32[0]; \ 131 (a)->s6_addr32[1] &= (m)->s6_addr32[1]; \ 132 (a)->s6_addr32[2] &= (m)->s6_addr32[2]; \ 133 (a)->s6_addr32[3] &= (m)->s6_addr32[3]; \ 134 } while (0) 135 136 static int 137 guess_ifindex(struct snl_state *ss, uint32_t fibnum, const struct sockaddr_in6 *dst) 138 { 139 struct snl_writer nw; 140 141 if (IN6_IS_ADDR_LINKLOCAL(&dst->sin6_addr)) 142 return (dst->sin6_scope_id); 143 else if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr)) 144 return (0); 145 146 147 snl_init_writer(ss, &nw); 148 149 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETROUTE); 150 struct rtmsg *rtm = snl_reserve_msg_object(&nw, struct rtmsg); 151 rtm->rtm_family = AF_INET6; 152 153 snl_add_msg_attr_ip(&nw, RTA_DST, (struct sockaddr *)dst); 154 snl_add_msg_attr_u32(&nw, RTA_TABLE, fibnum); 155 156 if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) 157 return (0); 158 159 hdr = snl_read_reply(ss, hdr->nlmsg_seq); 160 161 if (hdr->nlmsg_type != NL_RTM_NEWROUTE) { 162 /* No route found, unable to guess ifindex */ 163 return (0); 164 } 165 166 struct snl_parsed_route r = {}; 167 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r)) 168 return (0); 169 170 if (r.rta_multipath || (r.rta_rtflags & RTF_GATEWAY)) 171 return (0); 172 173 /* Check if the interface is of supported type */ 174 if (has_l2(ss, r.rta_oif)) 175 return (r.rta_oif); 176 177 /* Check the case when we matched the loopback route for P2P */ 178 snl_init_writer(ss, &nw); 179 hdr = snl_create_msg_request(&nw, RTM_GETNEXTHOP); 180 snl_reserve_msg_object(&nw, struct nhmsg); 181 182 int off = snl_add_msg_attr_nested(&nw, NHA_FREEBSD); 183 snl_add_msg_attr_u32(&nw, NHAF_KID, r.rta_knh_id); 184 snl_add_msg_attr_u8(&nw, NHAF_FAMILY, AF_INET); 185 snl_add_msg_attr_u32(&nw, NHAF_TABLE, fibnum); 186 snl_end_attr_nested(&nw, off); 187 188 if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr)) 189 return (0); 190 191 hdr = snl_read_reply(ss, hdr->nlmsg_seq); 192 193 if (hdr->nlmsg_type != NL_RTM_NEWNEXTHOP) { 194 /* No nexthop found, unable to guess ifindex */ 195 return (0); 196 } 197 198 struct snl_parsed_nhop nh = {}; 199 if (!snl_parse_nlmsg(ss, hdr, &snl_nhmsg_parser, &nh)) 200 return (0); 201 202 return (nh.nhaf_aif); 203 } 204 205 static uint32_t 206 fix_ifindex(struct snl_state *ss, uint32_t ifindex, const struct sockaddr_in6 *sa) 207 { 208 if (ifindex == 0) 209 ifindex = guess_ifindex(ss, get_myfib(), sa); 210 return (ifindex); 211 } 212 213 static void 214 print_entry(struct snl_parsed_neigh *neigh, struct snl_parsed_link_simple *link) 215 { 216 struct timeval now; 217 char host_buf[NI_MAXHOST]; 218 int addrwidth; 219 int llwidth; 220 int ifwidth; 221 char *ifname; 222 223 getnameinfo(neigh->nda_dst, sizeof(struct sockaddr_in6), host_buf, 224 sizeof(host_buf), NULL, 0, (opts.nflag ? NI_NUMERICHOST : 0)); 225 226 gettimeofday(&now, 0); 227 if (opts.tflag) 228 ts_print(&now); 229 230 struct sockaddr_dl sdl = { 231 .sdl_family = AF_LINK, 232 .sdl_type = link->ifi_type, 233 .sdl_len = sizeof(struct sockaddr_dl), 234 .sdl_alen = NLA_DATA_LEN(neigh->nda_lladdr), 235 }; 236 memcpy(sdl.sdl_data, NLA_DATA(neigh->nda_lladdr), sdl.sdl_alen); 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 (!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 print_entry(&neigh, &link); 374 if (cflag) { 375 char dst_str[INET6_ADDRSTRLEN]; 376 377 inet_ntop(AF_INET6, &dst->sin6_addr, dst_str, sizeof(dst_str)); 378 delete_nl(neigh.nda_ifindex, dst_str); 379 } 380 count++; 381 snl_clear_lb(&ss_req); 382 } 383 xo_close_list("neighbor-cache"); 384 385 snl_free(&ss_req); 386 snl_free(&ss_cmd); 387 388 return (count); 389 } 390 391 int 392 delete_nl(uint32_t ifindex, char *host) 393 { 394 struct snl_state ss = {}; 395 struct snl_writer nw; 396 struct sockaddr_in6 dst; 397 398 int gai_error = getaddr(host, &dst); 399 if (gai_error) { 400 xo_warnx("%s: %s", host, gai_strerror(gai_error)); 401 return 1; 402 } 403 404 nl_init_socket(&ss); 405 406 ifindex = fix_ifindex(&ss, ifindex, &dst); 407 if (ifindex == 0) { 408 xo_warnx("delete: cannot locate %s", host); 409 snl_free(&ss); 410 return (0); 411 } 412 413 snl_init_writer(&ss, &nw); 414 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_DELNEIGH); 415 struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg); 416 if (ndmsg != NULL) { 417 ndmsg->ndm_family = AF_INET6; 418 ndmsg->ndm_ifindex = ifindex; 419 } 420 snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)&dst); 421 422 if (!snl_finalize_msg(&nw) || !snl_send_message(&ss, hdr)) { 423 snl_free(&ss); 424 return (1); 425 } 426 427 struct snl_errmsg_data e = {}; 428 snl_read_reply_code(&ss, hdr->nlmsg_seq, &e); 429 if (e.error != 0) { 430 if (e.error_str != NULL) 431 xo_warnx("delete %s: %s (%s)", host, strerror(e.error), e.error_str); 432 else 433 xo_warnx("delete %s: %s", host, strerror(e.error)); 434 } else { 435 char host_buf[NI_MAXHOST]; 436 char ifix_buf[IFNAMSIZ]; 437 438 getnameinfo((struct sockaddr *)&dst, 439 dst.sin6_len, host_buf, 440 sizeof(host_buf), NULL, 0, 441 (opts.nflag ? NI_NUMERICHOST : 0)); 442 443 char *ifname = if_indextoname(ifindex, ifix_buf); 444 if (ifname == NULL) { 445 strlcpy(ifix_buf, "?", sizeof(ifix_buf)); 446 ifname = ifix_buf; 447 } 448 char abuf[INET6_ADDRSTRLEN]; 449 inet_ntop(AF_INET6, &dst.sin6_addr, abuf, sizeof(abuf)); 450 451 xo_open_instance("neighbor-cache"); 452 xo_emit("{:hostname/%s}{d:/ (%s) deleted\n}", host, host_buf); 453 xo_emit("{e:address/%s}{e:interface/%s}", abuf, ifname); 454 xo_close_instance("neighbor-cache"); 455 } 456 snl_free(&ss); 457 458 return (e.error != 0); 459 } 460 461 int 462 set_nl(uint32_t ifindex, struct sockaddr_in6 *dst, struct sockaddr_dl *sdl, char *host) 463 { 464 struct snl_state ss = {}; 465 struct snl_writer nw; 466 467 nl_init_socket(&ss); 468 469 ifindex = fix_ifindex(&ss, ifindex, dst); 470 if (ifindex == 0) { 471 xo_warnx("delete: cannot locate %s", host); 472 snl_free(&ss); 473 return (0); 474 } 475 476 snl_init_writer(&ss, &nw); 477 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_NEWNEIGH); 478 hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE; 479 struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg); 480 if (ndmsg != NULL) { 481 uint8_t nl_flags = NTF_STICKY; 482 483 ndmsg->ndm_family = AF_INET6; 484 ndmsg->ndm_ifindex = ifindex; 485 ndmsg->ndm_state = NUD_PERMANENT; 486 487 if (opts.flags & RTF_ANNOUNCE) 488 nl_flags |= NTF_PROXY; 489 ndmsg->ndm_flags = nl_flags; 490 } 491 snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)dst); 492 snl_add_msg_attr(&nw, NDA_LLADDR, sdl->sdl_alen, LLADDR(sdl)); 493 494 if (!snl_finalize_msg(&nw) || !snl_send_message(&ss, hdr)) { 495 snl_free(&ss); 496 return (1); 497 } 498 499 struct snl_errmsg_data e = {}; 500 snl_read_reply_code(&ss, hdr->nlmsg_seq, &e); 501 if (e.error != 0) { 502 if (e.error_str != NULL) 503 xo_warnx("set: %s: %s (%s)", host, strerror(e.error), e.error_str); 504 else 505 xo_warnx("set %s: %s", host, strerror(e.error)); 506 } 507 snl_free(&ss); 508 509 return (e.error != 0); 510 } 511 512