xref: /freebsd/usr.sbin/arp/arp_netlink.c (revision e0c4386e)
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 
443