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