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