7 #include <sys/bitcount.h>
9 #include <sys/linker.h>
10 #include <sys/module.h>
11 #include <sys/socket.h>
12 #include <sys/sysctl.h>
14 #include <sys/types.h>
16 #include <netinet/in.h>
17 #include <arpa/inet.h>
19 #include <net/ethernet.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>
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;
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);
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);
45 #define s6_addr32 __u6_addr.__u6_addr32
46 #define bitcount32(x) __bitcount32((uint32_t)(x))
48 inet6_get_plen(const struct in6_addr *addr)
51 return (bitcount32(addr->s6_addr32[0]) + bitcount32(addr->s6_addr32[1]) +
52 bitcount32(addr->s6_addr32[2]) + bitcount32(addr->s6_addr32[3]));
56 ip6_writemask(struct in6_addr *addr6, uint8_t mask)
60 for (cp = (uint32_t *)addr6; mask >= 32; mask -= 32)
63 *cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0);
66 static struct sockaddr *
67 get_netmask(struct snl_state *ss, int family, int plen)
69 if (family == AF_INET) {
73 struct sockaddr_in *sin = snl_allocz(ss, sizeof(*sin));
75 sin->sin_len = sizeof(*sin);
76 sin->sin_family = family;
77 sin->sin_addr.s_addr = htonl(plen ? ~((1 << (32 - plen)) - 1) : 0);
79 return (struct sockaddr *)sin;
80 } else if (family == AF_INET6) {
84 struct sockaddr_in6 *sin6 = snl_allocz(ss, sizeof(*sin6));
86 sin6->sin6_len = sizeof(*sin6);
87 sin6->sin6_family = family;
88 ip6_writemask(&sin6->sin6_addr, plen);
90 return (struct sockaddr *)sin6;
96 nl_init_socket(struct snl_state *ss)
98 if (snl_init(ss, NETLINK_ROUTE))
101 if (modfind("netlink") == -1 && errno == ENOENT) {
103 if (kldload("netlink") == -1)
104 err(1, "netlink is not loaded and load attempt failed");
105 if (snl_init(ss, NETLINK_ROUTE))
109 err(1, "unable to open netlink socket");
113 struct snl_state ss_cmd;
117 nl_helper_init(struct nl_helper *h)
119 nl_init_socket(&h->ss_cmd);
123 nl_helper_free(struct nl_helper *h)
125 snl_free(&h->ss_cmd);
128 static struct sockaddr *
129 get_addr(struct sockaddr_storage *so, int rtm_addrs, int addr_type)
131 struct sockaddr *sa = NULL;
133 if (rtm_addrs & (1 << addr_type))
134 sa = (struct sockaddr *)&so[addr_type];
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)
142 struct snl_state *ss = &h->ss_cmd;
143 struct snl_writer nw;
144 int nl_type = 0, nl_flags = 0;
146 snl_init_writer(ss, &nw);
150 nl_type = RTM_NEWROUTE;
151 nl_flags = NLM_F_CREATE | NLM_F_APPEND; /* Do append by default */
153 case RTSOCK_RTM_CHANGE:
154 nl_type = RTM_NEWROUTE;
155 nl_flags = NLM_F_REPLACE;
157 case RTSOCK_RTM_DELETE:
158 nl_type = RTM_DELROUTE;
161 nl_type = RTM_GETROUTE;
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);
174 struct nlmsghdr *hdr = snl_create_msg_request(&nw, nl_type);
175 hdr->nlmsg_flags |= nl_flags;
178 int rtm_type = RTN_UNICAST;
180 switch (dst->sa_family) {
183 struct sockaddr_in *mask4 = (struct sockaddr_in *)mask;
185 if ((rtm_flags & RTF_HOST) == 0 && mask4 != NULL)
186 plen = bitcount32(mask4->sin_addr.s_addr);
193 struct sockaddr_in6 *mask6 = (struct sockaddr_in6 *)mask;
195 if ((rtm_flags & RTF_HOST) == 0 && mask6 != NULL)
196 plen = inet6_get_plen(&mask6->sin6_addr);
205 if (rtm_flags & RTF_REJECT)
206 rtm_type = RTN_PROHIBIT;
207 else if (rtm_flags & RTF_BLACKHOLE)
208 rtm_type = RTN_BLACKHOLE;
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;
216 /* Request exact prefix match if mask is set */
217 if ((cmd == RTSOCK_RTM_GET) && (mask != NULL))
218 rtm->rtm_flags = RTM_F_PREFIX;
220 snl_add_msg_attr_ip(&nw, RTA_DST, dst);
221 snl_add_msg_attr_u32(&nw, RTA_TABLE, fib);
223 uint32_t rta_oif = 0;
226 if (rtm_flags & RTF_GATEWAY) {
227 if (gw->sa_family == dst->sa_family)
228 snl_add_msg_attr_ip(&nw, RTA_GATEWAY, gw);
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;
234 if (IN6_IS_ADDR_LINKLOCAL(&gw6->sin6_addr))
235 rta_oif = gw6->sin6_scope_id;
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;
245 if (dst->sa_family == AF_INET6 && rta_oif == 0) {
246 struct sockaddr_in6 *dst6 = (struct sockaddr_in6 *)dst;
248 if (IN6_IS_ADDR_LINKLOCAL(&dst6->sin6_addr))
249 rta_oif = dst6->sin6_scope_id;
253 snl_add_msg_attr_u32(&nw, RTA_OIF, rta_oif);
255 snl_add_msg_attr_u32(&nw, NL_RTA_RTFLAGS, rtm_flags);
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);
263 if (rt_metrics->rmx_weight > 0)
264 snl_add_msg_attr_u32(&nw, NL_RTA_WEIGHT, rt_metrics->rmx_weight);
266 if (snl_finalize_msg(&nw) && snl_send_message(ss, hdr)) {
267 struct snl_errmsg_data e = {};
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);
274 snl_parse_errmsg(ss, hdr, &e);
275 if (e.error == ESRCH)
276 warn("route has not been found");
278 warn("message indicates error %d", e.error);
284 if (snl_parse_errmsg(ss, hdr, &e))
291 rtmsg_nl(int cmd, int rtm_flags, int fib, int rtm_addrs,
292 struct sockaddr_storage *so, struct rt_metrics *rt_metrics)
294 struct nl_helper h = {};
297 int error = rtmsg_nl_int(&h, cmd, rtm_flags, fib, rtm_addrs, so, rt_metrics);
304 get_ifdata(struct nl_helper *h, uint32_t ifindex, struct snl_parsed_link_simple *link)
306 struct snl_state *ss = &h->ss_cmd;
307 struct snl_writer nw;
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);
313 ifmsg->ifi_index = ifindex;
314 if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr))
317 hdr = snl_read_reply(ss, hdr->nlmsg_seq);
319 if (hdr != NULL && hdr->nlmsg_type == RTM_NEWLINK) {
320 snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, link);
323 if (link->ifla_ifname == NULL) {
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;
335 print_getmsg(struct nl_helper *h, struct nlmsghdr *hdr, struct sockaddr *dst)
337 struct snl_state *ss = &h->ss_cmd;
339 struct snl_parsed_route r = { .rtax_weight = RT_DEFAULT_WEIGHT };
341 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r))
344 struct snl_parsed_link_simple link = {};
345 get_ifdata(h, r.rta_oif, &link);
348 r.rtax_mtu = link.ifla_mtu;
349 r.rta_rtflags |= (RTF_UP | RTF_DONE);
351 (void)printf(" route to: %s\n", routename(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);
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);
366 struct rt_metrics rmx = {
367 .rmx_mtu = r.rtax_mtu,
368 .rmx_weight = r.rtax_weight,
369 .rmx_expire = r.rta_expire,
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);
384 printf("%8ld \n", (long)(rmx.rmx_expire - ts.tv_sec));
388 print_prefix(struct nl_helper *h, char *buf, int bufsize, struct sockaddr *sa, int plen)
393 snprintf(buf, bufsize, "<NULL>");
397 switch (sa->sa_family) {
400 struct sockaddr_in *sin = (struct sockaddr_in *)sa;
401 char abuf[INET_ADDRSTRLEN];
403 inet_ntop(AF_INET, &sin->sin_addr, abuf, sizeof(abuf));
404 sz = snprintf(buf, bufsize, "%s", abuf);
409 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
410 char abuf[INET6_ADDRSTRLEN];
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 = {};
417 if (sin6->sin6_scope_id != 0) {
418 get_ifdata(h, sin6->sin6_scope_id, &link);
419 ifname = link.ifla_ifname;
423 sz = snprintf(buf, bufsize, "%s", abuf);
425 sz = snprintf(buf, bufsize, "%s%%%s", abuf, ifname);
429 snprintf(buf, bufsize, "unknown_af#%d", sa->sa_family);
434 snprintf(buf + sz, bufsize - sz, "/%d", plen);
439 print_line_prefix(const char *cmd, const char *name)
445 clock_gettime(CLOCK_REALTIME, &tp);
446 localtime_r(&tp.tv_sec, &tm);
448 strftime(buf, sizeof(buf), "%T", &tm);
449 int len = printf("%s.%03ld %s %s ", buf, tp.tv_nsec / 1000000, cmd, name);
455 get_action_name(struct nlmsghdr *hdr, int new_cmd)
457 if (hdr->nlmsg_type == new_cmd) {
458 //return ((hdr->nlmsg_flags & NLM_F_REPLACE) ? "replace" : "add");
465 print_nlmsg_route_nhop(struct nl_helper *h, struct snl_parsed_route *r,
466 struct rta_mpath_nh *nh, bool first)
468 // gw 10.0.0.1 ifp vtnet0 mtu 1500 table inet.0
469 if (nh->gw != NULL) {
471 print_prefix(h, gwbuf, sizeof(gwbuf), nh->gw, -1);
472 printf("gw %s ", gwbuf);
475 if (nh->ifindex != 0) {
476 struct snl_parsed_link_simple link = {};
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);
487 switch (r->rtm_family) {
489 printf("table inet.%d", r->rta_table);
492 printf("table inet6.%d", r->rta_table);
501 print_nlmsg_route(struct nl_helper *h, struct nlmsghdr *hdr)
503 struct snl_parsed_route r = { .rtax_weight = RT_DEFAULT_WEIGHT };
504 struct snl_state *ss = &h->ss_cmd;
506 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r))
509 // 20:19:41.333 add route 10.0.0.0/24 gw 10.0.0.1 ifp vtnet0 mtu 1500 table inet.0
511 const char *cmd = get_action_name(hdr, RTM_NEWROUTE);
512 int len = print_line_prefix(cmd, "route");
515 print_prefix(h, buf, sizeof(buf), r.rta_dst, r.rtm_dst_len);
516 len += strlen(buf) + 1;
519 switch (r.rtm_type) {
521 printf("blackhole\n");
523 case RTN_UNREACHABLE:
524 printf("unreach(reject)\n");
527 printf("prohibit(reject)\n");
531 if (r.rta_multipath != NULL) {
534 memset(buf, ' ', sizeof(buf));
537 for (int i = 0; i < r.rta_multipath->num_nhops; i++) {
538 struct rta_mpath_nh *nh = &r.rta_multipath->nhops[i];
542 print_nlmsg_route_nhop(h, &r, nh, first);
546 struct rta_mpath_nh nh = {
548 .ifindex = r.rta_oif,
549 .rtax_mtu = r.rtax_mtu,
552 print_nlmsg_route_nhop(h, &r, &nh, true);
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 */
567 print_nlmsg_link(struct nl_helper *h, struct nlmsghdr *hdr)
569 struct snl_parsed_link l = {};
570 struct snl_state *ss = &h->ss_cmd;
572 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser, &l))
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");
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]);
584 printf("mtu %u ", l.ifla_mtu);
590 print_nlmsg_addr(struct nl_helper *h, struct nlmsghdr *hdr)
592 struct snl_parsed_addr attrs = {};
593 struct snl_state *ss = &h->ss_cmd;
595 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_addr_parser, &attrs))
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");
603 struct sockaddr *addr = attrs.ifa_local ? attrs.ifa_local : attrs.ifa_address;
604 print_prefix(h, buf, sizeof(buf), addr, attrs.ifa_prefixlen);
607 struct snl_parsed_link_simple link = {};
608 get_ifdata(h, attrs.ifa_index, &link);
610 if (link.ifi_flags & IFF_POINTOPOINT) {
612 print_prefix(h, buf, sizeof(buf), attrs.ifa_address, -1);
613 printf("-> %s ", buf);
616 printf("iface %s ", link.ifla_ifname);
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) */
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 */
639 print_nlmsg_neigh(struct nl_helper *h, struct nlmsghdr *hdr)
641 struct snl_parsed_neigh attrs = {};
642 struct snl_state *ss = &h->ss_cmd;
644 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_neigh_parser, &attrs))
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");
652 print_prefix(h, buf, sizeof(buf), attrs.nda_dst, -1);
655 struct snl_parsed_link_simple link = {};
656 get_ifdata(h, attrs.nda_ifindex, &link);
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]);
665 if (attrs.nda_lladdr != NULL) {
666 int if_type = link.ifi_type;
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;
672 ll = (struct ether_addr *)NLA_DATA(attrs.nda_lladdr);
673 printf("lladdr %s ", ether_ntoa(ll));
675 struct sockaddr_dl sdl = {
676 .sdl_len = sizeof(sdl),
677 .sdl_family = AF_LINK,
678 .sdl_index = attrs.nda_ifindex,
680 .sdl_alen = NLA_DATA_LEN(attrs.nda_lladdr),
682 if (sdl.sdl_alen < sizeof(sdl.sdl_data)) {
683 void *ll = NLA_DATA(attrs.nda_lladdr);
685 memcpy(sdl.sdl_data, ll, sdl.sdl_alen);
686 printf("lladdr %s ", link_ntoa(&sdl));
691 if (link.ifla_ifname != NULL)
692 printf("iface %s ", link.ifla_ifname);
697 print_nlmsg_generic(struct nl_helper *h, struct nlmsghdr *hdr)
702 print_nlmsg(struct nl_helper *h, struct nlmsghdr *hdr)
704 switch (hdr->nlmsg_type) {
707 print_nlmsg_link(h, hdr);
711 print_nlmsg_addr(h, hdr);
715 print_nlmsg_route(h, hdr);
719 print_nlmsg_neigh(h, hdr);
722 print_nlmsg_generic(h, hdr);
725 snl_clear_lb(&h->ss_cmd);
731 struct snl_state ss_event = {};
734 nl_init_socket(&ss_event);
751 for (unsigned int i = 0; i < NL_ARRAY_LEN(groups); i++) {
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);
758 warn("Unable to subscribe to group %d", optval);
761 struct nlmsghdr *hdr;
762 while ((hdr = snl_read_message(&ss_event)) != NULL)
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);
776 print_flushed_route(struct snl_parsed_route *r, struct sockaddr *gw)
778 struct sockaddr *sa = r->rta_dst;
780 printf("%-20.20s ", r->rta_rtflags & RTF_HOST ?
781 routename(sa) : netname(sa));
783 printf("%-20.20s ", routename(sa));
784 if (r->rta_table >= 0)
785 printf("-fib %-3d ", r->rta_table);
790 flushroute_one(struct nl_helper *h, struct snl_parsed_route *r)
792 struct snl_state *ss = &h->ss_cmd;
793 struct snl_errmsg_data e = {};
794 struct snl_writer nw;
796 snl_init_writer(ss, &nw);
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;
803 snl_add_msg_attr_u32(&nw, RTA_TABLE, r->rta_table);
804 snl_add_msg_attr_ip(&nw, RTA_DST, r->rta_dst);
806 if (!snl_finalize_msg(&nw) || !snl_send_message(ss, hdr))
809 if (!snl_read_reply_code(ss, hdr->nlmsg_seq, &e)) {
811 if (e.error == EPERM)
812 errc(1, e.error, "RTM_DELROUTE failed:");
814 warnc(e.error, "RTM_DELROUTE failed:");
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];
825 print_flushed_route(r, nh->gw);
829 print_flushed_route(r, r->rta_gw);
836 flushroutes_fib_nl(int fib, int af)
838 struct snl_state ss = {};
839 struct snl_writer nw;
840 struct nl_helper h = {};
843 snl_init_writer(&ss, &nw);
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);
851 if (!snl_finalize_msg(&nw) || !snl_send_message(&ss, hdr)) {
856 struct snl_errmsg_data e = {};
857 uint32_t nlm_seq = hdr->nlmsg_seq;
861 while ((hdr = snl_read_reply_multi(&ss, nlm_seq, &e)) != NULL) {
862 struct snl_parsed_route r = { .rtax_weight = RT_DEFAULT_WEIGHT };
865 if (!snl_parse_nlmsg(&ss, hdr, &snl_rtm_route_parser, &r))
868 print_nlmsg(&h, hdr);
869 if (r.rta_table != (uint32_t)fib || r.rtm_family != af)
871 if ((r.rta_rtflags & RTF_GATEWAY) == 0)
876 if ((error = flushroute_one(&h, &r)) != 0) {
878 errc(1, error, "RTM_DELROUTE failed:");
880 warnc(error, "RTM_DELROUTE failed:");
882 snl_clear_lb(&h.ss_cmd);