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
30const char *routename(struct sockaddr *);
31const char *netname(struct sockaddr *);
32void printb(int, const char *);
33extern const char routeflags[];
34extern int verbose, debugonly;
35
36int rtmsg_nl(int cmd, int rtm_flags, int fib, int rtm_addrs, struct sockaddr_storage *so,
37    struct rt_metrics *rt_metrics);
38int flushroutes_fib_nl(int fib, int af);
39void monitor_nl(int fib);
40
41struct nl_helper;
42struct snl_msg_info;
43static void print_getmsg(struct nl_helper *h, struct nlmsghdr *hdr,
44    struct sockaddr *dst);
45static 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))
50static int
51inet6_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
58static void
59ip6_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
69static struct sockaddr *
70get_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
98static void
99nl_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
115struct nl_helper {
116	struct snl_state ss_cmd;
117};
118
119static void
120nl_helper_init(struct nl_helper *h)
121{
122	nl_init_socket(&h->ss_cmd);
123}
124
125static void
126nl_helper_free(struct nl_helper *h)
127{
128	snl_free(&h->ss_cmd);
129}
130
131static struct sockaddr *
132get_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
141static int
142rtmsg_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 ((hdr = 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				return (0);
277			}
278		}
279
280		if (snl_parse_errmsg(ss, hdr, &e)) {
281			switch (e.error) {
282			case (ESRCH):
283				warnx("route has not been found");
284				break;
285			default:
286				if (e.error == 0)
287					break;
288				warnc(e.error, "message indicates error");
289			}
290
291			return (e.error);
292		}
293	}
294
295	return (EINVAL);
296}
297
298int
299rtmsg_nl(int cmd, int rtm_flags, int fib, int rtm_addrs,
300    struct sockaddr_storage *so, struct rt_metrics *rt_metrics)
301{
302	struct nl_helper h = {};
303
304	nl_helper_init(&h);
305	int error = rtmsg_nl_int(&h, cmd, rtm_flags, fib, rtm_addrs, so, rt_metrics);
306	nl_helper_free(&h);
307
308	return (error);
309}
310
311static void
312get_ifdata(struct nl_helper *h, uint32_t ifindex, struct snl_parsed_link_simple *link)
313{
314	struct snl_state *ss = &h->ss_cmd;
315	struct snl_writer nw;
316
317	snl_init_writer(ss, &nw);
318	struct nlmsghdr *hdr = snl_create_msg_request(&nw, NL_RTM_GETLINK);
319	struct ifinfomsg *ifmsg = snl_reserve_msg_object(&nw, struct ifinfomsg);
320	if (ifmsg != NULL)
321		ifmsg->ifi_index = ifindex;
322	if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
323		return;
324
325	hdr = snl_read_reply(ss, hdr->nlmsg_seq);
326
327	if (hdr != NULL && hdr->nlmsg_type == RTM_NEWLINK) {
328		snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, link);
329	}
330
331	if (link->ifla_ifname == NULL) {
332		char ifname[16];
333
334		snprintf(ifname, sizeof(ifname), "if#%u", ifindex);
335		int len = strlen(ifname);
336		char *buf = snl_allocz(ss, len + 1);
337		strlcpy(buf, ifname, len + 1);
338		link->ifla_ifname = buf;
339	}
340}
341
342static void
343print_getmsg(struct nl_helper *h, struct nlmsghdr *hdr, struct sockaddr *dst)
344{
345	struct snl_state *ss = &h->ss_cmd;
346	struct timespec ts;
347	struct snl_parsed_route r = { .rtax_weight = RT_DEFAULT_WEIGHT };
348
349	if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r))
350		return;
351
352	struct snl_parsed_link_simple link = {};
353	get_ifdata(h, r.rta_oif, &link);
354
355	if (r.rtax_mtu == 0)
356		r.rtax_mtu = link.ifla_mtu;
357	r.rta_rtflags |= (RTF_UP | RTF_DONE);
358
359	(void)printf("   route to: %s\n", routename(dst));
360
361	if (r.rta_dst)
362		(void)printf("destination: %s\n", routename(r.rta_dst));
363	struct sockaddr *mask = get_netmask(ss, r.rtm_family, r.rtm_dst_len);
364	if (mask)
365		(void)printf("       mask: %s\n", routename(mask));
366	if (r.rta_gw && (r.rta_rtflags & RTF_GATEWAY))
367		(void)printf("    gateway: %s\n", routename(r.rta_gw));
368	(void)printf("        fib: %u\n", (unsigned int)r.rta_table);
369	if (link.ifla_ifname)
370		(void)printf("  interface: %s\n", link.ifla_ifname);
371	(void)printf("      flags: ");
372	printb(r.rta_rtflags, routeflags);
373
374	struct rt_metrics rmx = {
375		.rmx_mtu = r.rtax_mtu,
376		.rmx_weight = r.rtax_weight,
377		.rmx_expire = r.rta_expire,
378	};
379
380	printf("\n%9s %9s %9s %9s %9s %10s %9s\n", "recvpipe",
381	    "sendpipe", "ssthresh", "rtt,msec", "mtu   ", "weight", "expire");
382	printf("%8lu  ", rmx.rmx_recvpipe);
383	printf("%8lu  ", rmx.rmx_sendpipe);
384	printf("%8lu  ", rmx.rmx_ssthresh);
385	printf("%8lu  ", 0UL);
386	printf("%8lu  ", rmx.rmx_mtu);
387	printf("%8lu  ", rmx.rmx_weight);
388	if (rmx.rmx_expire > 0)
389		clock_gettime(CLOCK_REALTIME_FAST, &ts);
390	else
391		ts.tv_sec = 0;
392	printf("%8ld \n", (long)(rmx.rmx_expire - ts.tv_sec));
393}
394
395static void
396print_prefix(struct nl_helper *h, char *buf, int bufsize, struct sockaddr *sa, int plen)
397{
398	int sz = 0;
399
400	if (sa == NULL) {
401		snprintf(buf, bufsize, "<NULL>");
402		return;
403	}
404
405	switch (sa->sa_family) {
406	case AF_INET:
407		{
408			struct sockaddr_in *sin = (struct sockaddr_in *)sa;
409			char abuf[INET_ADDRSTRLEN];
410
411			inet_ntop(AF_INET, &sin->sin_addr, abuf, sizeof(abuf));
412			sz = snprintf(buf, bufsize, "%s", abuf);
413			break;
414		}
415	case AF_INET6:
416		{
417			struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
418			char abuf[INET6_ADDRSTRLEN];
419			char *ifname = NULL;
420
421			inet_ntop(AF_INET6, &sin6->sin6_addr, abuf, sizeof(abuf));
422			if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
423				struct snl_parsed_link_simple link = {};
424
425				if (sin6->sin6_scope_id != 0) {
426					get_ifdata(h, sin6->sin6_scope_id, &link);
427					ifname = link.ifla_ifname;
428				}
429			}
430			if (ifname == NULL)
431				sz = snprintf(buf, bufsize, "%s", abuf);
432			else
433				sz = snprintf(buf, bufsize, "%s%%%s", abuf, ifname);
434			break;
435		}
436	default:
437		snprintf(buf, bufsize, "unknown_af#%d", sa->sa_family);
438		plen = -1;
439	}
440
441	if (plen >= 0)
442		snprintf(buf + sz, bufsize - sz, "/%d", plen);
443}
444
445static int
446print_line_prefix(struct nlmsghdr *hdr, struct snl_msg_info *cinfo,
447    const char *cmd, const char *name)
448{
449	struct timespec tp;
450	struct tm tm;
451	char buf[32];
452
453	clock_gettime(CLOCK_REALTIME, &tp);
454	localtime_r(&tp.tv_sec, &tm);
455
456	strftime(buf, sizeof(buf), "%T", &tm);
457	int len = printf("%s.%03ld PID %4u %s %s ", buf, tp.tv_nsec / 1000000,
458	    cinfo->process_id, cmd, name);
459
460	return (len);
461}
462
463static const char *
464get_action_name(struct nlmsghdr *hdr, int new_cmd)
465{
466	if (hdr->nlmsg_type == new_cmd) {
467		//return ((hdr->nlmsg_flags & NLM_F_REPLACE) ? "replace" : "add");
468		return ("add/repl");
469	} else
470		return ("delete");
471}
472
473static void
474print_nlmsg_route_nhop(struct nl_helper *h, struct snl_parsed_route *r,
475    struct rta_mpath_nh *nh, bool first)
476{
477	// gw 10.0.0.1 ifp vtnet0 mtu 1500 table inet.0
478	if (nh->gw != NULL) {
479		char gwbuf[128];
480		print_prefix(h, gwbuf, sizeof(gwbuf), nh->gw, -1);
481		printf("gw %s ", gwbuf);
482	}
483
484	if (nh->ifindex != 0) {
485		struct snl_parsed_link_simple link = {};
486
487		get_ifdata(h, nh->ifindex, &link);
488		if (nh->rtax_mtu == 0)
489			nh->rtax_mtu = link.ifla_mtu;
490		printf("iface %s ", link.ifla_ifname);
491		if (nh->rtax_mtu != 0)
492			printf("mtu %d ", nh->rtax_mtu);
493	}
494
495	if (first) {
496		switch (r->rtm_family) {
497			case AF_INET:
498				printf("table inet.%d", r->rta_table);
499				break;
500			case AF_INET6:
501				printf("table inet6.%d", r->rta_table);
502				break;
503		}
504	}
505
506	printf("\n");
507}
508
509static void
510print_nlmsg_route(struct nl_helper *h, struct nlmsghdr *hdr,
511    struct snl_msg_info *cinfo)
512{
513	struct snl_parsed_route r = { .rtax_weight = RT_DEFAULT_WEIGHT };
514	struct snl_state *ss = &h->ss_cmd;
515
516	if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r))
517		return;
518
519	// 20:19:41.333 add route 10.0.0.0/24 gw 10.0.0.1 ifp vtnet0 mtu 1500 table inet.0
520
521	const char *cmd = get_action_name(hdr, RTM_NEWROUTE);
522	int len = print_line_prefix(hdr, cinfo, cmd, "route");
523
524	char buf[128];
525	print_prefix(h, buf, sizeof(buf), r.rta_dst, r.rtm_dst_len);
526	len += strlen(buf) + 1;
527	printf("%s ", buf);
528
529	switch (r.rtm_type) {
530	case RTN_BLACKHOLE:
531		printf("blackhole\n");
532		return;
533	case RTN_UNREACHABLE:
534		printf("unreach(reject)\n");
535		return;
536	case RTN_PROHIBIT:
537		printf("prohibit(reject)\n");
538		return;
539	}
540
541	if (r.rta_multipath.num_nhops != 0) {
542		bool first = true;
543
544		memset(buf, ' ', sizeof(buf));
545		buf[len] = '\0';
546
547		for (uint32_t i = 0; i < r.rta_multipath.num_nhops; i++) {
548			struct rta_mpath_nh *nh = r.rta_multipath.nhops[i];
549
550			if (!first)
551				printf("%s", buf);
552			print_nlmsg_route_nhop(h, &r, nh, first);
553			first = false;
554		}
555	} else {
556		struct rta_mpath_nh nh = {
557			.gw = r.rta_gw,
558			.ifindex = r.rta_oif,
559			.rtax_mtu = r.rtax_mtu,
560		};
561
562		print_nlmsg_route_nhop(h, &r, &nh, true);
563	}
564}
565
566static const char *operstate[] = {
567	"UNKNOWN",	/* 0, IF_OPER_UNKNOWN */
568	"NOTPRESENT",	/* 1, IF_OPER_NOTPRESENT */
569	"DOWN",		/* 2, IF_OPER_DOWN */
570	"LLDOWN",	/* 3, IF_OPER_LOWERLAYERDOWN */
571	"TESTING",	/* 4, IF_OPER_TESTING */
572	"DORMANT",	/* 5, IF_OPER_DORMANT */
573	"UP",		/* 6, IF_OPER_UP */
574};
575
576static void
577print_nlmsg_link(struct nl_helper *h, struct nlmsghdr *hdr,
578    struct snl_msg_info *cinfo)
579{
580	struct snl_parsed_link l = {};
581	struct snl_state *ss = &h->ss_cmd;
582
583	if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser, &l))
584		return;
585
586	// 20:19:41.333 add iface#3 vtnet0 admin UP oper UP mtu 1500 table inet.0
587	const char *cmd = get_action_name(hdr, RTM_NEWLINK);
588	print_line_prefix(hdr, cinfo, cmd, "iface");
589
590	printf("iface#%u %s ", l.ifi_index, l.ifla_ifname);
591	printf("admin %s ", (l.ifi_flags & IFF_UP) ? "UP" : "DOWN");
592	if (l.ifla_operstate < NL_ARRAY_LEN(operstate))
593		printf("oper %s ", operstate[l.ifla_operstate]);
594	if (l.ifla_mtu > 0)
595		printf("mtu %u ", l.ifla_mtu);
596
597	printf("\n");
598}
599
600static void
601print_nlmsg_addr(struct nl_helper *h, struct nlmsghdr *hdr,
602    struct snl_msg_info *cinfo)
603{
604	struct snl_parsed_addr attrs = {};
605	struct snl_state *ss = &h->ss_cmd;
606
607	if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_addr_parser, &attrs))
608		return;
609
610	// add addr 192.168.1.1/24 iface vtnet0
611	const char *cmd = get_action_name(hdr, RTM_NEWADDR);
612	print_line_prefix(hdr, cinfo, cmd, "addr");
613
614	char buf[128];
615	struct sockaddr *addr = attrs.ifa_local ? attrs.ifa_local : attrs.ifa_address;
616	print_prefix(h, buf, sizeof(buf), addr, attrs.ifa_prefixlen);
617	printf("%s ", buf);
618
619	struct snl_parsed_link_simple link = {};
620	get_ifdata(h, attrs.ifa_index, &link);
621
622	if (link.ifi_flags & IFF_POINTOPOINT) {
623		char buf[64];
624		print_prefix(h, buf, sizeof(buf), attrs.ifa_address, -1);
625		printf("-> %s ", buf);
626	}
627
628	printf("iface %s ", link.ifla_ifname);
629
630	printf("\n");
631}
632
633static const char *nudstate[] = {
634	"INCOMPLETE",		/* 0x01(0) */
635	"REACHABLE",		/* 0x02(1) */
636	"STALE",		/* 0x04(2) */
637	"DELAY",		/* 0x08(3) */
638	"PROBE",		/* 0x10(4) */
639	"FAILED",		/* 0x20(5) */
640};
641
642#define	NUD_INCOMPLETE		0x01	/* No lladdr, address resolution in progress */
643#define	NUD_REACHABLE		0x02	/* reachable & recently resolved */
644#define	NUD_STALE		0x04	/* has lladdr but it's stale */
645#define	NUD_DELAY		0x08	/* has lladdr, is stale, probes delayed */
646#define	NUD_PROBE		0x10	/* has lladdr, is stale, probes sent */
647#define	NUD_FAILED		0x20	/* unused */
648
649
650static void
651print_nlmsg_neigh(struct nl_helper *h, struct nlmsghdr *hdr,
652    struct snl_msg_info *cinfo)
653{
654	struct snl_parsed_neigh attrs = {};
655	struct snl_state *ss = &h->ss_cmd;
656
657	if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_neigh_parser, &attrs))
658		return;
659
660	// add addr 192.168.1.1 state %s lladdr %s iface vtnet0
661	const char *cmd = get_action_name(hdr, RTM_NEWNEIGH);
662	print_line_prefix(hdr, cinfo, cmd, "neigh");
663
664	char buf[128];
665	print_prefix(h, buf, sizeof(buf), attrs.nda_dst, -1);
666	printf("%s ", buf);
667
668	struct snl_parsed_link_simple link = {};
669	get_ifdata(h, attrs.nda_ifindex, &link);
670
671	for (unsigned int i = 0; i < NL_ARRAY_LEN(nudstate); i++) {
672		if ((1 << i) & attrs.ndm_state) {
673			printf("state %s ", nudstate[i]);
674			break;
675		}
676	}
677
678	if (attrs.nda_lladdr != NULL) {
679		int if_type = link.ifi_type;
680
681		if ((if_type == IFT_ETHER || if_type == IFT_L2VLAN || if_type == IFT_BRIDGE) &&
682		    NLA_DATA_LEN(attrs.nda_lladdr) == ETHER_ADDR_LEN) {
683			struct ether_addr *ll;
684
685			ll = (struct ether_addr *)NLA_DATA(attrs.nda_lladdr);
686			printf("lladdr %s ", ether_ntoa(ll));
687		} else {
688			struct sockaddr_dl sdl = {
689				.sdl_len = sizeof(sdl),
690				.sdl_family = AF_LINK,
691				.sdl_index = attrs.nda_ifindex,
692				.sdl_type = if_type,
693				.sdl_alen = NLA_DATA_LEN(attrs.nda_lladdr),
694			};
695			if (sdl.sdl_alen < sizeof(sdl.sdl_data)) {
696				void *ll = NLA_DATA(attrs.nda_lladdr);
697
698				memcpy(sdl.sdl_data, ll, sdl.sdl_alen);
699				printf("lladdr %s ", link_ntoa(&sdl));
700			}
701		}
702	}
703
704	if (link.ifla_ifname != NULL)
705		printf("iface %s ", link.ifla_ifname);
706	printf("\n");
707}
708
709static void
710print_nlmsg_generic(struct nl_helper *h, struct nlmsghdr *hdr, struct snl_msg_info *cinfo)
711{
712	const char *cmd = get_action_name(hdr, 0);
713	print_line_prefix(hdr, cinfo, cmd, "unknown message");
714	printf(" type %u\n", hdr->nlmsg_type);
715}
716
717static void
718print_nlmsg(struct nl_helper *h, struct nlmsghdr *hdr, struct snl_msg_info *cinfo)
719{
720	switch (hdr->nlmsg_type) {
721	case RTM_NEWLINK:
722	case RTM_DELLINK:
723		print_nlmsg_link(h, hdr, cinfo);
724		break;
725	case RTM_NEWADDR:
726	case RTM_DELADDR:
727		print_nlmsg_addr(h, hdr, cinfo);
728		break;
729	case RTM_NEWROUTE:
730	case RTM_DELROUTE:
731		print_nlmsg_route(h, hdr, cinfo);
732		break;
733	case RTM_NEWNEIGH:
734	case RTM_DELNEIGH:
735		print_nlmsg_neigh(h, hdr, cinfo);
736		break;
737	default:
738		print_nlmsg_generic(h, hdr, cinfo);
739	}
740
741	snl_clear_lb(&h->ss_cmd);
742}
743
744void
745monitor_nl(int fib)
746{
747	struct snl_state ss_event = {};
748	struct nl_helper h;
749
750	nl_init_socket(&ss_event);
751	nl_helper_init(&h);
752
753	int groups[] = {
754		RTNLGRP_LINK,
755		RTNLGRP_NEIGH,
756		RTNLGRP_NEXTHOP,
757#ifdef INET
758		RTNLGRP_IPV4_IFADDR,
759		RTNLGRP_IPV4_ROUTE,
760#endif
761#ifdef INET6
762		RTNLGRP_IPV6_IFADDR,
763		RTNLGRP_IPV6_ROUTE,
764#endif
765	};
766
767	int optval = 1;
768	socklen_t optlen = sizeof(optval);
769	setsockopt(ss_event.fd, SOL_NETLINK, NETLINK_MSG_INFO, &optval, optlen);
770
771	for (unsigned int i = 0; i < NL_ARRAY_LEN(groups); i++) {
772		int error;
773		int optval = groups[i];
774		socklen_t optlen = sizeof(optval);
775		error = setsockopt(ss_event.fd, SOL_NETLINK,
776		    NETLINK_ADD_MEMBERSHIP, &optval, optlen);
777		if (error != 0)
778			warn("Unable to subscribe to group %d", optval);
779	}
780
781	struct snl_msg_info attrs = {};
782	struct nlmsghdr *hdr;
783	while ((hdr = snl_read_message_dbg(&ss_event, &attrs)) != NULL)
784	{
785		print_nlmsg(&h, hdr, &attrs);
786		snl_clear_lb(&h.ss_cmd);
787		snl_clear_lb(&ss_event);
788	}
789
790	snl_free(&ss_event);
791	nl_helper_free(&h);
792	exit(0);
793}
794
795static void
796print_flushed_route(struct snl_parsed_route *r, struct sockaddr *gw)
797{
798	struct sockaddr *sa = r->rta_dst;
799
800	printf("%-20.20s ", r->rta_rtflags & RTF_HOST ?
801	    routename(sa) : netname(sa));
802	sa = gw;
803	printf("%-20.20s ", routename(sa));
804	printf("-fib %-3d ", r->rta_table);
805	printf("done\n");
806}
807
808static int
809flushroute_one(struct nl_helper *h, struct snl_parsed_route *r)
810{
811	struct snl_state *ss = &h->ss_cmd;
812	struct snl_errmsg_data e = {};
813	struct snl_writer nw;
814
815	snl_init_writer(ss, &nw);
816
817	struct nlmsghdr *hdr = snl_create_msg_request(&nw, NL_RTM_DELROUTE);
818	struct rtmsg *rtm = snl_reserve_msg_object(&nw, struct rtmsg);
819	rtm->rtm_family = r->rtm_family;
820	rtm->rtm_dst_len = r->rtm_dst_len;
821
822	snl_add_msg_attr_u32(&nw, RTA_TABLE, r->rta_table);
823	snl_add_msg_attr_ip(&nw, RTA_DST, r->rta_dst);
824
825	if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
826		return (ENOMEM);
827
828	if (!snl_read_reply_code(ss, hdr->nlmsg_seq, &e)) {
829		return (e.error);
830		if (e.error == EPERM)
831			errc(1, e.error, "RTM_DELROUTE failed:");
832		else
833			warnc(e.error, "RTM_DELROUTE failed:");
834		return (true);
835	};
836
837	if (verbose) {
838		struct snl_msg_info attrs = {};
839		print_nlmsg(h, hdr, &attrs);
840	}
841	else {
842		if (r->rta_multipath.num_nhops != 0) {
843			for (uint32_t i = 0; i < r->rta_multipath.num_nhops; i++) {
844				struct rta_mpath_nh *nh = r->rta_multipath.nhops[i];
845
846				print_flushed_route(r, nh->gw);
847			}
848
849		} else
850			print_flushed_route(r, r->rta_gw);
851	}
852
853	return (0);
854}
855
856int
857flushroutes_fib_nl(int fib, int af)
858{
859	struct snl_state ss = {};
860	struct snl_writer nw;
861	struct nl_helper h = {};
862
863	nl_init_socket(&ss);
864	snl_init_writer(&ss, &nw);
865
866	struct nlmsghdr *hdr = snl_create_msg_request(&nw, NL_RTM_GETROUTE);
867	hdr->nlmsg_flags |= NLM_F_DUMP;
868	struct rtmsg *rtm = snl_reserve_msg_object(&nw, struct rtmsg);
869	rtm->rtm_family = af;
870	snl_add_msg_attr_u32(&nw, RTA_TABLE, fib);
871
872	if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss, hdr)) {
873		snl_free(&ss);
874		return (EINVAL);
875	}
876
877	struct snl_errmsg_data e = {};
878	uint32_t nlm_seq = hdr->nlmsg_seq;
879
880	nl_helper_init(&h);
881
882	while ((hdr = snl_read_reply_multi(&ss, nlm_seq, &e)) != NULL) {
883		struct snl_parsed_route r = { .rtax_weight = RT_DEFAULT_WEIGHT };
884		int error;
885
886		if (!snl_parse_nlmsg(&ss, hdr, &snl_rtm_route_parser, &r))
887			continue;
888		if (verbose) {
889			struct snl_msg_info attrs = {};
890			print_nlmsg(&h, hdr, &attrs);
891		}
892		if (r.rta_table != (uint32_t)fib || r.rtm_family != af)
893			continue;
894		if ((r.rta_rtflags & RTF_GATEWAY) == 0)
895			continue;
896		if (debugonly)
897			continue;
898
899		if ((error = flushroute_one(&h, &r)) != 0) {
900			if (error == EPERM)
901				errc(1, error, "RTM_DELROUTE failed:");
902			else
903				warnc(error, "RTM_DELROUTE failed:");
904		}
905		snl_clear_lb(&h.ss_cmd);
906	}
907
908	snl_free(&ss);
909	nl_helper_free(&h);
910
911	return (e.error);
912}
913
914