/* * Copyright (c) 2014, Cisco Systems, Inc. All rights reserved. * * LICENSE_BEGIN * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * BSD license below: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * LICENSE_END * * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libnl_utils.h" #include "usnic_user_utils.h" #include "usnic_ip_utils.h" int usnic_arp_lookup(char *ifname, uint32_t ipaddr, int sockfd, uint8_t *macaddr) { struct arpreq req; struct sockaddr_in sinp; int err; int status; memset(&req, 0, sizeof req); strncpy(req.arp_dev, ifname, sizeof(req.arp_dev) - 1); memset(&sinp, 0, sizeof(sinp)); sinp.sin_family = AF_INET; sinp.sin_addr.s_addr = ipaddr; memcpy(&req.arp_pa, &sinp, sizeof(sinp)); err = 0; status = ioctl(sockfd, SIOCGARP, (char *)&req, sizeof(req)); if (status != -1 && (req.arp_flags & ATF_COM)) memcpy(macaddr, req.arp_ha.sa_data, 6); else if (status != -1) /* req.arp_flags & ATF_COM == 0 */ err = EAGAIN; else if (errno == ENXIO) /* ENXIO means no ARP entry was found */ err = EAGAIN; else /* status == -1 */ err = errno; return err; } static int usnic_arp_lookup_index(int if_index, uint32_t ipaddr, int sockfd, uint8_t *macaddr) { char ifname[IF_NAMESIZE]; if (if_indextoname((unsigned int)if_index, ifname) == NULL) { usnic_perr("if_indextoname failed. ifindex: %d", if_index); return errno; } return usnic_arp_lookup(ifname, ipaddr, sockfd, macaddr); } int usnic_arp_request(uint32_t ipaddr, int sockfd) { struct sockaddr_in sin; int err = 0; memset(&sin, 0, sizeof(sin)); sin.sin_addr.s_addr = ipaddr; sin.sin_port = htons(9); /* Send to Discard Protocol */ err = sendto(sockfd, NULL, 0, 0, (struct sockaddr *)&sin, sizeof(sin)); if (err == -1) { char buf[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &ipaddr, buf, sizeof(buf)); usnic_perr("Arp triggering socket sendto() failed. ip: %s", buf); } else err = 0; return err; } static int usnic_resolve_arp(int if_index, uint32_t ipaddr, uint8_t *macaddr) { int sockfd; int err; char buf[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &ipaddr, buf, sizeof(buf)); sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd == -1) { usnic_perr( "socket() failed when creating socket for arp resolution, ip: %s", buf); return ENXIO; } err = usnic_arp_lookup_index(if_index, ipaddr, sockfd, macaddr); if (err == EAGAIN || err == ENXIO) { /* entry is FAILED or INCOMPLETE or does not exist, send a dummy packet */ err = usnic_arp_request(ipaddr, sockfd); if (err) /* sendto failure, abort */ err = ENXIO; else err = EAGAIN; } close(sockfd); return err; } int usnic_resolve_dst(int if_index, uint32_t src_ip_addr, uint32_t dst_ip_addr, uint8_t *macaddr) { uint32_t nh_ip_addr = 0; int err; err = usnic_nl_rt_lookup(src_ip_addr, dst_ip_addr, if_index, &nh_ip_addr); if (err) { char ifname[IFNAMSIZ]; char src_buf[INET_ADDRSTRLEN]; char dst_buf[INET_ADDRSTRLEN]; if_indextoname((unsigned int)if_index, ifname); inet_ntop(AF_INET, &src_ip_addr, src_buf, sizeof(src_buf)); inet_ntop(AF_INET, &dst_ip_addr, dst_buf, sizeof(dst_buf)); usnic_err( "ip route lookup for dst: %s on if: %d device: %s src ip: %s failed\n", dst_buf, if_index, ifname, src_buf); return EHOSTUNREACH; } if (nh_ip_addr) { char nh_buf[INET_ADDRSTRLEN]; char src_buf[INET_ADDRSTRLEN]; char dst_buf[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &nh_ip_addr, nh_buf, sizeof(nh_buf)); inet_ntop(AF_INET, &src_ip_addr, src_buf, sizeof(src_buf)); inet_ntop(AF_INET, &dst_ip_addr, dst_buf, sizeof(dst_buf)); usnic_info("ip route for dest %s src %s is via %s\n", dst_buf, src_buf, nh_buf); } else { char src_buf[INET_ADDRSTRLEN]; char dst_buf[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &src_ip_addr, src_buf, sizeof(src_buf)); inet_ntop(AF_INET, &dst_ip_addr, dst_buf, sizeof(dst_buf)); usnic_info("ip route for dest %s src %s is directly connected\n", dst_buf, src_buf); } if (nh_ip_addr) return usnic_resolve_arp(if_index, nh_ip_addr, macaddr); else return usnic_resolve_arp(if_index, dst_ip_addr, macaddr); }