xref: /freebsd-src/usr.sbin/ndp/ndp_netlink.c (revision b6d4e3bee350b881b5efea03068c1247985b12fe)
191fbe081SAlexander V. Chernikov #include <sys/param.h>
291fbe081SAlexander V. Chernikov #include <sys/module.h>
391fbe081SAlexander V. Chernikov #include <sys/file.h>
491fbe081SAlexander V. Chernikov #include <sys/ioctl.h>
591fbe081SAlexander V. Chernikov #include <sys/linker.h>
691fbe081SAlexander V. Chernikov #include <sys/socket.h>
791fbe081SAlexander V. Chernikov #include <sys/sysctl.h>
891fbe081SAlexander V. Chernikov #include <sys/time.h>
991fbe081SAlexander V. Chernikov #include <sys/queue.h>
1091fbe081SAlexander V. Chernikov 
1191fbe081SAlexander V. Chernikov #include <net/if.h>
1291fbe081SAlexander V. Chernikov #include <net/if_dl.h>
1391fbe081SAlexander V. Chernikov #include <net/if_types.h>
1491fbe081SAlexander V. Chernikov 
1591fbe081SAlexander V. Chernikov #include <netinet/in.h>
1691fbe081SAlexander V. Chernikov #include <netinet/if_ether.h>
1791fbe081SAlexander V. Chernikov 
1891fbe081SAlexander V. Chernikov #include <netinet/icmp6.h>
1991fbe081SAlexander V. Chernikov #include <netinet6/in6_var.h>
2091fbe081SAlexander V. Chernikov #include <netinet6/nd6.h>
2191fbe081SAlexander V. Chernikov 
2291fbe081SAlexander V. Chernikov #include <arpa/inet.h>
2391fbe081SAlexander V. Chernikov 
2491fbe081SAlexander V. Chernikov #include <ctype.h>
2591fbe081SAlexander V. Chernikov #include <netdb.h>
2691fbe081SAlexander V. Chernikov #include <errno.h>
2791fbe081SAlexander V. Chernikov #include <nlist.h>
2891fbe081SAlexander V. Chernikov #include <stdio.h>
2991fbe081SAlexander V. Chernikov #include <string.h>
3091fbe081SAlexander V. Chernikov #include <paths.h>
3191fbe081SAlexander V. Chernikov #include <stdlib.h>
3291fbe081SAlexander V. Chernikov #include <fcntl.h>
3391fbe081SAlexander V. Chernikov #include <unistd.h>
3491fbe081SAlexander V. Chernikov #include <libxo/xo.h>
3591fbe081SAlexander V. Chernikov 
3691fbe081SAlexander V. Chernikov 
3791fbe081SAlexander V. Chernikov #include <netlink/netlink.h>
3891fbe081SAlexander V. Chernikov #include <netlink/netlink_route.h>
3991fbe081SAlexander V. Chernikov #include <netlink/netlink_snl.h>
4091fbe081SAlexander V. Chernikov #include <netlink/netlink_snl_route.h>
4191fbe081SAlexander V. Chernikov #include <netlink/netlink_snl_route_compat.h>
4291fbe081SAlexander V. Chernikov #include <netlink/netlink_snl_route_parsers.h>
4391fbe081SAlexander V. Chernikov 
4491fbe081SAlexander V. Chernikov #include "ndp.h"
4591fbe081SAlexander V. Chernikov 
4691fbe081SAlexander V. Chernikov #define RTF_ANNOUNCE	RTF_PROTO2
4791fbe081SAlexander V. Chernikov 
4891fbe081SAlexander V. Chernikov static void
4991fbe081SAlexander V. Chernikov nl_init_socket(struct snl_state *ss)
5091fbe081SAlexander V. Chernikov {
5191fbe081SAlexander V. Chernikov 	if (snl_init(ss, NETLINK_ROUTE))
5291fbe081SAlexander V. Chernikov 		return;
5391fbe081SAlexander V. Chernikov 
5491fbe081SAlexander V. Chernikov 	if (modfind("netlink") == -1 && errno == ENOENT) {
5591fbe081SAlexander V. Chernikov 		/* Try to load */
5691fbe081SAlexander V. Chernikov 		if (kldload("netlink") == -1)
5754ff53d8SYan-Hao Wang 			xo_err(1, "netlink is not loaded and load attempt failed");
5891fbe081SAlexander V. Chernikov 		if (snl_init(ss, NETLINK_ROUTE))
5991fbe081SAlexander V. Chernikov 			return;
6091fbe081SAlexander V. Chernikov 	}
6191fbe081SAlexander V. Chernikov 
6254ff53d8SYan-Hao Wang 	xo_err(1, "unable to open netlink socket");
6391fbe081SAlexander V. Chernikov }
6491fbe081SAlexander V. Chernikov 
6591fbe081SAlexander V. Chernikov static bool
6691fbe081SAlexander V. Chernikov get_link_info(struct snl_state *ss, uint32_t ifindex,
6791fbe081SAlexander V. Chernikov     struct snl_parsed_link_simple *link)
6891fbe081SAlexander V. Chernikov {
6991fbe081SAlexander V. Chernikov 	struct snl_writer nw;
7091fbe081SAlexander V. Chernikov 
7191fbe081SAlexander V. Chernikov 	snl_init_writer(ss, &nw);
7291fbe081SAlexander V. Chernikov 
7391fbe081SAlexander V. Chernikov 	struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK);
7491fbe081SAlexander V. Chernikov 	struct ifinfomsg *ifmsg = snl_reserve_msg_object(&nw, struct ifinfomsg);
7591fbe081SAlexander V. Chernikov 	if (ifmsg != NULL)
7691fbe081SAlexander V. Chernikov 		ifmsg->ifi_index = ifindex;
774f8f43b0SKristof Provost 	if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
7891fbe081SAlexander V. Chernikov 		return (false);
7991fbe081SAlexander V. Chernikov 
8091fbe081SAlexander V. Chernikov 	hdr = snl_read_reply(ss, hdr->nlmsg_seq);
8191fbe081SAlexander V. Chernikov 
8291fbe081SAlexander V. Chernikov 	if (hdr == NULL || hdr->nlmsg_type != RTM_NEWLINK)
8391fbe081SAlexander V. Chernikov 		return (false);
8491fbe081SAlexander V. Chernikov 
8591fbe081SAlexander V. Chernikov 	if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, link))
8691fbe081SAlexander V. Chernikov 		return (false);
8791fbe081SAlexander V. Chernikov 
8891fbe081SAlexander V. Chernikov 	return (true);
8991fbe081SAlexander V. Chernikov }
9091fbe081SAlexander V. Chernikov 
9191fbe081SAlexander V. Chernikov 
9291fbe081SAlexander V. Chernikov 
9391fbe081SAlexander V. Chernikov static bool
9491fbe081SAlexander V. Chernikov has_l2(struct snl_state *ss, uint32_t ifindex)
9591fbe081SAlexander V. Chernikov {
9691fbe081SAlexander V. Chernikov 	struct snl_parsed_link_simple link = {};
9791fbe081SAlexander V. Chernikov 
9891fbe081SAlexander V. Chernikov 	if (!get_link_info(ss, ifindex, &link))
9991fbe081SAlexander V. Chernikov 		return (false);
10091fbe081SAlexander V. Chernikov 
10191fbe081SAlexander V. Chernikov 	return (valid_type(link.ifi_type) != 0);
10291fbe081SAlexander V. Chernikov }
10391fbe081SAlexander V. Chernikov 
10491fbe081SAlexander V. Chernikov static uint32_t
1052d7842d0SJohn Baldwin get_myfib(void)
10691fbe081SAlexander V. Chernikov {
10791fbe081SAlexander V. Chernikov 	uint32_t fibnum = 0;
10891fbe081SAlexander V. Chernikov 	size_t len = sizeof(fibnum);
10991fbe081SAlexander V. Chernikov 
11091fbe081SAlexander V. Chernikov 	sysctlbyname("net.my_fibnum", (void *)&fibnum, &len, NULL, 0);
11191fbe081SAlexander V. Chernikov 
11291fbe081SAlexander V. Chernikov 	return (fibnum);
11391fbe081SAlexander V. Chernikov }
11491fbe081SAlexander V. Chernikov 
11591fbe081SAlexander V. Chernikov static void
11691fbe081SAlexander V. Chernikov ip6_writemask(struct in6_addr *addr6, uint8_t mask)
11791fbe081SAlexander V. Chernikov {
11891fbe081SAlexander V. Chernikov 	uint32_t *cp;
11991fbe081SAlexander V. Chernikov 
12091fbe081SAlexander V. Chernikov 	for (cp = (uint32_t *)addr6; mask >= 32; mask -= 32)
12191fbe081SAlexander V. Chernikov 		*cp++ = 0xFFFFFFFF;
12291fbe081SAlexander V. Chernikov 	if (mask > 0)
12391fbe081SAlexander V. Chernikov 		*cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0);
12491fbe081SAlexander V. Chernikov }
12591fbe081SAlexander V. Chernikov #define s6_addr32 __u6_addr.__u6_addr32
12691fbe081SAlexander V. Chernikov #define IN6_MASK_ADDR(a, m)	do { \
12791fbe081SAlexander V. Chernikov 	(a)->s6_addr32[0] &= (m)->s6_addr32[0]; \
12891fbe081SAlexander V. Chernikov 	(a)->s6_addr32[1] &= (m)->s6_addr32[1]; \
12991fbe081SAlexander V. Chernikov 	(a)->s6_addr32[2] &= (m)->s6_addr32[2]; \
13091fbe081SAlexander V. Chernikov 	(a)->s6_addr32[3] &= (m)->s6_addr32[3]; \
13191fbe081SAlexander V. Chernikov } while (0)
13291fbe081SAlexander V. Chernikov 
13391fbe081SAlexander V. Chernikov static int
13491fbe081SAlexander V. Chernikov guess_ifindex(struct snl_state *ss, uint32_t fibnum, const struct sockaddr_in6 *dst)
13591fbe081SAlexander V. Chernikov {
13691fbe081SAlexander V. Chernikov 	struct snl_writer nw;
13791fbe081SAlexander V. Chernikov 
13891fbe081SAlexander V. Chernikov 	if (IN6_IS_ADDR_LINKLOCAL(&dst->sin6_addr))
13991fbe081SAlexander V. Chernikov 		return (dst->sin6_scope_id);
14091fbe081SAlexander V. Chernikov 	else if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr))
14191fbe081SAlexander V. Chernikov 		return (0);
14291fbe081SAlexander V. Chernikov 
14391fbe081SAlexander V. Chernikov 
14491fbe081SAlexander V. Chernikov 	snl_init_writer(ss, &nw);
14591fbe081SAlexander V. Chernikov 
14691fbe081SAlexander V. Chernikov 	struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETROUTE);
14791fbe081SAlexander V. Chernikov 	struct rtmsg *rtm = snl_reserve_msg_object(&nw, struct rtmsg);
14891fbe081SAlexander V. Chernikov 	rtm->rtm_family = AF_INET6;
14991fbe081SAlexander V. Chernikov 
15091fbe081SAlexander V. Chernikov 	snl_add_msg_attr_ip(&nw, RTA_DST, (struct sockaddr *)dst);
15191fbe081SAlexander V. Chernikov 	snl_add_msg_attr_u32(&nw, RTA_TABLE, fibnum);
15291fbe081SAlexander V. Chernikov 
1534f8f43b0SKristof Provost 	if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
15491fbe081SAlexander V. Chernikov 		return (0);
15591fbe081SAlexander V. Chernikov 
15691fbe081SAlexander V. Chernikov 	hdr = snl_read_reply(ss, hdr->nlmsg_seq);
15791fbe081SAlexander V. Chernikov 
15891fbe081SAlexander V. Chernikov 	if (hdr->nlmsg_type != NL_RTM_NEWROUTE) {
15991fbe081SAlexander V. Chernikov 		/* No route found, unable to guess ifindex */
16091fbe081SAlexander V. Chernikov 		return (0);
16191fbe081SAlexander V. Chernikov 	}
16291fbe081SAlexander V. Chernikov 
16391fbe081SAlexander V. Chernikov 	struct snl_parsed_route r = {};
16491fbe081SAlexander V. Chernikov 	if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r))
16591fbe081SAlexander V. Chernikov 		return (0);
16691fbe081SAlexander V. Chernikov 
167c9090124SAlexander V. Chernikov 	if (r.rta_multipath.num_nhops > 0 || (r.rta_rtflags & RTF_GATEWAY))
16891fbe081SAlexander V. Chernikov 		return (0);
16991fbe081SAlexander V. Chernikov 
17091fbe081SAlexander V. Chernikov 	/* Check if the interface is of supported type */
17191fbe081SAlexander V. Chernikov 	if (has_l2(ss, r.rta_oif))
17291fbe081SAlexander V. Chernikov 		return (r.rta_oif);
17391fbe081SAlexander V. Chernikov 
17491fbe081SAlexander V. Chernikov 	/* Check the case when we matched the loopback route for P2P */
17591fbe081SAlexander V. Chernikov 	snl_init_writer(ss, &nw);
17691fbe081SAlexander V. Chernikov 	hdr = snl_create_msg_request(&nw, RTM_GETNEXTHOP);
17791fbe081SAlexander V. Chernikov 	snl_reserve_msg_object(&nw, struct nhmsg);
17891fbe081SAlexander V. Chernikov 
17991fbe081SAlexander V. Chernikov 	int off = snl_add_msg_attr_nested(&nw, NHA_FREEBSD);
18091fbe081SAlexander V. Chernikov 	snl_add_msg_attr_u32(&nw, NHAF_KID, r.rta_knh_id);
181934a24e5SR. Christian McDonald 	snl_add_msg_attr_u8(&nw, NHAF_FAMILY, AF_INET6);
18291fbe081SAlexander V. Chernikov 	snl_add_msg_attr_u32(&nw, NHAF_TABLE, fibnum);
18391fbe081SAlexander V. Chernikov 	snl_end_attr_nested(&nw, off);
18491fbe081SAlexander V. Chernikov 
1854f8f43b0SKristof Provost 	if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
18691fbe081SAlexander V. Chernikov 		return (0);
18791fbe081SAlexander V. Chernikov 
18891fbe081SAlexander V. Chernikov 	hdr = snl_read_reply(ss, hdr->nlmsg_seq);
18991fbe081SAlexander V. Chernikov 
19091fbe081SAlexander V. Chernikov 	if (hdr->nlmsg_type != NL_RTM_NEWNEXTHOP) {
19191fbe081SAlexander V. Chernikov 		/* No nexthop found, unable to guess ifindex */
19291fbe081SAlexander V. Chernikov 		return (0);
19391fbe081SAlexander V. Chernikov 	}
19491fbe081SAlexander V. Chernikov 
19591fbe081SAlexander V. Chernikov 	struct snl_parsed_nhop nh = {};
19691fbe081SAlexander V. Chernikov 	if (!snl_parse_nlmsg(ss, hdr, &snl_nhmsg_parser, &nh))
19791fbe081SAlexander V. Chernikov 		return (0);
19891fbe081SAlexander V. Chernikov 
19991fbe081SAlexander V. Chernikov 	return (nh.nhaf_aif);
20091fbe081SAlexander V. Chernikov }
20191fbe081SAlexander V. Chernikov 
20291fbe081SAlexander V. Chernikov static uint32_t
20391fbe081SAlexander V. Chernikov fix_ifindex(struct snl_state *ss, uint32_t ifindex, const struct sockaddr_in6 *sa)
20491fbe081SAlexander V. Chernikov {
20591fbe081SAlexander V. Chernikov 	if (ifindex == 0)
20691fbe081SAlexander V. Chernikov 		ifindex = guess_ifindex(ss, get_myfib(), sa);
20791fbe081SAlexander V. Chernikov 	return (ifindex);
20891fbe081SAlexander V. Chernikov }
20991fbe081SAlexander V. Chernikov 
21091fbe081SAlexander V. Chernikov static void
21191fbe081SAlexander V. Chernikov print_entry(struct snl_parsed_neigh *neigh, struct snl_parsed_link_simple *link)
21291fbe081SAlexander V. Chernikov {
21391fbe081SAlexander V. Chernikov 	struct timeval now;
21491fbe081SAlexander V. Chernikov 	char host_buf[NI_MAXHOST];
21591fbe081SAlexander V. Chernikov 	int addrwidth;
21691fbe081SAlexander V. Chernikov 	int llwidth;
21791fbe081SAlexander V. Chernikov 	int ifwidth;
21891fbe081SAlexander V. Chernikov 	char *ifname;
21991fbe081SAlexander V. Chernikov 
22091fbe081SAlexander V. Chernikov 	getnameinfo(neigh->nda_dst, sizeof(struct sockaddr_in6), host_buf,
22191fbe081SAlexander V. Chernikov 	    sizeof(host_buf), NULL, 0, (opts.nflag ? NI_NUMERICHOST : 0));
22291fbe081SAlexander V. Chernikov 
22391fbe081SAlexander V. Chernikov 	gettimeofday(&now, 0);
22491fbe081SAlexander V. Chernikov 	if (opts.tflag)
22591fbe081SAlexander V. Chernikov 		ts_print(&now);
22691fbe081SAlexander V. Chernikov 
22791fbe081SAlexander V. Chernikov 	struct sockaddr_dl sdl = {
22891fbe081SAlexander V. Chernikov 		.sdl_family = AF_LINK,
22991fbe081SAlexander V. Chernikov 		.sdl_type = link->ifi_type,
23091fbe081SAlexander V. Chernikov 		.sdl_len = sizeof(struct sockaddr_dl),
23191fbe081SAlexander V. Chernikov 	};
232b57df6fbSKristof Provost 
233b57df6fbSKristof Provost 	if (neigh->nda_lladdr) {
234b57df6fbSKristof Provost 		sdl.sdl_alen = NLA_DATA_LEN(neigh->nda_lladdr),
23591fbe081SAlexander V. Chernikov 		memcpy(sdl.sdl_data, NLA_DATA(neigh->nda_lladdr), sdl.sdl_alen);
236b57df6fbSKristof Provost 	}
23791fbe081SAlexander V. Chernikov 
23891fbe081SAlexander V. Chernikov 	addrwidth = strlen(host_buf);
23991fbe081SAlexander V. Chernikov 	if (addrwidth < W_ADDR)
24091fbe081SAlexander V. Chernikov 		addrwidth = W_ADDR;
24191fbe081SAlexander V. Chernikov 	llwidth = strlen(ether_str(&sdl));
24291fbe081SAlexander V. Chernikov 	if (W_ADDR + W_LL - addrwidth > llwidth)
24391fbe081SAlexander V. Chernikov 		llwidth = W_ADDR + W_LL - addrwidth;
24491fbe081SAlexander V. Chernikov 	ifname = link->ifla_ifname;
24591fbe081SAlexander V. Chernikov 	ifwidth = strlen(ifname);
24691fbe081SAlexander V. Chernikov 	if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth)
24791fbe081SAlexander V. Chernikov 		ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth;
24891fbe081SAlexander V. Chernikov 
24991fbe081SAlexander V. Chernikov 	xo_open_instance("neighbor-cache");
25091fbe081SAlexander V. Chernikov 	/* Compose format string for libxo, as it doesn't support *.* */
25191fbe081SAlexander V. Chernikov 	char xobuf[200];
25291fbe081SAlexander V. Chernikov 	snprintf(xobuf, sizeof(xobuf),
25391fbe081SAlexander V. Chernikov 	    "{:address/%%-%d.%ds/%%s} {:mac-address/%%-%d.%ds/%%s} {:interface/%%%d.%ds/%%s}",
25491fbe081SAlexander V. Chernikov 	    addrwidth, addrwidth, llwidth, llwidth, ifwidth, ifwidth);
25591fbe081SAlexander V. Chernikov 	xo_emit(xobuf, host_buf, ether_str(&sdl), ifname);
25691fbe081SAlexander V. Chernikov 
25791fbe081SAlexander V. Chernikov 	/* Print neighbor discovery specific information */
2587825619aSAlexander V. Chernikov 	time_t expire = (time_t)neigh->ndaf_next_ts;
25991fbe081SAlexander V. Chernikov 	int expire_in = expire - now.tv_sec;
26091fbe081SAlexander V. Chernikov 	if (expire > now.tv_sec)
26191fbe081SAlexander V. Chernikov 		xo_emit("{d:/ %-9.9s}{e:expires_sec/%d}", sec2str(expire_in), expire_in);
26291fbe081SAlexander V. Chernikov 	else if (expire == 0)
26391fbe081SAlexander V. Chernikov 		xo_emit("{d:/ %-9.9s}{en:permanent/true}", "permanent");
26491fbe081SAlexander V. Chernikov 	else
26591fbe081SAlexander V. Chernikov 		xo_emit("{d:/ %-9.9s}{e:expires_sec/%d}", "expired", expire_in);
26691fbe081SAlexander V. Chernikov 
26791fbe081SAlexander V. Chernikov 	const char *lle_state = "";
26891fbe081SAlexander V. Chernikov 	switch (neigh->ndm_state) {
26991fbe081SAlexander V. Chernikov 	case NUD_INCOMPLETE:
27091fbe081SAlexander V. Chernikov 		lle_state = "I";
27191fbe081SAlexander V. Chernikov 		break;
27291fbe081SAlexander V. Chernikov 	case NUD_REACHABLE:
27391fbe081SAlexander V. Chernikov 		lle_state = "R";
27491fbe081SAlexander V. Chernikov 		break;
27591fbe081SAlexander V. Chernikov 	case NUD_STALE:
27691fbe081SAlexander V. Chernikov 		lle_state = "S";
27791fbe081SAlexander V. Chernikov 		break;
27891fbe081SAlexander V. Chernikov 	case NUD_DELAY:
27991fbe081SAlexander V. Chernikov 		lle_state = "D";
28091fbe081SAlexander V. Chernikov 		break;
28191fbe081SAlexander V. Chernikov 	case NUD_PROBE:
28291fbe081SAlexander V. Chernikov 		lle_state = "P";
28391fbe081SAlexander V. Chernikov 		break;
28491fbe081SAlexander V. Chernikov 	case NUD_FAILED:
28591fbe081SAlexander V. Chernikov 		lle_state = "F";
28691fbe081SAlexander V. Chernikov 		break;
28791fbe081SAlexander V. Chernikov 	default:
28891fbe081SAlexander V. Chernikov 		lle_state = "N";
28991fbe081SAlexander V. Chernikov 		break;
29091fbe081SAlexander V. Chernikov 	}
29191fbe081SAlexander V. Chernikov 	xo_emit(" {:neighbor-state/%s}", lle_state);
29291fbe081SAlexander V. Chernikov 
29391fbe081SAlexander V. Chernikov 	bool isrouter = neigh->ndm_flags & NTF_ROUTER;
29491fbe081SAlexander V. Chernikov 
29591fbe081SAlexander V. Chernikov 	/*
29691fbe081SAlexander V. Chernikov 	 * other flags. R: router, P: proxy, W: ??
29791fbe081SAlexander V. Chernikov 	 */
29891fbe081SAlexander V. Chernikov 	char flgbuf[8];
29991fbe081SAlexander V. Chernikov 	snprintf(flgbuf, sizeof(flgbuf), "%s%s",
30091fbe081SAlexander V. Chernikov 	    isrouter ? "R" : "",
30191fbe081SAlexander V. Chernikov 	    (neigh->ndm_flags & NTF_PROXY) ? "p" : "");
30291fbe081SAlexander V. Chernikov 	xo_emit(" {:nd-flags/%s}", flgbuf);
30391fbe081SAlexander V. Chernikov 
30491fbe081SAlexander V. Chernikov 	if (neigh->nda_probes != 0)
30591fbe081SAlexander V. Chernikov 		xo_emit("{u:/ %d}", neigh->nda_probes);
30691fbe081SAlexander V. Chernikov 
30791fbe081SAlexander V. Chernikov 	xo_emit("\n");
30891fbe081SAlexander V. Chernikov 	xo_close_instance("neighbor-cache");
30991fbe081SAlexander V. Chernikov }
31091fbe081SAlexander V. Chernikov 
31191fbe081SAlexander V. Chernikov int
31291fbe081SAlexander V. Chernikov print_entries_nl(uint32_t ifindex, struct sockaddr_in6 *addr, bool cflag)
31391fbe081SAlexander V. Chernikov {
31491fbe081SAlexander V. Chernikov 	struct snl_state ss_req = {}, ss_cmd = {};
31591fbe081SAlexander V. Chernikov 	struct snl_parsed_link_simple link = {};
31691fbe081SAlexander V. Chernikov 	struct snl_writer nw;
317*b6d4e3beSZhenlei Huang 	struct nlmsghdr *hdr;
318*b6d4e3beSZhenlei Huang 	struct ndmsg *ndmsg;
31991fbe081SAlexander V. Chernikov 
32091fbe081SAlexander V. Chernikov 	nl_init_socket(&ss_req);
32191fbe081SAlexander V. Chernikov 	snl_init_writer(&ss_req, &nw);
32291fbe081SAlexander V. Chernikov 
323*b6d4e3beSZhenlei Huang 	/* Print header */
324*b6d4e3beSZhenlei Huang 	if (!opts.tflag && !cflag) {
325*b6d4e3beSZhenlei Huang 		char xobuf[200];
326*b6d4e3beSZhenlei Huang 		snprintf(xobuf, sizeof(xobuf),
327*b6d4e3beSZhenlei Huang 		    "{T:/%%-%d.%ds} {T:/%%-%d.%ds} {T:/%%%d.%ds} {T:/%%-9.9s} {T:/%%1s} {T:/%%5s}\n",
328*b6d4e3beSZhenlei Huang 		    W_ADDR, W_ADDR, W_LL, W_LL, W_IF, W_IF);
329*b6d4e3beSZhenlei Huang 		xo_emit(xobuf, "Neighbor", "Linklayer Address", "Netif", "Expire", "S", "Flags");
330*b6d4e3beSZhenlei Huang 	}
331*b6d4e3beSZhenlei Huang 
332*b6d4e3beSZhenlei Huang again:
333*b6d4e3beSZhenlei Huang 	hdr = snl_create_msg_request(&nw, RTM_GETNEIGH);
334*b6d4e3beSZhenlei Huang 	ndmsg = snl_reserve_msg_object(&nw, struct ndmsg);
33591fbe081SAlexander V. Chernikov 	if (ndmsg != NULL) {
33691fbe081SAlexander V. Chernikov 		ndmsg->ndm_family = AF_INET6;
33791fbe081SAlexander V. Chernikov 		ndmsg->ndm_ifindex = ifindex;
33891fbe081SAlexander V. Chernikov 	}
33991fbe081SAlexander V. Chernikov 
3404f8f43b0SKristof Provost 	if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss_req, hdr)) {
34191fbe081SAlexander V. Chernikov 		snl_free(&ss_req);
34291fbe081SAlexander V. Chernikov 		return (0);
34391fbe081SAlexander V. Chernikov 	}
34491fbe081SAlexander V. Chernikov 
34591fbe081SAlexander V. Chernikov 	uint32_t nlmsg_seq = hdr->nlmsg_seq;
34691fbe081SAlexander V. Chernikov 	struct snl_errmsg_data e = {};
34791fbe081SAlexander V. Chernikov 	int count = 0;
34891fbe081SAlexander V. Chernikov 	nl_init_socket(&ss_cmd);
34991fbe081SAlexander V. Chernikov 
35091fbe081SAlexander V. Chernikov 	xo_open_list("neighbor-cache");
35191fbe081SAlexander V. Chernikov 
35291fbe081SAlexander V. Chernikov 	while ((hdr = snl_read_reply_multi(&ss_req, nlmsg_seq, &e)) != NULL) {
35391fbe081SAlexander V. Chernikov 		struct snl_parsed_neigh neigh = {};
35491fbe081SAlexander V. Chernikov 
35591fbe081SAlexander V. Chernikov 		if (!snl_parse_nlmsg(&ss_req, hdr, &snl_rtm_neigh_parser, &neigh))
35691fbe081SAlexander V. Chernikov 			continue;
35791fbe081SAlexander V. Chernikov 
35891fbe081SAlexander V. Chernikov 		if (neigh.nda_ifindex != link.ifi_index) {
35991fbe081SAlexander V. Chernikov 			snl_clear_lb(&ss_cmd);
36091fbe081SAlexander V. Chernikov 			memset(&link, 0, sizeof(link));
36191fbe081SAlexander V. Chernikov 			if (!get_link_info(&ss_cmd, neigh.nda_ifindex, &link))
36291fbe081SAlexander V. Chernikov 				continue;
36391fbe081SAlexander V. Chernikov 		}
36491fbe081SAlexander V. Chernikov 
36591fbe081SAlexander V. Chernikov 		/* TODO: embed LL in the parser */
36691fbe081SAlexander V. Chernikov 		struct sockaddr_in6 *dst = (struct sockaddr_in6 *)neigh.nda_dst;
36791fbe081SAlexander V. Chernikov 		if (IN6_IS_ADDR_LINKLOCAL(&dst->sin6_addr))
36891fbe081SAlexander V. Chernikov 			dst->sin6_scope_id = neigh.nda_ifindex;
36991fbe081SAlexander V. Chernikov 
37091fbe081SAlexander V. Chernikov 		if (addr != NULL) {
37191fbe081SAlexander V. Chernikov 			if (IN6_ARE_ADDR_EQUAL(&addr->sin6_addr,
37291fbe081SAlexander V. Chernikov 			    &dst->sin6_addr) == 0 ||
37391fbe081SAlexander V. Chernikov 			    addr->sin6_scope_id != dst->sin6_scope_id)
37491fbe081SAlexander V. Chernikov 				continue;
37591fbe081SAlexander V. Chernikov 		}
37691fbe081SAlexander V. Chernikov 
37791fbe081SAlexander V. Chernikov 		if (cflag) {
37891fbe081SAlexander V. Chernikov 			char dst_str[INET6_ADDRSTRLEN];
37991fbe081SAlexander V. Chernikov 
38091fbe081SAlexander V. Chernikov 			inet_ntop(AF_INET6, &dst->sin6_addr, dst_str, sizeof(dst_str));
381934a24e5SR. Christian McDonald 			delete_nl(neigh.nda_ifindex, dst_str, false); /* no warn */
382934a24e5SR. Christian McDonald 		} else
383934a24e5SR. Christian McDonald 			print_entry(&neigh, &link);
384934a24e5SR. Christian McDonald 
38591fbe081SAlexander V. Chernikov 		count++;
38691fbe081SAlexander V. Chernikov 		snl_clear_lb(&ss_req);
38791fbe081SAlexander V. Chernikov 	}
388*b6d4e3beSZhenlei Huang 	if (opts.repeat) {
389*b6d4e3beSZhenlei Huang 		xo_emit("\n");
390*b6d4e3beSZhenlei Huang 		xo_flush();
391*b6d4e3beSZhenlei Huang 		sleep(opts.repeat);
392*b6d4e3beSZhenlei Huang 		goto again;
393*b6d4e3beSZhenlei Huang 	}
39491fbe081SAlexander V. Chernikov 	xo_close_list("neighbor-cache");
39591fbe081SAlexander V. Chernikov 
39691fbe081SAlexander V. Chernikov 	snl_free(&ss_req);
39791fbe081SAlexander V. Chernikov 	snl_free(&ss_cmd);
39891fbe081SAlexander V. Chernikov 
39991fbe081SAlexander V. Chernikov 	return (count);
40091fbe081SAlexander V. Chernikov }
40191fbe081SAlexander V. Chernikov 
40291fbe081SAlexander V. Chernikov int
403934a24e5SR. Christian McDonald delete_nl(uint32_t ifindex, char *host, bool warn)
40491fbe081SAlexander V. Chernikov {
405934a24e5SR. Christian McDonald #define xo_warnx(...) do { if (warn) { xo_warnx(__VA_ARGS__); } } while(0)
40691fbe081SAlexander V. Chernikov 	struct snl_state ss = {};
40791fbe081SAlexander V. Chernikov 	struct snl_writer nw;
40891fbe081SAlexander V. Chernikov 	struct sockaddr_in6 dst;
40991fbe081SAlexander V. Chernikov 
41091fbe081SAlexander V. Chernikov 	int gai_error = getaddr(host, &dst);
41191fbe081SAlexander V. Chernikov 	if (gai_error) {
41291fbe081SAlexander V. Chernikov 		xo_warnx("%s: %s", host, gai_strerror(gai_error));
41391fbe081SAlexander V. Chernikov 		return 1;
41491fbe081SAlexander V. Chernikov 	}
41591fbe081SAlexander V. Chernikov 
41691fbe081SAlexander V. Chernikov 	nl_init_socket(&ss);
41791fbe081SAlexander V. Chernikov 
41891fbe081SAlexander V. Chernikov 	ifindex = fix_ifindex(&ss, ifindex, &dst);
41991fbe081SAlexander V. Chernikov 	if (ifindex == 0) {
42091fbe081SAlexander V. Chernikov 		xo_warnx("delete: cannot locate %s", host);
42191fbe081SAlexander V. Chernikov 		snl_free(&ss);
42291fbe081SAlexander V. Chernikov 		return (0);
42391fbe081SAlexander V. Chernikov 	}
42491fbe081SAlexander V. Chernikov 
42591fbe081SAlexander V. Chernikov 	snl_init_writer(&ss, &nw);
42691fbe081SAlexander V. Chernikov 	struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_DELNEIGH);
42791fbe081SAlexander V. Chernikov 	struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg);
42891fbe081SAlexander V. Chernikov 	if (ndmsg != NULL) {
42991fbe081SAlexander V. Chernikov 		ndmsg->ndm_family = AF_INET6;
43091fbe081SAlexander V. Chernikov 		ndmsg->ndm_ifindex = ifindex;
43191fbe081SAlexander V. Chernikov 	}
43291fbe081SAlexander V. Chernikov 	snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)&dst);
43391fbe081SAlexander V. Chernikov 
4344f8f43b0SKristof Provost 	if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss, hdr)) {
43591fbe081SAlexander V. Chernikov 		snl_free(&ss);
43691fbe081SAlexander V. Chernikov 		return (1);
43791fbe081SAlexander V. Chernikov 	}
43891fbe081SAlexander V. Chernikov 
43991fbe081SAlexander V. Chernikov 	struct snl_errmsg_data e = {};
44091fbe081SAlexander V. Chernikov 	snl_read_reply_code(&ss, hdr->nlmsg_seq, &e);
44191fbe081SAlexander V. Chernikov 	if (e.error != 0) {
44291fbe081SAlexander V. Chernikov 		if (e.error_str != NULL)
44391fbe081SAlexander V. Chernikov 			xo_warnx("delete %s: %s (%s)", host, strerror(e.error), e.error_str);
44491fbe081SAlexander V. Chernikov 		else
44591fbe081SAlexander V. Chernikov 			xo_warnx("delete %s: %s", host, strerror(e.error));
44691fbe081SAlexander V. Chernikov 	} else {
44791fbe081SAlexander V. Chernikov 		char host_buf[NI_MAXHOST];
44891fbe081SAlexander V. Chernikov 		char ifix_buf[IFNAMSIZ];
44991fbe081SAlexander V. Chernikov 
45091fbe081SAlexander V. Chernikov 		getnameinfo((struct sockaddr *)&dst,
45191fbe081SAlexander V. Chernikov 		    dst.sin6_len, host_buf,
45291fbe081SAlexander V. Chernikov 		    sizeof(host_buf), NULL, 0,
45391fbe081SAlexander V. Chernikov 		    (opts.nflag ? NI_NUMERICHOST : 0));
45491fbe081SAlexander V. Chernikov 
45591fbe081SAlexander V. Chernikov 		char *ifname = if_indextoname(ifindex, ifix_buf);
45691fbe081SAlexander V. Chernikov 		if (ifname == NULL) {
45791fbe081SAlexander V. Chernikov 			strlcpy(ifix_buf, "?", sizeof(ifix_buf));
45891fbe081SAlexander V. Chernikov 			ifname = ifix_buf;
45991fbe081SAlexander V. Chernikov 		}
46091fbe081SAlexander V. Chernikov 		char abuf[INET6_ADDRSTRLEN];
46191fbe081SAlexander V. Chernikov 		inet_ntop(AF_INET6, &dst.sin6_addr, abuf, sizeof(abuf));
46291fbe081SAlexander V. Chernikov 
46391fbe081SAlexander V. Chernikov 		xo_open_instance("neighbor-cache");
46491fbe081SAlexander V. Chernikov 		xo_emit("{:hostname/%s}{d:/ (%s) deleted\n}", host, host_buf);
46591fbe081SAlexander V. Chernikov 		xo_emit("{e:address/%s}{e:interface/%s}", abuf, ifname);
46691fbe081SAlexander V. Chernikov 		xo_close_instance("neighbor-cache");
46791fbe081SAlexander V. Chernikov 	}
46891fbe081SAlexander V. Chernikov 	snl_free(&ss);
46991fbe081SAlexander V. Chernikov 
47091fbe081SAlexander V. Chernikov 	return (e.error != 0);
471934a24e5SR. Christian McDonald #undef xo_warnx /* see above */
47291fbe081SAlexander V. Chernikov }
47391fbe081SAlexander V. Chernikov 
47491fbe081SAlexander V. Chernikov int
47591fbe081SAlexander V. Chernikov set_nl(uint32_t ifindex, struct sockaddr_in6 *dst, struct sockaddr_dl *sdl, char *host)
47691fbe081SAlexander V. Chernikov {
47791fbe081SAlexander V. Chernikov 	struct snl_state ss = {};
47891fbe081SAlexander V. Chernikov 	struct snl_writer nw;
47991fbe081SAlexander V. Chernikov 
48091fbe081SAlexander V. Chernikov 	nl_init_socket(&ss);
48191fbe081SAlexander V. Chernikov 
48291fbe081SAlexander V. Chernikov 	ifindex = fix_ifindex(&ss, ifindex, dst);
48391fbe081SAlexander V. Chernikov 	if (ifindex == 0) {
48491fbe081SAlexander V. Chernikov 		xo_warnx("delete: cannot locate %s", host);
48591fbe081SAlexander V. Chernikov 		snl_free(&ss);
48691fbe081SAlexander V. Chernikov 		return (0);
48791fbe081SAlexander V. Chernikov 	}
48891fbe081SAlexander V. Chernikov 
48991fbe081SAlexander V. Chernikov 	snl_init_writer(&ss, &nw);
49091fbe081SAlexander V. Chernikov 	struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_NEWNEIGH);
49191fbe081SAlexander V. Chernikov 	hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
49291fbe081SAlexander V. Chernikov 	struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg);
49391fbe081SAlexander V. Chernikov 	if (ndmsg != NULL) {
49491fbe081SAlexander V. Chernikov 		uint8_t nl_flags = NTF_STICKY;
49591fbe081SAlexander V. Chernikov 
49691fbe081SAlexander V. Chernikov 		ndmsg->ndm_family = AF_INET6;
49791fbe081SAlexander V. Chernikov 		ndmsg->ndm_ifindex = ifindex;
49891fbe081SAlexander V. Chernikov 		ndmsg->ndm_state = NUD_PERMANENT;
49991fbe081SAlexander V. Chernikov 
50091fbe081SAlexander V. Chernikov 		if (opts.flags & RTF_ANNOUNCE)
50191fbe081SAlexander V. Chernikov 			nl_flags |= NTF_PROXY;
50291fbe081SAlexander V. Chernikov 		ndmsg->ndm_flags = nl_flags;
50391fbe081SAlexander V. Chernikov 	}
50491fbe081SAlexander V. Chernikov 	snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)dst);
50591fbe081SAlexander V. Chernikov 	snl_add_msg_attr(&nw, NDA_LLADDR, sdl->sdl_alen, LLADDR(sdl));
50691fbe081SAlexander V. Chernikov 
5074f8f43b0SKristof Provost 	if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss, hdr)) {
50891fbe081SAlexander V. Chernikov 		snl_free(&ss);
50991fbe081SAlexander V. Chernikov 		return (1);
51091fbe081SAlexander V. Chernikov 	}
51191fbe081SAlexander V. Chernikov 
51291fbe081SAlexander V. Chernikov 	struct snl_errmsg_data e = {};
51391fbe081SAlexander V. Chernikov 	snl_read_reply_code(&ss, hdr->nlmsg_seq, &e);
51491fbe081SAlexander V. Chernikov 	if (e.error != 0) {
51591fbe081SAlexander V. Chernikov 		if (e.error_str != NULL)
51691fbe081SAlexander V. Chernikov 			xo_warnx("set: %s: %s (%s)", host, strerror(e.error), e.error_str);
51791fbe081SAlexander V. Chernikov 		else
51891fbe081SAlexander V. Chernikov 			xo_warnx("set %s: %s", host, strerror(e.error));
51991fbe081SAlexander V. Chernikov 	}
52091fbe081SAlexander V. Chernikov 	snl_free(&ss);
52191fbe081SAlexander V. Chernikov 
52291fbe081SAlexander V. Chernikov 	return (e.error != 0);
52391fbe081SAlexander V. Chernikov }
52491fbe081SAlexander V. Chernikov 
525