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