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