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