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