xref: /freebsd/usr.sbin/arp/arp_netlink.c (revision 7ab1a32cd43cbae61ad4dd435d6a482bbf61cb52)
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
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
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
82 has_l2(struct snl_state *ss, uint32_t ifindex)
83 {
84 	struct snl_parsed_link_simple link = {};
85 
86 	if (!get_link_info(ss, ifindex, &link))
87 		return (false);
88 
89 	return (valid_type(link.ifi_type) != 0);
90 }
91 
92 static uint32_t
93 get_myfib(void)
94 {
95 	uint32_t fibnum = 0;
96 	size_t len = sizeof(fibnum);
97 
98 	sysctlbyname("net.my_fibnum", (void *)&fibnum, &len, NULL, 0);
99 
100 	return (fibnum);
101 }
102 
103 static int
104 guess_ifindex(struct snl_state *ss, uint32_t fibnum, struct in_addr addr)
105 {
106 	struct snl_writer nw;
107 
108 	snl_init_writer(ss, &nw);
109 
110 	struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETROUTE);
111 	struct rtmsg *rtm = snl_reserve_msg_object(&nw, struct rtmsg);
112 	rtm->rtm_family = AF_INET;
113 
114 	struct sockaddr_in dst = { .sin_family = AF_INET, .sin_addr = addr };
115 	snl_add_msg_attr_ip(&nw, RTA_DST, (struct sockaddr *)&dst);
116 	snl_add_msg_attr_u32(&nw, RTA_TABLE, fibnum);
117 
118 	if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
119 		return (0);
120 
121 	hdr = snl_read_reply(ss, hdr->nlmsg_seq);
122 
123 	if (hdr->nlmsg_type != NL_RTM_NEWROUTE) {
124 		/* No route found, unable to guess ifindex */
125 		return (0);
126 	}
127 
128 	struct snl_parsed_route r = {};
129 	if (!snl_parse_nlmsg(ss, hdr, &snl_rtm_route_parser, &r))
130 		return (0);
131 
132 	if (r.rta_multipath.num_nhops > 0 || (r.rta_rtflags & RTF_GATEWAY))
133 		return (0);
134 
135 	/* Check if the interface is of supported type */
136 	if (has_l2(ss, r.rta_oif))
137 		return (r.rta_oif);
138 
139 	/* Check the case when we matched the loopback route for P2P */
140 	snl_init_writer(ss, &nw);
141 	hdr = snl_create_msg_request(&nw, RTM_GETNEXTHOP);
142 	snl_reserve_msg_object(&nw, struct nhmsg);
143 
144 	int off = snl_add_msg_attr_nested(&nw, NHA_FREEBSD);
145 	snl_add_msg_attr_u32(&nw, NHAF_KID, r.rta_knh_id);
146 	snl_add_msg_attr_u8(&nw, NHAF_FAMILY, AF_INET);
147 	snl_add_msg_attr_u32(&nw, NHAF_TABLE, fibnum);
148 	snl_end_attr_nested(&nw, off);
149 
150 	if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(ss, hdr))
151 		return (0);
152 
153 	hdr = snl_read_reply(ss, hdr->nlmsg_seq);
154 
155 	if (hdr->nlmsg_type != NL_RTM_NEWNEXTHOP) {
156 		/* No nexthop found, unable to guess ifindex */
157 		return (0);
158 	}
159 
160 	struct snl_parsed_nhop nh = {};
161 	if (!snl_parse_nlmsg(ss, hdr, &snl_nhmsg_parser, &nh))
162 		return (0);
163 
164 	return (nh.nhaf_aif);
165 }
166 
167 static uint32_t
168 fix_ifindex(struct snl_state *ss, uint32_t ifindex, struct in_addr addr)
169 {
170 	if (ifindex == 0)
171 		ifindex = guess_ifindex(ss, get_myfib(), addr);
172 	return (ifindex);
173 }
174 
175 static void
176 print_entry(struct snl_parsed_neigh *neigh, struct snl_parsed_link_simple *link)
177 {
178 	const char *host;
179 	struct hostent *hp;
180 	struct sockaddr_in *addr = (struct sockaddr_in *)neigh->nda_dst;
181 
182 	xo_open_instance("arp-cache");
183 
184 	if (!opts.nflag)
185 		hp = gethostbyaddr((caddr_t)&(addr->sin_addr),
186 		    sizeof(addr->sin_addr), AF_INET);
187 	else
188 		hp = 0;
189 	if (hp)
190 		host = hp->h_name;
191 	else {
192 		host = "?";
193 		if (h_errno == TRY_AGAIN)
194 			opts.nflag = true;
195 	}
196 	xo_emit("{:hostname/%s} ({:ip-address/%s}) at ", host,
197 	    inet_ntoa(addr->sin_addr));
198 	if (neigh->nda_lladdr != NULL) {
199 		struct sockaddr_dl sdl = {
200 			.sdl_family = AF_LINK,
201 			.sdl_type = link->ifi_type,
202 			.sdl_len = sizeof(struct sockaddr_dl),
203 			.sdl_alen = NLA_DATA_LEN(neigh->nda_lladdr),
204 		};
205 		memcpy(sdl.sdl_data, NLA_DATA(neigh->nda_lladdr), sdl.sdl_alen);
206 
207 		if ((sdl.sdl_type == IFT_ETHER ||
208 		    sdl.sdl_type == IFT_L2VLAN ||
209 		    sdl.sdl_type == IFT_BRIDGE) &&
210 		    sdl.sdl_alen == ETHER_ADDR_LEN)
211 			xo_emit("{:mac-address/%s}",
212 			    ether_ntoa((struct ether_addr *)LLADDR(&sdl)));
213 		else {
214 
215 			xo_emit("{:mac-address/%s}", link_ntoa(&sdl));
216 		}
217 	} else
218 		xo_emit("{d:/(incomplete)}{en:incomplete/true}");
219 	xo_emit(" on {:interface/%s}", link->ifla_ifname);
220 
221 	if (neigh->ndaf_next_ts == 0)
222 		xo_emit("{d:/ permanent}{en:permanent/true}");
223 	else {
224 		time_t expire_time;
225 		struct timeval now;
226 
227 		gettimeofday(&now, 0);
228 		if ((expire_time = neigh->ndaf_next_ts - now.tv_sec) > 0)
229 			xo_emit(" expires in {:expires/%d} seconds",
230 			    (int)expire_time);
231 		else
232 			xo_emit("{d:/ expired}{en:expired/true}");
233 	}
234 
235 	if (neigh->ndm_flags & NTF_PROXY)
236 		xo_emit("{d:/ published}{en:published/true}");
237 
238 	switch(link->ifi_type) {
239 	case IFT_ETHER:
240 		xo_emit(" [{:type/ethernet}]");
241 		break;
242 	case IFT_FDDI:
243 		xo_emit(" [{:type/fddi}]");
244 		break;
245 	case IFT_ATM:
246 		xo_emit(" [{:type/atm}]");
247 		break;
248 	case IFT_L2VLAN:
249 		xo_emit(" [{:type/vlan}]");
250 		break;
251 	case IFT_IEEE1394:
252 		xo_emit(" [{:type/firewire}]");
253 		break;
254 	case IFT_BRIDGE:
255 		xo_emit(" [{:type/bridge}]");
256 		break;
257 	case IFT_INFINIBAND:
258 		xo_emit(" [{:type/infiniband}]");
259 		break;
260 	default:
261 		break;
262 	}
263 
264 	xo_emit("\n");
265 
266 	xo_close_instance("arp-cache");
267 }
268 
269 int
270 print_entries_nl(uint32_t ifindex, struct in_addr addr)
271 {
272 	struct snl_state ss_req = {}, ss_cmd = {};
273 	struct snl_parsed_link_simple link = {};
274 	struct snl_writer nw;
275 
276 	nl_init_socket(&ss_req);
277 	snl_init_writer(&ss_req, &nw);
278 
279 	struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_GETNEIGH);
280 	struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg);
281 	if (ndmsg != NULL) {
282 		ndmsg->ndm_family = AF_INET;
283 		/* let kernel filter results by interface if provided */
284 		ndmsg->ndm_ifindex = ifindex;
285 	}
286 
287 	if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss_req, hdr)) {
288 		snl_free(&ss_req);
289 		return (0);
290 	}
291 
292 	uint32_t nlmsg_seq = hdr->nlmsg_seq;
293 	struct snl_errmsg_data e = {};
294 	int count = 0;
295 	nl_init_socket(&ss_cmd);
296 
297 	while ((hdr = snl_read_reply_multi(&ss_req, nlmsg_seq, &e)) != NULL) {
298 		struct snl_parsed_neigh neigh = {};
299 		struct sockaddr_in *neighaddr;
300 
301 		if (!snl_parse_nlmsg(&ss_req, hdr, &snl_rtm_neigh_parser, &neigh))
302 			continue;
303 
304 		if (neigh.nda_ifindex != link.ifi_index) {
305 			snl_clear_lb(&ss_cmd);
306 			memset(&link, 0, sizeof(link));
307 			if (!get_link_info(&ss_cmd, neigh.nda_ifindex, &link))
308 				continue;
309 		}
310 
311 		/* filter results based on host if provided */
312 		neighaddr = (struct sockaddr_in *)neigh.nda_dst;
313 		if (addr.s_addr &&
314 		    (addr.s_addr != neighaddr->sin_addr.s_addr))
315 			continue;
316 
317 		print_entry(&neigh, &link);
318 		count++;
319 		snl_clear_lb(&ss_req);
320 	}
321 
322 	snl_free(&ss_req);
323 	snl_free(&ss_cmd);
324 
325 	return (count);
326 }
327 
328 int
329 delete_nl(uint32_t ifindex, char *host)
330 {
331 	struct snl_state ss = {};
332 	struct snl_writer nw;
333 	struct sockaddr_in *dst;
334 
335 	dst = getaddr(host);
336 	if (dst == NULL)
337 		return (1);
338 
339 	nl_init_socket(&ss);
340 
341 	ifindex = fix_ifindex(&ss, ifindex, dst->sin_addr);
342 	if (ifindex == 0) {
343 		xo_warnx("delete: cannot locate %s", host);
344 		snl_free(&ss);
345 		return (0);
346 	}
347 
348 	snl_init_writer(&ss, &nw);
349 	struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_DELNEIGH);
350 	struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg);
351 	if (ndmsg != NULL) {
352 		ndmsg->ndm_family = AF_INET;
353 		ndmsg->ndm_ifindex = ifindex;
354 	}
355 	snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)dst);
356 
357 	if (! (hdr = snl_finalize_msg(&nw)) || !snl_send_message(&ss, hdr)) {
358 		snl_free(&ss);
359 		return (1);
360 	}
361 
362 	struct snl_errmsg_data e = {};
363 	snl_read_reply_code(&ss, hdr->nlmsg_seq, &e);
364 	if (e.error != 0) {
365 		if (e.error_str != NULL)
366 			xo_warnx("delete %s: %s (%s)", host, strerror(e.error), e.error_str);
367 		else
368 			xo_warnx("delete %s: %s", host, strerror(e.error));
369 	} else
370 		printf("%s (%s) deleted\n", host, inet_ntoa(dst->sin_addr));
371 
372 	snl_free(&ss);
373 
374 	return (e.error != 0);
375 }
376 
377 int
378 set_nl(uint32_t ifindex, struct sockaddr_in *dst, struct sockaddr_dl *sdl, char *host)
379 {
380 	struct snl_state ss = {};
381 	struct snl_writer nw;
382 
383 	nl_init_socket(&ss);
384 
385 	ifindex = fix_ifindex(&ss, ifindex, dst->sin_addr);
386 	if (ifindex == 0) {
387 		xo_warnx("set: cannot locate %s", host);
388 		snl_free(&ss);
389 		return (0);
390 	}
391 
392 	snl_init_writer(&ss, &nw);
393 	struct nlmsghdr *hdr = snl_create_msg_request(&nw, RTM_NEWNEIGH);
394 	hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
395 	struct ndmsg *ndmsg = snl_reserve_msg_object(&nw, struct ndmsg);
396 	if (ndmsg != NULL) {
397 		uint8_t nl_flags = 0;
398 
399 		ndmsg->ndm_family = AF_INET;
400 		ndmsg->ndm_ifindex = ifindex;
401 		ndmsg->ndm_state = (opts.expire_time == 0) ? \
402 		    NUD_PERMANENT : NUD_NONE;
403 
404 		if (opts.flags & RTF_ANNOUNCE)
405 			nl_flags |= NTF_PROXY;
406 		if (opts.expire_time == 0)
407 			nl_flags |= NTF_STICKY;
408 		ndmsg->ndm_flags = nl_flags;
409 	}
410 	snl_add_msg_attr_ip(&nw, NDA_DST, (struct sockaddr *)dst);
411 	snl_add_msg_attr(&nw, NDA_LLADDR, sdl->sdl_alen, LLADDR(sdl));
412 
413 	if (opts.expire_time != 0) {
414 		struct timeval now;
415 
416 		gettimeofday(&now, 0);
417 		int off = snl_add_msg_attr_nested(&nw, NDA_FREEBSD);
418 		snl_add_msg_attr_u32(&nw, NDAF_NEXT_STATE_TS, now.tv_sec + opts.expire_time);
419 		snl_end_attr_nested(&nw, off);
420 	}
421 
422 	if (! (hdr = 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("set: %s: %s (%s)", host, strerror(e.error), e.error_str);
432 		else
433 			xo_warnx("set %s: %s", host, strerror(e.error));
434 	}
435 	snl_free(&ss);
436 
437 	return (e.error != 0);
438 }
439 
440