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