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
50static void
51nl_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
67static bool
68get_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
95static bool
96has_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
106static uint32_t
107get_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
117static void
118ip6_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
135static int
136guess_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
204static uint32_t
205fix_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
212static void
213print_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
313int
314print_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
394int
395delete_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
466int
467set_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