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