xref: /freebsd/sbin/route/route_netlink.c (revision 04dacd5691fb3de5fddd0868836c5a7ff9888370)
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <err.h>
5 #include <errno.h>
6 
7 #include <sys/bitcount.h>
8 #include <sys/param.h>
9 #include <sys/linker.h>
10 #include <sys/module.h>
11 #include <sys/socket.h>
12 #include <sys/sysctl.h>
13 #include <sys/time.h>
14 #include <sys/types.h>
15 
16 #include <netinet/in.h>
17 #include <arpa/inet.h>
18 
19 #include <net/ethernet.h>
20 #include <net/if.h>
21 #include <net/if_dl.h>
22 #include <net/if_types.h>
23 #include <netlink/netlink.h>
24 #include <netlink/netlink_route.h>
25 #include <netlink/netlink_snl.h>
26 #include <netlink/netlink_snl_route.h>
27 #include <netlink/netlink_snl_route_compat.h>
28 #include <netlink/netlink_snl_route_parsers.h>
29 
30 const char *routename(struct sockaddr *);
31 const char *netname(struct sockaddr *);
32 void printb(int, const char *);
33 extern const char routeflags[];
34 extern int verbose, debugonly;
35 
36 int rtmsg_nl(int cmd, int rtm_flags, int fib, int rtm_addrs, struct sockaddr_storage *so,
37     struct rt_metrics *rt_metrics);
38 int flushroutes_fib_nl(int fib, int af);
39 void monitor_nl(int fib);
40 
41 struct nl_helper;
42 struct snl_msg_info;
43 static void print_getmsg(struct nl_helper *h, struct nlmsghdr *hdr,
44     struct sockaddr *dst);
45 static void print_nlmsg(struct nl_helper *h, struct nlmsghdr *hdr,
46     struct snl_msg_info *cinfo);
47 
48 #define s6_addr32 __u6_addr.__u6_addr32
49 #define	bitcount32(x)	__bitcount32((uint32_t)(x))
50 static int
51 inet6_get_plen(const struct in6_addr *addr)
52 {
53 
54 	return (bitcount32(addr->s6_addr32[0]) + bitcount32(addr->s6_addr32[1]) +
55 	    bitcount32(addr->s6_addr32[2]) + bitcount32(addr->s6_addr32[3]));
56 }
57 
58 static void
59 ip6_writemask(struct in6_addr *addr6, uint8_t mask)
60 {
61 	uint32_t *cp;
62 
63 	for (cp = (uint32_t *)addr6; mask >= 32; mask -= 32)
64 		*cp++ = 0xFFFFFFFF;
65 	if (mask > 0)
66 		*cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0);
67 }
68 
69 static struct sockaddr *
70 get_netmask(struct snl_state *ss, int family, int plen)
71 {
72 	if (family == AF_INET) {
73 		if (plen == 32)
74 			return (NULL);
75 
76 		struct sockaddr_in *sin = snl_allocz(ss, sizeof(*sin));
77 
78 		sin->sin_len = sizeof(*sin);
79 		sin->sin_family = family;
80 		sin->sin_addr.s_addr = htonl(plen ? ~((1 << (32 - plen)) - 1) : 0);
81 
82 		return (struct sockaddr *)sin;
83 	} else if (family == AF_INET6) {
84 		if (plen == 128)
85 			return (NULL);
86 
87 		struct sockaddr_in6 *sin6 = snl_allocz(ss, sizeof(*sin6));
88 
89 		sin6->sin6_len = sizeof(*sin6);
90 		sin6->sin6_family = family;
91 		ip6_writemask(&sin6->sin6_addr, plen);
92 
93 		return (struct sockaddr *)sin6;
94 	}
95 	return (NULL);
96 }
97 
98 static void
99 nl_init_socket(struct snl_state *ss)
100 {
101 	if (snl_init(ss, NETLINK_ROUTE))
102 		return;
103 
104 	if (modfind("netlink") == -1 && errno == ENOENT) {
105 		/* Try to load */
106 		if (kldload("netlink") == -1)
107 			err(1, "netlink is not loaded and load attempt failed");
108 		if (snl_init(ss, NETLINK_ROUTE))
109 			return;
110 	}
111 
112 	err(1, "unable to open netlink socket");
113 }
114 
115 struct nl_helper {
116 	struct snl_state ss_cmd;
117 };
118 
119 static void
120 nl_helper_init(struct nl_helper *h)
121 {
122 	nl_init_socket(&h->ss_cmd);
123 }
124 
125 static void
126 nl_helper_free(struct nl_helper *h)
127 {
128 	snl_free(&h->ss_cmd);
129 }
130 
131 static struct sockaddr *
132 get_addr(struct sockaddr_storage *so, int rtm_addrs, int addr_type)
133 {
134 	struct sockaddr *sa = NULL;
135 
136 	if (rtm_addrs & (1 << addr_type))
137 		sa = (struct sockaddr *)&so[addr_type];
138 	return (sa);
139 }
140 
141 static int
142 rtmsg_nl_int(struct nl_helper *h, int cmd, int rtm_flags, int fib, int rtm_addrs,
143     struct sockaddr_storage *so, struct rt_metrics *rt_metrics)
144 {
145 	struct snl_state *ss = &h->ss_cmd;
146 	struct snl_writer nw;
147 	int nl_type = 0, nl_flags = 0;
148 
149 	snl_init_writer(ss, &nw);
150 
151 	switch (cmd) {
152 	case RTSOCK_RTM_ADD:
153 		nl_type = RTM_NEWROUTE;
154 		nl_flags = NLM_F_CREATE | NLM_F_APPEND; /* Do append by default */
155 		break;
156 	case RTSOCK_RTM_CHANGE:
157 		nl_type = RTM_NEWROUTE;
158 		nl_flags = NLM_F_REPLACE;
159 		break;
160 	case RTSOCK_RTM_DELETE:
161 		nl_type = RTM_DELROUTE;
162 		break;
163 	case RTSOCK_RTM_GET:
164 		nl_type = RTM_GETROUTE;
165 		break;
166 	default:
167 		exit(1);
168 	}
169 
170 	struct sockaddr *dst = get_addr(so, rtm_addrs, RTAX_DST);
171 	struct sockaddr *mask = get_addr(so, rtm_addrs, RTAX_NETMASK);
172 	struct sockaddr *gw = get_addr(so, rtm_addrs, RTAX_GATEWAY);
173 
174 	if (dst == NULL)
175 		return (EINVAL);
176 
177 	struct nlmsghdr *hdr = snl_create_msg_request(&nw, nl_type);
178 	hdr->nlmsg_flags |= nl_flags;
179 
180 	int plen = 0;
181 	int rtm_type = RTN_UNICAST;
182 
183 	switch (dst->sa_family) {
184 	case AF_INET:
185 	    {
186 		struct sockaddr_in *mask4 = (struct sockaddr_in *)mask;
187 
188 		if ((rtm_flags & RTF_HOST) == 0 && mask4 != NULL)
189 			plen = bitcount32(mask4->sin_addr.s_addr);
190 		else
191 			plen = 32;
192 		break;
193 	    }
194 	case AF_INET6:
195 	    {
196 		struct sockaddr_in6 *mask6 = (struct sockaddr_in6 *)mask;
197 
198 		if ((rtm_flags & RTF_HOST) == 0 && mask6 != NULL)
199 			plen = inet6_get_plen(&mask6->sin6_addr);
200 		else
201 			plen = 128;
202 		break;
203 	    }
204 	default:
205 		return (ENOTSUP);
206 	}
207 
208 	if (rtm_flags & RTF_REJECT)
209 		rtm_type = RTN_PROHIBIT;
210 	else if (rtm_flags & RTF_BLACKHOLE)
211 		rtm_type = RTN_BLACKHOLE;
212 
213 	struct rtmsg *rtm = snl_reserve_msg_object(&nw, struct rtmsg);
214 	rtm->rtm_family = dst->sa_family;
215 	rtm->rtm_protocol = RTPROT_STATIC;
216 	rtm->rtm_type = rtm_type;
217 	rtm->rtm_dst_len = plen;
218 
219 	/* Request exact prefix match if mask is set */
220 	if ((cmd == RTSOCK_RTM_GET) && (mask != NULL))
221 		rtm->rtm_flags = RTM_F_PREFIX;
222 
223 	snl_add_msg_attr_ip(&nw, RTA_DST, dst);
224 	snl_add_msg_attr_u32(&nw, RTA_TABLE, fib);
225 
226 	uint32_t rta_oif = 0;
227 
228 	if (gw != NULL) {
229 		if (rtm_flags & RTF_GATEWAY) {
230 			if (gw->sa_family == dst->sa_family)
231 				snl_add_msg_attr_ip(&nw, RTA_GATEWAY, gw);
232 			else
233 				snl_add_msg_attr_ipvia(&nw, RTA_VIA, gw);
234 			if (gw->sa_family == AF_INET6) {
235 				struct sockaddr_in6 *gw6 = (struct sockaddr_in6 *)gw;
236 
237 				if (IN6_IS_ADDR_LINKLOCAL(&gw6->sin6_addr))
238 					rta_oif = gw6->sin6_scope_id;
239 			}
240 		} else {
241 			/* Should be AF_LINK */
242 			struct sockaddr_dl *sdl = (struct sockaddr_dl *)gw;
243 			if (sdl->sdl_index != 0)
244 				rta_oif = sdl->sdl_index;
245 		}
246 	}
247 
248 	if (dst->sa_family == AF_INET6 && rta_oif == 0) {
249 		struct sockaddr_in6 *dst6 = (struct sockaddr_in6 *)dst;
250 
251 		if (IN6_IS_ADDR_LINKLOCAL(&dst6->sin6_addr))
252 			rta_oif = dst6->sin6_scope_id;
253 	}
254 
255 	if (rta_oif != 0)
256 		snl_add_msg_attr_u32(&nw, RTA_OIF, rta_oif);
257 	if (rtm_flags != 0)
258 		snl_add_msg_attr_u32(&nw, NL_RTA_RTFLAGS, rtm_flags);
259 
260 	if (rt_metrics->rmx_mtu > 0) {
261 		int off = snl_add_msg_attr_nested(&nw, RTA_METRICS);
262 		snl_add_msg_attr_u32(&nw, RTAX_MTU, rt_metrics->rmx_mtu);
263 		snl_end_attr_nested(&nw, off);
264 	}
265 
266 	if (rt_metrics->rmx_weight > 0)
267 		snl_add_msg_attr_u32(&nw, NL_RTA_WEIGHT, rt_metrics->rmx_weight);
268 
269 	if (snl_finalize_msg(&nw) && snl_send_message(ss, hdr)) {
270 		struct snl_errmsg_data e = {};
271 
272 		hdr = snl_read_reply(ss, hdr->nlmsg_seq);
273 		if (nl_type == NL_RTM_GETROUTE) {
274 			if (hdr->nlmsg_type == NL_RTM_NEWROUTE)
275 				print_getmsg(h, hdr, dst);
276 			else {
277 				snl_parse_errmsg(ss, hdr, &e);
278 				if (e.error == ESRCH)
279 					warn("route has not been found");
280 				else
281 					warn("message indicates error %d", e.error);
282 			}
283 
284 			return (0);
285 		}
286 
287 		if (snl_parse_errmsg(ss, hdr, &e))
288 			return (e.error);
289 	}
290 	return (EINVAL);
291 }
292 
293 int
294 rtmsg_nl(int cmd, int rtm_flags, int fib, int rtm_addrs,
295     struct sockaddr_storage *so, struct rt_metrics *rt_metrics)
296 {
297 	struct nl_helper h = {};
298 
299 	nl_helper_init(&h);
300 	int error = rtmsg_nl_int(&h, cmd, rtm_flags, fib, rtm_addrs, so, rt_metrics);
301 	nl_helper_free(&h);
302 
303 	return (error);
304 }
305 
306 static void
307 get_ifdata(struct nl_helper *h, uint32_t ifindex, struct snl_parsed_link_simple *link)
308 {
309 	struct snl_state *ss = &h->ss_cmd;
310 	struct snl_writer nw;
311 
312 	snl_init_writer(ss, &nw);
313 	struct nlmsghdr *hdr = snl_create_msg_request(&nw, NL_RTM_GETLINK);
314 	struct ifinfomsg *ifmsg = snl_reserve_msg_object(&nw, struct ifinfomsg);
315 	if (ifmsg != NULL)
316 		ifmsg->ifi_index = ifindex;
317 	if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr))
318 		return;
319 
320 	hdr = snl_read_reply(ss, hdr->nlmsg_seq);
321 
322 	if (hdr != NULL && hdr->nlmsg_type == RTM_NEWLINK) {
323 		snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, link);
324 	}
325 
326 	if (link->ifla_ifname == NULL) {
327 		char ifname[16];
328 
329 		snprintf(ifname, sizeof(ifname), "if#%u", ifindex);
330 		int len = strlen(ifname);
331 		char *buf = snl_allocz(ss, len + 1);
332 		strlcpy(buf, ifname, len + 1);
333 		link->ifla_ifname = buf;
334 	}
335 }
336 
337 static void
338 print_getmsg(struct nl_helper *h, struct nlmsghdr *hdr, struct sockaddr *dst)
339 {
340 	struct snl_state *ss = &h->ss_cmd;
341 	struct timespec ts;
342 	struct snl_parsed_route r = { .rtax_weight = RT_DEFAULT_WEIGHT };
343 
344 	if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r))
345 		return;
346 
347 	struct snl_parsed_link_simple link = {};
348 	get_ifdata(h, r.rta_oif, &link);
349 
350 	if (r.rtax_mtu == 0)
351 		r.rtax_mtu = link.ifla_mtu;
352 	r.rta_rtflags |= (RTF_UP | RTF_DONE);
353 
354 	(void)printf("   route to: %s\n", routename(dst));
355 
356 	if (r.rta_dst)
357 		(void)printf("destination: %s\n", routename(r.rta_dst));
358 	struct sockaddr *mask = get_netmask(ss, r.rtm_family, r.rtm_dst_len);
359 	if (mask)
360 		(void)printf("       mask: %s\n", routename(mask));
361 	if (r.rta_gw && (r.rta_rtflags & RTF_GATEWAY))
362 		(void)printf("    gateway: %s\n", routename(r.rta_gw));
363 	(void)printf("        fib: %u\n", (unsigned int)r.rta_table);
364 	if (link.ifla_ifname)
365 		(void)printf("  interface: %s\n", link.ifla_ifname);
366 	(void)printf("      flags: ");
367 	printb(r.rta_rtflags, routeflags);
368 
369 	struct rt_metrics rmx = {
370 		.rmx_mtu = r.rtax_mtu,
371 		.rmx_weight = r.rtax_weight,
372 		.rmx_expire = r.rta_expire,
373 	};
374 
375 	printf("\n%9s %9s %9s %9s %9s %10s %9s\n", "recvpipe",
376 	    "sendpipe", "ssthresh", "rtt,msec", "mtu   ", "weight", "expire");
377 	printf("%8lu  ", rmx.rmx_recvpipe);
378 	printf("%8lu  ", rmx.rmx_sendpipe);
379 	printf("%8lu  ", rmx.rmx_ssthresh);
380 	printf("%8lu  ", 0UL);
381 	printf("%8lu  ", rmx.rmx_mtu);
382 	printf("%8lu  ", rmx.rmx_weight);
383 	if (rmx.rmx_expire > 0)
384 		clock_gettime(CLOCK_REALTIME_FAST, &ts);
385 	else
386 		ts.tv_sec = 0;
387 	printf("%8ld \n", (long)(rmx.rmx_expire - ts.tv_sec));
388 }
389 
390 static void
391 print_prefix(struct nl_helper *h, char *buf, int bufsize, struct sockaddr *sa, int plen)
392 {
393 	int sz = 0;
394 
395 	if (sa == NULL) {
396 		snprintf(buf, bufsize, "<NULL>");
397 		return;
398 	}
399 
400 	switch (sa->sa_family) {
401 	case AF_INET:
402 		{
403 			struct sockaddr_in *sin = (struct sockaddr_in *)sa;
404 			char abuf[INET_ADDRSTRLEN];
405 
406 			inet_ntop(AF_INET, &sin->sin_addr, abuf, sizeof(abuf));
407 			sz = snprintf(buf, bufsize, "%s", abuf);
408 			break;
409 		}
410 	case AF_INET6:
411 		{
412 			struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
413 			char abuf[INET6_ADDRSTRLEN];
414 			char *ifname = NULL;
415 
416 			inet_ntop(AF_INET6, &sin6->sin6_addr, abuf, sizeof(abuf));
417 			if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
418 				struct snl_parsed_link_simple link = {};
419 
420 				if (sin6->sin6_scope_id != 0) {
421 					get_ifdata(h, sin6->sin6_scope_id, &link);
422 					ifname = link.ifla_ifname;
423 				}
424 			}
425 			if (ifname == NULL)
426 				sz = snprintf(buf, bufsize, "%s", abuf);
427 			else
428 				sz = snprintf(buf, bufsize, "%s%%%s", abuf, ifname);
429 			break;
430 		}
431 	default:
432 		snprintf(buf, bufsize, "unknown_af#%d", sa->sa_family);
433 		plen = -1;
434 	}
435 
436 	if (plen >= 0)
437 		snprintf(buf + sz, bufsize - sz, "/%d", plen);
438 }
439 
440 static int
441 print_line_prefix(struct nlmsghdr *hdr, struct snl_msg_info *cinfo,
442     const char *cmd, const char *name)
443 {
444 	struct timespec tp;
445 	struct tm tm;
446 	char buf[32];
447 
448 	clock_gettime(CLOCK_REALTIME, &tp);
449 	localtime_r(&tp.tv_sec, &tm);
450 
451 	strftime(buf, sizeof(buf), "%T", &tm);
452 	int len = printf("%s.%03ld PID %4u %s %s ", buf, tp.tv_nsec / 1000000,
453 	    cinfo->process_id, cmd, name);
454 
455 	return (len);
456 }
457 
458 static const char *
459 get_action_name(struct nlmsghdr *hdr, int new_cmd)
460 {
461 	if (hdr->nlmsg_type == new_cmd) {
462 		//return ((hdr->nlmsg_flags & NLM_F_REPLACE) ? "replace" : "add");
463 		return ("add/repl");
464 	} else
465 		return ("delete");
466 }
467 
468 static void
469 print_nlmsg_route_nhop(struct nl_helper *h, struct snl_parsed_route *r,
470     struct rta_mpath_nh *nh, bool first)
471 {
472 	// gw 10.0.0.1 ifp vtnet0 mtu 1500 table inet.0
473 	if (nh->gw != NULL) {
474 		char gwbuf[128];
475 		print_prefix(h, gwbuf, sizeof(gwbuf), nh->gw, -1);
476 		printf("gw %s ", gwbuf);
477 	}
478 
479 	if (nh->ifindex != 0) {
480 		struct snl_parsed_link_simple link = {};
481 
482 		get_ifdata(h, nh->ifindex, &link);
483 		if (nh->rtax_mtu == 0)
484 			nh->rtax_mtu = link.ifla_mtu;
485 		printf("iface %s ", link.ifla_ifname);
486 		if (nh->rtax_mtu != 0)
487 			printf("mtu %d ", nh->rtax_mtu);
488 	}
489 
490 	if (first) {
491 		switch (r->rtm_family) {
492 			case AF_INET:
493 				printf("table inet.%d", r->rta_table);
494 				break;
495 			case AF_INET6:
496 				printf("table inet6.%d", r->rta_table);
497 				break;
498 		}
499 	}
500 
501 	printf("\n");
502 }
503 
504 static void
505 print_nlmsg_route(struct nl_helper *h, struct nlmsghdr *hdr,
506     struct snl_msg_info *cinfo)
507 {
508 	struct snl_parsed_route r = { .rtax_weight = RT_DEFAULT_WEIGHT };
509 	struct snl_state *ss = &h->ss_cmd;
510 
511 	if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r))
512 		return;
513 
514 	// 20:19:41.333 add route 10.0.0.0/24 gw 10.0.0.1 ifp vtnet0 mtu 1500 table inet.0
515 
516 	const char *cmd = get_action_name(hdr, RTM_NEWROUTE);
517 	int len = print_line_prefix(hdr, cinfo, cmd, "route");
518 
519 	char buf[128];
520 	print_prefix(h, buf, sizeof(buf), r.rta_dst, r.rtm_dst_len);
521 	len += strlen(buf) + 1;
522 	printf("%s ", buf);
523 
524 	switch (r.rtm_type) {
525 	case RTN_BLACKHOLE:
526 		printf("blackhole\n");
527 		return;
528 	case RTN_UNREACHABLE:
529 		printf("unreach(reject)\n");
530 		return;
531 	case RTN_PROHIBIT:
532 		printf("prohibit(reject)\n");
533 		return;
534 	}
535 
536 	if (r.rta_multipath != NULL) {
537 		bool first = true;
538 
539 		memset(buf, ' ', sizeof(buf));
540 		buf[len] = '\0';
541 
542 		for (int i = 0; i < r.rta_multipath->num_nhops; i++) {
543 			struct rta_mpath_nh *nh = &r.rta_multipath->nhops[i];
544 
545 			if (!first)
546 				printf("%s", buf);
547 			print_nlmsg_route_nhop(h, &r, nh, first);
548 			first = false;
549 		}
550 	} else {
551 		struct rta_mpath_nh nh = {
552 			.gw = r.rta_gw,
553 			.ifindex = r.rta_oif,
554 			.rtax_mtu = r.rtax_mtu,
555 		};
556 
557 		print_nlmsg_route_nhop(h, &r, &nh, true);
558 	}
559 }
560 
561 static const char *operstate[] = {
562 	"UNKNOWN",	/* 0, IF_OPER_UNKNOWN */
563 	"NOTPRESENT",	/* 1, IF_OPER_NOTPRESENT */
564 	"DOWN",		/* 2, IF_OPER_DOWN */
565 	"LLDOWN",	/* 3, IF_OPER_LOWERLAYERDOWN */
566 	"TESTING",	/* 4, IF_OPER_TESTING */
567 	"DORMANT",	/* 5, IF_OPER_DORMANT */
568 	"UP",		/* 6, IF_OPER_UP */
569 };
570 
571 static void
572 print_nlmsg_link(struct nl_helper *h, struct nlmsghdr *hdr,
573     struct snl_msg_info *cinfo)
574 {
575 	struct snl_parsed_link l = {};
576 	struct snl_state *ss = &h->ss_cmd;
577 
578 	if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser, &l))
579 		return;
580 
581 	// 20:19:41.333 add iface#3 vtnet0 admin UP oper UP mtu 1500 table inet.0
582 	const char *cmd = get_action_name(hdr, RTM_NEWLINK);
583 	print_line_prefix(hdr, cinfo, cmd, "iface");
584 
585 	printf("iface#%u %s ", l.ifi_index, l.ifla_ifname);
586 	printf("admin %s ", (l.ifi_flags & IFF_UP) ? "UP" : "DOWN");
587 	if (l.ifla_operstate < NL_ARRAY_LEN(operstate))
588 		printf("oper %s ", operstate[l.ifla_operstate]);
589 	if (l.ifla_mtu > 0)
590 		printf("mtu %u ", l.ifla_mtu);
591 
592 	printf("\n");
593 }
594 
595 static void
596 print_nlmsg_addr(struct nl_helper *h, struct nlmsghdr *hdr,
597     struct snl_msg_info *cinfo)
598 {
599 	struct snl_parsed_addr attrs = {};
600 	struct snl_state *ss = &h->ss_cmd;
601 
602 	if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_addr_parser, &attrs))
603 		return;
604 
605 	// add addr 192.168.1.1/24 iface vtnet0
606 	const char *cmd = get_action_name(hdr, RTM_NEWADDR);
607 	print_line_prefix(hdr, cinfo, cmd, "addr");
608 
609 	char buf[128];
610 	struct sockaddr *addr = attrs.ifa_local ? attrs.ifa_local : attrs.ifa_address;
611 	print_prefix(h, buf, sizeof(buf), addr, attrs.ifa_prefixlen);
612 	printf("%s ", buf);
613 
614 	struct snl_parsed_link_simple link = {};
615 	get_ifdata(h, attrs.ifa_index, &link);
616 
617 	if (link.ifi_flags & IFF_POINTOPOINT) {
618 		char buf[64];
619 		print_prefix(h, buf, sizeof(buf), attrs.ifa_address, -1);
620 		printf("-> %s ", buf);
621 	}
622 
623 	printf("iface %s ", link.ifla_ifname);
624 
625 	printf("\n");
626 }
627 
628 static const char *nudstate[] = {
629 	"INCOMPLETE",		/* 0x01(0) */
630 	"REACHABLE",		/* 0x02(1) */
631 	"STALE",		/* 0x04(2) */
632 	"DELAY",		/* 0x08(3) */
633 	"PROBE",		/* 0x10(4) */
634 	"FAILED",		/* 0x20(5) */
635 };
636 
637 #define	NUD_INCOMPLETE		0x01	/* No lladdr, address resolution in progress */
638 #define	NUD_REACHABLE		0x02	/* reachable & recently resolved */
639 #define	NUD_STALE		0x04	/* has lladdr but it's stale */
640 #define	NUD_DELAY		0x08	/* has lladdr, is stale, probes delayed */
641 #define	NUD_PROBE		0x10	/* has lladdr, is stale, probes sent */
642 #define	NUD_FAILED		0x20	/* unused */
643 
644 
645 static void
646 print_nlmsg_neigh(struct nl_helper *h, struct nlmsghdr *hdr,
647     struct snl_msg_info *cinfo)
648 {
649 	struct snl_parsed_neigh attrs = {};
650 	struct snl_state *ss = &h->ss_cmd;
651 
652 	if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_neigh_parser, &attrs))
653 		return;
654 
655 	// add addr 192.168.1.1 state %s lladdr %s iface vtnet0
656 	const char *cmd = get_action_name(hdr, RTM_NEWNEIGH);
657 	print_line_prefix(hdr, cinfo, cmd, "neigh");
658 
659 	char buf[128];
660 	print_prefix(h, buf, sizeof(buf), attrs.nda_dst, -1);
661 	printf("%s ", buf);
662 
663 	struct snl_parsed_link_simple link = {};
664 	get_ifdata(h, attrs.nda_ifindex, &link);
665 
666 	for (unsigned int i = 0; i < NL_ARRAY_LEN(nudstate); i++) {
667 		if ((1 << i) & attrs.ndm_state) {
668 			printf("state %s ", nudstate[i]);
669 			break;
670 		}
671 	}
672 
673 	if (attrs.nda_lladdr != NULL) {
674 		int if_type = link.ifi_type;
675 
676 		if ((if_type == IFT_ETHER || if_type == IFT_L2VLAN || if_type == IFT_BRIDGE) &&
677 		    NLA_DATA_LEN(attrs.nda_lladdr) == ETHER_ADDR_LEN) {
678 			struct ether_addr *ll;
679 
680 			ll = (struct ether_addr *)NLA_DATA(attrs.nda_lladdr);
681 			printf("lladdr %s ", ether_ntoa(ll));
682 		} else {
683 			struct sockaddr_dl sdl = {
684 				.sdl_len = sizeof(sdl),
685 				.sdl_family = AF_LINK,
686 				.sdl_index = attrs.nda_ifindex,
687 				.sdl_type = if_type,
688 				.sdl_alen = NLA_DATA_LEN(attrs.nda_lladdr),
689 			};
690 			if (sdl.sdl_alen < sizeof(sdl.sdl_data)) {
691 				void *ll = NLA_DATA(attrs.nda_lladdr);
692 
693 				memcpy(sdl.sdl_data, ll, sdl.sdl_alen);
694 				printf("lladdr %s ", link_ntoa(&sdl));
695 			}
696 		}
697 	}
698 
699 	if (link.ifla_ifname != NULL)
700 		printf("iface %s ", link.ifla_ifname);
701 	printf("\n");
702 }
703 
704 static void
705 print_nlmsg_generic(struct nl_helper *h, struct nlmsghdr *hdr, struct snl_msg_info *cinfo)
706 {
707 	const char *cmd = get_action_name(hdr, 0);
708 	print_line_prefix(hdr, cinfo, cmd, "unknown message");
709 	printf(" type %u\n", hdr->nlmsg_type);
710 }
711 
712 static void
713 print_nlmsg(struct nl_helper *h, struct nlmsghdr *hdr, struct snl_msg_info *cinfo)
714 {
715 	switch (hdr->nlmsg_type) {
716 	case RTM_NEWLINK:
717 	case RTM_DELLINK:
718 		print_nlmsg_link(h, hdr, cinfo);
719 		break;
720 	case RTM_NEWADDR:
721 	case RTM_DELADDR:
722 		print_nlmsg_addr(h, hdr, cinfo);
723 		break;
724 	case RTM_NEWROUTE:
725 	case RTM_DELROUTE:
726 		print_nlmsg_route(h, hdr, cinfo);
727 		break;
728 	case RTM_NEWNEIGH:
729 	case RTM_DELNEIGH:
730 		print_nlmsg_neigh(h, hdr, cinfo);
731 		break;
732 	default:
733 		print_nlmsg_generic(h, hdr, cinfo);
734 	}
735 
736 	snl_clear_lb(&h->ss_cmd);
737 }
738 
739 void
740 monitor_nl(int fib)
741 {
742 	struct snl_state ss_event = {};
743 	struct nl_helper h;
744 
745 	nl_init_socket(&ss_event);
746 	nl_helper_init(&h);
747 
748 	int groups[] = {
749 		RTNLGRP_LINK,
750 		RTNLGRP_NEIGH,
751 		RTNLGRP_NEXTHOP,
752 #ifdef INET
753 		RTNLGRP_IPV4_IFADDR,
754 		RTNLGRP_IPV4_ROUTE,
755 #endif
756 #ifdef INET6
757 		RTNLGRP_IPV6_IFADDR,
758 		RTNLGRP_IPV6_ROUTE,
759 #endif
760 	};
761 
762 	int optval = 1;
763 	socklen_t optlen = sizeof(optval);
764 	setsockopt(ss_event.fd, SOL_NETLINK, NETLINK_MSG_INFO, &optval, optlen);
765 
766 	for (unsigned int i = 0; i < NL_ARRAY_LEN(groups); i++) {
767 		int error;
768 		int optval = groups[i];
769 		socklen_t optlen = sizeof(optval);
770 		error = setsockopt(ss_event.fd, SOL_NETLINK,
771 		    NETLINK_ADD_MEMBERSHIP, &optval, optlen);
772 		if (error != 0)
773 			warn("Unable to subscribe to group %d", optval);
774 	}
775 
776 	struct snl_msg_info attrs = {};
777 	struct nlmsghdr *hdr;
778 	while ((hdr = snl_read_message_dbg(&ss_event, &attrs)) != NULL)
779 	{
780 		print_nlmsg(&h, hdr, &attrs);
781 		snl_clear_lb(&h.ss_cmd);
782 		snl_clear_lb(&ss_event);
783 	}
784 
785 	snl_free(&ss_event);
786 	nl_helper_free(&h);
787 	exit(0);
788 }
789 
790 static void
791 print_flushed_route(struct snl_parsed_route *r, struct sockaddr *gw)
792 {
793 	struct sockaddr *sa = r->rta_dst;
794 
795 	printf("%-20.20s ", r->rta_rtflags & RTF_HOST ?
796 	    routename(sa) : netname(sa));
797 	sa = gw;
798 	printf("%-20.20s ", routename(sa));
799 	printf("-fib %-3d ", r->rta_table);
800 	printf("done\n");
801 }
802 
803 static int
804 flushroute_one(struct nl_helper *h, struct snl_parsed_route *r)
805 {
806 	struct snl_state *ss = &h->ss_cmd;
807 	struct snl_errmsg_data e = {};
808 	struct snl_writer nw;
809 
810 	snl_init_writer(ss, &nw);
811 
812 	struct nlmsghdr *hdr = snl_create_msg_request(&nw, NL_RTM_DELROUTE);
813 	struct rtmsg *rtm = snl_reserve_msg_object(&nw, struct rtmsg);
814 	rtm->rtm_family = r->rtm_family;
815 	rtm->rtm_dst_len = r->rtm_dst_len;
816 
817 	snl_add_msg_attr_u32(&nw, RTA_TABLE, r->rta_table);
818 	snl_add_msg_attr_ip(&nw, RTA_DST, r->rta_dst);
819 
820 	if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr))
821 		return (ENOMEM);
822 
823 	if (!snl_read_reply_code(ss, hdr->nlmsg_seq, &e)) {
824 		return (e.error);
825 		if (e.error == EPERM)
826 			errc(1, e.error, "RTM_DELROUTE failed:");
827 		else
828 			warnc(e.error, "RTM_DELROUTE failed:");
829 		return (true);
830 	};
831 
832 	if (verbose) {
833 		struct snl_msg_info attrs = {};
834 		print_nlmsg(h, hdr, &attrs);
835 	}
836 	else {
837 		if (r->rta_multipath != NULL) {
838 			for (int i = 0; i < r->rta_multipath->num_nhops; i++) {
839 				struct rta_mpath_nh *nh = &r->rta_multipath->nhops[i];
840 
841 				print_flushed_route(r, nh->gw);
842 			}
843 
844 		} else
845 			print_flushed_route(r, r->rta_gw);
846 	}
847 
848 	return (0);
849 }
850 
851 int
852 flushroutes_fib_nl(int fib, int af)
853 {
854 	struct snl_state ss = {};
855 	struct snl_writer nw;
856 	struct nl_helper h = {};
857 
858 	nl_init_socket(&ss);
859 	snl_init_writer(&ss, &nw);
860 
861 	struct nlmsghdr *hdr = snl_create_msg_request(&nw, NL_RTM_GETROUTE);
862 	hdr->nlmsg_flags |= NLM_F_DUMP;
863 	struct rtmsg *rtm = snl_reserve_msg_object(&nw, struct rtmsg);
864 	rtm->rtm_family = af;
865 	snl_add_msg_attr_u32(&nw, RTA_TABLE, fib);
866 
867 	if (!snl_finalize_msg(&nw) || !snl_send_message(&ss, hdr)) {
868 		snl_free(&ss);
869 		return (EINVAL);
870 	}
871 
872 	struct snl_errmsg_data e = {};
873 	uint32_t nlm_seq = hdr->nlmsg_seq;
874 
875 	nl_helper_init(&h);
876 
877 	while ((hdr = snl_read_reply_multi(&ss, nlm_seq, &e)) != NULL) {
878 		struct snl_parsed_route r = { .rtax_weight = RT_DEFAULT_WEIGHT };
879 		int error;
880 
881 		if (!snl_parse_nlmsg(&ss, hdr, &snl_rtm_route_parser, &r))
882 			continue;
883 		if (verbose) {
884 			struct snl_msg_info attrs = {};
885 			print_nlmsg(&h, hdr, &attrs);
886 		}
887 		if (r.rta_table != (uint32_t)fib || r.rtm_family != af)
888 			continue;
889 		if ((r.rta_rtflags & RTF_GATEWAY) == 0)
890 			continue;
891 		if (debugonly)
892 			continue;
893 
894 		if ((error = flushroute_one(&h, &r)) != 0) {
895 			if (error == EPERM)
896 				errc(1, error, "RTM_DELROUTE failed:");
897 			else
898 				warnc(error, "RTM_DELROUTE failed:");
899 		}
900 		snl_clear_lb(&h.ss_cmd);
901 	}
902 
903 	snl_free(&ss);
904 	nl_helper_free(&h);
905 
906 	return (e.error);
907 }
908 
909