| /* Licensed under the OpenIB.org BSD license (FreeBSD Variant) - See COPYING.md | |
| */ | |
| #include "config.h" | |
| #include <linux/netlink.h> | |
| #include <linux/rtnetlink.h> | |
| #include <endian.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <stdbool.h> | |
| #if HAVE_WORKING_IF_H | |
| #include <net/if.h> | |
| #endif | |
| #include <netlink/route/rtnl.h> | |
| #include <netlink/route/link.h> | |
| #include <netlink/route/route.h> | |
| #include <netlink/route/neighbour.h> | |
| #include <sys/types.h> | |
| #include <sys/socket.h> | |
| #include <sys/timerfd.h> | |
| #include <errno.h> | |
| #include <unistd.h> | |
| #include <ifaddrs.h> | |
| #include <netdb.h> | |
| #include <assert.h> | |
| #if !HAVE_WORKING_IF_H | |
| /* We need this decl from net/if.h but old systems do not let use co-include | |
| net/if.h and netlink/route/link.h */ | |
| extern unsigned int if_nametoindex(__const char *__ifname); | |
| #endif | |
| /* for PFX */ | |
| #include "ibverbs.h" | |
| #include <ccan/minmax.h> | |
| #include "neigh.h" | |
| #include <netlink/route/link/vlan.h> | |
| union sktaddr { | |
| struct sockaddr s; | |
| struct sockaddr_in s4; | |
| struct sockaddr_in6 s6; | |
| }; | |
| struct skt { | |
| union sktaddr sktaddr; | |
| socklen_t len; | |
| }; | |
| static int set_link_port(union sktaddr *s, __be16 port, int oif) | |
| { | |
| switch (s->s.sa_family) { | |
| case AF_INET: | |
| s->s4.sin_port = port; | |
| break; | |
| case AF_INET6: | |
| s->s6.sin6_port = port; | |
| s->s6.sin6_scope_id = oif; | |
| break; | |
| default: | |
| return -EINVAL; | |
| } | |
| return 0; | |
| } | |
| static bool cmp_address(const struct sockaddr *s1, | |
| const struct sockaddr *s2) | |
| { | |
| if (s1->sa_family != s2->sa_family) | |
| return false; | |
| switch (s1->sa_family) { | |
| case AF_INET: | |
| return ((struct sockaddr_in *)s1)->sin_addr.s_addr == | |
| ((struct sockaddr_in *)s2)->sin_addr.s_addr; | |
| case AF_INET6: | |
| return !memcmp( | |
| ((struct sockaddr_in6 *)s1)->sin6_addr.s6_addr, | |
| ((struct sockaddr_in6 *)s2)->sin6_addr.s6_addr, | |
| sizeof(((struct sockaddr_in6 *)s1)->sin6_addr.s6_addr)); | |
| default: | |
| return false; | |
| } | |
| } | |
| static int get_ifindex(const struct sockaddr *s) | |
| { | |
| struct ifaddrs *ifaddr, *ifa; | |
| int name2index = -ENODEV; | |
| if (-1 == getifaddrs(&ifaddr)) | |
| return errno; | |
| for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { | |
| if (ifa->ifa_addr == NULL) | |
| continue; | |
| if (cmp_address(ifa->ifa_addr, s)) { | |
| name2index = if_nametoindex(ifa->ifa_name); | |
| break; | |
| } | |
| } | |
| freeifaddrs(ifaddr); | |
| return name2index; | |
| } | |
| static struct nl_addr *get_neigh_mac(struct get_neigh_handler *neigh_handler) | |
| { | |
| struct rtnl_neigh *neigh; | |
| struct nl_addr *ll_addr = NULL; | |
| /* future optimization - if link local address - parse address and | |
| * return mac now instead of doing so after the routing CB. This | |
| * is of course referred to GIDs */ | |
| neigh = rtnl_neigh_get(neigh_handler->neigh_cache, | |
| neigh_handler->oif, | |
| neigh_handler->dst); | |
| if (neigh == NULL) | |
| return NULL; | |
| ll_addr = rtnl_neigh_get_lladdr(neigh); | |
| if (NULL != ll_addr) | |
| ll_addr = nl_addr_clone(ll_addr); | |
| rtnl_neigh_put(neigh); | |
| return ll_addr; | |
| } | |
| static void get_neigh_cb_event(struct nl_object *obj, void *arg) | |
| { | |
| struct get_neigh_handler *neigh_handler = | |
| (struct get_neigh_handler *)arg; | |
| /* assumed serilized callback (no parallel execution of function) */ | |
| if (nl_object_match_filter( | |
| obj, | |
| (struct nl_object *)neigh_handler->filter_neigh)) { | |
| struct rtnl_neigh *neigh = (struct rtnl_neigh *)obj; | |
| /* check that we didn't set it already */ | |
| if (neigh_handler->found_ll_addr == NULL) { | |
| if (rtnl_neigh_get_lladdr(neigh) == NULL) | |
| return; | |
| neigh_handler->found_ll_addr = | |
| nl_addr_clone(rtnl_neigh_get_lladdr(neigh)); | |
| } | |
| } | |
| } | |
| static int get_neigh_cb(struct nl_msg *msg, void *arg) | |
| { | |
| struct get_neigh_handler *neigh_handler = | |
| (struct get_neigh_handler *)arg; | |
| if (nl_msg_parse(msg, &get_neigh_cb_event, neigh_handler) < 0) | |
| errno = ENOMSG; | |
| return NL_OK; | |
| } | |
| static void set_neigh_filter(struct get_neigh_handler *neigh_handler, | |
| struct rtnl_neigh *filter) { | |
| neigh_handler->filter_neigh = filter; | |
| } | |
| static struct rtnl_neigh *create_filter_neigh_for_dst(struct nl_addr *dst_addr, | |
| int oif) | |
| { | |
| struct rtnl_neigh *filter_neigh; | |
| filter_neigh = rtnl_neigh_alloc(); | |
| if (filter_neigh == NULL) | |
| return NULL; | |
| rtnl_neigh_set_ifindex(filter_neigh, oif); | |
| rtnl_neigh_set_dst(filter_neigh, dst_addr); | |
| return filter_neigh; | |
| } | |
| #define PORT_DISCARD htobe16(9) | |
| #define SEND_PAYLOAD "H" | |
| static int create_socket(struct get_neigh_handler *neigh_handler, | |
| struct skt *addr_dst, int *psock_fd) | |
| { | |
| int err; | |
| struct skt addr_src; | |
| int sock_fd; | |
| memset(addr_dst, 0, sizeof(*addr_dst)); | |
| memset(&addr_src, 0, sizeof(addr_src)); | |
| addr_src.len = sizeof(addr_src.sktaddr); | |
| err = nl_addr_fill_sockaddr(neigh_handler->src, | |
| &addr_src.sktaddr.s, | |
| &addr_src.len); | |
| if (err) { | |
| errno = EADDRNOTAVAIL; | |
| return -1; | |
| } | |
| addr_dst->len = sizeof(addr_dst->sktaddr); | |
| err = nl_addr_fill_sockaddr(neigh_handler->dst, | |
| &addr_dst->sktaddr.s, | |
| &addr_dst->len); | |
| if (err) { | |
| errno = EADDRNOTAVAIL; | |
| return -1; | |
| } | |
| err = set_link_port(&addr_dst->sktaddr, PORT_DISCARD, | |
| neigh_handler->oif); | |
| if (err) | |
| return -1; | |
| sock_fd = socket(addr_dst->sktaddr.s.sa_family, | |
| SOCK_DGRAM | SOCK_CLOEXEC, 0); | |
| if (sock_fd == -1) | |
| return -1; | |
| err = bind(sock_fd, &addr_src.sktaddr.s, addr_src.len); | |
| if (err) { | |
| close(sock_fd); | |
| return -1; | |
| } | |
| *psock_fd = sock_fd; | |
| return 0; | |
| } | |
| #define NUM_OF_RETRIES 10 | |
| #define NUM_OF_TRIES ((NUM_OF_RETRIES) + 1) | |
| #if NUM_OF_TRIES < 1 | |
| #error "neigh: invalid value of NUM_OF_RETRIES" | |
| #endif | |
| static int create_timer(struct get_neigh_handler *neigh_handler) | |
| { | |
| int user_timeout = neigh_handler->timeout/NUM_OF_TRIES; | |
| struct timespec timeout = { | |
| .tv_sec = user_timeout / 1000, | |
| .tv_nsec = (user_timeout % 1000) * 1000000 | |
| }; | |
| struct itimerspec timer_time = {.it_value = timeout}; | |
| int timer_fd; | |
| timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); | |
| if (timer_fd == -1) | |
| return timer_fd; | |
| if (neigh_handler->timeout) { | |
| if (NUM_OF_TRIES <= 1) | |
| bzero(&timer_time.it_interval, | |
| sizeof(timer_time.it_interval)); | |
| else | |
| timer_time.it_interval = timeout; | |
| if (timerfd_settime(timer_fd, 0, &timer_time, NULL)) { | |
| close(timer_fd); | |
| return -1; | |
| } | |
| } | |
| return timer_fd; | |
| } | |
| #define UDP_SOCKET_MAX_SENDTO 100000ULL | |
| static int try_send_to(int sock_fd, void *buff, size_t buf_size, | |
| struct skt *addr_dst) | |
| { | |
| uint64_t max_count = UDP_SOCKET_MAX_SENDTO; | |
| int err; | |
| do { | |
| err = sendto(sock_fd, buff, buf_size, 0, | |
| &addr_dst->sktaddr.s, | |
| addr_dst->len); | |
| if (err > 0) | |
| err = 0; | |
| } while (-1 == err && EADDRNOTAVAIL == errno && --max_count); | |
| return err; | |
| } | |
| static struct nl_addr *process_get_neigh_mac( | |
| struct get_neigh_handler *neigh_handler) | |
| { | |
| int err; | |
| struct nl_addr *ll_addr = get_neigh_mac(neigh_handler); | |
| struct rtnl_neigh *neigh_filter; | |
| fd_set fdset; | |
| int sock_fd; | |
| int fd; | |
| int nfds; | |
| int timer_fd; | |
| int ret; | |
| struct skt addr_dst; | |
| char buff[sizeof(SEND_PAYLOAD)] = SEND_PAYLOAD; | |
| int retries = 0; | |
| if (NULL != ll_addr) | |
| return ll_addr; | |
| err = nl_socket_add_membership(neigh_handler->sock, | |
| RTNLGRP_NEIGH); | |
| if (err < 0) | |
| return NULL; | |
| neigh_filter = create_filter_neigh_for_dst(neigh_handler->dst, | |
| neigh_handler->oif); | |
| if (neigh_filter == NULL) | |
| return NULL; | |
| set_neigh_filter(neigh_handler, neigh_filter); | |
| nl_socket_disable_seq_check(neigh_handler->sock); | |
| nl_socket_modify_cb(neigh_handler->sock, NL_CB_VALID, NL_CB_CUSTOM, | |
| &get_neigh_cb, neigh_handler); | |
| fd = nl_socket_get_fd(neigh_handler->sock); | |
| err = create_socket(neigh_handler, &addr_dst, &sock_fd); | |
| if (err) | |
| return NULL; | |
| err = try_send_to(sock_fd, buff, sizeof(buff), &addr_dst); | |
| if (err) | |
| goto close_socket; | |
| timer_fd = create_timer(neigh_handler); | |
| if (timer_fd < 0) | |
| goto close_socket; | |
| nfds = max(fd, timer_fd) + 1; | |
| while (1) { | |
| FD_ZERO(&fdset); | |
| FD_SET(fd, &fdset); | |
| FD_SET(timer_fd, &fdset); | |
| /* wait for an incoming message on the netlink socket */ | |
| ret = select(nfds, &fdset, NULL, NULL, NULL); | |
| if (ret == -1) { | |
| goto select_err; | |
| } else if (ret) { | |
| if (FD_ISSET(fd, &fdset)) { | |
| nl_recvmsgs_default(neigh_handler->sock); | |
| if (neigh_handler->found_ll_addr) | |
| break; | |
| } else { | |
| nl_cache_refill(neigh_handler->sock, | |
| neigh_handler->neigh_cache); | |
| ll_addr = get_neigh_mac(neigh_handler); | |
| if (NULL != ll_addr) { | |
| break; | |
| } else if (FD_ISSET(timer_fd, &fdset) && | |
| retries < NUM_OF_RETRIES) { | |
| try_send_to(sock_fd, buff, sizeof(buff), | |
| &addr_dst); | |
| } | |
| } | |
| if (FD_ISSET(timer_fd, &fdset)) { | |
| uint64_t read_val; | |
| ssize_t __attribute__((unused)) rc; | |
| rc = | |
| read(timer_fd, &read_val, sizeof(read_val)); | |
| assert(rc == sizeof(read_val)); | |
| if (++retries >= NUM_OF_TRIES) { | |
| if (!errno) | |
| errno = EDESTADDRREQ; | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| select_err: | |
| close(timer_fd); | |
| close_socket: | |
| close(sock_fd); | |
| return ll_addr ? ll_addr : neigh_handler->found_ll_addr; | |
| } | |
| static int get_mcast_mac_ipv4(struct nl_addr *dst, struct nl_addr **ll_addr) | |
| { | |
| uint8_t mac_addr[6] = {0x01, 0x00, 0x5E}; | |
| uint32_t addr = be32toh(*(__be32 *)nl_addr_get_binary_addr(dst)); | |
| mac_addr[5] = addr & 0xFF; | |
| addr >>= 8; | |
| mac_addr[4] = addr & 0xFF; | |
| addr >>= 8; | |
| mac_addr[3] = addr & 0x7F; | |
| *ll_addr = nl_addr_build(AF_LLC, mac_addr, sizeof(mac_addr)); | |
| return *ll_addr == NULL ? -EINVAL : 0; | |
| } | |
| static int get_mcast_mac_ipv6(struct nl_addr *dst, struct nl_addr **ll_addr) | |
| { | |
| uint8_t mac_addr[6] = {0x33, 0x33}; | |
| memcpy(mac_addr + 2, (uint8_t *)nl_addr_get_binary_addr(dst) + 12, 4); | |
| *ll_addr = nl_addr_build(AF_LLC, mac_addr, sizeof(mac_addr)); | |
| return *ll_addr == NULL ? -EINVAL : 0; | |
| } | |
| static int get_link_local_mac_ipv6(struct nl_addr *dst, | |
| struct nl_addr **ll_addr) | |
| { | |
| uint8_t mac_addr[6]; | |
| memcpy(mac_addr + 3, (uint8_t *)nl_addr_get_binary_addr(dst) + 13, 3); | |
| memcpy(mac_addr, (uint8_t *)nl_addr_get_binary_addr(dst) + 8, 3); | |
| mac_addr[0] ^= 2; | |
| *ll_addr = nl_addr_build(AF_LLC, mac_addr, sizeof(mac_addr)); | |
| return *ll_addr == NULL ? -EINVAL : 0; | |
| } | |
| static const struct encoded_l3_addr { | |
| short family; | |
| uint8_t prefix_bits; | |
| const uint8_t data[16]; | |
| int (*getter)(struct nl_addr *dst, struct nl_addr **ll_addr); | |
| } encoded_prefixes[] = { | |
| {.family = AF_INET, | |
| .prefix_bits = 4, | |
| .data = {0xe0}, | |
| .getter = &get_mcast_mac_ipv4}, | |
| {.family = AF_INET6, | |
| .prefix_bits = 8, | |
| .data = {0xff}, | |
| .getter = &get_mcast_mac_ipv6}, | |
| {.family = AF_INET6, | |
| .prefix_bits = 64, | |
| .data = {0xfe, 0x80}, | |
| .getter = get_link_local_mac_ipv6}, | |
| }; | |
| static int nl_addr_cmp_prefix_msb(void *addr1, int len1, void *addr2, int len2) | |
| { | |
| int len = min(len1, len2); | |
| int bytes = len / 8; | |
| int d = memcmp(addr1, addr2, bytes); | |
| if (d == 0) { | |
| int mask = ((1UL << (len % 8)) - 1UL) << (8 - len); | |
| d = (((uint8_t *)addr1)[bytes] & mask) - | |
| (((uint8_t *)addr2)[bytes] & mask); | |
| } | |
| return d; | |
| } | |
| static int handle_encoded_mac(struct nl_addr *dst, struct nl_addr **ll_addr) | |
| { | |
| uint32_t family = nl_addr_get_family(dst); | |
| struct nl_addr *prefix = NULL; | |
| int i; | |
| int ret = 1; | |
| for (i = 0; | |
| i < sizeof(encoded_prefixes)/sizeof(encoded_prefixes[0]) && | |
| ret; prefix = NULL, i++) { | |
| if (encoded_prefixes[i].family != family) | |
| continue; | |
| prefix = nl_addr_build( | |
| family, (void *)encoded_prefixes[i].data, | |
| min_t(size_t, encoded_prefixes[i].prefix_bits / 8 + | |
| !!(encoded_prefixes[i].prefix_bits % 8), | |
| sizeof(encoded_prefixes[i].data))); | |
| if (prefix == NULL) | |
| return -ENOMEM; | |
| nl_addr_set_prefixlen(prefix, | |
| encoded_prefixes[i].prefix_bits); | |
| if (nl_addr_cmp_prefix_msb(nl_addr_get_binary_addr(dst), | |
| nl_addr_get_prefixlen(dst), | |
| nl_addr_get_binary_addr(prefix), | |
| nl_addr_get_prefixlen(prefix))) | |
| continue; | |
| ret = encoded_prefixes[i].getter(dst, ll_addr); | |
| nl_addr_put(prefix); | |
| } | |
| return ret; | |
| } | |
| static void get_route_cb_parser(struct nl_object *obj, void *arg) | |
| { | |
| struct get_neigh_handler *neigh_handler = | |
| (struct get_neigh_handler *)arg; | |
| struct rtnl_route *route = (struct rtnl_route *)obj; | |
| struct nl_addr *gateway = NULL; | |
| struct nl_addr *src = rtnl_route_get_pref_src(route); | |
| int oif; | |
| int type = rtnl_route_get_type(route); | |
| struct rtnl_link *link; | |
| struct rtnl_nexthop *nh = rtnl_route_nexthop_n(route, 0); | |
| if (nh != NULL) | |
| gateway = rtnl_route_nh_get_gateway(nh); | |
| oif = rtnl_route_nh_get_ifindex(nh); | |
| if (gateway) { | |
| nl_addr_put(neigh_handler->dst); | |
| neigh_handler->dst = nl_addr_clone(gateway); | |
| } | |
| if (RTN_BLACKHOLE == type || | |
| RTN_UNREACHABLE == type || | |
| RTN_PROHIBIT == type || | |
| RTN_THROW == type) { | |
| errno = ENETUNREACH; | |
| goto err; | |
| } | |
| if (!neigh_handler->src && src) | |
| neigh_handler->src = nl_addr_clone(src); | |
| if (neigh_handler->oif < 0 && oif > 0) | |
| neigh_handler->oif = oif; | |
| /* Link Local */ | |
| if (RTN_LOCAL == type) { | |
| struct nl_addr *lladdr; | |
| link = rtnl_link_get(neigh_handler->link_cache, | |
| neigh_handler->oif); | |
| if (link == NULL) | |
| goto err; | |
| lladdr = rtnl_link_get_addr(link); | |
| if (lladdr == NULL) | |
| goto err_link; | |
| neigh_handler->found_ll_addr = nl_addr_clone(lladdr); | |
| rtnl_link_put(link); | |
| } else { | |
| handle_encoded_mac( | |
| neigh_handler->dst, | |
| &neigh_handler->found_ll_addr); | |
| } | |
| return; | |
| err_link: | |
| rtnl_link_put(link); | |
| err: | |
| if (neigh_handler->src) { | |
| nl_addr_put(neigh_handler->src); | |
| neigh_handler->src = NULL; | |
| } | |
| } | |
| static int get_route_cb(struct nl_msg *msg, void *arg) | |
| { | |
| struct get_neigh_handler *neigh_handler = | |
| (struct get_neigh_handler *)arg; | |
| int err; | |
| err = nl_msg_parse(msg, &get_route_cb_parser, neigh_handler); | |
| if (err < 0) { | |
| errno = ENOMSG; | |
| return err; | |
| } | |
| if (!neigh_handler->dst || !neigh_handler->src || | |
| neigh_handler->oif <= 0) { | |
| errno = EINVAL; | |
| return -1; | |
| } | |
| if (NULL != neigh_handler->found_ll_addr) | |
| goto found; | |
| neigh_handler->found_ll_addr = | |
| process_get_neigh_mac(neigh_handler); | |
| found: | |
| return neigh_handler->found_ll_addr ? 0 : -1; | |
| } | |
| int neigh_get_oif_from_src(struct get_neigh_handler *neigh_handler) | |
| { | |
| int oif = -ENODEV; | |
| struct addrinfo *src_info; | |
| int err; | |
| err = nl_addr_info(neigh_handler->src, &src_info); | |
| if (err) { | |
| if (!errno) | |
| errno = ENXIO; | |
| return oif; | |
| } | |
| oif = get_ifindex(src_info->ai_addr); | |
| if (oif <= 0) | |
| goto free; | |
| free: | |
| freeaddrinfo(src_info); | |
| return oif; | |
| } | |
| int neigh_init_resources(struct get_neigh_handler *neigh_handler, int timeout) | |
| { | |
| int err; | |
| neigh_handler->sock = nl_socket_alloc(); | |
| if (neigh_handler->sock == NULL) { | |
| errno = ENOSYS; | |
| return -ENOSYS; | |
| } | |
| err = nl_connect(neigh_handler->sock, NETLINK_ROUTE); | |
| if (err < 0) | |
| goto free_socket; | |
| err = rtnl_link_alloc_cache(neigh_handler->sock, AF_UNSPEC, | |
| &neigh_handler->link_cache); | |
| if (err) { | |
| err = -1; | |
| errno = ENOMEM; | |
| goto free_socket; | |
| } | |
| nl_cache_mngt_provide(neigh_handler->link_cache); | |
| err = rtnl_route_alloc_cache(neigh_handler->sock, AF_UNSPEC, 0, | |
| &neigh_handler->route_cache); | |
| if (err) { | |
| err = -1; | |
| errno = ENOMEM; | |
| goto free_link_cache; | |
| } | |
| nl_cache_mngt_provide(neigh_handler->route_cache); | |
| err = rtnl_neigh_alloc_cache(neigh_handler->sock, | |
| &neigh_handler->neigh_cache); | |
| if (err) { | |
| err = -ENOMEM; | |
| goto free_route_cache; | |
| } | |
| nl_cache_mngt_provide(neigh_handler->neigh_cache); | |
| /* init structure */ | |
| neigh_handler->timeout = timeout; | |
| neigh_handler->oif = -1; | |
| neigh_handler->filter_neigh = NULL; | |
| neigh_handler->found_ll_addr = NULL; | |
| neigh_handler->dst = NULL; | |
| neigh_handler->src = NULL; | |
| neigh_handler->vid = -1; | |
| return 0; | |
| free_route_cache: | |
| nl_cache_mngt_unprovide(neigh_handler->route_cache); | |
| nl_cache_free(neigh_handler->route_cache); | |
| neigh_handler->route_cache = NULL; | |
| free_link_cache: | |
| nl_cache_mngt_unprovide(neigh_handler->link_cache); | |
| nl_cache_free(neigh_handler->link_cache); | |
| neigh_handler->link_cache = NULL; | |
| free_socket: | |
| nl_socket_free(neigh_handler->sock); | |
| neigh_handler->sock = NULL; | |
| return err; | |
| } | |
| uint16_t neigh_get_vlan_id_from_dev(struct get_neigh_handler *neigh_handler) | |
| { | |
| struct rtnl_link *link; | |
| int vid = 0xffff; | |
| link = rtnl_link_get(neigh_handler->link_cache, neigh_handler->oif); | |
| if (link == NULL) { | |
| errno = EINVAL; | |
| return vid; | |
| } | |
| if (rtnl_link_is_vlan(link)) | |
| vid = rtnl_link_vlan_get_id(link); | |
| rtnl_link_put(link); | |
| return vid >= 0 && vid <= 0xfff ? vid : 0xffff; | |
| } | |
| void neigh_set_vlan_id(struct get_neigh_handler *neigh_handler, uint16_t vid) | |
| { | |
| if (vid <= 0xfff) | |
| neigh_handler->vid = vid; | |
| } | |
| int neigh_set_dst(struct get_neigh_handler *neigh_handler, | |
| int family, void *buf, size_t size) | |
| { | |
| neigh_handler->dst = nl_addr_build(family, buf, size); | |
| return neigh_handler->dst == NULL; | |
| } | |
| int neigh_set_src(struct get_neigh_handler *neigh_handler, | |
| int family, void *buf, size_t size) | |
| { | |
| neigh_handler->src = nl_addr_build(family, buf, size); | |
| return neigh_handler->src == NULL; | |
| } | |
| void neigh_set_oif(struct get_neigh_handler *neigh_handler, int oif) | |
| { | |
| neigh_handler->oif = oif; | |
| } | |
| int neigh_get_ll(struct get_neigh_handler *neigh_handler, void *addr_buff, | |
| int addr_size) { | |
| int neigh_len; | |
| if (neigh_handler->found_ll_addr == NULL) | |
| return -EINVAL; | |
| neigh_len = nl_addr_get_len(neigh_handler->found_ll_addr); | |
| if (neigh_len > addr_size) | |
| return -EINVAL; | |
| memcpy(addr_buff, nl_addr_get_binary_addr(neigh_handler->found_ll_addr), | |
| neigh_len); | |
| return neigh_len; | |
| } | |
| void neigh_free_resources(struct get_neigh_handler *neigh_handler) | |
| { | |
| /* Should be released first because it's holding a reference to dst */ | |
| if (neigh_handler->filter_neigh != NULL) { | |
| rtnl_neigh_put(neigh_handler->filter_neigh); | |
| neigh_handler->filter_neigh = NULL; | |
| } | |
| if (neigh_handler->src != NULL) { | |
| nl_addr_put(neigh_handler->src); | |
| neigh_handler->src = NULL; | |
| } | |
| if (neigh_handler->dst != NULL) { | |
| nl_addr_put(neigh_handler->dst); | |
| neigh_handler->dst = NULL; | |
| } | |
| if (neigh_handler->found_ll_addr != NULL) { | |
| nl_addr_put(neigh_handler->found_ll_addr); | |
| neigh_handler->found_ll_addr = NULL; | |
| } | |
| if (neigh_handler->neigh_cache != NULL) { | |
| nl_cache_mngt_unprovide(neigh_handler->neigh_cache); | |
| nl_cache_free(neigh_handler->neigh_cache); | |
| neigh_handler->neigh_cache = NULL; | |
| } | |
| if (neigh_handler->route_cache != NULL) { | |
| nl_cache_mngt_unprovide(neigh_handler->route_cache); | |
| nl_cache_free(neigh_handler->route_cache); | |
| neigh_handler->route_cache = NULL; | |
| } | |
| if (neigh_handler->link_cache != NULL) { | |
| nl_cache_mngt_unprovide(neigh_handler->link_cache); | |
| nl_cache_free(neigh_handler->link_cache); | |
| neigh_handler->link_cache = NULL; | |
| } | |
| if (neigh_handler->sock != NULL) { | |
| nl_socket_free(neigh_handler->sock); | |
| neigh_handler->sock = NULL; | |
| } | |
| } | |
| int process_get_neigh(struct get_neigh_handler *neigh_handler) | |
| { | |
| struct nl_msg *m; | |
| struct rtmsg rmsg = { | |
| .rtm_family = nl_addr_get_family(neigh_handler->dst), | |
| .rtm_dst_len = nl_addr_get_prefixlen(neigh_handler->dst), | |
| }; | |
| int err; | |
| m = nlmsg_alloc_simple(RTM_GETROUTE, 0); | |
| if (m == NULL) | |
| return -ENOMEM; | |
| nlmsg_append(m, &rmsg, sizeof(rmsg), NLMSG_ALIGNTO); | |
| NLA_PUT_ADDR(m, RTA_DST, neigh_handler->dst); | |
| if (neigh_handler->oif > 0) | |
| NLA_PUT_U32(m, RTA_OIF, neigh_handler->oif); | |
| err = nl_send_auto(neigh_handler->sock, m); | |
| nlmsg_free(m); | |
| if (err < 0) | |
| return err; | |
| nl_socket_modify_cb(neigh_handler->sock, NL_CB_VALID, | |
| NL_CB_CUSTOM, &get_route_cb, neigh_handler); | |
| err = nl_recvmsgs_default(neigh_handler->sock); | |
| return err; | |
| nla_put_failure: | |
| nlmsg_free(m); | |
| return -ENOMEM; | |
| } |