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