xref: /freebsd/contrib/ofed/libibverbs/neigh.c (revision d6b92ffa)
1 /* Licensed under the OpenIB.org BSD license (FreeBSD Variant) - See COPYING.md
2  */
3 
4 #include "config.h"
5 #include <net/if_packet.h>
6 #include <linux/netlink.h>
7 #include <linux/rtnetlink.h>
8 #include <infiniband/endian.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <stdbool.h>
12 
13 #if HAVE_WORKING_IF_H
14 #include <net/if.h>
15 #endif
16 
17 #include <netlink/route/rtnl.h>
18 #include <netlink/route/link.h>
19 #include <netlink/route/route.h>
20 #include <netlink/route/neighbour.h>
21 
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <sys/timerfd.h>
25 #include <errno.h>
26 #include <unistd.h>
27 #include <ifaddrs.h>
28 #include <netdb.h>
29 #include <assert.h>
30 
31 #if !HAVE_WORKING_IF_H
32 /* We need this decl from net/if.h but old systems do not let use co-include
33    net/if.h and netlink/route/link.h */
34 extern unsigned int if_nametoindex(__const char *__ifname) __THROW;
35 #endif
36 
37 /* for PFX */
38 #include "ibverbs.h"
39 #include <sys/param.h>
40 
41 #include "neigh.h"
42 
43 #ifndef HAVE_LIBNL1
44 #include <netlink/route/link/vlan.h>
45 #endif
46 
47 static pthread_once_t device_neigh_alloc = PTHREAD_ONCE_INIT;
48 static struct nl_sock *zero_socket;
49 
50 union sktaddr {
51 	struct sockaddr s;
52 	struct sockaddr_in s4;
53 	struct sockaddr_in6 s6;
54 };
55 
56 struct skt {
57 	union sktaddr sktaddr;
58 	socklen_t len;
59 };
60 
set_link_port(union sktaddr * s,__be16 port,int oif)61 static int set_link_port(union sktaddr *s, __be16 port, int oif)
62 {
63 	switch (s->s.sa_family) {
64 	case AF_INET:
65 		s->s4.sin_port = port;
66 		break;
67 	case AF_INET6:
68 		s->s6.sin6_port = port;
69 		s->s6.sin6_scope_id = oif;
70 		break;
71 	default:
72 		return -EINVAL;
73 	}
74 
75 	return 0;
76 }
77 
cmp_address(const struct sockaddr * s1,const struct sockaddr * s2)78 static bool cmp_address(const struct sockaddr *s1,
79 			const struct sockaddr *s2)
80 {
81 	if (s1->sa_family != s2->sa_family)
82 		return false;
83 
84 	switch (s1->sa_family) {
85 	case AF_INET:
86 		return ((struct sockaddr_in *)s1)->sin_addr.s_addr ==
87 		       ((struct sockaddr_in *)s2)->sin_addr.s_addr;
88 	case AF_INET6:
89 		return !memcmp(
90 			((struct sockaddr_in6 *)s1)->sin6_addr.s6_addr,
91 			((struct sockaddr_in6 *)s2)->sin6_addr.s6_addr,
92 			sizeof(((struct sockaddr_in6 *)s1)->sin6_addr.s6_addr));
93 	default:
94 		return false;
95 	}
96 }
97 
get_ifindex(const struct sockaddr * s)98 static int get_ifindex(const struct sockaddr *s)
99 {
100 	struct ifaddrs *ifaddr, *ifa;
101 	int name2index = -ENODEV;
102 
103 	if (-1 == getifaddrs(&ifaddr))
104 		return errno;
105 
106 	for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
107 		if (ifa->ifa_addr == NULL)
108 			continue;
109 
110 		if (cmp_address(ifa->ifa_addr, s)) {
111 			name2index = if_nametoindex(ifa->ifa_name);
112 			break;
113 		}
114 	}
115 
116 	freeifaddrs(ifaddr);
117 
118 	return name2index;
119 }
120 
get_neigh_mac(struct get_neigh_handler * neigh_handler)121 static struct nl_addr *get_neigh_mac(struct get_neigh_handler *neigh_handler)
122 {
123 	struct rtnl_neigh *neigh;
124 	struct nl_addr *ll_addr = NULL;
125 
126 	/* future optimization - if link local address - parse address and
127 	 * return mac now instead of doing so after the routing CB. This
128 	 * is of course referred to GIDs */
129 	neigh = rtnl_neigh_get(neigh_handler->neigh_cache,
130 			       neigh_handler->oif,
131 			       neigh_handler->dst);
132 	if (neigh == NULL)
133 		return NULL;
134 
135 	ll_addr = rtnl_neigh_get_lladdr(neigh);
136 	if (NULL != ll_addr)
137 		ll_addr = nl_addr_clone(ll_addr);
138 
139 	rtnl_neigh_put(neigh);
140 	return ll_addr;
141 }
142 
get_neigh_cb_event(struct nl_object * obj,void * arg)143 static void get_neigh_cb_event(struct nl_object *obj, void *arg)
144 {
145 	struct get_neigh_handler *neigh_handler =
146 		(struct get_neigh_handler *)arg;
147 	/* assumed serilized callback (no parallel execution of function) */
148 	if (nl_object_match_filter(
149 		obj,
150 		(struct nl_object *)neigh_handler->filter_neigh)) {
151 		struct rtnl_neigh *neigh = (struct rtnl_neigh *)obj;
152 		/* check that we didn't set it already */
153 		if (neigh_handler->found_ll_addr == NULL) {
154 			if (rtnl_neigh_get_lladdr(neigh) == NULL)
155 				return;
156 
157 			neigh_handler->found_ll_addr =
158 				nl_addr_clone(rtnl_neigh_get_lladdr(neigh));
159 		}
160 	}
161 }
162 
get_neigh_cb(struct nl_msg * msg,void * arg)163 static int get_neigh_cb(struct nl_msg *msg, void *arg)
164 {
165 	struct get_neigh_handler *neigh_handler =
166 		(struct get_neigh_handler *)arg;
167 
168 	if (nl_msg_parse(msg, &get_neigh_cb_event, neigh_handler) < 0)
169 		errno = ENOMSG;
170 
171 	return NL_OK;
172 }
173 
set_neigh_filter(struct get_neigh_handler * neigh_handler,struct rtnl_neigh * filter)174 static void set_neigh_filter(struct get_neigh_handler *neigh_handler,
175 			     struct rtnl_neigh *filter) {
176 	neigh_handler->filter_neigh = filter;
177 }
178 
create_filter_neigh_for_dst(struct nl_addr * dst_addr,int oif)179 static struct rtnl_neigh *create_filter_neigh_for_dst(struct nl_addr *dst_addr,
180 						      int oif)
181 {
182 	struct rtnl_neigh *filter_neigh;
183 
184 	filter_neigh = rtnl_neigh_alloc();
185 	if (filter_neigh == NULL)
186 		return NULL;
187 
188 	rtnl_neigh_set_ifindex(filter_neigh, oif);
189 	rtnl_neigh_set_dst(filter_neigh, dst_addr);
190 
191 	return filter_neigh;
192 }
193 
194 #define PORT_DISCARD htobe16(9)
195 #define SEND_PAYLOAD "H"
196 
create_socket(struct get_neigh_handler * neigh_handler,struct skt * addr_dst,int * psock_fd)197 static int create_socket(struct get_neigh_handler *neigh_handler,
198 			 struct skt *addr_dst, int *psock_fd)
199 {
200 	int err;
201 	struct skt addr_src;
202 	int sock_fd;
203 
204 	memset(addr_dst, 0, sizeof(*addr_dst));
205 	memset(&addr_src, 0, sizeof(addr_src));
206 	addr_src.len = sizeof(addr_src.sktaddr);
207 
208 	err = nl_addr_fill_sockaddr(neigh_handler->src,
209 				    &addr_src.sktaddr.s,
210 				    &addr_src.len);
211 	if (err) {
212 		errno = EADDRNOTAVAIL;
213 		return -1;
214 	}
215 
216 	addr_dst->len = sizeof(addr_dst->sktaddr);
217 	err = nl_addr_fill_sockaddr(neigh_handler->dst,
218 				    &addr_dst->sktaddr.s,
219 				    &addr_dst->len);
220 	if (err) {
221 		errno = EADDRNOTAVAIL;
222 		return -1;
223 	}
224 
225 	err = set_link_port(&addr_dst->sktaddr, PORT_DISCARD,
226 			    neigh_handler->oif);
227 	if (err)
228 		return -1;
229 
230 	sock_fd = socket(addr_dst->sktaddr.s.sa_family,
231 			 SOCK_DGRAM | SOCK_CLOEXEC, 0);
232 	if (sock_fd == -1)
233 		return -1;
234 	err = bind(sock_fd, &addr_src.sktaddr.s, addr_src.len);
235 	if (err) {
236 		close(sock_fd);
237 		return -1;
238 	}
239 
240 	*psock_fd = sock_fd;
241 
242 	return 0;
243 }
244 
245 #define NUM_OF_RETRIES 10
246 #define NUM_OF_TRIES ((NUM_OF_RETRIES) + 1)
247 #if NUM_OF_TRIES < 1
248 #error "neigh: invalid value of NUM_OF_RETRIES"
249 #endif
create_timer(struct get_neigh_handler * neigh_handler)250 static int create_timer(struct get_neigh_handler *neigh_handler)
251 {
252 	int user_timeout = neigh_handler->timeout/NUM_OF_TRIES;
253 	struct timespec timeout = {
254 		.tv_sec = user_timeout / 1000,
255 		.tv_nsec = (user_timeout % 1000) * 1000000
256 	};
257 	struct itimerspec timer_time = {.it_value = timeout};
258 	int timer_fd;
259 
260 	timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
261 	if (timer_fd == -1)
262 		return timer_fd;
263 
264 	if (neigh_handler->timeout) {
265 		if (NUM_OF_TRIES <= 1)
266 			bzero(&timer_time.it_interval,
267 			      sizeof(timer_time.it_interval));
268 		else
269 			timer_time.it_interval = timeout;
270 		if (timerfd_settime(timer_fd, 0, &timer_time, NULL)) {
271 			close(timer_fd);
272 			return -1;
273 		}
274 	}
275 
276 	return timer_fd;
277 }
278 
279 #define UDP_SOCKET_MAX_SENDTO 100000ULL
try_send_to(int sock_fd,void * buff,size_t buf_size,struct skt * addr_dst)280 static int try_send_to(int sock_fd, void *buff, size_t buf_size,
281 		       struct skt *addr_dst)
282 {
283 	uint64_t max_count = UDP_SOCKET_MAX_SENDTO;
284 	int err;
285 
286 	do {
287 		err = sendto(sock_fd, buff, buf_size, 0,
288 			     &addr_dst->sktaddr.s,
289 			     addr_dst->len);
290 		if (err > 0)
291 			err = 0;
292 	} while (-1 == err && EADDRNOTAVAIL == errno && --max_count);
293 
294 	return err;
295 }
296 
process_get_neigh_mac(struct get_neigh_handler * neigh_handler)297 static struct nl_addr *process_get_neigh_mac(
298 		struct get_neigh_handler *neigh_handler)
299 {
300 	int err;
301 	struct nl_addr *ll_addr = get_neigh_mac(neigh_handler);
302 	struct rtnl_neigh *neigh_filter;
303 	fd_set fdset;
304 	int sock_fd;
305 	int fd;
306 	int nfds;
307 	int timer_fd;
308 	int ret;
309 	struct skt addr_dst;
310 	char buff[sizeof(SEND_PAYLOAD)] = SEND_PAYLOAD;
311 	int retries = 0;
312 
313 	if (NULL != ll_addr)
314 		return ll_addr;
315 
316 	err = nl_socket_add_membership(neigh_handler->sock,
317 				       RTNLGRP_NEIGH);
318 	if (err < 0)
319 		return NULL;
320 
321 	neigh_filter = create_filter_neigh_for_dst(neigh_handler->dst,
322 						   neigh_handler->oif);
323 	if (neigh_filter == NULL)
324 		return NULL;
325 
326 	set_neigh_filter(neigh_handler, neigh_filter);
327 
328 	nl_socket_disable_seq_check(neigh_handler->sock);
329 	nl_socket_modify_cb(neigh_handler->sock, NL_CB_VALID, NL_CB_CUSTOM,
330 			    &get_neigh_cb, neigh_handler);
331 
332 	fd = nl_socket_get_fd(neigh_handler->sock);
333 
334 	err = create_socket(neigh_handler, &addr_dst, &sock_fd);
335 
336 	if (err)
337 		return NULL;
338 
339 	err = try_send_to(sock_fd, buff, sizeof(buff), &addr_dst);
340 	if (err)
341 		goto close_socket;
342 
343 	timer_fd = create_timer(neigh_handler);
344 	if (timer_fd < 0)
345 		goto close_socket;
346 
347 	nfds = MAX(fd, timer_fd) + 1;
348 
349 	while (1) {
350 		FD_ZERO(&fdset);
351 		FD_SET(fd, &fdset);
352 		FD_SET(timer_fd, &fdset);
353 
354 		/* wait for an incoming message on the netlink socket */
355 		ret = select(nfds, &fdset, NULL, NULL, NULL);
356 		if (ret == -1) {
357 			goto select_err;
358 		} else if (ret) {
359 			if (FD_ISSET(fd, &fdset)) {
360 				nl_recvmsgs_default(neigh_handler->sock);
361 				if (neigh_handler->found_ll_addr)
362 					break;
363 			} else {
364 				nl_cache_refill(neigh_handler->sock,
365 						neigh_handler->neigh_cache);
366 				ll_addr = get_neigh_mac(neigh_handler);
367 				if (NULL != ll_addr) {
368 					break;
369 				} else if (FD_ISSET(timer_fd, &fdset) &&
370 					   retries < NUM_OF_RETRIES) {
371 					try_send_to(sock_fd, buff, sizeof(buff),
372 						    &addr_dst);
373 				}
374 			}
375 
376 			if (FD_ISSET(timer_fd, &fdset)) {
377 				uint64_t read_val;
378 				ssize_t rc;
379 
380 				rc =
381 				    read(timer_fd, &read_val, sizeof(read_val));
382 				assert(rc == sizeof(read_val));
383 				if (++retries >=  NUM_OF_TRIES) {
384 					if (!errno)
385 						errno = EDESTADDRREQ;
386 					break;
387 				}
388 			}
389 		}
390 	}
391 select_err:
392 	close(timer_fd);
393 close_socket:
394 	close(sock_fd);
395 	return ll_addr ? ll_addr : neigh_handler->found_ll_addr;
396 }
397 
get_mcast_mac_ipv4(struct nl_addr * dst,struct nl_addr ** ll_addr)398 static int get_mcast_mac_ipv4(struct nl_addr *dst, struct nl_addr **ll_addr)
399 {
400 	uint8_t mac_addr[6] = {0x01, 0x00, 0x5E};
401 	uint32_t addr = be32toh(*(__be32 *)nl_addr_get_binary_addr(dst));
402 
403 	mac_addr[5] = addr & 0xFF;
404 	addr >>= 8;
405 	mac_addr[4] = addr & 0xFF;
406 	addr >>= 8;
407 	mac_addr[3] = addr & 0x7F;
408 
409 	*ll_addr = nl_addr_build(AF_LLC, mac_addr, sizeof(mac_addr));
410 
411 	return *ll_addr == NULL ? -EINVAL : 0;
412 }
413 
get_mcast_mac_ipv6(struct nl_addr * dst,struct nl_addr ** ll_addr)414 static int get_mcast_mac_ipv6(struct nl_addr *dst, struct nl_addr **ll_addr)
415 {
416 	uint8_t mac_addr[6] = {0x33, 0x33};
417 
418 	memcpy(mac_addr + 2, (uint8_t *)nl_addr_get_binary_addr(dst) + 12, 4);
419 
420 	*ll_addr = nl_addr_build(AF_LLC, mac_addr, sizeof(mac_addr));
421 
422 	return *ll_addr == NULL ? -EINVAL : 0;
423 }
424 
get_link_local_mac_ipv6(struct nl_addr * dst,struct nl_addr ** ll_addr)425 static int get_link_local_mac_ipv6(struct nl_addr *dst,
426 				   struct nl_addr **ll_addr)
427 {
428 	uint8_t mac_addr[6];
429 
430 	memcpy(mac_addr + 3, (uint8_t *)nl_addr_get_binary_addr(dst) + 13, 3);
431 	memcpy(mac_addr, (uint8_t *)nl_addr_get_binary_addr(dst) + 8, 3);
432 	mac_addr[0] ^= 2;
433 
434 	*ll_addr = nl_addr_build(AF_LLC, mac_addr, sizeof(mac_addr));
435 	return *ll_addr == NULL ? -EINVAL : 0;
436 }
437 
438 static const struct encoded_l3_addr {
439 	short family;
440 	uint8_t prefix_bits;
441 	const uint8_t data[16];
442 	int (*getter)(struct nl_addr *dst, struct nl_addr **ll_addr);
443 } encoded_prefixes[] = {
444 	{.family = AF_INET,
445 	 .prefix_bits = 4,
446 	 .data = {0xe0},
447 	 .getter = &get_mcast_mac_ipv4},
448 	{.family = AF_INET6,
449 	 .prefix_bits = 8,
450 	 .data = {0xff},
451 	 .getter = &get_mcast_mac_ipv6},
452 	{.family = AF_INET6,
453 	 .prefix_bits = 64,
454 	 .data = {0xfe, 0x80},
455 	 .getter = get_link_local_mac_ipv6},
456 };
457 
nl_addr_cmp_prefix_msb(void * addr1,int len1,void * addr2,int len2)458 static int nl_addr_cmp_prefix_msb(void *addr1, int len1, void *addr2, int len2)
459 {
460 	int len = min(len1, len2);
461 	int bytes = len / 8;
462 	int d = memcmp(addr1, addr2, bytes);
463 
464 	if (d == 0) {
465 		int mask = ((1UL << (len % 8)) - 1UL) << (8 - len);
466 
467 		d = (((uint8_t *)addr1)[bytes] & mask) -
468 		    (((uint8_t *)addr2)[bytes] & mask);
469 	}
470 
471 	return d;
472 }
473 
handle_encoded_mac(struct nl_addr * dst,struct nl_addr ** ll_addr)474 static int handle_encoded_mac(struct nl_addr *dst, struct nl_addr **ll_addr)
475 {
476 	uint32_t family = nl_addr_get_family(dst);
477 	struct nl_addr *prefix = NULL;
478 	int i;
479 	int ret = 1;
480 
481 	for (i = 0;
482 	     i < sizeof(encoded_prefixes)/sizeof(encoded_prefixes[0]) &&
483 	     ret; prefix = NULL, i++) {
484 		if (encoded_prefixes[i].family != family)
485 			continue;
486 
487 		prefix = nl_addr_build(
488 		    family, (void *)encoded_prefixes[i].data,
489 		    min_t(size_t, encoded_prefixes[i].prefix_bits / 8 +
490 				      !!(encoded_prefixes[i].prefix_bits % 8),
491 			  sizeof(encoded_prefixes[i].data)));
492 
493 		if (prefix == NULL)
494 			return -ENOMEM;
495 		nl_addr_set_prefixlen(prefix,
496 				      encoded_prefixes[i].prefix_bits);
497 
498 		if (nl_addr_cmp_prefix_msb(nl_addr_get_binary_addr(dst),
499 					   nl_addr_get_prefixlen(dst),
500 					   nl_addr_get_binary_addr(prefix),
501 					   nl_addr_get_prefixlen(prefix)))
502 			continue;
503 
504 		ret = encoded_prefixes[i].getter(dst, ll_addr);
505 		nl_addr_put(prefix);
506 	}
507 
508 	return ret;
509 }
510 
get_route_cb_parser(struct nl_object * obj,void * arg)511 static void get_route_cb_parser(struct nl_object *obj, void *arg)
512 {
513 	struct get_neigh_handler *neigh_handler =
514 		(struct get_neigh_handler *)arg;
515 
516 	struct rtnl_route *route = (struct rtnl_route *)obj;
517 	struct nl_addr *gateway = NULL;
518 	struct nl_addr *src = rtnl_route_get_pref_src(route);
519 	int oif;
520 	int type = rtnl_route_get_type(route);
521 	struct rtnl_link *link;
522 
523 	struct rtnl_nexthop *nh = rtnl_route_nexthop_n(route, 0);
524 
525 	if (nh != NULL)
526 		gateway = rtnl_route_nh_get_gateway(nh);
527 	oif = rtnl_route_nh_get_ifindex(nh);
528 
529 	if (gateway) {
530 		nl_addr_put(neigh_handler->dst);
531 		neigh_handler->dst = nl_addr_clone(gateway);
532 	}
533 
534 	if (RTN_BLACKHOLE == type ||
535 	    RTN_UNREACHABLE == type ||
536 	    RTN_PROHIBIT == type ||
537 	    RTN_THROW == type) {
538 		errno = ENETUNREACH;
539 		goto err;
540 	}
541 
542 	if (!neigh_handler->src && src)
543 		neigh_handler->src = nl_addr_clone(src);
544 
545 	if (neigh_handler->oif < 0 && oif > 0)
546 		neigh_handler->oif = oif;
547 
548 	/* Link Local */
549 	if (RTN_LOCAL == type) {
550 		struct nl_addr *lladdr;
551 
552 		link = rtnl_link_get(neigh_handler->link_cache,
553 				     neigh_handler->oif);
554 
555 		if (link == NULL)
556 			goto err;
557 
558 		lladdr = rtnl_link_get_addr(link);
559 
560 		if (lladdr == NULL)
561 			goto err_link;
562 
563 		neigh_handler->found_ll_addr = nl_addr_clone(lladdr);
564 		rtnl_link_put(link);
565 	} else {
566 		handle_encoded_mac(
567 			neigh_handler->dst,
568 			&neigh_handler->found_ll_addr);
569 	}
570 
571 	return;
572 
573 err_link:
574 	rtnl_link_put(link);
575 err:
576 	if (neigh_handler->src) {
577 		nl_addr_put(neigh_handler->src);
578 		neigh_handler->src = NULL;
579 	}
580 }
581 
get_route_cb(struct nl_msg * msg,void * arg)582 static int get_route_cb(struct nl_msg *msg, void *arg)
583 {
584 	struct get_neigh_handler *neigh_handler =
585 		(struct get_neigh_handler *)arg;
586 	int err;
587 
588 	err = nl_msg_parse(msg, &get_route_cb_parser, neigh_handler);
589 	if (err < 0) {
590 		errno = ENOMSG;
591 		return err;
592 	}
593 
594 	if (!neigh_handler->dst || !neigh_handler->src ||
595 	    neigh_handler->oif <= 0) {
596 		errno = EINVAL;
597 		return -1;
598 	}
599 
600 	if (NULL != neigh_handler->found_ll_addr)
601 		goto found;
602 
603 	neigh_handler->found_ll_addr =
604 		process_get_neigh_mac(neigh_handler);
605 
606 found:
607 	return neigh_handler->found_ll_addr ? 0 : -1;
608 }
609 
neigh_get_oif_from_src(struct get_neigh_handler * neigh_handler)610 int neigh_get_oif_from_src(struct get_neigh_handler *neigh_handler)
611 {
612 	int oif = -ENODEV;
613 	struct addrinfo *src_info;
614 	int err;
615 
616 	err = nl_addr_info(neigh_handler->src, &src_info);
617 	if (err) {
618 		if (!errno)
619 			errno = ENXIO;
620 		return oif;
621 	}
622 
623 	oif = get_ifindex(src_info->ai_addr);
624 	if (oif <= 0)
625 		goto free;
626 
627 free:
628 	freeaddrinfo(src_info);
629 	return oif;
630 }
631 
alloc_zero_based_socket(void)632 static void alloc_zero_based_socket(void)
633 {
634 	zero_socket = nl_socket_alloc();
635 }
636 
neigh_init_resources(struct get_neigh_handler * neigh_handler,int timeout)637 int neigh_init_resources(struct get_neigh_handler *neigh_handler, int timeout)
638 {
639 	int err;
640 
641 	pthread_once(&device_neigh_alloc, &alloc_zero_based_socket);
642 	neigh_handler->sock = nl_socket_alloc();
643 	if (neigh_handler->sock == NULL) {
644 		errno = ENOMEM;
645 		return -1;
646 	}
647 
648 	err = nl_connect(neigh_handler->sock, NETLINK_ROUTE);
649 	if (err < 0)
650 		goto free_socket;
651 
652 	err = rtnl_link_alloc_cache(neigh_handler->sock, AF_UNSPEC,
653 				    &neigh_handler->link_cache);
654 	if (err) {
655 		err = -1;
656 		errno = ENOMEM;
657 		goto close_connection;
658 	}
659 
660 	nl_cache_mngt_provide(neigh_handler->link_cache);
661 
662 	err = rtnl_route_alloc_cache(neigh_handler->sock, AF_UNSPEC, 0,
663 				     &neigh_handler->route_cache);
664 	if (err) {
665 		err = -1;
666 		errno = ENOMEM;
667 		goto free_link_cache;
668 	}
669 
670 	nl_cache_mngt_provide(neigh_handler->route_cache);
671 
672 	err = rtnl_neigh_alloc_cache(neigh_handler->sock,
673 				     &neigh_handler->neigh_cache);
674 	if (err) {
675 		err = -ENOMEM;
676 		goto free_route_cache;
677 	}
678 
679 	nl_cache_mngt_provide(neigh_handler->neigh_cache);
680 
681 	/* init structure */
682 	neigh_handler->timeout = timeout;
683 	neigh_handler->oif = -1;
684 	neigh_handler->filter_neigh = NULL;
685 	neigh_handler->found_ll_addr = NULL;
686 	neigh_handler->dst = NULL;
687 	neigh_handler->src = NULL;
688 	neigh_handler->vid = -1;
689 
690 	return 0;
691 
692 free_route_cache:
693 	nl_cache_mngt_unprovide(neigh_handler->route_cache);
694 	nl_cache_free(neigh_handler->route_cache);
695 	neigh_handler->route_cache = NULL;
696 free_link_cache:
697 	nl_cache_mngt_unprovide(neigh_handler->link_cache);
698 	nl_cache_free(neigh_handler->link_cache);
699 	neigh_handler->link_cache = NULL;
700 close_connection:
701 	nl_close(neigh_handler->sock);
702 free_socket:
703 	nl_socket_free(neigh_handler->sock);
704 	neigh_handler->sock = NULL;
705 	return err;
706 }
707 
neigh_get_vlan_id_from_dev(struct get_neigh_handler * neigh_handler)708 uint16_t neigh_get_vlan_id_from_dev(struct get_neigh_handler *neigh_handler)
709 {
710 	struct rtnl_link *link;
711 	int vid = 0xffff;
712 
713 	link = rtnl_link_get(neigh_handler->link_cache, neigh_handler->oif);
714 	if (link == NULL) {
715 		errno = EINVAL;
716 		return vid;
717 	}
718 
719 	if (rtnl_link_is_vlan(link))
720 		vid = rtnl_link_vlan_get_id(link);
721 	rtnl_link_put(link);
722 	return vid >= 0 && vid <= 0xfff ? vid : 0xffff;
723 }
724 
neigh_set_vlan_id(struct get_neigh_handler * neigh_handler,uint16_t vid)725 void neigh_set_vlan_id(struct get_neigh_handler *neigh_handler, uint16_t vid)
726 {
727 	if (vid <= 0xfff)
728 		neigh_handler->vid = vid;
729 }
730 
neigh_set_dst(struct get_neigh_handler * neigh_handler,int family,void * buf,size_t size)731 int neigh_set_dst(struct get_neigh_handler *neigh_handler,
732 		  int family, void *buf, size_t size)
733 {
734 	neigh_handler->dst = nl_addr_build(family, buf, size);
735 	return neigh_handler->dst == NULL;
736 }
737 
neigh_set_src(struct get_neigh_handler * neigh_handler,int family,void * buf,size_t size)738 int neigh_set_src(struct get_neigh_handler *neigh_handler,
739 		  int family, void *buf, size_t size)
740 {
741 	neigh_handler->src = nl_addr_build(family, buf, size);
742 	return neigh_handler->src == NULL;
743 }
744 
neigh_set_oif(struct get_neigh_handler * neigh_handler,int oif)745 void neigh_set_oif(struct get_neigh_handler *neigh_handler, int oif)
746 {
747 	neigh_handler->oif = oif;
748 }
749 
neigh_get_ll(struct get_neigh_handler * neigh_handler,void * addr_buff,int addr_size)750 int neigh_get_ll(struct get_neigh_handler *neigh_handler, void *addr_buff,
751 		 int addr_size) {
752 	int neigh_len;
753 
754 	if (neigh_handler->found_ll_addr == NULL)
755 		return -EINVAL;
756 
757 	 neigh_len = nl_addr_get_len(neigh_handler->found_ll_addr);
758 
759 	if (neigh_len > addr_size)
760 		return -EINVAL;
761 
762 	memcpy(addr_buff, nl_addr_get_binary_addr(neigh_handler->found_ll_addr),
763 	       neigh_len);
764 
765 	return neigh_len;
766 }
767 
neigh_free_resources(struct get_neigh_handler * neigh_handler)768 void neigh_free_resources(struct get_neigh_handler *neigh_handler)
769 {
770 	/* Should be released first because it's holding a reference to dst */
771 	if (neigh_handler->filter_neigh != NULL) {
772 		rtnl_neigh_put(neigh_handler->filter_neigh);
773 		neigh_handler->filter_neigh = NULL;
774 	}
775 
776 	if (neigh_handler->src != NULL) {
777 		nl_addr_put(neigh_handler->src);
778 		neigh_handler->src = NULL;
779 	}
780 
781 	if (neigh_handler->dst != NULL) {
782 		nl_addr_put(neigh_handler->dst);
783 		neigh_handler->dst = NULL;
784 	}
785 
786 	if (neigh_handler->found_ll_addr != NULL) {
787 		nl_addr_put(neigh_handler->found_ll_addr);
788 		neigh_handler->found_ll_addr = NULL;
789 	}
790 
791 	if (neigh_handler->neigh_cache != NULL) {
792 		nl_cache_mngt_unprovide(neigh_handler->neigh_cache);
793 		nl_cache_free(neigh_handler->neigh_cache);
794 		neigh_handler->neigh_cache = NULL;
795 	}
796 
797 	if (neigh_handler->route_cache != NULL) {
798 		nl_cache_mngt_unprovide(neigh_handler->route_cache);
799 		nl_cache_free(neigh_handler->route_cache);
800 		neigh_handler->route_cache = NULL;
801 	}
802 
803 	if (neigh_handler->link_cache != NULL) {
804 		nl_cache_mngt_unprovide(neigh_handler->link_cache);
805 		nl_cache_free(neigh_handler->link_cache);
806 		neigh_handler->link_cache = NULL;
807 	}
808 
809 	if (neigh_handler->sock != NULL) {
810 		nl_close(neigh_handler->sock);
811 		nl_socket_free(neigh_handler->sock);
812 		neigh_handler->sock = NULL;
813 	}
814 }
815 
process_get_neigh(struct get_neigh_handler * neigh_handler)816 int process_get_neigh(struct get_neigh_handler *neigh_handler)
817 {
818 	struct nl_msg *m;
819 	struct rtmsg rmsg = {
820 		.rtm_family = nl_addr_get_family(neigh_handler->dst),
821 		.rtm_dst_len = nl_addr_get_prefixlen(neigh_handler->dst),
822 	};
823 	int err;
824 
825 	m = nlmsg_alloc_simple(RTM_GETROUTE, 0);
826 
827 	if (m == NULL)
828 		return -ENOMEM;
829 
830 	nlmsg_append(m, &rmsg, sizeof(rmsg), NLMSG_ALIGNTO);
831 
832 	nla_put_addr(m, RTA_DST, neigh_handler->dst);
833 
834 	if (neigh_handler->oif > 0)
835 		nla_put_u32(m, RTA_OIF, neigh_handler->oif);
836 
837 	err = nl_send_auto_complete(neigh_handler->sock, m);
838 	nlmsg_free(m);
839 	if (err < 0)
840 		return err;
841 
842 	nl_socket_modify_cb(neigh_handler->sock, NL_CB_VALID,
843 			    NL_CB_CUSTOM, &get_route_cb, neigh_handler);
844 
845 	err = nl_recvmsgs_default(neigh_handler->sock);
846 
847 	return err;
848 }
849