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