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