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