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 != NULL) { 537 bool first = true; 538 539 memset(buf, ' ', sizeof(buf)); 540 buf[len] = '\0'; 541 542 for (int 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 != NULL) { 838 for (int 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 909