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