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