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
inet6_get_plen(const struct in6_addr * addr)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
ip6_writemask(struct in6_addr * addr6,uint8_t mask)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 *
get_netmask(struct snl_state * ss,int family,int plen)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
nl_init_socket(struct snl_state * ss)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
nl_helper_init(struct nl_helper * h)120 nl_helper_init(struct nl_helper *h)
121 {
122 nl_init_socket(&h->ss_cmd);
123 }
124
125 static void
nl_helper_free(struct nl_helper * h)126 nl_helper_free(struct nl_helper *h)
127 {
128 snl_free(&h->ss_cmd);
129 }
130
131 static struct sockaddr *
get_addr(struct sockaddr_storage * so,int rtm_addrs,int addr_type)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
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)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_expire > 0)
267 snl_add_msg_attr_u32(&nw, NL_RTA_EXPIRES, rt_metrics->rmx_expire);
268
269 if (rt_metrics->rmx_weight > 0)
270 snl_add_msg_attr_u32(&nw, NL_RTA_WEIGHT, rt_metrics->rmx_weight);
271
272 if ((hdr = snl_finalize_msg(&nw)) && snl_send_message(ss, hdr)) {
273 struct snl_errmsg_data e = {};
274
275 hdr = snl_read_reply(ss, hdr->nlmsg_seq);
276 if (nl_type == NL_RTM_GETROUTE) {
277 if (hdr->nlmsg_type == NL_RTM_NEWROUTE) {
278 print_getmsg(h, hdr, dst);
279 return (0);
280 }
281 }
282
283 if (snl_parse_errmsg(ss, hdr, &e)) {
284 switch (e.error) {
285 case (ESRCH):
286 warnx("route has not been found");
287 break;
288 default:
289 if (e.error == 0)
290 break;
291 warnc(e.error, "message indicates error");
292 }
293
294 return (e.error);
295 }
296 }
297
298 return (EINVAL);
299 }
300
301 int
rtmsg_nl(int cmd,int rtm_flags,int fib,int rtm_addrs,struct sockaddr_storage * so,struct rt_metrics * rt_metrics)302 rtmsg_nl(int cmd, int rtm_flags, int fib, int rtm_addrs,
303 struct sockaddr_storage *so, struct rt_metrics *rt_metrics)
304 {
305 struct nl_helper h = {};
306
307 nl_helper_init(&h);
308 int error = rtmsg_nl_int(&h, cmd, rtm_flags, fib, rtm_addrs, so, rt_metrics);
309 nl_helper_free(&h);
310
311 return (error);
312 }
313
314 static void
get_ifdata(struct nl_helper * h,uint32_t ifindex,struct snl_parsed_link_simple * link)315 get_ifdata(struct nl_helper *h, uint32_t ifindex, struct snl_parsed_link_simple *link)
316 {
317 struct snl_state *ss = &h->ss_cmd;
318 struct snl_writer nw;
319
320 snl_init_writer(ss, &nw);
321 struct nlmsghdr *hdr = snl_create_msg_request(&nw, NL_RTM_GETLINK);
322 struct ifinfomsg *ifmsg = snl_reserve_msg_object(&nw, struct ifinfomsg);
323 if (ifmsg != NULL)
324 ifmsg->ifi_index = ifindex;
325 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
326 return;
327
328 hdr = snl_read_reply(ss, hdr->nlmsg_seq);
329
330 if (hdr != NULL && hdr->nlmsg_type == RTM_NEWLINK) {
331 snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, link);
332 }
333
334 if (link->ifla_ifname == NULL) {
335 char ifname[16];
336
337 snprintf(ifname, sizeof(ifname), "if#%u", ifindex);
338 int len = strlen(ifname);
339 char *buf = snl_allocz(ss, len + 1);
340 strlcpy(buf, ifname, len + 1);
341 link->ifla_ifname = buf;
342 }
343 }
344
345 static void
print_getmsg(struct nl_helper * h,struct nlmsghdr * hdr,struct sockaddr * dst)346 print_getmsg(struct nl_helper *h, struct nlmsghdr *hdr, struct sockaddr *dst)
347 {
348 struct snl_state *ss = &h->ss_cmd;
349 struct snl_parsed_route r = { .rtax_weight = RT_DEFAULT_WEIGHT };
350
351 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r))
352 return;
353
354 struct snl_parsed_link_simple link = {};
355 get_ifdata(h, r.rta_oif, &link);
356
357 if (r.rtax_mtu == 0)
358 r.rtax_mtu = link.ifla_mtu;
359 r.rta_rtflags |= (RTF_UP | RTF_DONE);
360
361 (void)printf(" route to: %s\n", routename(dst));
362
363 if (r.rta_dst)
364 (void)printf("destination: %s\n", routename(r.rta_dst));
365 struct sockaddr *mask = get_netmask(ss, r.rtm_family, r.rtm_dst_len);
366 if (mask)
367 (void)printf(" mask: %s\n", routename(mask));
368 if (r.rta_gw && (r.rta_rtflags & RTF_GATEWAY))
369 (void)printf(" gateway: %s\n", routename(r.rta_gw));
370 (void)printf(" fib: %u\n", (unsigned int)r.rta_table);
371 if (link.ifla_ifname)
372 (void)printf(" interface: %s\n", link.ifla_ifname);
373 (void)printf(" flags: ");
374 printb(r.rta_rtflags, routeflags);
375
376 struct rt_metrics rmx = {
377 .rmx_mtu = r.rtax_mtu,
378 .rmx_weight = r.rtax_weight,
379 .rmx_expire = r.rta_expire,
380 };
381
382 printf("\n%9s %9s %9s %9s %9s %10s %9s\n", "recvpipe",
383 "sendpipe", "ssthresh", "rtt,msec", "mtu ", "weight", "expire");
384 printf("%8lu ", rmx.rmx_recvpipe);
385 printf("%8lu ", rmx.rmx_sendpipe);
386 printf("%8lu ", rmx.rmx_ssthresh);
387 printf("%8lu ", 0UL);
388 printf("%8lu ", rmx.rmx_mtu);
389 printf("%8lu ", rmx.rmx_weight);
390 printf("%8ld \n", rmx.rmx_expire);
391 }
392
393 static void
print_prefix(struct nl_helper * h,char * buf,int bufsize,struct sockaddr * sa,int plen)394 print_prefix(struct nl_helper *h, char *buf, int bufsize, struct sockaddr *sa, int plen)
395 {
396 int sz = 0;
397
398 if (sa == NULL) {
399 snprintf(buf, bufsize, "<NULL>");
400 return;
401 }
402
403 switch (sa->sa_family) {
404 case AF_INET:
405 {
406 struct sockaddr_in *sin = (struct sockaddr_in *)sa;
407 char abuf[INET_ADDRSTRLEN];
408
409 inet_ntop(AF_INET, &sin->sin_addr, abuf, sizeof(abuf));
410 sz = snprintf(buf, bufsize, "%s", abuf);
411 break;
412 }
413 case AF_INET6:
414 {
415 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
416 char abuf[INET6_ADDRSTRLEN];
417 char *ifname = NULL;
418
419 inet_ntop(AF_INET6, &sin6->sin6_addr, abuf, sizeof(abuf));
420 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
421 struct snl_parsed_link_simple link = {};
422
423 if (sin6->sin6_scope_id != 0) {
424 get_ifdata(h, sin6->sin6_scope_id, &link);
425 ifname = link.ifla_ifname;
426 }
427 }
428 if (ifname == NULL)
429 sz = snprintf(buf, bufsize, "%s", abuf);
430 else
431 sz = snprintf(buf, bufsize, "%s%%%s", abuf, ifname);
432 break;
433 }
434 default:
435 snprintf(buf, bufsize, "unknown_af#%d", sa->sa_family);
436 plen = -1;
437 }
438
439 if (plen >= 0)
440 snprintf(buf + sz, bufsize - sz, "/%d", plen);
441 }
442
443 static int
print_line_prefix(struct nlmsghdr * hdr,struct snl_msg_info * cinfo,const char * cmd,const char * name)444 print_line_prefix(struct nlmsghdr *hdr, struct snl_msg_info *cinfo,
445 const char *cmd, const char *name)
446 {
447 struct timespec tp;
448 struct tm tm;
449 char buf[32];
450
451 clock_gettime(CLOCK_REALTIME, &tp);
452 localtime_r(&tp.tv_sec, &tm);
453
454 strftime(buf, sizeof(buf), "%T", &tm);
455 int len = printf("%s.%03ld PID %4u %s %s ", buf, tp.tv_nsec / 1000000,
456 cinfo->process_id, cmd, name);
457
458 return (len);
459 }
460
461 static const char *
get_action_name(struct nlmsghdr * hdr,int new_cmd)462 get_action_name(struct nlmsghdr *hdr, int new_cmd)
463 {
464 if (hdr->nlmsg_type == new_cmd) {
465 //return ((hdr->nlmsg_flags & NLM_F_REPLACE) ? "replace" : "add");
466 return ("add/repl");
467 } else
468 return ("delete");
469 }
470
471 static void
print_nlmsg_route_nhop(struct nl_helper * h,struct snl_parsed_route * r,struct rta_mpath_nh * nh,bool first)472 print_nlmsg_route_nhop(struct nl_helper *h, struct snl_parsed_route *r,
473 struct rta_mpath_nh *nh, bool first)
474 {
475 // gw 10.0.0.1 ifp vtnet0 mtu 1500 table inet.0
476 if (nh->gw != NULL) {
477 char gwbuf[128];
478 print_prefix(h, gwbuf, sizeof(gwbuf), nh->gw, -1);
479 printf("gw %s ", gwbuf);
480 }
481
482 if (nh->ifindex != 0) {
483 struct snl_parsed_link_simple link = {};
484
485 get_ifdata(h, nh->ifindex, &link);
486 if (nh->rtax_mtu == 0)
487 nh->rtax_mtu = link.ifla_mtu;
488 printf("iface %s ", link.ifla_ifname);
489 if (nh->rtax_mtu != 0)
490 printf("mtu %d ", nh->rtax_mtu);
491 if (nh->rta_expire > 0)
492 printf("expire %u ", nh->rta_expire);
493 }
494
495 if (first) {
496 switch (r->rtm_family) {
497 case AF_INET:
498 printf("table inet.%d", r->rta_table);
499 break;
500 case AF_INET6:
501 printf("table inet6.%d", r->rta_table);
502 break;
503 }
504 }
505
506 printf("\n");
507 }
508
509 static void
print_nlmsg_route(struct nl_helper * h,struct nlmsghdr * hdr,struct snl_msg_info * cinfo)510 print_nlmsg_route(struct nl_helper *h, struct nlmsghdr *hdr,
511 struct snl_msg_info *cinfo)
512 {
513 struct snl_parsed_route r = { .rtax_weight = RT_DEFAULT_WEIGHT };
514 struct snl_state *ss = &h->ss_cmd;
515
516 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r))
517 return;
518
519 // 20:19:41.333 add route 10.0.0.0/24 gw 10.0.0.1 ifp vtnet0 mtu 1500 table inet.0
520
521 const char *cmd = get_action_name(hdr, RTM_NEWROUTE);
522 int len = print_line_prefix(hdr, cinfo, cmd, "route");
523
524 char buf[128];
525 print_prefix(h, buf, sizeof(buf), r.rta_dst, r.rtm_dst_len);
526 len += strlen(buf) + 1;
527 printf("%s ", buf);
528
529 switch (r.rtm_type) {
530 case RTN_BLACKHOLE:
531 printf("blackhole\n");
532 return;
533 case RTN_UNREACHABLE:
534 printf("unreach(reject)\n");
535 return;
536 case RTN_PROHIBIT:
537 printf("prohibit(reject)\n");
538 return;
539 }
540
541 if (r.rta_multipath.num_nhops != 0) {
542 bool first = true;
543
544 memset(buf, ' ', sizeof(buf));
545 buf[len] = '\0';
546
547 for (uint32_t i = 0; i < r.rta_multipath.num_nhops; i++) {
548 struct rta_mpath_nh *nh = r.rta_multipath.nhops[i];
549
550 if (!first)
551 printf("%s", buf);
552 print_nlmsg_route_nhop(h, &r, nh, first);
553 first = false;
554 }
555 } else {
556 struct rta_mpath_nh nh = {
557 .gw = r.rta_gw,
558 .ifindex = r.rta_oif,
559 .rtax_mtu = r.rtax_mtu,
560 };
561
562 print_nlmsg_route_nhop(h, &r, &nh, true);
563 }
564 }
565
566 static const char *operstate[] = {
567 "UNKNOWN", /* 0, IF_OPER_UNKNOWN */
568 "NOTPRESENT", /* 1, IF_OPER_NOTPRESENT */
569 "DOWN", /* 2, IF_OPER_DOWN */
570 "LLDOWN", /* 3, IF_OPER_LOWERLAYERDOWN */
571 "TESTING", /* 4, IF_OPER_TESTING */
572 "DORMANT", /* 5, IF_OPER_DORMANT */
573 "UP", /* 6, IF_OPER_UP */
574 };
575
576 static void
print_nlmsg_link(struct nl_helper * h,struct nlmsghdr * hdr,struct snl_msg_info * cinfo)577 print_nlmsg_link(struct nl_helper *h, struct nlmsghdr *hdr,
578 struct snl_msg_info *cinfo)
579 {
580 struct snl_parsed_link l = {};
581 struct snl_state *ss = &h->ss_cmd;
582
583 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser, &l))
584 return;
585
586 // 20:19:41.333 add iface#3 vtnet0 admin UP oper UP mtu 1500 table inet.0
587 const char *cmd = get_action_name(hdr, RTM_NEWLINK);
588 print_line_prefix(hdr, cinfo, cmd, "iface");
589
590 printf("iface#%u %s ", l.ifi_index, l.ifla_ifname);
591 printf("admin %s ", (l.ifi_flags & IFF_UP) ? "UP" : "DOWN");
592 if (l.ifla_operstate < nitems(operstate))
593 printf("oper %s ", operstate[l.ifla_operstate]);
594 if (l.ifla_mtu > 0)
595 printf("mtu %u ", l.ifla_mtu);
596
597 printf("\n");
598 }
599
600 static void
print_nlmsg_addr(struct nl_helper * h,struct nlmsghdr * hdr,struct snl_msg_info * cinfo)601 print_nlmsg_addr(struct nl_helper *h, struct nlmsghdr *hdr,
602 struct snl_msg_info *cinfo)
603 {
604 struct snl_parsed_addr attrs = {};
605 struct snl_state *ss = &h->ss_cmd;
606
607 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_addr_parser, &attrs))
608 return;
609
610 // add addr 192.168.1.1/24 iface vtnet0
611 const char *cmd = get_action_name(hdr, RTM_NEWADDR);
612 print_line_prefix(hdr, cinfo, cmd, "addr");
613
614 char buf[128];
615 struct sockaddr *addr = attrs.ifa_local ? attrs.ifa_local : attrs.ifa_address;
616 print_prefix(h, buf, sizeof(buf), addr, attrs.ifa_prefixlen);
617 printf("%s ", buf);
618
619 struct snl_parsed_link_simple link = {};
620 get_ifdata(h, attrs.ifa_index, &link);
621
622 if (link.ifi_flags & IFF_POINTOPOINT) {
623 char buf[64];
624 print_prefix(h, buf, sizeof(buf), attrs.ifa_address, -1);
625 printf("-> %s ", buf);
626 }
627
628 printf("iface %s ", link.ifla_ifname);
629
630 printf("\n");
631 }
632
633 static const char *nudstate[] = {
634 "INCOMPLETE", /* 0x01(0) */
635 "REACHABLE", /* 0x02(1) */
636 "STALE", /* 0x04(2) */
637 "DELAY", /* 0x08(3) */
638 "PROBE", /* 0x10(4) */
639 "FAILED", /* 0x20(5) */
640 };
641
642 #define NUD_INCOMPLETE 0x01 /* No lladdr, address resolution in progress */
643 #define NUD_REACHABLE 0x02 /* reachable & recently resolved */
644 #define NUD_STALE 0x04 /* has lladdr but it's stale */
645 #define NUD_DELAY 0x08 /* has lladdr, is stale, probes delayed */
646 #define NUD_PROBE 0x10 /* has lladdr, is stale, probes sent */
647 #define NUD_FAILED 0x20 /* unused */
648
649
650 static void
print_nlmsg_neigh(struct nl_helper * h,struct nlmsghdr * hdr,struct snl_msg_info * cinfo)651 print_nlmsg_neigh(struct nl_helper *h, struct nlmsghdr *hdr,
652 struct snl_msg_info *cinfo)
653 {
654 struct snl_parsed_neigh attrs = {};
655 struct snl_state *ss = &h->ss_cmd;
656
657 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_neigh_parser, &attrs))
658 return;
659
660 // add addr 192.168.1.1 state %s lladdr %s iface vtnet0
661 const char *cmd = get_action_name(hdr, RTM_NEWNEIGH);
662 print_line_prefix(hdr, cinfo, cmd, "neigh");
663
664 char buf[128];
665 print_prefix(h, buf, sizeof(buf), attrs.nda_dst, -1);
666 printf("%s ", buf);
667
668 struct snl_parsed_link_simple link = {};
669 get_ifdata(h, attrs.nda_ifindex, &link);
670
671 for (unsigned int i = 0; i < nitems(nudstate); i++) {
672 if ((1 << i) & attrs.ndm_state) {
673 printf("state %s ", nudstate[i]);
674 break;
675 }
676 }
677
678 if (attrs.nda_lladdr != NULL) {
679 int if_type = link.ifi_type;
680
681 if ((if_type == IFT_ETHER || if_type == IFT_L2VLAN || if_type == IFT_BRIDGE) &&
682 NLA_DATA_LEN(attrs.nda_lladdr) == ETHER_ADDR_LEN) {
683 struct ether_addr *ll;
684
685 ll = (struct ether_addr *)NLA_DATA(attrs.nda_lladdr);
686 printf("lladdr %s ", ether_ntoa(ll));
687 } else {
688 struct sockaddr_dl sdl = {
689 .sdl_len = sizeof(sdl),
690 .sdl_family = AF_LINK,
691 .sdl_index = attrs.nda_ifindex,
692 .sdl_type = if_type,
693 .sdl_alen = NLA_DATA_LEN(attrs.nda_lladdr),
694 };
695 if (sdl.sdl_alen < sizeof(sdl.sdl_data)) {
696 void *ll = NLA_DATA(attrs.nda_lladdr);
697
698 memcpy(sdl.sdl_data, ll, sdl.sdl_alen);
699 printf("lladdr %s ", link_ntoa(&sdl));
700 }
701 }
702 }
703
704 if (link.ifla_ifname != NULL)
705 printf("iface %s ", link.ifla_ifname);
706 printf("\n");
707 }
708
709 static void
print_nlmsg_generic(struct nl_helper * h,struct nlmsghdr * hdr,struct snl_msg_info * cinfo)710 print_nlmsg_generic(struct nl_helper *h, struct nlmsghdr *hdr, struct snl_msg_info *cinfo)
711 {
712 const char *cmd = get_action_name(hdr, 0);
713 print_line_prefix(hdr, cinfo, cmd, "unknown message");
714 printf(" type %u\n", hdr->nlmsg_type);
715 }
716
717 static void
print_nlmsg(struct nl_helper * h,struct nlmsghdr * hdr,struct snl_msg_info * cinfo)718 print_nlmsg(struct nl_helper *h, struct nlmsghdr *hdr, struct snl_msg_info *cinfo)
719 {
720 switch (hdr->nlmsg_type) {
721 case RTM_NEWLINK:
722 case RTM_DELLINK:
723 print_nlmsg_link(h, hdr, cinfo);
724 break;
725 case RTM_NEWADDR:
726 case RTM_DELADDR:
727 print_nlmsg_addr(h, hdr, cinfo);
728 break;
729 case RTM_NEWROUTE:
730 case RTM_DELROUTE:
731 print_nlmsg_route(h, hdr, cinfo);
732 break;
733 case RTM_NEWNEIGH:
734 case RTM_DELNEIGH:
735 print_nlmsg_neigh(h, hdr, cinfo);
736 break;
737 default:
738 print_nlmsg_generic(h, hdr, cinfo);
739 }
740
741 fflush(stdout);
742 snl_clear_lb(&h->ss_cmd);
743 }
744
745 void
monitor_nl(int fib)746 monitor_nl(int fib)
747 {
748 struct snl_state ss_event = {};
749 struct nl_helper h;
750
751 nl_init_socket(&ss_event);
752 nl_helper_init(&h);
753
754 int groups[] = {
755 RTNLGRP_LINK,
756 RTNLGRP_NEIGH,
757 RTNLGRP_NEXTHOP,
758 #ifdef INET
759 RTNLGRP_IPV4_IFADDR,
760 RTNLGRP_IPV4_ROUTE,
761 #endif
762 #ifdef INET6
763 RTNLGRP_IPV6_IFADDR,
764 RTNLGRP_IPV6_ROUTE,
765 #endif
766 };
767
768 int optval = 1;
769 socklen_t optlen = sizeof(optval);
770 setsockopt(ss_event.fd, SOL_NETLINK, NETLINK_MSG_INFO, &optval, optlen);
771
772 for (unsigned int i = 0; i < nitems(groups); i++) {
773 int error;
774 int optval = groups[i];
775 socklen_t optlen = sizeof(optval);
776 error = setsockopt(ss_event.fd, SOL_NETLINK,
777 NETLINK_ADD_MEMBERSHIP, &optval, optlen);
778 if (error != 0)
779 warn("Unable to subscribe to group %d", optval);
780 }
781
782 struct snl_msg_info attrs = {};
783 struct nlmsghdr *hdr;
784 while ((hdr = snl_read_message_dbg(&ss_event, &attrs)) != NULL)
785 {
786 print_nlmsg(&h, hdr, &attrs);
787 snl_clear_lb(&h.ss_cmd);
788 snl_clear_lb(&ss_event);
789 }
790
791 snl_free(&ss_event);
792 nl_helper_free(&h);
793 exit(0);
794 }
795
796 static void
print_flushed_route(struct snl_parsed_route * r,struct sockaddr * gw)797 print_flushed_route(struct snl_parsed_route *r, struct sockaddr *gw)
798 {
799 struct sockaddr *sa = r->rta_dst;
800
801 printf("%-20.20s ", r->rta_rtflags & RTF_HOST ?
802 routename(sa) : netname(sa));
803 sa = gw;
804 printf("%-20.20s ", routename(sa));
805 printf("-fib %-3d ", r->rta_table);
806 printf("done\n");
807 }
808
809 static int
flushroute_one(struct nl_helper * h,struct snl_parsed_route * r)810 flushroute_one(struct nl_helper *h, struct snl_parsed_route *r)
811 {
812 struct snl_state *ss = &h->ss_cmd;
813 struct snl_errmsg_data e = {};
814 struct snl_writer nw;
815
816 snl_init_writer(ss, &nw);
817
818 struct nlmsghdr *hdr = snl_create_msg_request(&nw, NL_RTM_DELROUTE);
819 struct rtmsg *rtm = snl_reserve_msg_object(&nw, struct rtmsg);
820 rtm->rtm_family = r->rtm_family;
821 rtm->rtm_dst_len = r->rtm_dst_len;
822
823 snl_add_msg_attr_u32(&nw, RTA_TABLE, r->rta_table);
824 snl_add_msg_attr_ip(&nw, RTA_DST, r->rta_dst);
825
826 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
827 return (ENOMEM);
828
829 if (!snl_read_reply_code(ss, hdr->nlmsg_seq, &e)) {
830 return (e.error);
831 if (e.error == EPERM)
832 errc(1, e.error, "RTM_DELROUTE failed:");
833 else
834 warnc(e.error, "RTM_DELROUTE failed:");
835 return (true);
836 };
837
838 if (verbose) {
839 struct snl_msg_info attrs = {};
840 print_nlmsg(h, hdr, &attrs);
841 }
842 else {
843 if (r->rta_multipath.num_nhops != 0) {
844 for (uint32_t i = 0; i < r->rta_multipath.num_nhops; i++) {
845 struct rta_mpath_nh *nh = r->rta_multipath.nhops[i];
846
847 print_flushed_route(r, nh->gw);
848 }
849
850 } else
851 print_flushed_route(r, r->rta_gw);
852 }
853
854 return (0);
855 }
856
857 int
flushroutes_fib_nl(int fib,int af)858 flushroutes_fib_nl(int fib, int af)
859 {
860 struct snl_state ss = {};
861 struct snl_writer nw;
862 struct nl_helper h = {};
863
864 nl_init_socket(&ss);
865 snl_init_writer(&ss, &nw);
866
867 struct nlmsghdr *hdr = snl_create_msg_request(&nw, NL_RTM_GETROUTE);
868 hdr->nlmsg_flags |= NLM_F_DUMP;
869 struct rtmsg *rtm = snl_reserve_msg_object(&nw, struct rtmsg);
870 rtm->rtm_family = af;
871 snl_add_msg_attr_u32(&nw, RTA_TABLE, fib);
872
873 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss, hdr)) {
874 snl_free(&ss);
875 return (EINVAL);
876 }
877
878 struct snl_errmsg_data e = {};
879 uint32_t nlm_seq = hdr->nlmsg_seq;
880
881 nl_helper_init(&h);
882
883 while ((hdr = snl_read_reply_multi(&ss, nlm_seq, &e)) != NULL) {
884 struct snl_parsed_route r = { .rtax_weight = RT_DEFAULT_WEIGHT };
885 int error;
886
887 if (!snl_parse_nlmsg(&ss, hdr, &snl_rtm_route_parser, &r))
888 continue;
889 if (verbose) {
890 struct snl_msg_info attrs = {};
891 print_nlmsg(&h, hdr, &attrs);
892 }
893 if (r.rta_table != (uint32_t)fib || r.rtm_family != af)
894 continue;
895 if ((r.rta_rtflags & RTF_GATEWAY) == 0)
896 continue;
897 if (debugonly)
898 continue;
899
900 if ((error = flushroute_one(&h, &r)) != 0) {
901 if (error == EPERM)
902 errc(1, error, "RTM_DELROUTE failed:");
903 else
904 warnc(error, "RTM_DELROUTE failed:");
905 }
906 snl_clear_lb(&h.ss_cmd);
907 }
908
909 snl_free(&ss);
910 nl_helper_free(&h);
911
912 return (e.error);
913 }
914
915