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
37static void
38nl_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
54static bool
55get_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
82static bool
83has_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
93static uint32_t
94get_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
104static int
105guess_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
168static uint32_t
169fix_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
176static void
177print_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
270int
271print_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
329int
330delete_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
378int
379set_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("set: cannot locate %s", host);
389		snl_free(&ss);
390		return (0);
391	}
392
393	snl_init_writer(&ss, &nw);
394	struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_NEWNEIGH);
395	hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
396	struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg);
397	if (ndmsg != NULL) {
398		uint8_t nl_flags = 0;
399
400		ndmsg->ndm_family = AF_INET;
401		ndmsg->ndm_ifindex = ifindex;
402		ndmsg->ndm_state = (opts.expire_time == 0) ? \
403		    NUD_PERMANENT : NUD_NONE;
404
405		if (opts.flags & RTF_ANNOUNCE)
406			nl_flags |= NTF_PROXY;
407		if (opts.expire_time == 0)
408			nl_flags |= NTF_STICKY;
409		ndmsg->ndm_flags = nl_flags;
410	}
411	snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)dst);
412	snl_add_msg_attr(&nw, NDA_LLADDR, sdl->sdl_alen, LLADDR(sdl));
413
414	if (opts.expire_time != 0) {
415		struct timeval now;
416
417		gettimeofday(&now, 0);
418		int off = snl_add_msg_attr_nested(&nw, NDA_FREEBSD);
419		snl_add_msg_attr_u32(&nw, NDAF_NEXT_STATE_TS, now.tv_sec + opts.expire_time);
420		snl_end_attr_nested(&nw, off);
421	}
422
423	if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss, hdr)) {
424		snl_free(&ss);
425		return (1);
426	}
427
428	struct snl_errmsg_data e = {};
429	snl_read_reply_code(&ss, hdr->nlmsg_seq, &e);
430	if (e.error != 0) {
431		if (e.error_str != NULL)
432			xo_warnx("set: %s: %s (%s)", host, strerror(e.error), e.error_str);
433		else
434			xo_warnx("set %s: %s", host, strerror(e.error));
435	}
436	snl_free(&ss);
437
438	return (e.error != 0);
439}
440
441