]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/arp/arp_netlink.c
awk: Merge upstream 2nd Edition Awk Book
[FreeBSD/FreeBSD.git] / usr.sbin / arp / arp_netlink.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <stdbool.h>
5 #include <err.h>
6 #include <errno.h>
7 #include <netdb.h>
8
9 #include <sys/bitcount.h>
10 #include <sys/param.h>
11 #include <sys/linker.h>
12 #include <sys/module.h>
13 #include <sys/socket.h>
14 #include <sys/sysctl.h>
15 #include <sys/time.h>
16 #include <sys/types.h>
17
18 #include <netinet/in.h>
19 #include <arpa/inet.h>
20
21 #include <net/ethernet.h>
22 #include <net/if.h>
23 #include <net/if_dl.h>
24 #include <net/if_types.h>
25 #include <netlink/netlink.h>
26 #include <netlink/netlink_route.h>
27 #include <netlink/netlink_snl.h>
28 #include <netlink/netlink_snl_route.h>
29 #include <netlink/netlink_snl_route_compat.h>
30 #include <netlink/netlink_snl_route_parsers.h>
31
32 #include <libxo/xo.h>
33 #include "arp.h"
34
35 #define RTF_ANNOUNCE    RTF_PROTO2
36
37 static void
38 nl_init_socket(struct snl_state *ss)
39 {
40         if (snl_init(ss, NETLINK_ROUTE))
41                 return;
42
43         if (modfind("netlink") == -1 && errno == ENOENT) {
44                 /* Try to load */
45                 if (kldload("netlink") == -1)
46                         err(1, "netlink is not loaded and load attempt failed");
47                 if (snl_init(ss, NETLINK_ROUTE))
48                         return;
49         }
50
51         err(1, "unable to open netlink socket");
52 }
53
54 static bool
55 get_link_info(struct snl_state *ss, uint32_t ifindex,
56     struct snl_parsed_link_simple *link)
57 {
58         struct snl_writer nw;
59
60         snl_init_writer(ss, &nw);
61
62         struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETLINK);
63         struct ifinfomsg *ifmsg = snl_reserve_msg_object(&nw, struct ifinfomsg);
64         if (ifmsg != NULL)
65                 ifmsg->ifi_index = ifindex;
66         if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
67                 return (false);
68
69         hdr = snl_read_reply(ss, hdr->nlmsg_seq);
70
71         if (hdr == NULL || hdr->nlmsg_type != RTM_NEWLINK)
72                 return (false);
73
74         if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_link_parser_simple, link))
75                 return (false);
76
77         return (true);
78 }
79
80
81
82 static bool
83 has_l2(struct snl_state *ss, uint32_t ifindex)
84 {
85         struct snl_parsed_link_simple link = {};
86
87         if (!get_link_info(ss, ifindex, &link))
88                 return (false);
89
90         return (valid_type(link.ifi_type) != 0);
91 }
92
93 static uint32_t
94 get_myfib(void)
95 {
96         uint32_t fibnum = 0;
97         size_t len = sizeof(fibnum);
98
99         sysctlbyname("net.my_fibnum", (void *)&fibnum, &len, NULL, 0);
100
101         return (fibnum);
102 }
103
104 static int
105 guess_ifindex(struct snl_state *ss, uint32_t fibnum, struct in_addr addr)
106 {
107         struct snl_writer nw;
108
109         snl_init_writer(ss, &nw);
110
111         struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETROUTE);
112         struct rtmsg *rtm = snl_reserve_msg_object(&nw, struct rtmsg);
113         rtm->rtm_family = AF_INET;
114
115         struct sockaddr_in dst = { .sin_family = AF_INET, .sin_addr = addr };
116         snl_add_msg_attr_ip(&nw, RTA_DST, (struct sockaddr *)&dst);
117         snl_add_msg_attr_u32(&nw, RTA_TABLE, fibnum);
118
119         if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
120                 return (0);
121
122         hdr = snl_read_reply(ss, hdr->nlmsg_seq);
123
124         if (hdr->nlmsg_type != NL_RTM_NEWROUTE) {
125                 /* No route found, unable to guess ifindex */
126                 return (0);
127         }
128
129         struct snl_parsed_route r = {};
130         if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r))
131                 return (0);
132
133         if (r.rta_multipath.num_nhops > 0 || (r.rta_rtflags & RTF_GATEWAY))
134                 return (0);
135
136         /* Check if the interface is of supported type */
137         if (has_l2(ss, r.rta_oif))
138                 return (r.rta_oif);
139
140         /* Check the case when we matched the loopback route for P2P */
141         snl_init_writer(ss, &nw);
142         hdr = snl_create_msg_request(&nw, RTM_GETNEXTHOP);
143         snl_reserve_msg_object(&nw, struct nhmsg);
144
145         int off = snl_add_msg_attr_nested(&nw, NHA_FREEBSD);
146         snl_add_msg_attr_u32(&nw, NHAF_KID, r.rta_knh_id);
147         snl_add_msg_attr_u8(&nw, NHAF_FAMILY, AF_INET);
148         snl_add_msg_attr_u32(&nw, NHAF_TABLE, fibnum);
149         snl_end_attr_nested(&nw, off);
150
151         if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
152                 return (0);
153
154         hdr = snl_read_reply(ss, hdr->nlmsg_seq);
155
156         if (hdr->nlmsg_type != NL_RTM_NEWNEXTHOP) {
157                 /* No nexthop found, unable to guess ifindex */
158                 return (0);
159         }
160
161         struct snl_parsed_nhop nh = {};
162         if (!snl_parse_nlmsg(ss, hdr, &snl_nhmsg_parser, &nh))
163                 return (0);
164
165         return (nh.nhaf_aif);
166 }
167
168 static uint32_t
169 fix_ifindex(struct snl_state *ss, uint32_t ifindex, struct in_addr addr)
170 {
171         if (ifindex == 0)
172                 ifindex = guess_ifindex(ss, get_myfib(), addr);
173         return (ifindex);
174 }
175
176 static void
177 print_entry(struct snl_parsed_neigh *neigh, struct snl_parsed_link_simple *link)
178 {
179         const char *host;
180         struct hostent *hp;
181         struct sockaddr_in *addr = (struct sockaddr_in *)neigh->nda_dst;
182
183         xo_open_instance("arp-cache");
184
185         if (!opts.nflag)
186                 hp = gethostbyaddr((caddr_t)&(addr->sin_addr),
187                     sizeof(addr->sin_addr), AF_INET);
188         else
189                 hp = 0;
190         if (hp)
191                 host = hp->h_name;
192         else {
193                 host = "?";
194                 if (h_errno == TRY_AGAIN)
195                         opts.nflag = true;
196         }
197         xo_emit("{:hostname/%s} ({:ip-address/%s}) at ", host,
198             inet_ntoa(addr->sin_addr));
199         if (neigh->nda_lladdr != NULL) {
200                 struct sockaddr_dl sdl = {
201                         .sdl_family = AF_LINK,
202                         .sdl_type = link->ifi_type,
203                         .sdl_len = sizeof(struct sockaddr_dl),
204                         .sdl_alen = NLA_DATA_LEN(neigh->nda_lladdr),
205                 };
206                 memcpy(sdl.sdl_data, NLA_DATA(neigh->nda_lladdr), sdl.sdl_alen);
207
208                 if ((sdl.sdl_type == IFT_ETHER ||
209                     sdl.sdl_type == IFT_L2VLAN ||
210                     sdl.sdl_type == IFT_BRIDGE) &&
211                     sdl.sdl_alen == ETHER_ADDR_LEN)
212                         xo_emit("{:mac-address/%s}",
213                             ether_ntoa((struct ether_addr *)LLADDR(&sdl)));
214                 else {
215
216                         xo_emit("{:mac-address/%s}", link_ntoa(&sdl));
217                 }
218         } else
219                 xo_emit("{d:/(incomplete)}{en:incomplete/true}");
220         xo_emit(" on {:interface/%s}", link->ifla_ifname);
221
222         if (neigh->ndaf_next_ts == 0)
223                 xo_emit("{d:/ permanent}{en:permanent/true}");
224         else {
225                 time_t expire_time;
226                 struct timeval now;
227
228                 gettimeofday(&now, 0);
229                 if ((expire_time = neigh->ndaf_next_ts - now.tv_sec) > 0)
230                         xo_emit(" expires in {:expires/%d} seconds",
231                             (int)expire_time);
232                 else
233                         xo_emit("{d:/ expired}{en:expired/true}");
234         }
235
236         if (neigh->ndm_flags & NTF_PROXY)
237                 xo_emit("{d:/ published}{en:published/true}");
238
239         switch(link->ifi_type) {
240         case IFT_ETHER:
241                 xo_emit(" [{:type/ethernet}]");
242                 break;
243         case IFT_FDDI:
244                 xo_emit(" [{:type/fddi}]");
245                 break;
246         case IFT_ATM:
247                 xo_emit(" [{:type/atm}]");
248                 break;
249         case IFT_L2VLAN:
250                 xo_emit(" [{:type/vlan}]");
251                 break;
252         case IFT_IEEE1394:
253                 xo_emit(" [{:type/firewire}]");
254                 break;
255         case IFT_BRIDGE:
256                 xo_emit(" [{:type/bridge}]");
257                 break;
258         case IFT_INFINIBAND:
259                 xo_emit(" [{:type/infiniband}]");
260                 break;
261         default:
262                 break;
263         }
264
265         xo_emit("\n");
266
267         xo_close_instance("arp-cache");
268 }
269
270 int
271 print_entries_nl(uint32_t ifindex, struct in_addr addr)
272 {
273         struct snl_state ss_req = {}, ss_cmd = {};
274         struct snl_parsed_link_simple link = {};
275         struct snl_writer nw;
276
277         nl_init_socket(&ss_req);
278         snl_init_writer(&ss_req, &nw);
279
280         struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETNEIGH);
281         struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg);
282         if (ndmsg != NULL) {
283                 ndmsg->ndm_family = AF_INET;
284                 /* let kernel filter results by interface if provided */
285                 ndmsg->ndm_ifindex = ifindex;
286         }
287
288         if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss_req, hdr)) {
289                 snl_free(&ss_req);
290                 return (0);
291         }
292
293         uint32_t nlmsg_seq = hdr->nlmsg_seq;
294         struct snl_errmsg_data e = {};
295         int count = 0;
296         nl_init_socket(&ss_cmd);
297
298         while ((hdr = snl_read_reply_multi(&ss_req, nlmsg_seq, &e)) != NULL) {
299                 struct snl_parsed_neigh neigh = {};
300                 struct sockaddr_in *neighaddr;
301
302                 if (!snl_parse_nlmsg(&ss_req, hdr, &snl_rtm_neigh_parser, &neigh))
303                         continue;
304
305                 if (neigh.nda_ifindex != link.ifi_index) {
306                         snl_clear_lb(&ss_cmd);
307                         memset(&link, 0, sizeof(link));
308                         if (!get_link_info(&ss_cmd, neigh.nda_ifindex, &link))
309                                 continue;
310                 }
311
312                 /* filter results based on host if provided */
313                 neighaddr = (struct sockaddr_in *)neigh.nda_dst;
314                 if (addr.s_addr &&
315                     (addr.s_addr != neighaddr->sin_addr.s_addr))
316                         continue;
317
318                 print_entry(&neigh, &link);
319                 count++;
320                 snl_clear_lb(&ss_req);
321         }
322
323         snl_free(&ss_req);
324         snl_free(&ss_cmd);
325
326         return (count);
327 }
328
329 int
330 delete_nl(uint32_t ifindex, char *host)
331 {
332         struct snl_state ss = {};
333         struct snl_writer nw;
334         struct sockaddr_in *dst;
335
336         dst = getaddr(host);
337         if (dst == NULL)
338                 return (1);
339
340         nl_init_socket(&ss);
341
342         ifindex = fix_ifindex(&ss, ifindex, dst->sin_addr);
343         if (ifindex == 0) {
344                 xo_warnx("delete: cannot locate %s", host);
345                 snl_free(&ss);
346                 return (0);
347         }
348
349         snl_init_writer(&ss, &nw);
350         struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_DELNEIGH);
351         struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg);
352         if (ndmsg != NULL) {
353                 ndmsg->ndm_family = AF_INET;
354                 ndmsg->ndm_ifindex = ifindex;
355         }
356         snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)dst);
357
358         if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss, hdr)) {
359                 snl_free(&ss);
360                 return (1);
361         }
362
363         struct snl_errmsg_data e = {};
364         snl_read_reply_code(&ss, hdr->nlmsg_seq, &e);
365         if (e.error != 0) {
366                 if (e.error_str != NULL)
367                         xo_warnx("delete %s: %s (%s)", host, strerror(e.error), e.error_str);
368                 else
369                         xo_warnx("delete %s: %s", host, strerror(e.error));
370         } else
371                 printf("%s (%s) deleted\n", host, inet_ntoa(dst->sin_addr));
372
373         snl_free(&ss);
374
375         return (e.error != 0);
376 }
377
378 int
379 set_nl(uint32_t ifindex, struct sockaddr_in *dst, struct sockaddr_dl *sdl, char *host)
380 {
381         struct snl_state ss = {};
382         struct snl_writer nw;
383
384         nl_init_socket(&ss);
385
386         ifindex = fix_ifindex(&ss, ifindex, dst->sin_addr);
387         if (ifindex == 0) {
388                 xo_warnx("delete: cannot locate %s", host);
389                 snl_free(&ss);
390                 return (0);
391         }
392
393         if (opts.expire_time != 0)
394                 opts.flags &= ~RTF_STATIC;
395
396         snl_init_writer(&ss, &nw);
397         struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_NEWNEIGH);
398         hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
399         struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg);
400         if (ndmsg != NULL) {
401                 uint8_t nl_flags = 0;
402
403                 ndmsg->ndm_family = AF_INET;
404                 ndmsg->ndm_ifindex = ifindex;
405                 ndmsg->ndm_state = (opts.flags & RTF_STATIC) ? NUD_PERMANENT : NUD_NONE;
406
407                 if (opts.flags & RTF_ANNOUNCE)
408                         nl_flags |= NTF_PROXY;
409                 if (opts.flags & RTF_STATIC)
410                         nl_flags |= NTF_STICKY;
411                 ndmsg->ndm_flags = nl_flags;
412         }
413         snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)dst);
414         snl_add_msg_attr(&nw, NDA_LLADDR, sdl->sdl_alen, LLADDR(sdl));
415         
416         if (opts.expire_time != 0) {
417                 struct timeval now;
418
419                 gettimeofday(&now, 0);
420                 int off = snl_add_msg_attr_nested(&nw, NDA_FREEBSD);
421                 snl_add_msg_attr_u32(&nw, NDAF_NEXT_STATE_TS, now.tv_sec + opts.expire_time);
422                 snl_end_attr_nested(&nw, off);
423         }
424
425         if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss, hdr)) {
426                 snl_free(&ss);
427                 return (1);
428         }
429
430         struct snl_errmsg_data e = {};
431         snl_read_reply_code(&ss, hdr->nlmsg_seq, &e);
432         if (e.error != 0) {
433                 if (e.error_str != NULL)
434                         xo_warnx("set: %s: %s (%s)", host, strerror(e.error), e.error_str);
435                 else
436                         xo_warnx("set %s: %s", host, strerror(e.error));
437         }
438         snl_free(&ss);
439
440         return (e.error != 0);
441 }
442