1 #include <sys/param.h>
2 #include <sys/module.h>
3 #include <sys/file.h>
4 #include <sys/ioctl.h>
5 #include <sys/linker.h>
6 #include <sys/socket.h>
7 #include <sys/sysctl.h>
8 #include <sys/time.h>
9 #include <sys/queue.h>
10
11 #include <net/if.h>
12 #include <net/if_dl.h>
13 #include <net/if_types.h>
14
15 #include <netinet/in.h>
16 #include <netinet/if_ether.h>
17
18 #include <netinet/icmp6.h>
19 #include <netinet6/in6_var.h>
20 #include <netinet6/nd6.h>
21
22 #include <arpa/inet.h>
23
24 #include <ctype.h>
25 #include <netdb.h>
26 #include <errno.h>
27 #include <nlist.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <paths.h>
31 #include <stdlib.h>
32 #include <fcntl.h>
33 #include <unistd.h>
34 #include <libxo/xo.h>
35
36
37 #include <netlink/netlink.h>
38 #include <netlink/netlink_route.h>
39 #include <netlink/netlink_snl.h>
40 #include <netlink/netlink_snl_route.h>
41 #include <netlink/netlink_snl_route_compat.h>
42 #include <netlink/netlink_snl_route_parsers.h>
43
44 #include "ndp.h"
45
46 #define RTF_ANNOUNCE RTF_PROTO2
47
48 static void
nl_init_socket(struct snl_state * ss)49 nl_init_socket(struct snl_state *ss)
50 {
51 if (snl_init(ss, NETLINK_ROUTE))
52 return;
53
54 if (modfind("netlink") == -1 && errno == ENOENT) {
55 /* Try to load */
56 if (kldload("netlink") == -1)
57 xo_err(1, "netlink is not loaded and load attempt failed");
58 if (snl_init(ss, NETLINK_ROUTE))
59 return;
60 }
61
62 xo_err(1, "unable to open netlink socket");
63 }
64
65 static bool
get_link_info(struct snl_state * ss,uint32_t ifindex,struct snl_parsed_link_simple * link)66 get_link_info(struct snl_state *ss, uint32_t ifindex,
67 struct snl_parsed_link_simple *link)
68 {
69 struct snl_writer nw;
70
71 snl_init_writer(ss, &nw);
72
73 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK);
74 struct ifinfomsg *ifmsg = snl_reserve_msg_object(&nw, struct ifinfomsg);
75 if (ifmsg != NULL)
76 ifmsg->ifi_index = ifindex;
77 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
78 return (false);
79
80 hdr = snl_read_reply(ss, hdr->nlmsg_seq);
81
82 if (hdr == NULL || hdr->nlmsg_type != RTM_NEWLINK)
83 return (false);
84
85 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, link))
86 return (false);
87
88 return (true);
89 }
90
91
92
93 static bool
has_l2(struct snl_state * ss,uint32_t ifindex)94 has_l2(struct snl_state *ss, uint32_t ifindex)
95 {
96 struct snl_parsed_link_simple link = {};
97
98 if (!get_link_info(ss, ifindex, &link))
99 return (false);
100
101 return (valid_type(link.ifi_type) != 0);
102 }
103
104 static uint32_t
get_myfib(void)105 get_myfib(void)
106 {
107 uint32_t fibnum = 0;
108 size_t len = sizeof(fibnum);
109
110 sysctlbyname("net.my_fibnum", (void *)&fibnum, &len, NULL, 0);
111
112 return (fibnum);
113 }
114
115 static void
ip6_writemask(struct in6_addr * addr6,uint8_t mask)116 ip6_writemask(struct in6_addr *addr6, uint8_t mask)
117 {
118 uint32_t *cp;
119
120 for (cp = (uint32_t *)addr6; mask >= 32; mask -= 32)
121 *cp++ = 0xFFFFFFFF;
122 if (mask > 0)
123 *cp = htonl(mask ? ~((1 << (32 - mask)) - 1) : 0);
124 }
125 #define s6_addr32 __u6_addr.__u6_addr32
126 #define IN6_MASK_ADDR(a, m) do { \
127 (a)->s6_addr32[0] &= (m)->s6_addr32[0]; \
128 (a)->s6_addr32[1] &= (m)->s6_addr32[1]; \
129 (a)->s6_addr32[2] &= (m)->s6_addr32[2]; \
130 (a)->s6_addr32[3] &= (m)->s6_addr32[3]; \
131 } while (0)
132
133 static int
guess_ifindex(struct snl_state * ss,uint32_t fibnum,const struct sockaddr_in6 * dst)134 guess_ifindex(struct snl_state *ss, uint32_t fibnum, const struct sockaddr_in6 *dst)
135 {
136 struct snl_writer nw;
137
138 if (IN6_IS_ADDR_LINKLOCAL(&dst->sin6_addr))
139 return (dst->sin6_scope_id);
140 else if (IN6_IS_ADDR_MULTICAST(&dst->sin6_addr))
141 return (0);
142
143
144 snl_init_writer(ss, &nw);
145
146 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETROUTE);
147 struct rtmsg *rtm = snl_reserve_msg_object(&nw, struct rtmsg);
148 rtm->rtm_family = AF_INET6;
149
150 snl_add_msg_attr_ip(&nw, RTA_DST, (struct sockaddr *)dst);
151 snl_add_msg_attr_u32(&nw, RTA_TABLE, fibnum);
152
153 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
154 return (0);
155
156 hdr = snl_read_reply(ss, hdr->nlmsg_seq);
157
158 if (hdr->nlmsg_type != NL_RTM_NEWROUTE) {
159 /* No route found, unable to guess ifindex */
160 return (0);
161 }
162
163 struct snl_parsed_route r = {};
164 if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r))
165 return (0);
166
167 if (r.rta_multipath.num_nhops > 0 || (r.rta_rtflags & RTF_GATEWAY))
168 return (0);
169
170 /* Check if the interface is of supported type */
171 if (has_l2(ss, r.rta_oif))
172 return (r.rta_oif);
173
174 /* Check the case when we matched the loopback route for P2P */
175 snl_init_writer(ss, &nw);
176 hdr = snl_create_msg_request(&nw, RTM_GETNEXTHOP);
177 snl_reserve_msg_object(&nw, struct nhmsg);
178
179 int off = snl_add_msg_attr_nested(&nw, NHA_FREEBSD);
180 snl_add_msg_attr_u32(&nw, NHAF_KID, r.rta_knh_id);
181 snl_add_msg_attr_u8(&nw, NHAF_FAMILY, AF_INET6);
182 snl_add_msg_attr_u32(&nw, NHAF_TABLE, fibnum);
183 snl_end_attr_nested(&nw, off);
184
185 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
186 return (0);
187
188 hdr = snl_read_reply(ss, hdr->nlmsg_seq);
189
190 if (hdr->nlmsg_type != NL_RTM_NEWNEXTHOP) {
191 /* No nexthop found, unable to guess ifindex */
192 return (0);
193 }
194
195 struct snl_parsed_nhop nh = {};
196 if (!snl_parse_nlmsg(ss, hdr, &snl_nhmsg_parser, &nh))
197 return (0);
198
199 return (nh.nhaf_aif);
200 }
201
202 static uint32_t
fix_ifindex(struct snl_state * ss,uint32_t ifindex,const struct sockaddr_in6 * sa)203 fix_ifindex(struct snl_state *ss, uint32_t ifindex, const struct sockaddr_in6 *sa)
204 {
205 if (ifindex == 0)
206 ifindex = guess_ifindex(ss, get_myfib(), sa);
207 return (ifindex);
208 }
209
210 static void
print_entry(struct snl_parsed_neigh * neigh,struct snl_parsed_link_simple * link)211 print_entry(struct snl_parsed_neigh *neigh, struct snl_parsed_link_simple *link)
212 {
213 struct timeval now;
214 char host_buf[NI_MAXHOST];
215 int addrwidth;
216 int llwidth;
217 int ifwidth;
218 char *ifname;
219
220 getnameinfo(neigh->nda_dst, sizeof(struct sockaddr_in6), host_buf,
221 sizeof(host_buf), NULL, 0, (opts.nflag ? NI_NUMERICHOST : 0));
222
223 gettimeofday(&now, 0);
224 if (opts.tflag)
225 ts_print(&now);
226
227 struct sockaddr_dl sdl = {
228 .sdl_family = AF_LINK,
229 .sdl_type = link->ifi_type,
230 .sdl_len = sizeof(struct sockaddr_dl),
231 };
232
233 if (neigh->nda_lladdr) {
234 sdl.sdl_alen = NLA_DATA_LEN(neigh->nda_lladdr),
235 memcpy(sdl.sdl_data, NLA_DATA(neigh->nda_lladdr), sdl.sdl_alen);
236 }
237
238 addrwidth = strlen(host_buf);
239 if (addrwidth < W_ADDR)
240 addrwidth = W_ADDR;
241 llwidth = strlen(ether_str(&sdl));
242 if (W_ADDR + W_LL - addrwidth > llwidth)
243 llwidth = W_ADDR + W_LL - addrwidth;
244 ifname = link->ifla_ifname;
245 ifwidth = strlen(ifname);
246 if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth)
247 ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth;
248
249 xo_open_instance("neighbor-cache");
250 /* Compose format string for libxo, as it doesn't support *.* */
251 char xobuf[200];
252 snprintf(xobuf, sizeof(xobuf),
253 "{:address/%%-%d.%ds/%%s} {:mac-address/%%-%d.%ds/%%s} {:interface/%%%d.%ds/%%s}",
254 addrwidth, addrwidth, llwidth, llwidth, ifwidth, ifwidth);
255 xo_emit(xobuf, host_buf, ether_str(&sdl), ifname);
256
257 /* Print neighbor discovery specific information */
258 time_t expire = (time_t)neigh->ndaf_next_ts;
259 int expire_in = expire - now.tv_sec;
260 if (expire > now.tv_sec)
261 xo_emit("{d:/ %-9.9s}{e:expires_sec/%d}", sec2str(expire_in), expire_in);
262 else if (expire == 0)
263 xo_emit("{d:/ %-9.9s}{en:permanent/true}", "permanent");
264 else
265 xo_emit("{d:/ %-9.9s}{e:expires_sec/%d}", "expired", expire_in);
266
267 const char *lle_state = "";
268 switch (neigh->ndm_state) {
269 case NUD_INCOMPLETE:
270 lle_state = "I";
271 break;
272 case NUD_REACHABLE:
273 lle_state = "R";
274 break;
275 case NUD_STALE:
276 lle_state = "S";
277 break;
278 case NUD_DELAY:
279 lle_state = "D";
280 break;
281 case NUD_PROBE:
282 lle_state = "P";
283 break;
284 case NUD_FAILED:
285 lle_state = "F";
286 break;
287 default:
288 lle_state = "N";
289 break;
290 }
291 xo_emit(" {:neighbor-state/%s}", lle_state);
292
293 bool isrouter = neigh->ndm_flags & NTF_ROUTER;
294
295 /*
296 * other flags. R: router, P: proxy, W: ??
297 */
298 char flgbuf[8];
299 snprintf(flgbuf, sizeof(flgbuf), "%s%s",
300 isrouter ? "R" : "",
301 (neigh->ndm_flags & NTF_PROXY) ? "p" : "");
302 xo_emit(" {:nd-flags/%s}", flgbuf);
303
304 if (neigh->nda_probes != 0)
305 xo_emit("{u:/ %d}", neigh->nda_probes);
306
307 xo_emit("\n");
308 xo_close_instance("neighbor-cache");
309 }
310
311 int
print_entries_nl(uint32_t ifindex,struct sockaddr_in6 * addr,bool cflag)312 print_entries_nl(uint32_t ifindex, struct sockaddr_in6 *addr, bool cflag)
313 {
314 struct snl_state ss_req = {}, ss_cmd = {};
315 struct snl_parsed_link_simple link = {};
316 struct snl_writer nw;
317 struct nlmsghdr *hdr;
318 struct ndmsg *ndmsg;
319
320 nl_init_socket(&ss_req);
321 snl_init_writer(&ss_req, &nw);
322
323 /* Print header */
324 if (!opts.tflag && !cflag) {
325 char xobuf[200];
326 snprintf(xobuf, sizeof(xobuf),
327 "{T:/%%-%d.%ds} {T:/%%-%d.%ds} {T:/%%%d.%ds} {T:/%%-9.9s} {T:/%%1s} {T:/%%5s}\n",
328 W_ADDR, W_ADDR, W_LL, W_LL, W_IF, W_IF);
329 xo_emit(xobuf, "Neighbor", "Linklayer Address", "Netif", "Expire", "S", "Flags");
330 }
331
332 again:
333 hdr = snl_create_msg_request(&nw, RTM_GETNEIGH);
334 ndmsg = snl_reserve_msg_object(&nw, struct ndmsg);
335 if (ndmsg != NULL) {
336 ndmsg->ndm_family = AF_INET6;
337 ndmsg->ndm_ifindex = ifindex;
338 }
339
340 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss_req, hdr)) {
341 snl_free(&ss_req);
342 return (0);
343 }
344
345 uint32_t nlmsg_seq = hdr->nlmsg_seq;
346 struct snl_errmsg_data e = {};
347 int count = 0;
348 nl_init_socket(&ss_cmd);
349
350 xo_open_list("neighbor-cache");
351
352 while ((hdr = snl_read_reply_multi(&ss_req, nlmsg_seq, &e)) != NULL) {
353 struct snl_parsed_neigh neigh = {};
354
355 if (!snl_parse_nlmsg(&ss_req, hdr, &snl_rtm_neigh_parser, &neigh))
356 continue;
357
358 if (neigh.nda_ifindex != link.ifi_index) {
359 snl_clear_lb(&ss_cmd);
360 memset(&link, 0, sizeof(link));
361 if (!get_link_info(&ss_cmd, neigh.nda_ifindex, &link))
362 continue;
363 }
364
365 /* TODO: embed LL in the parser */
366 struct sockaddr_in6 *dst = (struct sockaddr_in6 *)neigh.nda_dst;
367 if (IN6_IS_ADDR_LINKLOCAL(&dst->sin6_addr))
368 dst->sin6_scope_id = neigh.nda_ifindex;
369
370 if (addr != NULL) {
371 if (IN6_ARE_ADDR_EQUAL(&addr->sin6_addr,
372 &dst->sin6_addr) == 0 ||
373 addr->sin6_scope_id != dst->sin6_scope_id)
374 continue;
375 }
376
377 if (cflag) {
378 char dst_str[INET6_ADDRSTRLEN];
379
380 inet_ntop(AF_INET6, &dst->sin6_addr, dst_str, sizeof(dst_str));
381 delete_nl(neigh.nda_ifindex, dst_str, false); /* no warn */
382 } else
383 print_entry(&neigh, &link);
384
385 count++;
386 snl_clear_lb(&ss_req);
387 }
388 if (opts.repeat) {
389 xo_emit("\n");
390 xo_flush();
391 sleep(opts.repeat);
392 goto again;
393 }
394 xo_close_list("neighbor-cache");
395
396 snl_free(&ss_req);
397 snl_free(&ss_cmd);
398
399 return (count);
400 }
401
402 int
delete_nl(uint32_t ifindex,char * host,bool warn)403 delete_nl(uint32_t ifindex, char *host, bool warn)
404 {
405 #define xo_warnx(...) do { if (warn) { xo_warnx(__VA_ARGS__); } } while(0)
406 struct snl_state ss = {};
407 struct snl_writer nw;
408 struct sockaddr_in6 dst;
409
410 int gai_error = getaddr(host, &dst);
411 if (gai_error) {
412 xo_warnx("%s: %s", host, gai_strerror(gai_error));
413 return 1;
414 }
415
416 nl_init_socket(&ss);
417
418 ifindex = fix_ifindex(&ss, ifindex, &dst);
419 if (ifindex == 0) {
420 xo_warnx("delete: cannot locate %s", host);
421 snl_free(&ss);
422 return (0);
423 }
424
425 snl_init_writer(&ss, &nw);
426 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_DELNEIGH);
427 struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg);
428 if (ndmsg != NULL) {
429 ndmsg->ndm_family = AF_INET6;
430 ndmsg->ndm_ifindex = ifindex;
431 }
432 snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)&dst);
433
434 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss, hdr)) {
435 snl_free(&ss);
436 return (1);
437 }
438
439 struct snl_errmsg_data e = {};
440 snl_read_reply_code(&ss, hdr->nlmsg_seq, &e);
441 if (e.error != 0) {
442 if (e.error_str != NULL)
443 xo_warnx("delete %s: %s (%s)", host, strerror(e.error), e.error_str);
444 else
445 xo_warnx("delete %s: %s", host, strerror(e.error));
446 } else {
447 char host_buf[NI_MAXHOST];
448 char ifix_buf[IFNAMSIZ];
449
450 getnameinfo((struct sockaddr *)&dst,
451 dst.sin6_len, host_buf,
452 sizeof(host_buf), NULL, 0,
453 (opts.nflag ? NI_NUMERICHOST : 0));
454
455 char *ifname = if_indextoname(ifindex, ifix_buf);
456 if (ifname == NULL) {
457 strlcpy(ifix_buf, "?", sizeof(ifix_buf));
458 ifname = ifix_buf;
459 }
460 char abuf[INET6_ADDRSTRLEN];
461 inet_ntop(AF_INET6, &dst.sin6_addr, abuf, sizeof(abuf));
462
463 xo_open_instance("neighbor-cache");
464 xo_emit("{:hostname/%s}{d:/ (%s) deleted\n}", host, host_buf);
465 xo_emit("{e:address/%s}{e:interface/%s}", abuf, ifname);
466 xo_close_instance("neighbor-cache");
467 }
468 snl_free(&ss);
469
470 return (e.error != 0);
471 #undef xo_warnx /* see above */
472 }
473
474 int
set_nl(uint32_t ifindex,struct sockaddr_in6 * dst,struct sockaddr_dl * sdl,char * host)475 set_nl(uint32_t ifindex, struct sockaddr_in6 *dst, struct sockaddr_dl *sdl, char *host)
476 {
477 struct snl_state ss = {};
478 struct snl_writer nw;
479
480 nl_init_socket(&ss);
481
482 ifindex = fix_ifindex(&ss, ifindex, dst);
483 if (ifindex == 0) {
484 xo_warnx("delete: cannot locate %s", host);
485 snl_free(&ss);
486 return (0);
487 }
488
489 snl_init_writer(&ss, &nw);
490 struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_NEWNEIGH);
491 hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
492 struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg);
493 if (ndmsg != NULL) {
494 uint8_t nl_flags = NTF_STICKY;
495
496 ndmsg->ndm_family = AF_INET6;
497 ndmsg->ndm_ifindex = ifindex;
498 ndmsg->ndm_state = NUD_PERMANENT;
499
500 if (opts.flags & RTF_ANNOUNCE)
501 nl_flags |= NTF_PROXY;
502 ndmsg->ndm_flags = nl_flags;
503 }
504 snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)dst);
505 snl_add_msg_attr(&nw, NDA_LLADDR, sdl->sdl_alen, LLADDR(sdl));
506
507 if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss, hdr)) {
508 snl_free(&ss);
509 return (1);
510 }
511
512 struct snl_errmsg_data e = {};
513 snl_read_reply_code(&ss, hdr->nlmsg_seq, &e);
514 if (e.error != 0) {
515 if (e.error_str != NULL)
516 xo_warnx("set: %s: %s (%s)", host, strerror(e.error), e.error_str);
517 else
518 xo_warnx("set %s: %s", host, strerror(e.error));
519 }
520 snl_free(&ss);
521
522 return (e.error != 0);
523 }
524
525