2 #include <sys/module.h>
5 #include <sys/linker.h>
6 #include <sys/socket.h>
7 #include <sys/sysctl.h>
12 #include <net/if_dl.h>
13 #include <net/if_types.h>
15 #include <netinet/in.h>
16 #include <netinet/if_ether.h>
18 #include <netinet/icmp6.h>
19 #include <netinet6/in6_var.h>
20 #include <netinet6/nd6.h>
22 #include <arpa/inet.h>
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>
48 #define RTF_ANNOUNCE RTF_PROTO2
51 nl_init_socket(struct snl_state *ss)
53 if (snl_init(ss, NETLINK_ROUTE))
56 if (modfind("netlink") == -1 && errno == ENOENT) {
58 if (kldload("netlink") == -1)
59 err(1, "netlink is not loaded and load attempt failed");
60 if (snl_init(ss, NETLINK_ROUTE))
64 err(1, "unable to open netlink socket");
68 get_link_info(struct snl_state *ss, uint32_t ifindex,
69 struct snl_parsed_link_simple *link)
73 snl_init_writer(ss, &nw);
75 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK);
76 struct ifinfomsg *ifmsg = snl_reserve_msg_object(&nw, struct ifinfomsg);
78 ifmsg->ifi_index = ifindex;
79 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
82 hdr = snl_read_reply(ss, hdr->nlmsg_seq);
84 if (hdr == NULL || hdr->nlmsg_type != RTM_NEWLINK)
87 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, link))
96 has_l2(struct snl_state *ss, uint32_t ifindex)
98 struct snl_parsed_link_simple link = {};
100 if (!get_link_info(ss, ifindex, &link))
103 return (valid_type(link.ifi_type) != 0);
110 size_t len = sizeof(fibnum);
112 sysctlbyname("net.my_fibnum", (void *)&fibnum, &len, NULL, 0);
118 ip6_writemask(struct in6_addr *addr6, uint8_t mask)
122 for (cp = (uint32_t *)addr6; mask >= 32; mask -= 32)
125 *cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0);
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]; \
136 guess_ifindex(struct snl_state *ss, uint32_t fibnum, const struct sockaddr_in6 *dst)
138 struct snl_writer nw;
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))
146 snl_init_writer(ss, &nw);
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;
152 snl_add_msg_attr_ip(&nw, RTA_DST, (struct sockaddr *)dst);
153 snl_add_msg_attr_u32(&nw, RTA_TABLE, fibnum);
155 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
158 hdr = snl_read_reply(ss, hdr->nlmsg_seq);
160 if (hdr->nlmsg_type != NL_RTM_NEWROUTE) {
161 /* No route found, unable to guess ifindex */
165 struct snl_parsed_route r = {};
166 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r))
169 if (r.rta_multipath.num_nhops > 0 || (r.rta_rtflags & RTF_GATEWAY))
172 /* Check if the interface is of supported type */
173 if (has_l2(ss, r.rta_oif))
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);
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);
187 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
190 hdr = snl_read_reply(ss, hdr->nlmsg_seq);
192 if (hdr->nlmsg_type != NL_RTM_NEWNEXTHOP) {
193 /* No nexthop found, unable to guess ifindex */
197 struct snl_parsed_nhop nh = {};
198 if (!snl_parse_nlmsg(ss, hdr, &snl_nhmsg_parser, &nh))
201 return (nh.nhaf_aif);
205 fix_ifindex(struct snl_state *ss, uint32_t ifindex, const struct sockaddr_in6 *sa)
208 ifindex = guess_ifindex(ss, get_myfib(), sa);
213 print_entry(struct snl_parsed_neigh *neigh, struct snl_parsed_link_simple *link)
216 char host_buf[NI_MAXHOST];
222 getnameinfo(neigh->nda_dst, sizeof(struct sockaddr_in6), host_buf,
223 sizeof(host_buf), NULL, 0, (opts.nflag ? NI_NUMERICHOST : 0));
225 gettimeofday(&now, 0);
229 struct sockaddr_dl sdl = {
230 .sdl_family = AF_LINK,
231 .sdl_type = link->ifi_type,
232 .sdl_len = sizeof(struct sockaddr_dl),
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);
240 addrwidth = strlen(host_buf);
241 if (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;
251 xo_open_instance("neighbor-cache");
252 /* Compose format string for libxo, as it doesn't support *.* */
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);
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");
267 xo_emit("{d:/ %-9.9s}{e:expires_sec/%d}", "expired", expire_in);
269 const char *lle_state = "";
270 switch (neigh->ndm_state) {
293 xo_emit(" {:neighbor-state/%s}", lle_state);
295 bool isrouter = neigh->ndm_flags & NTF_ROUTER;
298 * other flags. R: router, P: proxy, W: ??
301 snprintf(flgbuf, sizeof(flgbuf), "%s%s",
303 (neigh->ndm_flags & NTF_PROXY) ? "p" : "");
304 xo_emit(" {:nd-flags/%s}", flgbuf);
306 if (neigh->nda_probes != 0)
307 xo_emit("{u:/ %d}", neigh->nda_probes);
310 xo_close_instance("neighbor-cache");
314 print_entries_nl(uint32_t ifindex, struct sockaddr_in6 *addr, bool cflag)
316 struct snl_state ss_req = {}, ss_cmd = {};
317 struct snl_parsed_link_simple link = {};
318 struct snl_writer nw;
320 nl_init_socket(&ss_req);
321 snl_init_writer(&ss_req, &nw);
323 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETNEIGH);
324 struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg);
326 ndmsg->ndm_family = AF_INET6;
327 ndmsg->ndm_ifindex = ifindex;
330 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss_req, hdr)) {
335 uint32_t nlmsg_seq = hdr->nlmsg_seq;
336 struct snl_errmsg_data e = {};
338 nl_init_socket(&ss_cmd);
341 if (!opts.tflag && !cflag) {
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");
348 xo_open_list("neighbor-cache");
350 while ((hdr = snl_read_reply_multi(&ss_req, nlmsg_seq, &e)) != NULL) {
351 struct snl_parsed_neigh neigh = {};
353 if (!snl_parse_nlmsg(&ss_req, hdr, &snl_rtm_neigh_parser, &neigh))
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))
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;
369 if (IN6_ARE_ADDR_EQUAL(&addr->sin6_addr,
370 &dst->sin6_addr) == 0 ||
371 addr->sin6_scope_id != dst->sin6_scope_id)
376 char dst_str[INET6_ADDRSTRLEN];
378 inet_ntop(AF_INET6, &dst->sin6_addr, dst_str, sizeof(dst_str));
379 delete_nl(neigh.nda_ifindex, dst_str, false); /* no warn */
381 print_entry(&neigh, &link);
384 snl_clear_lb(&ss_req);
386 xo_close_list("neighbor-cache");
395 delete_nl(uint32_t ifindex, char *host, bool warn)
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;
402 int gai_error = getaddr(host, &dst);
404 xo_warnx("%s: %s", host, gai_strerror(gai_error));
410 ifindex = fix_ifindex(&ss, ifindex, &dst);
412 xo_warnx("delete: cannot locate %s", host);
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);
421 ndmsg->ndm_family = AF_INET6;
422 ndmsg->ndm_ifindex = ifindex;
424 snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)&dst);
426 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss, hdr)) {
431 struct snl_errmsg_data e = {};
432 snl_read_reply_code(&ss, hdr->nlmsg_seq, &e);
434 if (e.error_str != NULL)
435 xo_warnx("delete %s: %s (%s)", host, strerror(e.error), e.error_str);
437 xo_warnx("delete %s: %s", host, strerror(e.error));
439 char host_buf[NI_MAXHOST];
440 char ifix_buf[IFNAMSIZ];
442 getnameinfo((struct sockaddr *)&dst,
443 dst.sin6_len, host_buf,
444 sizeof(host_buf), NULL, 0,
445 (opts.nflag ? NI_NUMERICHOST : 0));
447 char *ifname = if_indextoname(ifindex, ifix_buf);
448 if (ifname == NULL) {
449 strlcpy(ifix_buf, "?", sizeof(ifix_buf));
452 char abuf[INET6_ADDRSTRLEN];
453 inet_ntop(AF_INET6, &dst.sin6_addr, abuf, sizeof(abuf));
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");
462 return (e.error != 0);
463 #undef xo_warnx /* see above */
467 set_nl(uint32_t ifindex, struct sockaddr_in6 *dst, struct sockaddr_dl *sdl, char *host)
469 struct snl_state ss = {};
470 struct snl_writer nw;
474 ifindex = fix_ifindex(&ss, ifindex, dst);
476 xo_warnx("delete: cannot locate %s", host);
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);
486 uint8_t nl_flags = NTF_STICKY;
488 ndmsg->ndm_family = AF_INET6;
489 ndmsg->ndm_ifindex = ifindex;
490 ndmsg->ndm_state = NUD_PERMANENT;
492 if (opts.flags & RTF_ANNOUNCE)
493 nl_flags |= NTF_PROXY;
494 ndmsg->ndm_flags = nl_flags;
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));
499 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss, hdr)) {
504 struct snl_errmsg_data e = {};
505 snl_read_reply_code(&ss, hdr->nlmsg_seq, &e);
507 if (e.error_str != NULL)
508 xo_warnx("set: %s: %s (%s)", host, strerror(e.error), e.error_str);
510 xo_warnx("set %s: %s", host, strerror(e.error));
514 return (e.error != 0);