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