xref: /freebsd/usr.sbin/ndp/ndp_netlink.c (revision 8bab661a3316d8bd9b9fbd11a3b4371b91507bd2)
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 
50 static void
51 nl_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 
67 static bool
68 get_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 (!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 
95 static bool
96 has_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 
106 static uint32_t
107 get_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 
117 static void
118 ip6_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 
135 static int
136 guess_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 (!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_INET);
184 	snl_add_msg_attr_u32(&nw, NHAF_TABLE, fibnum);
185 	snl_end_attr_nested(&nw, off);
186 
187 	if (!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 
204 static uint32_t
205 fix_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 
212 static void
213 print_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 		.sdl_alen = NLA_DATA_LEN(neigh->nda_lladdr),
234 	};
235 	memcpy(sdl.sdl_data, NLA_DATA(neigh->nda_lladdr), sdl.sdl_alen);
236 
237 	addrwidth = strlen(host_buf);
238 	if (addrwidth < W_ADDR)
239 		addrwidth = W_ADDR;
240 	llwidth = strlen(ether_str(&sdl));
241 	if (W_ADDR + W_LL - addrwidth > llwidth)
242 		llwidth = W_ADDR + W_LL - addrwidth;
243 	ifname = link->ifla_ifname;
244 	ifwidth = strlen(ifname);
245 	if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth)
246 		ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth;
247 
248 	xo_open_instance("neighbor-cache");
249 	/* Compose format string for libxo, as it doesn't support *.* */
250 	char xobuf[200];
251 	snprintf(xobuf, sizeof(xobuf),
252 	    "{:address/%%-%d.%ds/%%s} {:mac-address/%%-%d.%ds/%%s} {:interface/%%%d.%ds/%%s}",
253 	    addrwidth, addrwidth, llwidth, llwidth, ifwidth, ifwidth);
254 	xo_emit(xobuf, host_buf, ether_str(&sdl), ifname);
255 
256 	/* Print neighbor discovery specific information */
257 	time_t expire = (time_t)neigh->ndaf_next_ts;
258 	int expire_in = expire - now.tv_sec;
259 	if (expire > now.tv_sec)
260 		xo_emit("{d:/ %-9.9s}{e:expires_sec/%d}", sec2str(expire_in), expire_in);
261 	else if (expire == 0)
262 		xo_emit("{d:/ %-9.9s}{en:permanent/true}", "permanent");
263 	else
264 		xo_emit("{d:/ %-9.9s}{e:expires_sec/%d}", "expired", expire_in);
265 
266 	const char *lle_state = "";
267 	switch (neigh->ndm_state) {
268 	case NUD_INCOMPLETE:
269 		lle_state = "I";
270 		break;
271 	case NUD_REACHABLE:
272 		lle_state = "R";
273 		break;
274 	case NUD_STALE:
275 		lle_state = "S";
276 		break;
277 	case NUD_DELAY:
278 		lle_state = "D";
279 		break;
280 	case NUD_PROBE:
281 		lle_state = "P";
282 		break;
283 	case NUD_FAILED:
284 		lle_state = "F";
285 		break;
286 	default:
287 		lle_state = "N";
288 		break;
289 	}
290 	xo_emit(" {:neighbor-state/%s}", lle_state);
291 
292 	bool isrouter = neigh->ndm_flags & NTF_ROUTER;
293 
294 	/*
295 	 * other flags. R: router, P: proxy, W: ??
296 	 */
297 	char flgbuf[8];
298 	snprintf(flgbuf, sizeof(flgbuf), "%s%s",
299 	    isrouter ? "R" : "",
300 	    (neigh->ndm_flags & NTF_PROXY) ? "p" : "");
301 	xo_emit(" {:nd-flags/%s}", flgbuf);
302 
303 	if (neigh->nda_probes != 0)
304 		xo_emit("{u:/ %d}", neigh->nda_probes);
305 
306 	xo_emit("\n");
307 	xo_close_instance("neighbor-cache");
308 }
309 
310 int
311 print_entries_nl(uint32_t ifindex, struct sockaddr_in6 *addr, bool cflag)
312 {
313 	struct snl_state ss_req = {}, ss_cmd = {};
314 	struct snl_parsed_link_simple link = {};
315 	struct snl_writer nw;
316 
317 	nl_init_socket(&ss_req);
318 	snl_init_writer(&ss_req, &nw);
319 
320 	struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETNEIGH);
321 	struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg);
322 	if (ndmsg != NULL) {
323 		ndmsg->ndm_family = AF_INET6;
324 		ndmsg->ndm_ifindex = ifindex;
325 	}
326 
327 	if (!snl_finalize_msg(&nw) || !snl_send_message(&ss_req, hdr)) {
328 		snl_free(&ss_req);
329 		return (0);
330 	}
331 
332 	uint32_t nlmsg_seq = hdr->nlmsg_seq;
333 	struct snl_errmsg_data e = {};
334 	int count = 0;
335 	nl_init_socket(&ss_cmd);
336 
337 	/* Print header */
338 	if (!opts.tflag && !cflag) {
339 		char xobuf[200];
340 		snprintf(xobuf, sizeof(xobuf),
341 		    "{T:/%%-%d.%ds} {T:/%%-%d.%ds} {T:/%%%d.%ds} {T:/%%-9.9s} {T:%%1s} {T:%%5s}\n",
342 		    W_ADDR, W_ADDR, W_LL, W_LL, W_IF, W_IF);
343 		xo_emit(xobuf, "Neighbor", "Linklayer Address", "Netif", "Expire", "S", "Flags");
344 	}
345 	xo_open_list("neighbor-cache");
346 
347 	while ((hdr = snl_read_reply_multi(&ss_req, nlmsg_seq, &e)) != NULL) {
348 		struct snl_parsed_neigh neigh = {};
349 
350 		if (!snl_parse_nlmsg(&ss_req, hdr, &snl_rtm_neigh_parser, &neigh))
351 			continue;
352 
353 		if (neigh.nda_ifindex != link.ifi_index) {
354 			snl_clear_lb(&ss_cmd);
355 			memset(&link, 0, sizeof(link));
356 			if (!get_link_info(&ss_cmd, neigh.nda_ifindex, &link))
357 				continue;
358 		}
359 
360 		/* TODO: embed LL in the parser */
361 		struct sockaddr_in6 *dst = (struct sockaddr_in6 *)neigh.nda_dst;
362 		if (IN6_IS_ADDR_LINKLOCAL(&dst->sin6_addr))
363 			dst->sin6_scope_id = neigh.nda_ifindex;
364 
365 		if (addr != NULL) {
366 			if (IN6_ARE_ADDR_EQUAL(&addr->sin6_addr,
367 			    &dst->sin6_addr) == 0 ||
368 			    addr->sin6_scope_id != dst->sin6_scope_id)
369 				continue;
370 		}
371 
372 		print_entry(&neigh, &link);
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);
378 		}
379 		count++;
380 		snl_clear_lb(&ss_req);
381 	}
382 	xo_close_list("neighbor-cache");
383 
384 	snl_free(&ss_req);
385 	snl_free(&ss_cmd);
386 
387 	return (count);
388 }
389 
390 int
391 delete_nl(uint32_t ifindex, char *host)
392 {
393 	struct snl_state ss = {};
394 	struct snl_writer nw;
395 	struct sockaddr_in6 dst;
396 
397 	int gai_error = getaddr(host, &dst);
398 	if (gai_error) {
399 		xo_warnx("%s: %s", host, gai_strerror(gai_error));
400 		return 1;
401 	}
402 
403 	nl_init_socket(&ss);
404 
405 	ifindex = fix_ifindex(&ss, ifindex, &dst);
406 	if (ifindex == 0) {
407 		xo_warnx("delete: cannot locate %s", host);
408 		snl_free(&ss);
409 		return (0);
410 	}
411 
412 	snl_init_writer(&ss, &nw);
413 	struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_DELNEIGH);
414 	struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg);
415 	if (ndmsg != NULL) {
416 		ndmsg->ndm_family = AF_INET6;
417 		ndmsg->ndm_ifindex = ifindex;
418 	}
419 	snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)&dst);
420 
421 	if (!snl_finalize_msg(&nw) || !snl_send_message(&ss, hdr)) {
422 		snl_free(&ss);
423 		return (1);
424 	}
425 
426 	struct snl_errmsg_data e = {};
427 	snl_read_reply_code(&ss, hdr->nlmsg_seq, &e);
428 	if (e.error != 0) {
429 		if (e.error_str != NULL)
430 			xo_warnx("delete %s: %s (%s)", host, strerror(e.error), e.error_str);
431 		else
432 			xo_warnx("delete %s: %s", host, strerror(e.error));
433 	} else {
434 		char host_buf[NI_MAXHOST];
435 		char ifix_buf[IFNAMSIZ];
436 
437 		getnameinfo((struct sockaddr *)&dst,
438 		    dst.sin6_len, host_buf,
439 		    sizeof(host_buf), NULL, 0,
440 		    (opts.nflag ? NI_NUMERICHOST : 0));
441 
442 		char *ifname = if_indextoname(ifindex, ifix_buf);
443 		if (ifname == NULL) {
444 			strlcpy(ifix_buf, "?", sizeof(ifix_buf));
445 			ifname = ifix_buf;
446 		}
447 		char abuf[INET6_ADDRSTRLEN];
448 		inet_ntop(AF_INET6, &dst.sin6_addr, abuf, sizeof(abuf));
449 
450 		xo_open_instance("neighbor-cache");
451 		xo_emit("{:hostname/%s}{d:/ (%s) deleted\n}", host, host_buf);
452 		xo_emit("{e:address/%s}{e:interface/%s}", abuf, ifname);
453 		xo_close_instance("neighbor-cache");
454 	}
455 	snl_free(&ss);
456 
457 	return (e.error != 0);
458 }
459 
460 int
461 set_nl(uint32_t ifindex, struct sockaddr_in6 *dst, struct sockaddr_dl *sdl, char *host)
462 {
463 	struct snl_state ss = {};
464 	struct snl_writer nw;
465 
466 	nl_init_socket(&ss);
467 
468 	ifindex = fix_ifindex(&ss, ifindex, dst);
469 	if (ifindex == 0) {
470 		xo_warnx("delete: cannot locate %s", host);
471 		snl_free(&ss);
472 		return (0);
473 	}
474 
475 	snl_init_writer(&ss, &nw);
476 	struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_NEWNEIGH);
477 	hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
478 	struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg);
479 	if (ndmsg != NULL) {
480 		uint8_t nl_flags = NTF_STICKY;
481 
482 		ndmsg->ndm_family = AF_INET6;
483 		ndmsg->ndm_ifindex = ifindex;
484 		ndmsg->ndm_state = NUD_PERMANENT;
485 
486 		if (opts.flags & RTF_ANNOUNCE)
487 			nl_flags |= NTF_PROXY;
488 		ndmsg->ndm_flags = nl_flags;
489 	}
490 	snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)dst);
491 	snl_add_msg_attr(&nw, NDA_LLADDR, sdl->sdl_alen, LLADDR(sdl));
492 
493 	if (!snl_finalize_msg(&nw) || !snl_send_message(&ss, hdr)) {
494 		snl_free(&ss);
495 		return (1);
496 	}
497 
498 	struct snl_errmsg_data e = {};
499 	snl_read_reply_code(&ss, hdr->nlmsg_seq, &e);
500 	if (e.error != 0) {
501 		if (e.error_str != NULL)
502 			xo_warnx("set: %s: %s (%s)", host, strerror(e.error), e.error_str);
503 		else
504 			xo_warnx("set %s: %s", host, strerror(e.error));
505 	}
506 	snl_free(&ss);
507 
508 	return (e.error != 0);
509 }
510 
511