1321936Shselasky/* Licensed under the OpenIB.org BSD license (FreeBSD Variant) - See COPYING.md
2321936Shselasky */
3321936Shselasky
4321936Shselasky#include "config.h"
5321936Shselasky#include <net/if_packet.h>
6321936Shselasky#include <linux/netlink.h>
7321936Shselasky#include <linux/rtnetlink.h>
8321936Shselasky#include <infiniband/endian.h>
9321936Shselasky#include <stdio.h>
10321936Shselasky#include <stdlib.h>
11321936Shselasky#include <stdbool.h>
12321936Shselasky
13321936Shselasky#if HAVE_WORKING_IF_H
14321936Shselasky#include <net/if.h>
15321936Shselasky#endif
16321936Shselasky
17321936Shselasky#include <netlink/route/rtnl.h>
18321936Shselasky#include <netlink/route/link.h>
19321936Shselasky#include <netlink/route/route.h>
20321936Shselasky#include <netlink/route/neighbour.h>
21321936Shselasky
22321936Shselasky#include <sys/types.h>
23321936Shselasky#include <sys/socket.h>
24321936Shselasky#include <sys/timerfd.h>
25321936Shselasky#include <errno.h>
26321936Shselasky#include <unistd.h>
27321936Shselasky#include <ifaddrs.h>
28321936Shselasky#include <netdb.h>
29321936Shselasky#include <assert.h>
30321936Shselasky
31321936Shselasky#if !HAVE_WORKING_IF_H
32321936Shselasky/* We need this decl from net/if.h but old systems do not let use co-include
33321936Shselasky   net/if.h and netlink/route/link.h */
34321936Shselaskyextern unsigned int if_nametoindex(__const char *__ifname) __THROW;
35321936Shselasky#endif
36321936Shselasky
37321936Shselasky/* for PFX */
38321936Shselasky#include "ibverbs.h"
39321936Shselasky#include <sys/param.h>
40321936Shselasky
41321936Shselasky#include "neigh.h"
42321936Shselasky
43321936Shselasky#ifndef HAVE_LIBNL1
44321936Shselasky#include <netlink/route/link/vlan.h>
45321936Shselasky#endif
46321936Shselasky
47321936Shselaskystatic pthread_once_t device_neigh_alloc = PTHREAD_ONCE_INIT;
48321936Shselaskystatic struct nl_sock *zero_socket;
49321936Shselasky
50321936Shselaskyunion sktaddr {
51321936Shselasky	struct sockaddr s;
52321936Shselasky	struct sockaddr_in s4;
53321936Shselasky	struct sockaddr_in6 s6;
54321936Shselasky};
55321936Shselasky
56321936Shselaskystruct skt {
57321936Shselasky	union sktaddr sktaddr;
58321936Shselasky	socklen_t len;
59321936Shselasky};
60321936Shselasky
61321936Shselaskystatic int set_link_port(union sktaddr *s, __be16 port, int oif)
62321936Shselasky{
63321936Shselasky	switch (s->s.sa_family) {
64321936Shselasky	case AF_INET:
65321936Shselasky		s->s4.sin_port = port;
66321936Shselasky		break;
67321936Shselasky	case AF_INET6:
68321936Shselasky		s->s6.sin6_port = port;
69321936Shselasky		s->s6.sin6_scope_id = oif;
70321936Shselasky		break;
71321936Shselasky	default:
72321936Shselasky		return -EINVAL;
73321936Shselasky	}
74321936Shselasky
75321936Shselasky	return 0;
76321936Shselasky}
77321936Shselasky
78321936Shselaskystatic bool cmp_address(const struct sockaddr *s1,
79321936Shselasky			const struct sockaddr *s2)
80321936Shselasky{
81321936Shselasky	if (s1->sa_family != s2->sa_family)
82321936Shselasky		return false;
83321936Shselasky
84321936Shselasky	switch (s1->sa_family) {
85321936Shselasky	case AF_INET:
86321936Shselasky		return ((struct sockaddr_in *)s1)->sin_addr.s_addr ==
87321936Shselasky		       ((struct sockaddr_in *)s2)->sin_addr.s_addr;
88321936Shselasky	case AF_INET6:
89321936Shselasky		return !memcmp(
90321936Shselasky			((struct sockaddr_in6 *)s1)->sin6_addr.s6_addr,
91321936Shselasky			((struct sockaddr_in6 *)s2)->sin6_addr.s6_addr,
92321936Shselasky			sizeof(((struct sockaddr_in6 *)s1)->sin6_addr.s6_addr));
93321936Shselasky	default:
94321936Shselasky		return false;
95321936Shselasky	}
96321936Shselasky}
97321936Shselasky
98321936Shselaskystatic int get_ifindex(const struct sockaddr *s)
99321936Shselasky{
100321936Shselasky	struct ifaddrs *ifaddr, *ifa;
101321936Shselasky	int name2index = -ENODEV;
102321936Shselasky
103321936Shselasky	if (-1 == getifaddrs(&ifaddr))
104321936Shselasky		return errno;
105321936Shselasky
106321936Shselasky	for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
107321936Shselasky		if (ifa->ifa_addr == NULL)
108321936Shselasky			continue;
109321936Shselasky
110321936Shselasky		if (cmp_address(ifa->ifa_addr, s)) {
111321936Shselasky			name2index = if_nametoindex(ifa->ifa_name);
112321936Shselasky			break;
113321936Shselasky		}
114321936Shselasky	}
115321936Shselasky
116321936Shselasky	freeifaddrs(ifaddr);
117321936Shselasky
118321936Shselasky	return name2index;
119321936Shselasky}
120321936Shselasky
121321936Shselaskystatic struct nl_addr *get_neigh_mac(struct get_neigh_handler *neigh_handler)
122321936Shselasky{
123321936Shselasky	struct rtnl_neigh *neigh;
124321936Shselasky	struct nl_addr *ll_addr = NULL;
125321936Shselasky
126321936Shselasky	/* future optimization - if link local address - parse address and
127321936Shselasky	 * return mac now instead of doing so after the routing CB. This
128321936Shselasky	 * is of course referred to GIDs */
129321936Shselasky	neigh = rtnl_neigh_get(neigh_handler->neigh_cache,
130321936Shselasky			       neigh_handler->oif,
131321936Shselasky			       neigh_handler->dst);
132321936Shselasky	if (neigh == NULL)
133321936Shselasky		return NULL;
134321936Shselasky
135321936Shselasky	ll_addr = rtnl_neigh_get_lladdr(neigh);
136321936Shselasky	if (NULL != ll_addr)
137321936Shselasky		ll_addr = nl_addr_clone(ll_addr);
138321936Shselasky
139321936Shselasky	rtnl_neigh_put(neigh);
140321936Shselasky	return ll_addr;
141321936Shselasky}
142321936Shselasky
143321936Shselaskystatic void get_neigh_cb_event(struct nl_object *obj, void *arg)
144321936Shselasky{
145321936Shselasky	struct get_neigh_handler *neigh_handler =
146321936Shselasky		(struct get_neigh_handler *)arg;
147321936Shselasky	/* assumed serilized callback (no parallel execution of function) */
148321936Shselasky	if (nl_object_match_filter(
149321936Shselasky		obj,
150321936Shselasky		(struct nl_object *)neigh_handler->filter_neigh)) {
151321936Shselasky		struct rtnl_neigh *neigh = (struct rtnl_neigh *)obj;
152321936Shselasky		/* check that we didn't set it already */
153321936Shselasky		if (neigh_handler->found_ll_addr == NULL) {
154321936Shselasky			if (rtnl_neigh_get_lladdr(neigh) == NULL)
155321936Shselasky				return;
156321936Shselasky
157321936Shselasky			neigh_handler->found_ll_addr =
158321936Shselasky				nl_addr_clone(rtnl_neigh_get_lladdr(neigh));
159321936Shselasky		}
160321936Shselasky	}
161321936Shselasky}
162321936Shselasky
163321936Shselaskystatic int get_neigh_cb(struct nl_msg *msg, void *arg)
164321936Shselasky{
165321936Shselasky	struct get_neigh_handler *neigh_handler =
166321936Shselasky		(struct get_neigh_handler *)arg;
167321936Shselasky
168321936Shselasky	if (nl_msg_parse(msg, &get_neigh_cb_event, neigh_handler) < 0)
169321936Shselasky		errno = ENOMSG;
170321936Shselasky
171321936Shselasky	return NL_OK;
172321936Shselasky}
173321936Shselasky
174321936Shselaskystatic void set_neigh_filter(struct get_neigh_handler *neigh_handler,
175321936Shselasky			     struct rtnl_neigh *filter) {
176321936Shselasky	neigh_handler->filter_neigh = filter;
177321936Shselasky}
178321936Shselasky
179321936Shselaskystatic struct rtnl_neigh *create_filter_neigh_for_dst(struct nl_addr *dst_addr,
180321936Shselasky						      int oif)
181321936Shselasky{
182321936Shselasky	struct rtnl_neigh *filter_neigh;
183321936Shselasky
184321936Shselasky	filter_neigh = rtnl_neigh_alloc();
185321936Shselasky	if (filter_neigh == NULL)
186321936Shselasky		return NULL;
187321936Shselasky
188321936Shselasky	rtnl_neigh_set_ifindex(filter_neigh, oif);
189321936Shselasky	rtnl_neigh_set_dst(filter_neigh, dst_addr);
190321936Shselasky
191321936Shselasky	return filter_neigh;
192321936Shselasky}
193321936Shselasky
194321936Shselasky#define PORT_DISCARD htobe16(9)
195321936Shselasky#define SEND_PAYLOAD "H"
196321936Shselasky
197321936Shselaskystatic int create_socket(struct get_neigh_handler *neigh_handler,
198321936Shselasky			 struct skt *addr_dst, int *psock_fd)
199321936Shselasky{
200321936Shselasky	int err;
201321936Shselasky	struct skt addr_src;
202321936Shselasky	int sock_fd;
203321936Shselasky
204321936Shselasky	memset(addr_dst, 0, sizeof(*addr_dst));
205321936Shselasky	memset(&addr_src, 0, sizeof(addr_src));
206321936Shselasky	addr_src.len = sizeof(addr_src.sktaddr);
207321936Shselasky
208321936Shselasky	err = nl_addr_fill_sockaddr(neigh_handler->src,
209321936Shselasky				    &addr_src.sktaddr.s,
210321936Shselasky				    &addr_src.len);
211321936Shselasky	if (err) {
212321936Shselasky		errno = EADDRNOTAVAIL;
213321936Shselasky		return -1;
214321936Shselasky	}
215321936Shselasky
216321936Shselasky	addr_dst->len = sizeof(addr_dst->sktaddr);
217321936Shselasky	err = nl_addr_fill_sockaddr(neigh_handler->dst,
218321936Shselasky				    &addr_dst->sktaddr.s,
219321936Shselasky				    &addr_dst->len);
220321936Shselasky	if (err) {
221321936Shselasky		errno = EADDRNOTAVAIL;
222321936Shselasky		return -1;
223321936Shselasky	}
224321936Shselasky
225321936Shselasky	err = set_link_port(&addr_dst->sktaddr, PORT_DISCARD,
226321936Shselasky			    neigh_handler->oif);
227321936Shselasky	if (err)
228321936Shselasky		return -1;
229321936Shselasky
230321936Shselasky	sock_fd = socket(addr_dst->sktaddr.s.sa_family,
231321936Shselasky			 SOCK_DGRAM | SOCK_CLOEXEC, 0);
232321936Shselasky	if (sock_fd == -1)
233321936Shselasky		return -1;
234321936Shselasky	err = bind(sock_fd, &addr_src.sktaddr.s, addr_src.len);
235321936Shselasky	if (err) {
236321936Shselasky		close(sock_fd);
237321936Shselasky		return -1;
238321936Shselasky	}
239321936Shselasky
240321936Shselasky	*psock_fd = sock_fd;
241321936Shselasky
242321936Shselasky	return 0;
243321936Shselasky}
244321936Shselasky
245321936Shselasky#define NUM_OF_RETRIES 10
246321936Shselasky#define NUM_OF_TRIES ((NUM_OF_RETRIES) + 1)
247321936Shselasky#if NUM_OF_TRIES < 1
248321936Shselasky#error "neigh: invalid value of NUM_OF_RETRIES"
249321936Shselasky#endif
250321936Shselaskystatic int create_timer(struct get_neigh_handler *neigh_handler)
251321936Shselasky{
252321936Shselasky	int user_timeout = neigh_handler->timeout/NUM_OF_TRIES;
253321936Shselasky	struct timespec timeout = {
254321936Shselasky		.tv_sec = user_timeout / 1000,
255321936Shselasky		.tv_nsec = (user_timeout % 1000) * 1000000
256321936Shselasky	};
257321936Shselasky	struct itimerspec timer_time = {.it_value = timeout};
258321936Shselasky	int timer_fd;
259321936Shselasky
260321936Shselasky	timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
261321936Shselasky	if (timer_fd == -1)
262321936Shselasky		return timer_fd;
263321936Shselasky
264321936Shselasky	if (neigh_handler->timeout) {
265321936Shselasky		if (NUM_OF_TRIES <= 1)
266321936Shselasky			bzero(&timer_time.it_interval,
267321936Shselasky			      sizeof(timer_time.it_interval));
268321936Shselasky		else
269321936Shselasky			timer_time.it_interval = timeout;
270321936Shselasky		if (timerfd_settime(timer_fd, 0, &timer_time, NULL)) {
271321936Shselasky			close(timer_fd);
272321936Shselasky			return -1;
273321936Shselasky		}
274321936Shselasky	}
275321936Shselasky
276321936Shselasky	return timer_fd;
277321936Shselasky}
278321936Shselasky
279321936Shselasky#define UDP_SOCKET_MAX_SENDTO 100000ULL
280321936Shselaskystatic int try_send_to(int sock_fd, void *buff, size_t buf_size,
281321936Shselasky		       struct skt *addr_dst)
282321936Shselasky{
283321936Shselasky	uint64_t max_count = UDP_SOCKET_MAX_SENDTO;
284321936Shselasky	int err;
285321936Shselasky
286321936Shselasky	do {
287321936Shselasky		err = sendto(sock_fd, buff, buf_size, 0,
288321936Shselasky			     &addr_dst->sktaddr.s,
289321936Shselasky			     addr_dst->len);
290321936Shselasky		if (err > 0)
291321936Shselasky			err = 0;
292321936Shselasky	} while (-1 == err && EADDRNOTAVAIL == errno && --max_count);
293321936Shselasky
294321936Shselasky	return err;
295321936Shselasky}
296321936Shselasky
297321936Shselaskystatic struct nl_addr *process_get_neigh_mac(
298321936Shselasky		struct get_neigh_handler *neigh_handler)
299321936Shselasky{
300321936Shselasky	int err;
301321936Shselasky	struct nl_addr *ll_addr = get_neigh_mac(neigh_handler);
302321936Shselasky	struct rtnl_neigh *neigh_filter;
303321936Shselasky	fd_set fdset;
304321936Shselasky	int sock_fd;
305321936Shselasky	int fd;
306321936Shselasky	int nfds;
307321936Shselasky	int timer_fd;
308321936Shselasky	int ret;
309321936Shselasky	struct skt addr_dst;
310321936Shselasky	char buff[sizeof(SEND_PAYLOAD)] = SEND_PAYLOAD;
311321936Shselasky	int retries = 0;
312321936Shselasky
313321936Shselasky	if (NULL != ll_addr)
314321936Shselasky		return ll_addr;
315321936Shselasky
316321936Shselasky	err = nl_socket_add_membership(neigh_handler->sock,
317321936Shselasky				       RTNLGRP_NEIGH);
318321936Shselasky	if (err < 0)
319321936Shselasky		return NULL;
320321936Shselasky
321321936Shselasky	neigh_filter = create_filter_neigh_for_dst(neigh_handler->dst,
322321936Shselasky						   neigh_handler->oif);
323321936Shselasky	if (neigh_filter == NULL)
324321936Shselasky		return NULL;
325321936Shselasky
326321936Shselasky	set_neigh_filter(neigh_handler, neigh_filter);
327321936Shselasky
328321936Shselasky	nl_socket_disable_seq_check(neigh_handler->sock);
329321936Shselasky	nl_socket_modify_cb(neigh_handler->sock, NL_CB_VALID, NL_CB_CUSTOM,
330321936Shselasky			    &get_neigh_cb, neigh_handler);
331321936Shselasky
332321936Shselasky	fd = nl_socket_get_fd(neigh_handler->sock);
333321936Shselasky
334321936Shselasky	err = create_socket(neigh_handler, &addr_dst, &sock_fd);
335321936Shselasky
336321936Shselasky	if (err)
337321936Shselasky		return NULL;
338321936Shselasky
339321936Shselasky	err = try_send_to(sock_fd, buff, sizeof(buff), &addr_dst);
340321936Shselasky	if (err)
341321936Shselasky		goto close_socket;
342321936Shselasky
343321936Shselasky	timer_fd = create_timer(neigh_handler);
344321936Shselasky	if (timer_fd < 0)
345321936Shselasky		goto close_socket;
346321936Shselasky
347321936Shselasky	nfds = MAX(fd, timer_fd) + 1;
348321936Shselasky
349321936Shselasky	while (1) {
350321936Shselasky		FD_ZERO(&fdset);
351321936Shselasky		FD_SET(fd, &fdset);
352321936Shselasky		FD_SET(timer_fd, &fdset);
353321936Shselasky
354321936Shselasky		/* wait for an incoming message on the netlink socket */
355321936Shselasky		ret = select(nfds, &fdset, NULL, NULL, NULL);
356321936Shselasky		if (ret == -1) {
357321936Shselasky			goto select_err;
358321936Shselasky		} else if (ret) {
359321936Shselasky			if (FD_ISSET(fd, &fdset)) {
360321936Shselasky				nl_recvmsgs_default(neigh_handler->sock);
361321936Shselasky				if (neigh_handler->found_ll_addr)
362321936Shselasky					break;
363321936Shselasky			} else {
364321936Shselasky				nl_cache_refill(neigh_handler->sock,
365321936Shselasky						neigh_handler->neigh_cache);
366321936Shselasky				ll_addr = get_neigh_mac(neigh_handler);
367321936Shselasky				if (NULL != ll_addr) {
368321936Shselasky					break;
369321936Shselasky				} else if (FD_ISSET(timer_fd, &fdset) &&
370321936Shselasky					   retries < NUM_OF_RETRIES) {
371321936Shselasky					try_send_to(sock_fd, buff, sizeof(buff),
372321936Shselasky						    &addr_dst);
373321936Shselasky				}
374321936Shselasky			}
375321936Shselasky
376321936Shselasky			if (FD_ISSET(timer_fd, &fdset)) {
377321936Shselasky				uint64_t read_val;
378321936Shselasky				ssize_t rc;
379321936Shselasky
380321936Shselasky				rc =
381321936Shselasky				    read(timer_fd, &read_val, sizeof(read_val));
382321936Shselasky				assert(rc == sizeof(read_val));
383321936Shselasky				if (++retries >=  NUM_OF_TRIES) {
384321936Shselasky					if (!errno)
385321936Shselasky						errno = EDESTADDRREQ;
386321936Shselasky					break;
387321936Shselasky				}
388321936Shselasky			}
389321936Shselasky		}
390321936Shselasky	}
391321936Shselaskyselect_err:
392321936Shselasky	close(timer_fd);
393321936Shselaskyclose_socket:
394321936Shselasky	close(sock_fd);
395321936Shselasky	return ll_addr ? ll_addr : neigh_handler->found_ll_addr;
396321936Shselasky}
397321936Shselasky
398321936Shselaskystatic int get_mcast_mac_ipv4(struct nl_addr *dst, struct nl_addr **ll_addr)
399321936Shselasky{
400321936Shselasky	uint8_t mac_addr[6] = {0x01, 0x00, 0x5E};
401321936Shselasky	uint32_t addr = be32toh(*(__be32 *)nl_addr_get_binary_addr(dst));
402321936Shselasky
403321936Shselasky	mac_addr[5] = addr & 0xFF;
404321936Shselasky	addr >>= 8;
405321936Shselasky	mac_addr[4] = addr & 0xFF;
406321936Shselasky	addr >>= 8;
407321936Shselasky	mac_addr[3] = addr & 0x7F;
408321936Shselasky
409321936Shselasky	*ll_addr = nl_addr_build(AF_LLC, mac_addr, sizeof(mac_addr));
410321936Shselasky
411321936Shselasky	return *ll_addr == NULL ? -EINVAL : 0;
412321936Shselasky}
413321936Shselasky
414321936Shselaskystatic int get_mcast_mac_ipv6(struct nl_addr *dst, struct nl_addr **ll_addr)
415321936Shselasky{
416321936Shselasky	uint8_t mac_addr[6] = {0x33, 0x33};
417321936Shselasky
418321936Shselasky	memcpy(mac_addr + 2, (uint8_t *)nl_addr_get_binary_addr(dst) + 12, 4);
419321936Shselasky
420321936Shselasky	*ll_addr = nl_addr_build(AF_LLC, mac_addr, sizeof(mac_addr));
421321936Shselasky
422321936Shselasky	return *ll_addr == NULL ? -EINVAL : 0;
423321936Shselasky}
424321936Shselasky
425321936Shselaskystatic int get_link_local_mac_ipv6(struct nl_addr *dst,
426321936Shselasky				   struct nl_addr **ll_addr)
427321936Shselasky{
428321936Shselasky	uint8_t mac_addr[6];
429321936Shselasky
430321936Shselasky	memcpy(mac_addr + 3, (uint8_t *)nl_addr_get_binary_addr(dst) + 13, 3);
431321936Shselasky	memcpy(mac_addr, (uint8_t *)nl_addr_get_binary_addr(dst) + 8, 3);
432321936Shselasky	mac_addr[0] ^= 2;
433321936Shselasky
434321936Shselasky	*ll_addr = nl_addr_build(AF_LLC, mac_addr, sizeof(mac_addr));
435321936Shselasky	return *ll_addr == NULL ? -EINVAL : 0;
436321936Shselasky}
437321936Shselasky
438321936Shselaskystatic const struct encoded_l3_addr {
439321936Shselasky	short family;
440321936Shselasky	uint8_t prefix_bits;
441321936Shselasky	const uint8_t data[16];
442321936Shselasky	int (*getter)(struct nl_addr *dst, struct nl_addr **ll_addr);
443321936Shselasky} encoded_prefixes[] = {
444321936Shselasky	{.family = AF_INET,
445321936Shselasky	 .prefix_bits = 4,
446321936Shselasky	 .data = {0xe0},
447321936Shselasky	 .getter = &get_mcast_mac_ipv4},
448321936Shselasky	{.family = AF_INET6,
449321936Shselasky	 .prefix_bits = 8,
450321936Shselasky	 .data = {0xff},
451321936Shselasky	 .getter = &get_mcast_mac_ipv6},
452321936Shselasky	{.family = AF_INET6,
453321936Shselasky	 .prefix_bits = 64,
454321936Shselasky	 .data = {0xfe, 0x80},
455321936Shselasky	 .getter = get_link_local_mac_ipv6},
456321936Shselasky};
457321936Shselasky
458321936Shselaskystatic int nl_addr_cmp_prefix_msb(void *addr1, int len1, void *addr2, int len2)
459321936Shselasky{
460321936Shselasky	int len = min(len1, len2);
461321936Shselasky	int bytes = len / 8;
462321936Shselasky	int d = memcmp(addr1, addr2, bytes);
463321936Shselasky
464321936Shselasky	if (d == 0) {
465321936Shselasky		int mask = ((1UL << (len % 8)) - 1UL) << (8 - len);
466321936Shselasky
467321936Shselasky		d = (((uint8_t *)addr1)[bytes] & mask) -
468321936Shselasky		    (((uint8_t *)addr2)[bytes] & mask);
469321936Shselasky	}
470321936Shselasky
471321936Shselasky	return d;
472321936Shselasky}
473321936Shselasky
474321936Shselaskystatic int handle_encoded_mac(struct nl_addr *dst, struct nl_addr **ll_addr)
475321936Shselasky{
476321936Shselasky	uint32_t family = nl_addr_get_family(dst);
477321936Shselasky	struct nl_addr *prefix = NULL;
478321936Shselasky	int i;
479321936Shselasky	int ret = 1;
480321936Shselasky
481321936Shselasky	for (i = 0;
482321936Shselasky	     i < sizeof(encoded_prefixes)/sizeof(encoded_prefixes[0]) &&
483321936Shselasky	     ret; prefix = NULL, i++) {
484321936Shselasky		if (encoded_prefixes[i].family != family)
485321936Shselasky			continue;
486321936Shselasky
487321936Shselasky		prefix = nl_addr_build(
488321936Shselasky		    family, (void *)encoded_prefixes[i].data,
489321936Shselasky		    min_t(size_t, encoded_prefixes[i].prefix_bits / 8 +
490321936Shselasky				      !!(encoded_prefixes[i].prefix_bits % 8),
491321936Shselasky			  sizeof(encoded_prefixes[i].data)));
492321936Shselasky
493321936Shselasky		if (prefix == NULL)
494321936Shselasky			return -ENOMEM;
495321936Shselasky		nl_addr_set_prefixlen(prefix,
496321936Shselasky				      encoded_prefixes[i].prefix_bits);
497321936Shselasky
498321936Shselasky		if (nl_addr_cmp_prefix_msb(nl_addr_get_binary_addr(dst),
499321936Shselasky					   nl_addr_get_prefixlen(dst),
500321936Shselasky					   nl_addr_get_binary_addr(prefix),
501321936Shselasky					   nl_addr_get_prefixlen(prefix)))
502321936Shselasky			continue;
503321936Shselasky
504321936Shselasky		ret = encoded_prefixes[i].getter(dst, ll_addr);
505321936Shselasky		nl_addr_put(prefix);
506321936Shselasky	}
507321936Shselasky
508321936Shselasky	return ret;
509321936Shselasky}
510321936Shselasky
511321936Shselaskystatic void get_route_cb_parser(struct nl_object *obj, void *arg)
512321936Shselasky{
513321936Shselasky	struct get_neigh_handler *neigh_handler =
514321936Shselasky		(struct get_neigh_handler *)arg;
515321936Shselasky
516321936Shselasky	struct rtnl_route *route = (struct rtnl_route *)obj;
517321936Shselasky	struct nl_addr *gateway = NULL;
518321936Shselasky	struct nl_addr *src = rtnl_route_get_pref_src(route);
519321936Shselasky	int oif;
520321936Shselasky	int type = rtnl_route_get_type(route);
521321936Shselasky	struct rtnl_link *link;
522321936Shselasky
523321936Shselasky	struct rtnl_nexthop *nh = rtnl_route_nexthop_n(route, 0);
524321936Shselasky
525321936Shselasky	if (nh != NULL)
526321936Shselasky		gateway = rtnl_route_nh_get_gateway(nh);
527321936Shselasky	oif = rtnl_route_nh_get_ifindex(nh);
528321936Shselasky
529321936Shselasky	if (gateway) {
530321936Shselasky		nl_addr_put(neigh_handler->dst);
531321936Shselasky		neigh_handler->dst = nl_addr_clone(gateway);
532321936Shselasky	}
533321936Shselasky
534321936Shselasky	if (RTN_BLACKHOLE == type ||
535321936Shselasky	    RTN_UNREACHABLE == type ||
536321936Shselasky	    RTN_PROHIBIT == type ||
537321936Shselasky	    RTN_THROW == type) {
538321936Shselasky		errno = ENETUNREACH;
539321936Shselasky		goto err;
540321936Shselasky	}
541321936Shselasky
542321936Shselasky	if (!neigh_handler->src && src)
543321936Shselasky		neigh_handler->src = nl_addr_clone(src);
544321936Shselasky
545321936Shselasky	if (neigh_handler->oif < 0 && oif > 0)
546321936Shselasky		neigh_handler->oif = oif;
547321936Shselasky
548321936Shselasky	/* Link Local */
549321936Shselasky	if (RTN_LOCAL == type) {
550321936Shselasky		struct nl_addr *lladdr;
551321936Shselasky
552321936Shselasky		link = rtnl_link_get(neigh_handler->link_cache,
553321936Shselasky				     neigh_handler->oif);
554321936Shselasky
555321936Shselasky		if (link == NULL)
556321936Shselasky			goto err;
557321936Shselasky
558321936Shselasky		lladdr = rtnl_link_get_addr(link);
559321936Shselasky
560321936Shselasky		if (lladdr == NULL)
561321936Shselasky			goto err_link;
562321936Shselasky
563321936Shselasky		neigh_handler->found_ll_addr = nl_addr_clone(lladdr);
564321936Shselasky		rtnl_link_put(link);
565321936Shselasky	} else {
566321936Shselasky		handle_encoded_mac(
567321936Shselasky			neigh_handler->dst,
568321936Shselasky			&neigh_handler->found_ll_addr);
569321936Shselasky	}
570321936Shselasky
571321936Shselasky	return;
572321936Shselasky
573321936Shselaskyerr_link:
574321936Shselasky	rtnl_link_put(link);
575321936Shselaskyerr:
576321936Shselasky	if (neigh_handler->src) {
577321936Shselasky		nl_addr_put(neigh_handler->src);
578321936Shselasky		neigh_handler->src = NULL;
579321936Shselasky	}
580321936Shselasky}
581321936Shselasky
582321936Shselaskystatic int get_route_cb(struct nl_msg *msg, void *arg)
583321936Shselasky{
584321936Shselasky	struct get_neigh_handler *neigh_handler =
585321936Shselasky		(struct get_neigh_handler *)arg;
586321936Shselasky	int err;
587321936Shselasky
588321936Shselasky	err = nl_msg_parse(msg, &get_route_cb_parser, neigh_handler);
589321936Shselasky	if (err < 0) {
590321936Shselasky		errno = ENOMSG;
591321936Shselasky		return err;
592321936Shselasky	}
593321936Shselasky
594321936Shselasky	if (!neigh_handler->dst || !neigh_handler->src ||
595321936Shselasky	    neigh_handler->oif <= 0) {
596321936Shselasky		errno = EINVAL;
597321936Shselasky		return -1;
598321936Shselasky	}
599321936Shselasky
600321936Shselasky	if (NULL != neigh_handler->found_ll_addr)
601321936Shselasky		goto found;
602321936Shselasky
603321936Shselasky	neigh_handler->found_ll_addr =
604321936Shselasky		process_get_neigh_mac(neigh_handler);
605321936Shselasky
606321936Shselaskyfound:
607321936Shselasky	return neigh_handler->found_ll_addr ? 0 : -1;
608321936Shselasky}
609321936Shselasky
610321936Shselaskyint neigh_get_oif_from_src(struct get_neigh_handler *neigh_handler)
611321936Shselasky{
612321936Shselasky	int oif = -ENODEV;
613321936Shselasky	struct addrinfo *src_info;
614321936Shselasky	int err;
615321936Shselasky
616321936Shselasky	err = nl_addr_info(neigh_handler->src, &src_info);
617321936Shselasky	if (err) {
618321936Shselasky		if (!errno)
619321936Shselasky			errno = ENXIO;
620321936Shselasky		return oif;
621321936Shselasky	}
622321936Shselasky
623321936Shselasky	oif = get_ifindex(src_info->ai_addr);
624321936Shselasky	if (oif <= 0)
625321936Shselasky		goto free;
626321936Shselasky
627321936Shselaskyfree:
628321936Shselasky	freeaddrinfo(src_info);
629321936Shselasky	return oif;
630321936Shselasky}
631321936Shselasky
632321936Shselaskystatic void alloc_zero_based_socket(void)
633321936Shselasky{
634321936Shselasky	zero_socket = nl_socket_alloc();
635321936Shselasky}
636321936Shselasky
637321936Shselaskyint neigh_init_resources(struct get_neigh_handler *neigh_handler, int timeout)
638321936Shselasky{
639321936Shselasky	int err;
640321936Shselasky
641321936Shselasky	pthread_once(&device_neigh_alloc, &alloc_zero_based_socket);
642321936Shselasky	neigh_handler->sock = nl_socket_alloc();
643321936Shselasky	if (neigh_handler->sock == NULL) {
644321936Shselasky		errno = ENOMEM;
645321936Shselasky		return -1;
646321936Shselasky	}
647321936Shselasky
648321936Shselasky	err = nl_connect(neigh_handler->sock, NETLINK_ROUTE);
649321936Shselasky	if (err < 0)
650321936Shselasky		goto free_socket;
651321936Shselasky
652321936Shselasky	err = rtnl_link_alloc_cache(neigh_handler->sock, AF_UNSPEC,
653321936Shselasky				    &neigh_handler->link_cache);
654321936Shselasky	if (err) {
655321936Shselasky		err = -1;
656321936Shselasky		errno = ENOMEM;
657321936Shselasky		goto close_connection;
658321936Shselasky	}
659321936Shselasky
660321936Shselasky	nl_cache_mngt_provide(neigh_handler->link_cache);
661321936Shselasky
662321936Shselasky	err = rtnl_route_alloc_cache(neigh_handler->sock, AF_UNSPEC, 0,
663321936Shselasky				     &neigh_handler->route_cache);
664321936Shselasky	if (err) {
665321936Shselasky		err = -1;
666321936Shselasky		errno = ENOMEM;
667321936Shselasky		goto free_link_cache;
668321936Shselasky	}
669321936Shselasky
670321936Shselasky	nl_cache_mngt_provide(neigh_handler->route_cache);
671321936Shselasky
672321936Shselasky	err = rtnl_neigh_alloc_cache(neigh_handler->sock,
673321936Shselasky				     &neigh_handler->neigh_cache);
674321936Shselasky	if (err) {
675321936Shselasky		err = -ENOMEM;
676321936Shselasky		goto free_route_cache;
677321936Shselasky	}
678321936Shselasky
679321936Shselasky	nl_cache_mngt_provide(neigh_handler->neigh_cache);
680321936Shselasky
681321936Shselasky	/* init structure */
682321936Shselasky	neigh_handler->timeout = timeout;
683321936Shselasky	neigh_handler->oif = -1;
684321936Shselasky	neigh_handler->filter_neigh = NULL;
685321936Shselasky	neigh_handler->found_ll_addr = NULL;
686321936Shselasky	neigh_handler->dst = NULL;
687321936Shselasky	neigh_handler->src = NULL;
688321936Shselasky	neigh_handler->vid = -1;
689321936Shselasky
690321936Shselasky	return 0;
691321936Shselasky
692321936Shselaskyfree_route_cache:
693321936Shselasky	nl_cache_mngt_unprovide(neigh_handler->route_cache);
694321936Shselasky	nl_cache_free(neigh_handler->route_cache);
695321936Shselasky	neigh_handler->route_cache = NULL;
696321936Shselaskyfree_link_cache:
697321936Shselasky	nl_cache_mngt_unprovide(neigh_handler->link_cache);
698321936Shselasky	nl_cache_free(neigh_handler->link_cache);
699321936Shselasky	neigh_handler->link_cache = NULL;
700321936Shselaskyclose_connection:
701321936Shselasky	nl_close(neigh_handler->sock);
702321936Shselaskyfree_socket:
703321936Shselasky	nl_socket_free(neigh_handler->sock);
704321936Shselasky	neigh_handler->sock = NULL;
705321936Shselasky	return err;
706321936Shselasky}
707321936Shselasky
708321936Shselaskyuint16_t neigh_get_vlan_id_from_dev(struct get_neigh_handler *neigh_handler)
709321936Shselasky{
710321936Shselasky	struct rtnl_link *link;
711321936Shselasky	int vid = 0xffff;
712321936Shselasky
713321936Shselasky	link = rtnl_link_get(neigh_handler->link_cache, neigh_handler->oif);
714321936Shselasky	if (link == NULL) {
715321936Shselasky		errno = EINVAL;
716321936Shselasky		return vid;
717321936Shselasky	}
718321936Shselasky
719321936Shselasky	if (rtnl_link_is_vlan(link))
720321936Shselasky		vid = rtnl_link_vlan_get_id(link);
721321936Shselasky	rtnl_link_put(link);
722321936Shselasky	return vid >= 0 && vid <= 0xfff ? vid : 0xffff;
723321936Shselasky}
724321936Shselasky
725321936Shselaskyvoid neigh_set_vlan_id(struct get_neigh_handler *neigh_handler, uint16_t vid)
726321936Shselasky{
727321936Shselasky	if (vid <= 0xfff)
728321936Shselasky		neigh_handler->vid = vid;
729321936Shselasky}
730321936Shselasky
731321936Shselaskyint neigh_set_dst(struct get_neigh_handler *neigh_handler,
732321936Shselasky		  int family, void *buf, size_t size)
733321936Shselasky{
734321936Shselasky	neigh_handler->dst = nl_addr_build(family, buf, size);
735321936Shselasky	return neigh_handler->dst == NULL;
736321936Shselasky}
737321936Shselasky
738321936Shselaskyint neigh_set_src(struct get_neigh_handler *neigh_handler,
739321936Shselasky		  int family, void *buf, size_t size)
740321936Shselasky{
741321936Shselasky	neigh_handler->src = nl_addr_build(family, buf, size);
742321936Shselasky	return neigh_handler->src == NULL;
743321936Shselasky}
744321936Shselasky
745321936Shselaskyvoid neigh_set_oif(struct get_neigh_handler *neigh_handler, int oif)
746321936Shselasky{
747321936Shselasky	neigh_handler->oif = oif;
748321936Shselasky}
749321936Shselasky
750321936Shselaskyint neigh_get_ll(struct get_neigh_handler *neigh_handler, void *addr_buff,
751321936Shselasky		 int addr_size) {
752321936Shselasky	int neigh_len;
753321936Shselasky
754321936Shselasky	if (neigh_handler->found_ll_addr == NULL)
755321936Shselasky		return -EINVAL;
756321936Shselasky
757321936Shselasky	 neigh_len = nl_addr_get_len(neigh_handler->found_ll_addr);
758321936Shselasky
759321936Shselasky	if (neigh_len > addr_size)
760321936Shselasky		return -EINVAL;
761321936Shselasky
762321936Shselasky	memcpy(addr_buff, nl_addr_get_binary_addr(neigh_handler->found_ll_addr),
763321936Shselasky	       neigh_len);
764321936Shselasky
765321936Shselasky	return neigh_len;
766321936Shselasky}
767321936Shselasky
768321936Shselaskyvoid neigh_free_resources(struct get_neigh_handler *neigh_handler)
769321936Shselasky{
770321936Shselasky	/* Should be released first because it's holding a reference to dst */
771321936Shselasky	if (neigh_handler->filter_neigh != NULL) {
772321936Shselasky		rtnl_neigh_put(neigh_handler->filter_neigh);
773321936Shselasky		neigh_handler->filter_neigh = NULL;
774321936Shselasky	}
775321936Shselasky
776321936Shselasky	if (neigh_handler->src != NULL) {
777321936Shselasky		nl_addr_put(neigh_handler->src);
778321936Shselasky		neigh_handler->src = NULL;
779321936Shselasky	}
780321936Shselasky
781321936Shselasky	if (neigh_handler->dst != NULL) {
782321936Shselasky		nl_addr_put(neigh_handler->dst);
783321936Shselasky		neigh_handler->dst = NULL;
784321936Shselasky	}
785321936Shselasky
786321936Shselasky	if (neigh_handler->found_ll_addr != NULL) {
787321936Shselasky		nl_addr_put(neigh_handler->found_ll_addr);
788321936Shselasky		neigh_handler->found_ll_addr = NULL;
789321936Shselasky	}
790321936Shselasky
791321936Shselasky	if (neigh_handler->neigh_cache != NULL) {
792321936Shselasky		nl_cache_mngt_unprovide(neigh_handler->neigh_cache);
793321936Shselasky		nl_cache_free(neigh_handler->neigh_cache);
794321936Shselasky		neigh_handler->neigh_cache = NULL;
795321936Shselasky	}
796321936Shselasky
797321936Shselasky	if (neigh_handler->route_cache != NULL) {
798321936Shselasky		nl_cache_mngt_unprovide(neigh_handler->route_cache);
799321936Shselasky		nl_cache_free(neigh_handler->route_cache);
800321936Shselasky		neigh_handler->route_cache = NULL;
801321936Shselasky	}
802321936Shselasky
803321936Shselasky	if (neigh_handler->link_cache != NULL) {
804321936Shselasky		nl_cache_mngt_unprovide(neigh_handler->link_cache);
805321936Shselasky		nl_cache_free(neigh_handler->link_cache);
806321936Shselasky		neigh_handler->link_cache = NULL;
807321936Shselasky	}
808321936Shselasky
809321936Shselasky	if (neigh_handler->sock != NULL) {
810321936Shselasky		nl_close(neigh_handler->sock);
811321936Shselasky		nl_socket_free(neigh_handler->sock);
812321936Shselasky		neigh_handler->sock = NULL;
813321936Shselasky	}
814321936Shselasky}
815321936Shselasky
816321936Shselaskyint process_get_neigh(struct get_neigh_handler *neigh_handler)
817321936Shselasky{
818321936Shselasky	struct nl_msg *m;
819321936Shselasky	struct rtmsg rmsg = {
820321936Shselasky		.rtm_family = nl_addr_get_family(neigh_handler->dst),
821321936Shselasky		.rtm_dst_len = nl_addr_get_prefixlen(neigh_handler->dst),
822321936Shselasky	};
823321936Shselasky	int err;
824321936Shselasky
825321936Shselasky	m = nlmsg_alloc_simple(RTM_GETROUTE, 0);
826321936Shselasky
827321936Shselasky	if (m == NULL)
828321936Shselasky		return -ENOMEM;
829321936Shselasky
830321936Shselasky	nlmsg_append(m, &rmsg, sizeof(rmsg), NLMSG_ALIGNTO);
831321936Shselasky
832321936Shselasky	nla_put_addr(m, RTA_DST, neigh_handler->dst);
833321936Shselasky
834321936Shselasky	if (neigh_handler->oif > 0)
835321936Shselasky		nla_put_u32(m, RTA_OIF, neigh_handler->oif);
836321936Shselasky
837321936Shselasky	err = nl_send_auto_complete(neigh_handler->sock, m);
838321936Shselasky	nlmsg_free(m);
839321936Shselasky	if (err < 0)
840321936Shselasky		return err;
841321936Shselasky
842321936Shselasky	nl_socket_modify_cb(neigh_handler->sock, NL_CB_VALID,
843321936Shselasky			    NL_CB_CUSTOM, &get_route_cb, neigh_handler);
844321936Shselasky
845321936Shselasky	err = nl_recvmsgs_default(neigh_handler->sock);
846321936Shselasky
847321936Shselasky	return err;
848321936Shselasky}
849