xref: /freebsd/usr.sbin/ndp/ndp_netlink.c (revision 96190b4fef3b4a0cc3ca0606b0c4e3e69a5e6717)
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