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
36f262b06aSAlexander 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;
4230d7e724SAlexander V. Chernikov struct snl_msg_info;
4330d7e724SAlexander V. Chernikov static void print_getmsg(struct nl_helper *h, struct nlmsghdr *hdr,
4430d7e724SAlexander V. Chernikov struct sockaddr *dst);
4530d7e724SAlexander V. Chernikov static void print_nlmsg(struct nl_helper *h, struct nlmsghdr *hdr,
4630d7e724SAlexander V. Chernikov struct snl_msg_info *cinfo);
47c597432eSAlexander V. Chernikov
48c597432eSAlexander V. Chernikov #define s6_addr32 __u6_addr.__u6_addr32
49c597432eSAlexander V. Chernikov #define bitcount32(x) __bitcount32((uint32_t)(x))
50c597432eSAlexander V. Chernikov static int
inet6_get_plen(const struct in6_addr * addr)51c597432eSAlexander V. Chernikov inet6_get_plen(const struct in6_addr *addr)
52c597432eSAlexander V. Chernikov {
53c597432eSAlexander V. Chernikov
54c597432eSAlexander V. Chernikov return (bitcount32(addr->s6_addr32[0]) + bitcount32(addr->s6_addr32[1]) +
55c597432eSAlexander V. Chernikov bitcount32(addr->s6_addr32[2]) + bitcount32(addr->s6_addr32[3]));
56c597432eSAlexander V. Chernikov }
57c597432eSAlexander V. Chernikov
58c597432eSAlexander V. Chernikov static void
ip6_writemask(struct in6_addr * addr6,uint8_t mask)59c597432eSAlexander V. Chernikov ip6_writemask(struct in6_addr *addr6, uint8_t mask)
60c597432eSAlexander V. Chernikov {
61c597432eSAlexander V. Chernikov uint32_t *cp;
62c597432eSAlexander V. Chernikov
63c597432eSAlexander V. Chernikov for (cp = (uint32_t *)addr6; mask >= 32; mask -= 32)
64c597432eSAlexander V. Chernikov *cp++ = 0xFFFFFFFF;
65c597432eSAlexander V. Chernikov if (mask > 0)
66c597432eSAlexander V. Chernikov *cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0);
67c597432eSAlexander V. Chernikov }
68c597432eSAlexander V. Chernikov
69c597432eSAlexander V. Chernikov static struct sockaddr *
get_netmask(struct snl_state * ss,int family,int plen)70c597432eSAlexander V. Chernikov get_netmask(struct snl_state *ss, int family, int plen)
71c597432eSAlexander V. Chernikov {
72c597432eSAlexander V. Chernikov if (family == AF_INET) {
73c597432eSAlexander V. Chernikov if (plen == 32)
74c597432eSAlexander V. Chernikov return (NULL);
75c597432eSAlexander V. Chernikov
76c597432eSAlexander V. Chernikov struct sockaddr_in *sin = snl_allocz(ss, sizeof(*sin));
77c597432eSAlexander V. Chernikov
78c597432eSAlexander V. Chernikov sin->sin_len = sizeof(*sin);
79c597432eSAlexander V. Chernikov sin->sin_family = family;
80c597432eSAlexander V. Chernikov sin->sin_addr.s_addr = htonl(plen ? ~((1 << (32 - plen)) - 1) : 0);
81c597432eSAlexander V. Chernikov
82c597432eSAlexander V. Chernikov return (struct sockaddr *)sin;
83c597432eSAlexander V. Chernikov } else if (family == AF_INET6) {
84c597432eSAlexander V. Chernikov if (plen == 128)
85c597432eSAlexander V. Chernikov return (NULL);
86c597432eSAlexander V. Chernikov
87c597432eSAlexander V. Chernikov struct sockaddr_in6 *sin6 = snl_allocz(ss, sizeof(*sin6));
88c597432eSAlexander V. Chernikov
89c597432eSAlexander V. Chernikov sin6->sin6_len = sizeof(*sin6);
90c597432eSAlexander V. Chernikov sin6->sin6_family = family;
91c597432eSAlexander V. Chernikov ip6_writemask(&sin6->sin6_addr, plen);
92c597432eSAlexander V. Chernikov
93c597432eSAlexander V. Chernikov return (struct sockaddr *)sin6;
94c597432eSAlexander V. Chernikov }
95c597432eSAlexander V. Chernikov return (NULL);
96c597432eSAlexander V. Chernikov }
97c597432eSAlexander V. Chernikov
98140ddebaSAlexander V. Chernikov static void
nl_init_socket(struct snl_state * ss)99140ddebaSAlexander V. Chernikov nl_init_socket(struct snl_state *ss)
100140ddebaSAlexander V. Chernikov {
101140ddebaSAlexander V. Chernikov if (snl_init(ss, NETLINK_ROUTE))
102140ddebaSAlexander V. Chernikov return;
103140ddebaSAlexander V. Chernikov
104140ddebaSAlexander V. Chernikov if (modfind("netlink") == -1 && errno == ENOENT) {
105140ddebaSAlexander V. Chernikov /* Try to load */
106140ddebaSAlexander V. Chernikov if (kldload("netlink") == -1)
107140ddebaSAlexander V. Chernikov err(1, "netlink is not loaded and load attempt failed");
108140ddebaSAlexander V. Chernikov if (snl_init(ss, NETLINK_ROUTE))
109140ddebaSAlexander V. Chernikov return;
110140ddebaSAlexander V. Chernikov }
111140ddebaSAlexander V. Chernikov
112140ddebaSAlexander V. Chernikov err(1, "unable to open netlink socket");
113140ddebaSAlexander V. Chernikov }
114140ddebaSAlexander V. Chernikov
115c597432eSAlexander V. Chernikov struct nl_helper {
116c597432eSAlexander V. Chernikov struct snl_state ss_cmd;
117c597432eSAlexander V. Chernikov };
118c597432eSAlexander V. Chernikov
119c597432eSAlexander V. Chernikov static void
nl_helper_init(struct nl_helper * h)120c597432eSAlexander V. Chernikov nl_helper_init(struct nl_helper *h)
121c597432eSAlexander V. Chernikov {
122140ddebaSAlexander V. Chernikov nl_init_socket(&h->ss_cmd);
123c597432eSAlexander V. Chernikov }
124c597432eSAlexander V. Chernikov
125c597432eSAlexander V. Chernikov static void
nl_helper_free(struct nl_helper * h)126c597432eSAlexander V. Chernikov nl_helper_free(struct nl_helper *h)
127c597432eSAlexander V. Chernikov {
128c597432eSAlexander V. Chernikov snl_free(&h->ss_cmd);
129c597432eSAlexander V. Chernikov }
130c597432eSAlexander V. Chernikov
131f262b06aSAlexander V. Chernikov static struct sockaddr *
get_addr(struct sockaddr_storage * so,int rtm_addrs,int addr_type)132f262b06aSAlexander V. Chernikov get_addr(struct sockaddr_storage *so, int rtm_addrs, int addr_type)
133f262b06aSAlexander V. Chernikov {
134f262b06aSAlexander V. Chernikov struct sockaddr *sa = NULL;
135f262b06aSAlexander V. Chernikov
136f262b06aSAlexander V. Chernikov if (rtm_addrs & (1 << addr_type))
137f262b06aSAlexander V. Chernikov sa = (struct sockaddr *)&so[addr_type];
138f262b06aSAlexander V. Chernikov return (sa);
139f262b06aSAlexander V. Chernikov }
140f262b06aSAlexander V. Chernikov
141c597432eSAlexander V. Chernikov 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)142f262b06aSAlexander V. Chernikov rtmsg_nl_int(struct nl_helper *h, int cmd, int rtm_flags, int fib, int rtm_addrs,
143c597432eSAlexander V. Chernikov struct sockaddr_storage *so, struct rt_metrics *rt_metrics)
144c597432eSAlexander V. Chernikov {
145c597432eSAlexander V. Chernikov struct snl_state *ss = &h->ss_cmd;
146c597432eSAlexander V. Chernikov struct snl_writer nw;
147c597432eSAlexander V. Chernikov int nl_type = 0, nl_flags = 0;
148c597432eSAlexander V. Chernikov
149c597432eSAlexander V. Chernikov snl_init_writer(ss, &nw);
150c597432eSAlexander V. Chernikov
151c597432eSAlexander V. Chernikov switch (cmd) {
152c597432eSAlexander V. Chernikov case RTSOCK_RTM_ADD:
153c597432eSAlexander V. Chernikov nl_type = RTM_NEWROUTE;
154c597432eSAlexander V. Chernikov nl_flags = NLM_F_CREATE | NLM_F_APPEND; /* Do append by default */
155c597432eSAlexander V. Chernikov break;
156c597432eSAlexander V. Chernikov case RTSOCK_RTM_CHANGE:
157c597432eSAlexander V. Chernikov nl_type = RTM_NEWROUTE;
158c597432eSAlexander V. Chernikov nl_flags = NLM_F_REPLACE;
159c597432eSAlexander V. Chernikov break;
160c597432eSAlexander V. Chernikov case RTSOCK_RTM_DELETE:
161c597432eSAlexander V. Chernikov nl_type = RTM_DELROUTE;
162c597432eSAlexander V. Chernikov break;
163c597432eSAlexander V. Chernikov case RTSOCK_RTM_GET:
164c597432eSAlexander V. Chernikov nl_type = RTM_GETROUTE;
165c597432eSAlexander V. Chernikov break;
166c597432eSAlexander V. Chernikov default:
167c597432eSAlexander V. Chernikov exit(1);
168c597432eSAlexander V. Chernikov }
169c597432eSAlexander V. Chernikov
170f262b06aSAlexander V. Chernikov struct sockaddr *dst = get_addr(so, rtm_addrs, RTAX_DST);
171f262b06aSAlexander V. Chernikov struct sockaddr *mask = get_addr(so, rtm_addrs, RTAX_NETMASK);
172f262b06aSAlexander V. Chernikov struct sockaddr *gw = get_addr(so, rtm_addrs, RTAX_GATEWAY);
173c597432eSAlexander V. Chernikov
174c597432eSAlexander V. Chernikov if (dst == NULL)
175c597432eSAlexander V. Chernikov return (EINVAL);
176c597432eSAlexander V. Chernikov
177c597432eSAlexander V. Chernikov struct nlmsghdr *hdr = snl_create_msg_request(&nw, nl_type);
178c597432eSAlexander V. Chernikov hdr->nlmsg_flags |= nl_flags;
179c597432eSAlexander V. Chernikov
180c597432eSAlexander V. Chernikov int plen = 0;
181c597432eSAlexander V. Chernikov int rtm_type = RTN_UNICAST;
182c597432eSAlexander V. Chernikov
183c597432eSAlexander V. Chernikov switch (dst->sa_family) {
184c597432eSAlexander V. Chernikov case AF_INET:
185c597432eSAlexander V. Chernikov {
186c597432eSAlexander V. Chernikov struct sockaddr_in *mask4 = (struct sockaddr_in *)mask;
187c597432eSAlexander V. Chernikov
1883a151e31SAlexander V. Chernikov if ((rtm_flags & RTF_HOST) == 0 && mask4 != NULL)
1893a151e31SAlexander V. Chernikov plen = bitcount32(mask4->sin_addr.s_addr);
1903a151e31SAlexander V. Chernikov else
1913a151e31SAlexander V. Chernikov plen = 32;
192c597432eSAlexander V. Chernikov break;
193c597432eSAlexander V. Chernikov }
194c597432eSAlexander V. Chernikov case AF_INET6:
195c597432eSAlexander V. Chernikov {
196c597432eSAlexander V. Chernikov struct sockaddr_in6 *mask6 = (struct sockaddr_in6 *)mask;
197c597432eSAlexander V. Chernikov
1983a151e31SAlexander V. Chernikov if ((rtm_flags & RTF_HOST) == 0 && mask6 != NULL)
1993a151e31SAlexander V. Chernikov plen = inet6_get_plen(&mask6->sin6_addr);
2003a151e31SAlexander V. Chernikov else
2013a151e31SAlexander V. Chernikov plen = 128;
202c597432eSAlexander V. Chernikov break;
203c597432eSAlexander V. Chernikov }
204c597432eSAlexander V. Chernikov default:
205c597432eSAlexander V. Chernikov return (ENOTSUP);
206c597432eSAlexander V. Chernikov }
207c597432eSAlexander V. Chernikov
208c597432eSAlexander V. Chernikov if (rtm_flags & RTF_REJECT)
209c597432eSAlexander V. Chernikov rtm_type = RTN_PROHIBIT;
210c597432eSAlexander V. Chernikov else if (rtm_flags & RTF_BLACKHOLE)
211c597432eSAlexander V. Chernikov rtm_type = RTN_BLACKHOLE;
212c597432eSAlexander V. Chernikov
213c597432eSAlexander V. Chernikov struct rtmsg *rtm = snl_reserve_msg_object(&nw, struct rtmsg);
214c597432eSAlexander V. Chernikov rtm->rtm_family = dst->sa_family;
215c597432eSAlexander V. Chernikov rtm->rtm_protocol = RTPROT_STATIC;
216c597432eSAlexander V. Chernikov rtm->rtm_type = rtm_type;
217c597432eSAlexander V. Chernikov rtm->rtm_dst_len = plen;
218c597432eSAlexander V. Chernikov
21968f9e135SAlexander V. Chernikov /* Request exact prefix match if mask is set */
22068f9e135SAlexander V. Chernikov if ((cmd == RTSOCK_RTM_GET) && (mask != NULL))
22168f9e135SAlexander V. Chernikov rtm->rtm_flags = RTM_F_PREFIX;
22268f9e135SAlexander V. Chernikov
223c597432eSAlexander V. Chernikov snl_add_msg_attr_ip(&nw, RTA_DST, dst);
224c597432eSAlexander V. Chernikov snl_add_msg_attr_u32(&nw, RTA_TABLE, fib);
225c597432eSAlexander V. Chernikov
226ee8f5c2aSAlexander V. Chernikov uint32_t rta_oif = 0;
227ee8f5c2aSAlexander V. Chernikov
228f262b06aSAlexander V. Chernikov if (gw != NULL) {
229c597432eSAlexander V. Chernikov if (rtm_flags & RTF_GATEWAY) {
230c597432eSAlexander V. Chernikov if (gw->sa_family == dst->sa_family)
231c597432eSAlexander V. Chernikov snl_add_msg_attr_ip(&nw, RTA_GATEWAY, gw);
232c597432eSAlexander V. Chernikov else
233c597432eSAlexander V. Chernikov snl_add_msg_attr_ipvia(&nw, RTA_VIA, gw);
234ee8f5c2aSAlexander V. Chernikov if (gw->sa_family == AF_INET6) {
235ee8f5c2aSAlexander V. Chernikov struct sockaddr_in6 *gw6 = (struct sockaddr_in6 *)gw;
236ee8f5c2aSAlexander V. Chernikov
237ee8f5c2aSAlexander V. Chernikov if (IN6_IS_ADDR_LINKLOCAL(&gw6->sin6_addr))
238ee8f5c2aSAlexander V. Chernikov rta_oif = gw6->sin6_scope_id;
239ee8f5c2aSAlexander V. Chernikov }
240f262b06aSAlexander V. Chernikov } else {
241c597432eSAlexander V. Chernikov /* Should be AF_LINK */
242c597432eSAlexander V. Chernikov struct sockaddr_dl *sdl = (struct sockaddr_dl *)gw;
243c597432eSAlexander V. Chernikov if (sdl->sdl_index != 0)
244ee8f5c2aSAlexander V. Chernikov rta_oif = sdl->sdl_index;
245c597432eSAlexander V. Chernikov }
246f262b06aSAlexander V. Chernikov }
247c597432eSAlexander V. Chernikov
248ee8f5c2aSAlexander V. Chernikov if (dst->sa_family == AF_INET6 && rta_oif == 0) {
249ee8f5c2aSAlexander V. Chernikov struct sockaddr_in6 *dst6 = (struct sockaddr_in6 *)dst;
250ee8f5c2aSAlexander V. Chernikov
251ee8f5c2aSAlexander V. Chernikov if (IN6_IS_ADDR_LINKLOCAL(&dst6->sin6_addr))
252ee8f5c2aSAlexander V. Chernikov rta_oif = dst6->sin6_scope_id;
253ee8f5c2aSAlexander V. Chernikov }
254ee8f5c2aSAlexander V. Chernikov
255ee8f5c2aSAlexander V. Chernikov if (rta_oif != 0)
256ee8f5c2aSAlexander V. Chernikov snl_add_msg_attr_u32(&nw, RTA_OIF, rta_oif);
257c597432eSAlexander V. Chernikov if (rtm_flags != 0)
258c597432eSAlexander V. Chernikov snl_add_msg_attr_u32(&nw, NL_RTA_RTFLAGS, rtm_flags);
259c597432eSAlexander V. Chernikov
260c597432eSAlexander V. Chernikov if (rt_metrics->rmx_mtu > 0) {
261c597432eSAlexander V. Chernikov int off = snl_add_msg_attr_nested(&nw, RTA_METRICS);
262c597432eSAlexander V. Chernikov snl_add_msg_attr_u32(&nw, RTAX_MTU, rt_metrics->rmx_mtu);
263c597432eSAlexander V. Chernikov snl_end_attr_nested(&nw, off);
264c597432eSAlexander V. Chernikov }
265c597432eSAlexander V. Chernikov
266c597432eSAlexander V. Chernikov if (rt_metrics->rmx_weight > 0)
267c597432eSAlexander V. Chernikov snl_add_msg_attr_u32(&nw, NL_RTA_WEIGHT, rt_metrics->rmx_weight);
268c597432eSAlexander V. Chernikov
2694f8f43b0SKristof Provost if ((hdr = snl_finalize_msg(&nw)) && snl_send_message(ss, hdr)) {
270c597432eSAlexander V. Chernikov struct snl_errmsg_data e = {};
271c597432eSAlexander V. Chernikov
272c597432eSAlexander V. Chernikov hdr = snl_read_reply(ss, hdr->nlmsg_seq);
273c597432eSAlexander V. Chernikov if (nl_type == NL_RTM_GETROUTE) {
2742a78083fSR. Christian McDonald if (hdr->nlmsg_type == NL_RTM_NEWROUTE) {
275c597432eSAlexander V. Chernikov print_getmsg(h, hdr, dst);
276c597432eSAlexander V. Chernikov return (0);
277c597432eSAlexander V. Chernikov }
2782a78083fSR. Christian McDonald }
279c597432eSAlexander V. Chernikov
2802a78083fSR. Christian McDonald if (snl_parse_errmsg(ss, hdr, &e)) {
2812a78083fSR. Christian McDonald switch (e.error) {
2822a78083fSR. Christian McDonald case (ESRCH):
2832a78083fSR. Christian McDonald warnx("route has not been found");
2842a78083fSR. Christian McDonald break;
2852a78083fSR. Christian McDonald default:
2862a78083fSR. Christian McDonald if (e.error == 0)
2872a78083fSR. Christian McDonald break;
2882a78083fSR. Christian McDonald warnc(e.error, "message indicates error");
2892a78083fSR. Christian McDonald }
2902a78083fSR. Christian McDonald
291c597432eSAlexander V. Chernikov return (e.error);
292c597432eSAlexander V. Chernikov }
2932a78083fSR. Christian McDonald }
2942a78083fSR. Christian McDonald
295c597432eSAlexander V. Chernikov return (EINVAL);
296c597432eSAlexander V. Chernikov }
297c597432eSAlexander V. Chernikov
298c597432eSAlexander V. Chernikov int
rtmsg_nl(int cmd,int rtm_flags,int fib,int rtm_addrs,struct sockaddr_storage * so,struct rt_metrics * rt_metrics)299f262b06aSAlexander V. Chernikov rtmsg_nl(int cmd, int rtm_flags, int fib, int rtm_addrs,
300f262b06aSAlexander V. Chernikov struct sockaddr_storage *so, struct rt_metrics *rt_metrics)
301c597432eSAlexander V. Chernikov {
302c597432eSAlexander V. Chernikov struct nl_helper h = {};
303c597432eSAlexander V. Chernikov
304c597432eSAlexander V. Chernikov nl_helper_init(&h);
305f262b06aSAlexander V. Chernikov int error = rtmsg_nl_int(&h, cmd, rtm_flags, fib, rtm_addrs, so, rt_metrics);
306c597432eSAlexander V. Chernikov nl_helper_free(&h);
307c597432eSAlexander V. Chernikov
308c597432eSAlexander V. Chernikov return (error);
309c597432eSAlexander V. Chernikov }
310c597432eSAlexander V. Chernikov
311c597432eSAlexander V. Chernikov static void
get_ifdata(struct nl_helper * h,uint32_t ifindex,struct snl_parsed_link_simple * link)312c597432eSAlexander V. Chernikov get_ifdata(struct nl_helper *h, uint32_t ifindex, struct snl_parsed_link_simple *link)
313c597432eSAlexander V. Chernikov {
314c597432eSAlexander V. Chernikov struct snl_state *ss = &h->ss_cmd;
315c597432eSAlexander V. Chernikov struct snl_writer nw;
316c597432eSAlexander V. Chernikov
317c597432eSAlexander V. Chernikov snl_init_writer(ss, &nw);
318c597432eSAlexander V. Chernikov struct nlmsghdr *hdr = snl_create_msg_request(&nw, NL_RTM_GETLINK);
319c597432eSAlexander V. Chernikov struct ifinfomsg *ifmsg = snl_reserve_msg_object(&nw, struct ifinfomsg);
320c597432eSAlexander V. Chernikov if (ifmsg != NULL)
321c597432eSAlexander V. Chernikov ifmsg->ifi_index = ifindex;
3224f8f43b0SKristof Provost if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
323c597432eSAlexander V. Chernikov return;
324c597432eSAlexander V. Chernikov
325c597432eSAlexander V. Chernikov hdr = snl_read_reply(ss, hdr->nlmsg_seq);
326c597432eSAlexander V. Chernikov
327c597432eSAlexander V. Chernikov if (hdr != NULL && hdr->nlmsg_type == RTM_NEWLINK) {
328c597432eSAlexander V. Chernikov snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, link);
329c597432eSAlexander V. Chernikov }
330c597432eSAlexander V. Chernikov
331c597432eSAlexander V. Chernikov if (link->ifla_ifname == NULL) {
332c597432eSAlexander V. Chernikov char ifname[16];
333c597432eSAlexander V. Chernikov
334c597432eSAlexander V. Chernikov snprintf(ifname, sizeof(ifname), "if#%u", ifindex);
335c597432eSAlexander V. Chernikov int len = strlen(ifname);
336c597432eSAlexander V. Chernikov char *buf = snl_allocz(ss, len + 1);
337c597432eSAlexander V. Chernikov strlcpy(buf, ifname, len + 1);
338c597432eSAlexander V. Chernikov link->ifla_ifname = buf;
339c597432eSAlexander V. Chernikov }
340c597432eSAlexander V. Chernikov }
341c597432eSAlexander V. Chernikov
342c597432eSAlexander V. Chernikov static void
print_getmsg(struct nl_helper * h,struct nlmsghdr * hdr,struct sockaddr * dst)343c597432eSAlexander V. Chernikov print_getmsg(struct nl_helper *h, struct nlmsghdr *hdr, struct sockaddr *dst)
344c597432eSAlexander V. Chernikov {
345c597432eSAlexander V. Chernikov struct snl_state *ss = &h->ss_cmd;
346c597432eSAlexander V. Chernikov struct timespec ts;
347c597432eSAlexander V. Chernikov struct snl_parsed_route r = { .rtax_weight = RT_DEFAULT_WEIGHT };
348c597432eSAlexander V. Chernikov
349c597432eSAlexander V. Chernikov if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r))
350c597432eSAlexander V. Chernikov return;
351c597432eSAlexander V. Chernikov
352c597432eSAlexander V. Chernikov struct snl_parsed_link_simple link = {};
353c597432eSAlexander V. Chernikov get_ifdata(h, r.rta_oif, &link);
354c597432eSAlexander V. Chernikov
355c597432eSAlexander V. Chernikov if (r.rtax_mtu == 0)
356c597432eSAlexander V. Chernikov r.rtax_mtu = link.ifla_mtu;
357c597432eSAlexander V. Chernikov r.rta_rtflags |= (RTF_UP | RTF_DONE);
358c597432eSAlexander V. Chernikov
359c597432eSAlexander V. Chernikov (void)printf(" route to: %s\n", routename(dst));
360c597432eSAlexander V. Chernikov
361c597432eSAlexander V. Chernikov if (r.rta_dst)
362c597432eSAlexander V. Chernikov (void)printf("destination: %s\n", routename(r.rta_dst));
363c597432eSAlexander V. Chernikov struct sockaddr *mask = get_netmask(ss, r.rtm_family, r.rtm_dst_len);
364c597432eSAlexander V. Chernikov if (mask)
365c597432eSAlexander V. Chernikov (void)printf(" mask: %s\n", routename(mask));
366c597432eSAlexander V. Chernikov if (r.rta_gw && (r.rta_rtflags & RTF_GATEWAY))
367c597432eSAlexander V. Chernikov (void)printf(" gateway: %s\n", routename(r.rta_gw));
368c597432eSAlexander V. Chernikov (void)printf(" fib: %u\n", (unsigned int)r.rta_table);
369c597432eSAlexander V. Chernikov if (link.ifla_ifname)
370c597432eSAlexander V. Chernikov (void)printf(" interface: %s\n", link.ifla_ifname);
371c597432eSAlexander V. Chernikov (void)printf(" flags: ");
372c597432eSAlexander V. Chernikov printb(r.rta_rtflags, routeflags);
373c597432eSAlexander V. Chernikov
374c597432eSAlexander V. Chernikov struct rt_metrics rmx = {
375c597432eSAlexander V. Chernikov .rmx_mtu = r.rtax_mtu,
376c597432eSAlexander V. Chernikov .rmx_weight = r.rtax_weight,
377c597432eSAlexander V. Chernikov .rmx_expire = r.rta_expire,
378c597432eSAlexander V. Chernikov };
379c597432eSAlexander V. Chernikov
380c597432eSAlexander V. Chernikov printf("\n%9s %9s %9s %9s %9s %10s %9s\n", "recvpipe",
381c597432eSAlexander V. Chernikov "sendpipe", "ssthresh", "rtt,msec", "mtu ", "weight", "expire");
382c597432eSAlexander V. Chernikov printf("%8lu ", rmx.rmx_recvpipe);
383c597432eSAlexander V. Chernikov printf("%8lu ", rmx.rmx_sendpipe);
384c597432eSAlexander V. Chernikov printf("%8lu ", rmx.rmx_ssthresh);
385c597432eSAlexander V. Chernikov printf("%8lu ", 0UL);
386c597432eSAlexander V. Chernikov printf("%8lu ", rmx.rmx_mtu);
387c597432eSAlexander V. Chernikov printf("%8lu ", rmx.rmx_weight);
388c597432eSAlexander V. Chernikov if (rmx.rmx_expire > 0)
389c597432eSAlexander V. Chernikov clock_gettime(CLOCK_REALTIME_FAST, &ts);
390c597432eSAlexander V. Chernikov else
391c597432eSAlexander V. Chernikov ts.tv_sec = 0;
392c597432eSAlexander V. Chernikov printf("%8ld \n", (long)(rmx.rmx_expire - ts.tv_sec));
393c597432eSAlexander V. Chernikov }
394c597432eSAlexander V. Chernikov
395c597432eSAlexander V. Chernikov static void
print_prefix(struct nl_helper * h,char * buf,int bufsize,struct sockaddr * sa,int plen)396c597432eSAlexander V. Chernikov print_prefix(struct nl_helper *h, char *buf, int bufsize, struct sockaddr *sa, int plen)
397c597432eSAlexander V. Chernikov {
398c597432eSAlexander V. Chernikov int sz = 0;
399c597432eSAlexander V. Chernikov
400c597432eSAlexander V. Chernikov if (sa == NULL) {
401c597432eSAlexander V. Chernikov snprintf(buf, bufsize, "<NULL>");
402c597432eSAlexander V. Chernikov return;
403c597432eSAlexander V. Chernikov }
404c597432eSAlexander V. Chernikov
405c597432eSAlexander V. Chernikov switch (sa->sa_family) {
406c597432eSAlexander V. Chernikov case AF_INET:
407c597432eSAlexander V. Chernikov {
408c597432eSAlexander V. Chernikov struct sockaddr_in *sin = (struct sockaddr_in *)sa;
409c597432eSAlexander V. Chernikov char abuf[INET_ADDRSTRLEN];
410c597432eSAlexander V. Chernikov
411c597432eSAlexander V. Chernikov inet_ntop(AF_INET, &sin->sin_addr, abuf, sizeof(abuf));
412c597432eSAlexander V. Chernikov sz = snprintf(buf, bufsize, "%s", abuf);
413c597432eSAlexander V. Chernikov break;
414c597432eSAlexander V. Chernikov }
415c597432eSAlexander V. Chernikov case AF_INET6:
416c597432eSAlexander V. Chernikov {
417c597432eSAlexander V. Chernikov struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
418c597432eSAlexander V. Chernikov char abuf[INET6_ADDRSTRLEN];
419c597432eSAlexander V. Chernikov char *ifname = NULL;
420c597432eSAlexander V. Chernikov
421c597432eSAlexander V. Chernikov inet_ntop(AF_INET6, &sin6->sin6_addr, abuf, sizeof(abuf));
422c597432eSAlexander V. Chernikov if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
423c597432eSAlexander V. Chernikov struct snl_parsed_link_simple link = {};
424c597432eSAlexander V. Chernikov
425c597432eSAlexander V. Chernikov if (sin6->sin6_scope_id != 0) {
426c597432eSAlexander V. Chernikov get_ifdata(h, sin6->sin6_scope_id, &link);
427c597432eSAlexander V. Chernikov ifname = link.ifla_ifname;
428c597432eSAlexander V. Chernikov }
429c597432eSAlexander V. Chernikov }
430c597432eSAlexander V. Chernikov if (ifname == NULL)
431c597432eSAlexander V. Chernikov sz = snprintf(buf, bufsize, "%s", abuf);
432c597432eSAlexander V. Chernikov else
433c597432eSAlexander V. Chernikov sz = snprintf(buf, bufsize, "%s%%%s", abuf, ifname);
434c597432eSAlexander V. Chernikov break;
435c597432eSAlexander V. Chernikov }
436c597432eSAlexander V. Chernikov default:
437c597432eSAlexander V. Chernikov snprintf(buf, bufsize, "unknown_af#%d", sa->sa_family);
438c597432eSAlexander V. Chernikov plen = -1;
439c597432eSAlexander V. Chernikov }
440c597432eSAlexander V. Chernikov
441c597432eSAlexander V. Chernikov if (plen >= 0)
442c597432eSAlexander V. Chernikov snprintf(buf + sz, bufsize - sz, "/%d", plen);
443c597432eSAlexander V. Chernikov }
444c597432eSAlexander V. Chernikov
445c597432eSAlexander V. Chernikov static int
print_line_prefix(struct nlmsghdr * hdr,struct snl_msg_info * cinfo,const char * cmd,const char * name)44630d7e724SAlexander V. Chernikov print_line_prefix(struct nlmsghdr *hdr, struct snl_msg_info *cinfo,
44730d7e724SAlexander V. Chernikov const char *cmd, const char *name)
448c597432eSAlexander V. Chernikov {
449c597432eSAlexander V. Chernikov struct timespec tp;
450c597432eSAlexander V. Chernikov struct tm tm;
451c597432eSAlexander V. Chernikov char buf[32];
452c597432eSAlexander V. Chernikov
453c597432eSAlexander V. Chernikov clock_gettime(CLOCK_REALTIME, &tp);
454c597432eSAlexander V. Chernikov localtime_r(&tp.tv_sec, &tm);
455c597432eSAlexander V. Chernikov
456c597432eSAlexander V. Chernikov strftime(buf, sizeof(buf), "%T", &tm);
45730d7e724SAlexander V. Chernikov int len = printf("%s.%03ld PID %4u %s %s ", buf, tp.tv_nsec / 1000000,
45830d7e724SAlexander V. Chernikov cinfo->process_id, cmd, name);
459c597432eSAlexander V. Chernikov
460c597432eSAlexander V. Chernikov return (len);
461c597432eSAlexander V. Chernikov }
462c597432eSAlexander V. Chernikov
463c597432eSAlexander V. Chernikov static const char *
get_action_name(struct nlmsghdr * hdr,int new_cmd)464c597432eSAlexander V. Chernikov get_action_name(struct nlmsghdr *hdr, int new_cmd)
465c597432eSAlexander V. Chernikov {
466c597432eSAlexander V. Chernikov if (hdr->nlmsg_type == new_cmd) {
467c597432eSAlexander V. Chernikov //return ((hdr->nlmsg_flags & NLM_F_REPLACE) ? "replace" : "add");
468c597432eSAlexander V. Chernikov return ("add/repl");
469c597432eSAlexander V. Chernikov } else
470c597432eSAlexander V. Chernikov return ("delete");
471c597432eSAlexander V. Chernikov }
472c597432eSAlexander V. Chernikov
473c597432eSAlexander V. Chernikov static void
print_nlmsg_route_nhop(struct nl_helper * h,struct snl_parsed_route * r,struct rta_mpath_nh * nh,bool first)474c597432eSAlexander V. Chernikov print_nlmsg_route_nhop(struct nl_helper *h, struct snl_parsed_route *r,
475c597432eSAlexander V. Chernikov struct rta_mpath_nh *nh, bool first)
476c597432eSAlexander V. Chernikov {
477c597432eSAlexander V. Chernikov // gw 10.0.0.1 ifp vtnet0 mtu 1500 table inet.0
478c597432eSAlexander V. Chernikov if (nh->gw != NULL) {
479c597432eSAlexander V. Chernikov char gwbuf[128];
480c597432eSAlexander V. Chernikov print_prefix(h, gwbuf, sizeof(gwbuf), nh->gw, -1);
481c597432eSAlexander V. Chernikov printf("gw %s ", gwbuf);
482c597432eSAlexander V. Chernikov }
483c597432eSAlexander V. Chernikov
484c597432eSAlexander V. Chernikov if (nh->ifindex != 0) {
485c597432eSAlexander V. Chernikov struct snl_parsed_link_simple link = {};
486c597432eSAlexander V. Chernikov
487c597432eSAlexander V. Chernikov get_ifdata(h, nh->ifindex, &link);
488c597432eSAlexander V. Chernikov if (nh->rtax_mtu == 0)
489c597432eSAlexander V. Chernikov nh->rtax_mtu = link.ifla_mtu;
490c597432eSAlexander V. Chernikov printf("iface %s ", link.ifla_ifname);
491c597432eSAlexander V. Chernikov if (nh->rtax_mtu != 0)
492c597432eSAlexander V. Chernikov printf("mtu %d ", nh->rtax_mtu);
493c597432eSAlexander V. Chernikov }
494c597432eSAlexander V. Chernikov
495c597432eSAlexander V. Chernikov if (first) {
496c597432eSAlexander V. Chernikov switch (r->rtm_family) {
497c597432eSAlexander V. Chernikov case AF_INET:
498c597432eSAlexander V. Chernikov printf("table inet.%d", r->rta_table);
499c597432eSAlexander V. Chernikov break;
500c597432eSAlexander V. Chernikov case AF_INET6:
501c597432eSAlexander V. Chernikov printf("table inet6.%d", r->rta_table);
502c597432eSAlexander V. Chernikov break;
503c597432eSAlexander V. Chernikov }
504c597432eSAlexander V. Chernikov }
505c597432eSAlexander V. Chernikov
506c597432eSAlexander V. Chernikov printf("\n");
507c597432eSAlexander V. Chernikov }
508c597432eSAlexander V. Chernikov
509c597432eSAlexander V. Chernikov static void
print_nlmsg_route(struct nl_helper * h,struct nlmsghdr * hdr,struct snl_msg_info * cinfo)51030d7e724SAlexander V. Chernikov print_nlmsg_route(struct nl_helper *h, struct nlmsghdr *hdr,
51130d7e724SAlexander V. Chernikov struct snl_msg_info *cinfo)
512c597432eSAlexander V. Chernikov {
513c597432eSAlexander V. Chernikov struct snl_parsed_route r = { .rtax_weight = RT_DEFAULT_WEIGHT };
514c597432eSAlexander V. Chernikov struct snl_state *ss = &h->ss_cmd;
515c597432eSAlexander V. Chernikov
516c597432eSAlexander V. Chernikov if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r))
517c597432eSAlexander V. Chernikov return;
518c597432eSAlexander V. Chernikov
519c597432eSAlexander 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
520c597432eSAlexander V. Chernikov
521c597432eSAlexander V. Chernikov const char *cmd = get_action_name(hdr, RTM_NEWROUTE);
52230d7e724SAlexander V. Chernikov int len = print_line_prefix(hdr, cinfo, cmd, "route");
523c597432eSAlexander V. Chernikov
524c597432eSAlexander V. Chernikov char buf[128];
525c597432eSAlexander V. Chernikov print_prefix(h, buf, sizeof(buf), r.rta_dst, r.rtm_dst_len);
526c597432eSAlexander V. Chernikov len += strlen(buf) + 1;
527c597432eSAlexander V. Chernikov printf("%s ", buf);
528c597432eSAlexander V. Chernikov
529c597432eSAlexander V. Chernikov switch (r.rtm_type) {
530c597432eSAlexander V. Chernikov case RTN_BLACKHOLE:
531c597432eSAlexander V. Chernikov printf("blackhole\n");
532c597432eSAlexander V. Chernikov return;
533c597432eSAlexander V. Chernikov case RTN_UNREACHABLE:
534c597432eSAlexander V. Chernikov printf("unreach(reject)\n");
535c597432eSAlexander V. Chernikov return;
536c597432eSAlexander V. Chernikov case RTN_PROHIBIT:
537c597432eSAlexander V. Chernikov printf("prohibit(reject)\n");
538c597432eSAlexander V. Chernikov return;
539c597432eSAlexander V. Chernikov }
540c597432eSAlexander V. Chernikov
541656a39c1SAlexander V. Chernikov if (r.rta_multipath.num_nhops != 0) {
542c597432eSAlexander V. Chernikov bool first = true;
543c597432eSAlexander V. Chernikov
544c597432eSAlexander V. Chernikov memset(buf, ' ', sizeof(buf));
545c597432eSAlexander V. Chernikov buf[len] = '\0';
546c597432eSAlexander V. Chernikov
547656a39c1SAlexander V. Chernikov for (uint32_t i = 0; i < r.rta_multipath.num_nhops; i++) {
548656a39c1SAlexander V. Chernikov struct rta_mpath_nh *nh = r.rta_multipath.nhops[i];
549c597432eSAlexander V. Chernikov
550c597432eSAlexander V. Chernikov if (!first)
551c597432eSAlexander V. Chernikov printf("%s", buf);
552c597432eSAlexander V. Chernikov print_nlmsg_route_nhop(h, &r, nh, first);
553c597432eSAlexander V. Chernikov first = false;
554c597432eSAlexander V. Chernikov }
555c597432eSAlexander V. Chernikov } else {
556c597432eSAlexander V. Chernikov struct rta_mpath_nh nh = {
557c597432eSAlexander V. Chernikov .gw = r.rta_gw,
558c597432eSAlexander V. Chernikov .ifindex = r.rta_oif,
559c597432eSAlexander V. Chernikov .rtax_mtu = r.rtax_mtu,
560c597432eSAlexander V. Chernikov };
561c597432eSAlexander V. Chernikov
562c597432eSAlexander V. Chernikov print_nlmsg_route_nhop(h, &r, &nh, true);
563c597432eSAlexander V. Chernikov }
564c597432eSAlexander V. Chernikov }
565c597432eSAlexander V. Chernikov
566c597432eSAlexander V. Chernikov static const char *operstate[] = {
567c597432eSAlexander V. Chernikov "UNKNOWN", /* 0, IF_OPER_UNKNOWN */
568c597432eSAlexander V. Chernikov "NOTPRESENT", /* 1, IF_OPER_NOTPRESENT */
569c597432eSAlexander V. Chernikov "DOWN", /* 2, IF_OPER_DOWN */
570c597432eSAlexander V. Chernikov "LLDOWN", /* 3, IF_OPER_LOWERLAYERDOWN */
571c597432eSAlexander V. Chernikov "TESTING", /* 4, IF_OPER_TESTING */
572c597432eSAlexander V. Chernikov "DORMANT", /* 5, IF_OPER_DORMANT */
573c597432eSAlexander V. Chernikov "UP", /* 6, IF_OPER_UP */
574c597432eSAlexander V. Chernikov };
575c597432eSAlexander V. Chernikov
576c597432eSAlexander V. Chernikov static void
print_nlmsg_link(struct nl_helper * h,struct nlmsghdr * hdr,struct snl_msg_info * cinfo)57730d7e724SAlexander V. Chernikov print_nlmsg_link(struct nl_helper *h, struct nlmsghdr *hdr,
57830d7e724SAlexander V. Chernikov struct snl_msg_info *cinfo)
579c597432eSAlexander V. Chernikov {
580c597432eSAlexander V. Chernikov struct snl_parsed_link l = {};
581c597432eSAlexander V. Chernikov struct snl_state *ss = &h->ss_cmd;
582c597432eSAlexander V. Chernikov
583c597432eSAlexander V. Chernikov if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser, &l))
584c597432eSAlexander V. Chernikov return;
585c597432eSAlexander V. Chernikov
586c597432eSAlexander V. Chernikov // 20:19:41.333 add iface#3 vtnet0 admin UP oper UP mtu 1500 table inet.0
587c597432eSAlexander V. Chernikov const char *cmd = get_action_name(hdr, RTM_NEWLINK);
58830d7e724SAlexander V. Chernikov print_line_prefix(hdr, cinfo, cmd, "iface");
589c597432eSAlexander V. Chernikov
590c597432eSAlexander V. Chernikov printf("iface#%u %s ", l.ifi_index, l.ifla_ifname);
591c597432eSAlexander V. Chernikov printf("admin %s ", (l.ifi_flags & IFF_UP) ? "UP" : "DOWN");
592*29f61502SGleb Smirnoff if (l.ifla_operstate < nitems(operstate))
593c597432eSAlexander V. Chernikov printf("oper %s ", operstate[l.ifla_operstate]);
594c597432eSAlexander V. Chernikov if (l.ifla_mtu > 0)
595c597432eSAlexander V. Chernikov printf("mtu %u ", l.ifla_mtu);
596c597432eSAlexander V. Chernikov
597c597432eSAlexander V. Chernikov printf("\n");
598c597432eSAlexander V. Chernikov }
599c597432eSAlexander V. Chernikov
600c597432eSAlexander V. Chernikov static void
print_nlmsg_addr(struct nl_helper * h,struct nlmsghdr * hdr,struct snl_msg_info * cinfo)60130d7e724SAlexander V. Chernikov print_nlmsg_addr(struct nl_helper *h, struct nlmsghdr *hdr,
60230d7e724SAlexander V. Chernikov struct snl_msg_info *cinfo)
603c597432eSAlexander V. Chernikov {
604c597432eSAlexander V. Chernikov struct snl_parsed_addr attrs = {};
605c597432eSAlexander V. Chernikov struct snl_state *ss = &h->ss_cmd;
606c597432eSAlexander V. Chernikov
607c597432eSAlexander V. Chernikov if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_addr_parser, &attrs))
608c597432eSAlexander V. Chernikov return;
609c597432eSAlexander V. Chernikov
610c597432eSAlexander V. Chernikov // add addr 192.168.1.1/24 iface vtnet0
611c597432eSAlexander V. Chernikov const char *cmd = get_action_name(hdr, RTM_NEWADDR);
61230d7e724SAlexander V. Chernikov print_line_prefix(hdr, cinfo, cmd, "addr");
613c597432eSAlexander V. Chernikov
614c597432eSAlexander V. Chernikov char buf[128];
615c597432eSAlexander V. Chernikov struct sockaddr *addr = attrs.ifa_local ? attrs.ifa_local : attrs.ifa_address;
616c597432eSAlexander V. Chernikov print_prefix(h, buf, sizeof(buf), addr, attrs.ifa_prefixlen);
617c597432eSAlexander V. Chernikov printf("%s ", buf);
618c597432eSAlexander V. Chernikov
619c597432eSAlexander V. Chernikov struct snl_parsed_link_simple link = {};
620c597432eSAlexander V. Chernikov get_ifdata(h, attrs.ifa_index, &link);
621c597432eSAlexander V. Chernikov
622c597432eSAlexander V. Chernikov if (link.ifi_flags & IFF_POINTOPOINT) {
623c597432eSAlexander V. Chernikov char buf[64];
624c597432eSAlexander V. Chernikov print_prefix(h, buf, sizeof(buf), attrs.ifa_address, -1);
625c597432eSAlexander V. Chernikov printf("-> %s ", buf);
626c597432eSAlexander V. Chernikov }
627c597432eSAlexander V. Chernikov
628c597432eSAlexander V. Chernikov printf("iface %s ", link.ifla_ifname);
629c597432eSAlexander V. Chernikov
630c597432eSAlexander V. Chernikov printf("\n");
631c597432eSAlexander V. Chernikov }
632c597432eSAlexander V. Chernikov
633c597432eSAlexander V. Chernikov static const char *nudstate[] = {
634c597432eSAlexander V. Chernikov "INCOMPLETE", /* 0x01(0) */
635c597432eSAlexander V. Chernikov "REACHABLE", /* 0x02(1) */
636c597432eSAlexander V. Chernikov "STALE", /* 0x04(2) */
637c597432eSAlexander V. Chernikov "DELAY", /* 0x08(3) */
638c597432eSAlexander V. Chernikov "PROBE", /* 0x10(4) */
639c597432eSAlexander V. Chernikov "FAILED", /* 0x20(5) */
640c597432eSAlexander V. Chernikov };
641c597432eSAlexander V. Chernikov
642c597432eSAlexander V. Chernikov #define NUD_INCOMPLETE 0x01 /* No lladdr, address resolution in progress */
643c597432eSAlexander V. Chernikov #define NUD_REACHABLE 0x02 /* reachable & recently resolved */
644c597432eSAlexander V. Chernikov #define NUD_STALE 0x04 /* has lladdr but it's stale */
645c597432eSAlexander V. Chernikov #define NUD_DELAY 0x08 /* has lladdr, is stale, probes delayed */
646c597432eSAlexander V. Chernikov #define NUD_PROBE 0x10 /* has lladdr, is stale, probes sent */
647c597432eSAlexander V. Chernikov #define NUD_FAILED 0x20 /* unused */
648c597432eSAlexander V. Chernikov
649c597432eSAlexander V. Chernikov
650c597432eSAlexander V. Chernikov static void
print_nlmsg_neigh(struct nl_helper * h,struct nlmsghdr * hdr,struct snl_msg_info * cinfo)65130d7e724SAlexander V. Chernikov print_nlmsg_neigh(struct nl_helper *h, struct nlmsghdr *hdr,
65230d7e724SAlexander V. Chernikov struct snl_msg_info *cinfo)
653c597432eSAlexander V. Chernikov {
654c597432eSAlexander V. Chernikov struct snl_parsed_neigh attrs = {};
655c597432eSAlexander V. Chernikov struct snl_state *ss = &h->ss_cmd;
656c597432eSAlexander V. Chernikov
657c597432eSAlexander V. Chernikov if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_neigh_parser, &attrs))
658c597432eSAlexander V. Chernikov return;
659c597432eSAlexander V. Chernikov
660c597432eSAlexander V. Chernikov // add addr 192.168.1.1 state %s lladdr %s iface vtnet0
661c597432eSAlexander V. Chernikov const char *cmd = get_action_name(hdr, RTM_NEWNEIGH);
66230d7e724SAlexander V. Chernikov print_line_prefix(hdr, cinfo, cmd, "neigh");
663c597432eSAlexander V. Chernikov
664c597432eSAlexander V. Chernikov char buf[128];
665c597432eSAlexander V. Chernikov print_prefix(h, buf, sizeof(buf), attrs.nda_dst, -1);
666c597432eSAlexander V. Chernikov printf("%s ", buf);
667c597432eSAlexander V. Chernikov
668c597432eSAlexander V. Chernikov struct snl_parsed_link_simple link = {};
669c597432eSAlexander V. Chernikov get_ifdata(h, attrs.nda_ifindex, &link);
670c597432eSAlexander V. Chernikov
671*29f61502SGleb Smirnoff for (unsigned int i = 0; i < nitems(nudstate); i++) {
672c597432eSAlexander V. Chernikov if ((1 << i) & attrs.ndm_state) {
673c597432eSAlexander V. Chernikov printf("state %s ", nudstate[i]);
674c597432eSAlexander V. Chernikov break;
675c597432eSAlexander V. Chernikov }
676c597432eSAlexander V. Chernikov }
677c597432eSAlexander V. Chernikov
678c597432eSAlexander V. Chernikov if (attrs.nda_lladdr != NULL) {
679c597432eSAlexander V. Chernikov int if_type = link.ifi_type;
680c597432eSAlexander V. Chernikov
681c597432eSAlexander V. Chernikov if ((if_type == IFT_ETHER || if_type == IFT_L2VLAN || if_type == IFT_BRIDGE) &&
682c597432eSAlexander V. Chernikov NLA_DATA_LEN(attrs.nda_lladdr) == ETHER_ADDR_LEN) {
683c597432eSAlexander V. Chernikov struct ether_addr *ll;
684c597432eSAlexander V. Chernikov
685c597432eSAlexander V. Chernikov ll = (struct ether_addr *)NLA_DATA(attrs.nda_lladdr);
686c597432eSAlexander V. Chernikov printf("lladdr %s ", ether_ntoa(ll));
687c597432eSAlexander V. Chernikov } else {
688c597432eSAlexander V. Chernikov struct sockaddr_dl sdl = {
689c597432eSAlexander V. Chernikov .sdl_len = sizeof(sdl),
690c597432eSAlexander V. Chernikov .sdl_family = AF_LINK,
691c597432eSAlexander V. Chernikov .sdl_index = attrs.nda_ifindex,
692c597432eSAlexander V. Chernikov .sdl_type = if_type,
693c597432eSAlexander V. Chernikov .sdl_alen = NLA_DATA_LEN(attrs.nda_lladdr),
694c597432eSAlexander V. Chernikov };
695c597432eSAlexander V. Chernikov if (sdl.sdl_alen < sizeof(sdl.sdl_data)) {
696c597432eSAlexander V. Chernikov void *ll = NLA_DATA(attrs.nda_lladdr);
697c597432eSAlexander V. Chernikov
698c597432eSAlexander V. Chernikov memcpy(sdl.sdl_data, ll, sdl.sdl_alen);
699c597432eSAlexander V. Chernikov printf("lladdr %s ", link_ntoa(&sdl));
700c597432eSAlexander V. Chernikov }
701c597432eSAlexander V. Chernikov }
702c597432eSAlexander V. Chernikov }
703c597432eSAlexander V. Chernikov
704c597432eSAlexander V. Chernikov if (link.ifla_ifname != NULL)
705c597432eSAlexander V. Chernikov printf("iface %s ", link.ifla_ifname);
706c597432eSAlexander V. Chernikov printf("\n");
707c597432eSAlexander V. Chernikov }
708c597432eSAlexander V. Chernikov
709c597432eSAlexander V. Chernikov static void
print_nlmsg_generic(struct nl_helper * h,struct nlmsghdr * hdr,struct snl_msg_info * cinfo)71030d7e724SAlexander V. Chernikov print_nlmsg_generic(struct nl_helper *h, struct nlmsghdr *hdr, struct snl_msg_info *cinfo)
711c597432eSAlexander V. Chernikov {
71230d7e724SAlexander V. Chernikov const char *cmd = get_action_name(hdr, 0);
71330d7e724SAlexander V. Chernikov print_line_prefix(hdr, cinfo, cmd, "unknown message");
71430d7e724SAlexander V. Chernikov printf(" type %u\n", hdr->nlmsg_type);
715c597432eSAlexander V. Chernikov }
716c597432eSAlexander V. Chernikov
717c597432eSAlexander V. Chernikov static void
print_nlmsg(struct nl_helper * h,struct nlmsghdr * hdr,struct snl_msg_info * cinfo)71830d7e724SAlexander V. Chernikov print_nlmsg(struct nl_helper *h, struct nlmsghdr *hdr, struct snl_msg_info *cinfo)
719c597432eSAlexander V. Chernikov {
720c597432eSAlexander V. Chernikov switch (hdr->nlmsg_type) {
721c597432eSAlexander V. Chernikov case RTM_NEWLINK:
722c597432eSAlexander V. Chernikov case RTM_DELLINK:
72330d7e724SAlexander V. Chernikov print_nlmsg_link(h, hdr, cinfo);
724c597432eSAlexander V. Chernikov break;
725c597432eSAlexander V. Chernikov case RTM_NEWADDR:
726c597432eSAlexander V. Chernikov case RTM_DELADDR:
72730d7e724SAlexander V. Chernikov print_nlmsg_addr(h, hdr, cinfo);
728c597432eSAlexander V. Chernikov break;
729c597432eSAlexander V. Chernikov case RTM_NEWROUTE:
730c597432eSAlexander V. Chernikov case RTM_DELROUTE:
73130d7e724SAlexander V. Chernikov print_nlmsg_route(h, hdr, cinfo);
732c597432eSAlexander V. Chernikov break;
733c597432eSAlexander V. Chernikov case RTM_NEWNEIGH:
734c597432eSAlexander V. Chernikov case RTM_DELNEIGH:
73530d7e724SAlexander V. Chernikov print_nlmsg_neigh(h, hdr, cinfo);
736c597432eSAlexander V. Chernikov break;
737c597432eSAlexander V. Chernikov default:
73830d7e724SAlexander V. Chernikov print_nlmsg_generic(h, hdr, cinfo);
739c597432eSAlexander V. Chernikov }
740c597432eSAlexander V. Chernikov
741c597432eSAlexander V. Chernikov snl_clear_lb(&h->ss_cmd);
742c597432eSAlexander V. Chernikov }
743c597432eSAlexander V. Chernikov
744c597432eSAlexander V. Chernikov void
monitor_nl(int fib)745c597432eSAlexander V. Chernikov monitor_nl(int fib)
746c597432eSAlexander V. Chernikov {
747c597432eSAlexander V. Chernikov struct snl_state ss_event = {};
748c597432eSAlexander V. Chernikov struct nl_helper h;
749c597432eSAlexander V. Chernikov
750140ddebaSAlexander V. Chernikov nl_init_socket(&ss_event);
751c597432eSAlexander V. Chernikov nl_helper_init(&h);
752c597432eSAlexander V. Chernikov
753c597432eSAlexander V. Chernikov int groups[] = {
754c597432eSAlexander V. Chernikov RTNLGRP_LINK,
755c597432eSAlexander V. Chernikov RTNLGRP_NEIGH,
756c597432eSAlexander V. Chernikov RTNLGRP_NEXTHOP,
757c597432eSAlexander V. Chernikov #ifdef INET
758c597432eSAlexander V. Chernikov RTNLGRP_IPV4_IFADDR,
759c597432eSAlexander V. Chernikov RTNLGRP_IPV4_ROUTE,
760c597432eSAlexander V. Chernikov #endif
761c597432eSAlexander V. Chernikov #ifdef INET6
762c597432eSAlexander V. Chernikov RTNLGRP_IPV6_IFADDR,
763c597432eSAlexander V. Chernikov RTNLGRP_IPV6_ROUTE,
764c597432eSAlexander V. Chernikov #endif
765c597432eSAlexander V. Chernikov };
766c597432eSAlexander V. Chernikov
76730d7e724SAlexander V. Chernikov int optval = 1;
76830d7e724SAlexander V. Chernikov socklen_t optlen = sizeof(optval);
76930d7e724SAlexander V. Chernikov setsockopt(ss_event.fd, SOL_NETLINK, NETLINK_MSG_INFO, &optval, optlen);
77030d7e724SAlexander V. Chernikov
771*29f61502SGleb Smirnoff for (unsigned int i = 0; i < nitems(groups); i++) {
772c597432eSAlexander V. Chernikov int error;
773c597432eSAlexander V. Chernikov int optval = groups[i];
774c597432eSAlexander V. Chernikov socklen_t optlen = sizeof(optval);
775c597432eSAlexander V. Chernikov error = setsockopt(ss_event.fd, SOL_NETLINK,
776c597432eSAlexander V. Chernikov NETLINK_ADD_MEMBERSHIP, &optval, optlen);
777c597432eSAlexander V. Chernikov if (error != 0)
778c597432eSAlexander V. Chernikov warn("Unable to subscribe to group %d", optval);
779c597432eSAlexander V. Chernikov }
780c597432eSAlexander V. Chernikov
78130d7e724SAlexander V. Chernikov struct snl_msg_info attrs = {};
782c597432eSAlexander V. Chernikov struct nlmsghdr *hdr;
78330d7e724SAlexander V. Chernikov while ((hdr = snl_read_message_dbg(&ss_event, &attrs)) != NULL)
784c597432eSAlexander V. Chernikov {
78530d7e724SAlexander V. Chernikov print_nlmsg(&h, hdr, &attrs);
786c597432eSAlexander V. Chernikov snl_clear_lb(&h.ss_cmd);
787c597432eSAlexander V. Chernikov snl_clear_lb(&ss_event);
788c597432eSAlexander V. Chernikov }
789c597432eSAlexander V. Chernikov
790c597432eSAlexander V. Chernikov snl_free(&ss_event);
791c597432eSAlexander V. Chernikov nl_helper_free(&h);
792c597432eSAlexander V. Chernikov exit(0);
793c597432eSAlexander V. Chernikov }
794c597432eSAlexander V. Chernikov
795c597432eSAlexander V. Chernikov static void
print_flushed_route(struct snl_parsed_route * r,struct sockaddr * gw)796c597432eSAlexander V. Chernikov print_flushed_route(struct snl_parsed_route *r, struct sockaddr *gw)
797c597432eSAlexander V. Chernikov {
798c597432eSAlexander V. Chernikov struct sockaddr *sa = r->rta_dst;
799c597432eSAlexander V. Chernikov
800c597432eSAlexander V. Chernikov printf("%-20.20s ", r->rta_rtflags & RTF_HOST ?
801c597432eSAlexander V. Chernikov routename(sa) : netname(sa));
802c597432eSAlexander V. Chernikov sa = gw;
803c597432eSAlexander V. Chernikov printf("%-20.20s ", routename(sa));
804c597432eSAlexander V. Chernikov printf("-fib %-3d ", r->rta_table);
805c597432eSAlexander V. Chernikov printf("done\n");
806c597432eSAlexander V. Chernikov }
807c597432eSAlexander V. Chernikov
808c597432eSAlexander V. Chernikov static int
flushroute_one(struct nl_helper * h,struct snl_parsed_route * r)809c597432eSAlexander V. Chernikov flushroute_one(struct nl_helper *h, struct snl_parsed_route *r)
810c597432eSAlexander V. Chernikov {
811c597432eSAlexander V. Chernikov struct snl_state *ss = &h->ss_cmd;
812c597432eSAlexander V. Chernikov struct snl_errmsg_data e = {};
813c597432eSAlexander V. Chernikov struct snl_writer nw;
814c597432eSAlexander V. Chernikov
815c597432eSAlexander V. Chernikov snl_init_writer(ss, &nw);
816c597432eSAlexander V. Chernikov
817c597432eSAlexander V. Chernikov struct nlmsghdr *hdr = snl_create_msg_request(&nw, NL_RTM_DELROUTE);
818c597432eSAlexander V. Chernikov struct rtmsg *rtm = snl_reserve_msg_object(&nw, struct rtmsg);
819c597432eSAlexander V. Chernikov rtm->rtm_family = r->rtm_family;
820c597432eSAlexander V. Chernikov rtm->rtm_dst_len = r->rtm_dst_len;
821c597432eSAlexander V. Chernikov
822c597432eSAlexander V. Chernikov snl_add_msg_attr_u32(&nw, RTA_TABLE, r->rta_table);
823c597432eSAlexander V. Chernikov snl_add_msg_attr_ip(&nw, RTA_DST, r->rta_dst);
824c597432eSAlexander V. Chernikov
8254f8f43b0SKristof Provost if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
826c597432eSAlexander V. Chernikov return (ENOMEM);
827c597432eSAlexander V. Chernikov
828c597432eSAlexander V. Chernikov if (!snl_read_reply_code(ss, hdr->nlmsg_seq, &e)) {
829c597432eSAlexander V. Chernikov return (e.error);
830c597432eSAlexander V. Chernikov if (e.error == EPERM)
831c597432eSAlexander V. Chernikov errc(1, e.error, "RTM_DELROUTE failed:");
832c597432eSAlexander V. Chernikov else
833c597432eSAlexander V. Chernikov warnc(e.error, "RTM_DELROUTE failed:");
834c597432eSAlexander V. Chernikov return (true);
835c597432eSAlexander V. Chernikov };
836c597432eSAlexander V. Chernikov
83730d7e724SAlexander V. Chernikov if (verbose) {
83830d7e724SAlexander V. Chernikov struct snl_msg_info attrs = {};
83930d7e724SAlexander V. Chernikov print_nlmsg(h, hdr, &attrs);
84030d7e724SAlexander V. Chernikov }
841c597432eSAlexander V. Chernikov else {
842656a39c1SAlexander V. Chernikov if (r->rta_multipath.num_nhops != 0) {
843656a39c1SAlexander V. Chernikov for (uint32_t i = 0; i < r->rta_multipath.num_nhops; i++) {
844656a39c1SAlexander V. Chernikov struct rta_mpath_nh *nh = r->rta_multipath.nhops[i];
845c597432eSAlexander V. Chernikov
846c597432eSAlexander V. Chernikov print_flushed_route(r, nh->gw);
847c597432eSAlexander V. Chernikov }
848c597432eSAlexander V. Chernikov
849c597432eSAlexander V. Chernikov } else
850c597432eSAlexander V. Chernikov print_flushed_route(r, r->rta_gw);
851c597432eSAlexander V. Chernikov }
852c597432eSAlexander V. Chernikov
853c597432eSAlexander V. Chernikov return (0);
854c597432eSAlexander V. Chernikov }
855c597432eSAlexander V. Chernikov
856c597432eSAlexander V. Chernikov int
flushroutes_fib_nl(int fib,int af)857c597432eSAlexander V. Chernikov flushroutes_fib_nl(int fib, int af)
858c597432eSAlexander V. Chernikov {
859c597432eSAlexander V. Chernikov struct snl_state ss = {};
860c597432eSAlexander V. Chernikov struct snl_writer nw;
861c597432eSAlexander V. Chernikov struct nl_helper h = {};
862c597432eSAlexander V. Chernikov
863140ddebaSAlexander V. Chernikov nl_init_socket(&ss);
864c597432eSAlexander V. Chernikov snl_init_writer(&ss, &nw);
865c597432eSAlexander V. Chernikov
866c597432eSAlexander V. Chernikov struct nlmsghdr *hdr = snl_create_msg_request(&nw, NL_RTM_GETROUTE);
867c597432eSAlexander V. Chernikov hdr->nlmsg_flags |= NLM_F_DUMP;
868c597432eSAlexander V. Chernikov struct rtmsg *rtm = snl_reserve_msg_object(&nw, struct rtmsg);
869c597432eSAlexander V. Chernikov rtm->rtm_family = af;
870c597432eSAlexander V. Chernikov snl_add_msg_attr_u32(&nw, RTA_TABLE, fib);
871c597432eSAlexander V. Chernikov
8724f8f43b0SKristof Provost if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss, hdr)) {
873c597432eSAlexander V. Chernikov snl_free(&ss);
874c597432eSAlexander V. Chernikov return (EINVAL);
875c597432eSAlexander V. Chernikov }
876c597432eSAlexander V. Chernikov
877c597432eSAlexander V. Chernikov struct snl_errmsg_data e = {};
878c597432eSAlexander V. Chernikov uint32_t nlm_seq = hdr->nlmsg_seq;
879c597432eSAlexander V. Chernikov
880c597432eSAlexander V. Chernikov nl_helper_init(&h);
881c597432eSAlexander V. Chernikov
882c597432eSAlexander V. Chernikov while ((hdr = snl_read_reply_multi(&ss, nlm_seq, &e)) != NULL) {
883c597432eSAlexander V. Chernikov struct snl_parsed_route r = { .rtax_weight = RT_DEFAULT_WEIGHT };
884c597432eSAlexander V. Chernikov int error;
885c597432eSAlexander V. Chernikov
886c597432eSAlexander V. Chernikov if (!snl_parse_nlmsg(&ss, hdr, &snl_rtm_route_parser, &r))
887c597432eSAlexander V. Chernikov continue;
88830d7e724SAlexander V. Chernikov if (verbose) {
88930d7e724SAlexander V. Chernikov struct snl_msg_info attrs = {};
89030d7e724SAlexander V. Chernikov print_nlmsg(&h, hdr, &attrs);
89130d7e724SAlexander V. Chernikov }
892c597432eSAlexander V. Chernikov if (r.rta_table != (uint32_t)fib || r.rtm_family != af)
893c597432eSAlexander V. Chernikov continue;
894c597432eSAlexander V. Chernikov if ((r.rta_rtflags & RTF_GATEWAY) == 0)
895c597432eSAlexander V. Chernikov continue;
896c597432eSAlexander V. Chernikov if (debugonly)
897c597432eSAlexander V. Chernikov continue;
898c597432eSAlexander V. Chernikov
899c597432eSAlexander V. Chernikov if ((error = flushroute_one(&h, &r)) != 0) {
900c597432eSAlexander V. Chernikov if (error == EPERM)
901c597432eSAlexander V. Chernikov errc(1, error, "RTM_DELROUTE failed:");
902c597432eSAlexander V. Chernikov else
903c597432eSAlexander V. Chernikov warnc(error, "RTM_DELROUTE failed:");
904c597432eSAlexander V. Chernikov }
905c597432eSAlexander V. Chernikov snl_clear_lb(&h.ss_cmd);
906c597432eSAlexander V. Chernikov }
907c597432eSAlexander V. Chernikov
908c597432eSAlexander V. Chernikov snl_free(&ss);
909c597432eSAlexander V. Chernikov nl_helper_free(&h);
910c597432eSAlexander V. Chernikov
911c597432eSAlexander V. Chernikov return (e.error);
912c597432eSAlexander V. Chernikov }
913c597432eSAlexander V. Chernikov
914