1331772Shselasky/*- 2331772Shselasky * SPDX-License-Identifier: BSD-2-Clause OR GPL-2.0 3331772Shselasky * 4319974Shselasky * Copyright (c) 2005 Voltaire Inc. All rights reserved. 5319974Shselasky * Copyright (c) 2002-2005, Network Appliance, Inc. All rights reserved. 6319974Shselasky * Copyright (c) 1999-2005, Mellanox Technologies, Inc. All rights reserved. 7319974Shselasky * Copyright (c) 2005 Intel Corporation. All rights reserved. 8319974Shselasky * 9319974Shselasky * This software is available to you under a choice of one of two 10319974Shselasky * licenses. You may choose to be licensed under the terms of the GNU 11319974Shselasky * General Public License (GPL) Version 2, available from the file 12319974Shselasky * COPYING in the main directory of this source tree, or the 13319974Shselasky * OpenIB.org BSD license below: 14319974Shselasky * 15319974Shselasky * Redistribution and use in source and binary forms, with or 16319974Shselasky * without modification, are permitted provided that the following 17319974Shselasky * conditions are met: 18319974Shselasky * 19319974Shselasky * - Redistributions of source code must retain the above 20319974Shselasky * copyright notice, this list of conditions and the following 21319974Shselasky * disclaimer. 22319974Shselasky * 23319974Shselasky * - Redistributions in binary form must reproduce the above 24319974Shselasky * copyright notice, this list of conditions and the following 25319974Shselasky * disclaimer in the documentation and/or other materials 26319974Shselasky * provided with the distribution. 27319974Shselasky * 28319974Shselasky * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 29319974Shselasky * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 30319974Shselasky * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 31319974Shselasky * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 32319974Shselasky * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 33319974Shselasky * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 34319974Shselasky * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 35319974Shselasky * SOFTWARE. 36319974Shselasky */ 37319974Shselasky 38337096Shselasky#include <sys/cdefs.h> 39337096Shselasky__FBSDID("$FreeBSD: stable/11/sys/ofed/drivers/infiniband/core/ib_addr.c 341882 2018-12-12 11:16:32Z hselasky $"); 40337096Shselasky 41319974Shselasky#include <linux/mutex.h> 42319974Shselasky#include <linux/inetdevice.h> 43319974Shselasky#include <linux/slab.h> 44319974Shselasky#include <linux/workqueue.h> 45319974Shselasky#include <linux/module.h> 46319974Shselasky#include <net/route.h> 47319974Shselasky#include <net/netevent.h> 48319974Shselasky#include <rdma/ib_addr.h> 49319974Shselasky#include <rdma/ib.h> 50319974Shselasky 51319974Shselasky#include <netinet/if_ether.h> 52319974Shselasky#include <netinet/ip_var.h> 53319974Shselasky#include <netinet6/scope6_var.h> 54319974Shselasky#include <netinet6/in6_pcb.h> 55319974Shselasky 56319974Shselasky#include "core_priv.h" 57319974Shselasky 58319974Shselaskystruct addr_req { 59319974Shselasky struct list_head list; 60319974Shselasky struct sockaddr_storage src_addr; 61319974Shselasky struct sockaddr_storage dst_addr; 62319974Shselasky struct rdma_dev_addr *addr; 63319974Shselasky struct rdma_addr_client *client; 64319974Shselasky void *context; 65319974Shselasky void (*callback)(int status, struct sockaddr *src_addr, 66319974Shselasky struct rdma_dev_addr *addr, void *context); 67337069Shselasky int timeout; 68319974Shselasky int status; 69319974Shselasky}; 70319974Shselasky 71319974Shselaskystatic void process_req(struct work_struct *work); 72319974Shselasky 73319974Shselaskystatic DEFINE_MUTEX(lock); 74319974Shselaskystatic LIST_HEAD(req_list); 75319974Shselaskystatic DECLARE_DELAYED_WORK(work, process_req); 76319974Shselaskystatic struct workqueue_struct *addr_wq; 77319974Shselasky 78319974Shselaskyint rdma_addr_size(struct sockaddr *addr) 79319974Shselasky{ 80319974Shselasky switch (addr->sa_family) { 81319974Shselasky case AF_INET: 82319974Shselasky return sizeof(struct sockaddr_in); 83319974Shselasky case AF_INET6: 84319974Shselasky return sizeof(struct sockaddr_in6); 85319974Shselasky case AF_IB: 86319974Shselasky return sizeof(struct sockaddr_ib); 87319974Shselasky default: 88319974Shselasky return 0; 89319974Shselasky } 90319974Shselasky} 91319974ShselaskyEXPORT_SYMBOL(rdma_addr_size); 92319974Shselasky 93337085Shselaskyint rdma_addr_size_in6(struct sockaddr_in6 *addr) 94337085Shselasky{ 95337085Shselasky int ret = rdma_addr_size((struct sockaddr *) addr); 96337085Shselasky 97337085Shselasky return ret <= sizeof(*addr) ? ret : 0; 98337085Shselasky} 99337085ShselaskyEXPORT_SYMBOL(rdma_addr_size_in6); 100337085Shselasky 101337085Shselaskyint rdma_addr_size_kss(struct sockaddr_storage *addr) 102337085Shselasky{ 103337085Shselasky int ret = rdma_addr_size((struct sockaddr *) addr); 104337085Shselasky 105337085Shselasky return ret <= sizeof(*addr) ? ret : 0; 106337085Shselasky} 107337085ShselaskyEXPORT_SYMBOL(rdma_addr_size_kss); 108337085Shselasky 109319974Shselaskystatic struct rdma_addr_client self; 110319974Shselasky 111319974Shselaskyvoid rdma_addr_register_client(struct rdma_addr_client *client) 112319974Shselasky{ 113319974Shselasky atomic_set(&client->refcount, 1); 114319974Shselasky init_completion(&client->comp); 115319974Shselasky} 116319974ShselaskyEXPORT_SYMBOL(rdma_addr_register_client); 117319974Shselasky 118319974Shselaskystatic inline void put_client(struct rdma_addr_client *client) 119319974Shselasky{ 120319974Shselasky if (atomic_dec_and_test(&client->refcount)) 121319974Shselasky complete(&client->comp); 122319974Shselasky} 123319974Shselasky 124319974Shselaskyvoid rdma_addr_unregister_client(struct rdma_addr_client *client) 125319974Shselasky{ 126319974Shselasky put_client(client); 127319974Shselasky wait_for_completion(&client->comp); 128319974Shselasky} 129319974ShselaskyEXPORT_SYMBOL(rdma_addr_unregister_client); 130319974Shselasky 131319974Shselaskystatic inline void 132319974Shselaskyrdma_copy_addr_sub(u8 *dst, const u8 *src, unsigned min, unsigned max) 133319974Shselasky{ 134319974Shselasky if (min > max) 135319974Shselasky min = max; 136319974Shselasky memcpy(dst, src, min); 137319974Shselasky memset(dst + min, 0, max - min); 138319974Shselasky} 139319974Shselasky 140319974Shselaskyint rdma_copy_addr(struct rdma_dev_addr *dev_addr, struct net_device *dev, 141319974Shselasky const unsigned char *dst_dev_addr) 142319974Shselasky{ 143331781Shselasky /* check for loopback device */ 144337074Shselasky if (dev->if_flags & IFF_LOOPBACK) { 145331781Shselasky dev_addr->dev_type = ARPHRD_ETHER; 146331781Shselasky memset(dev_addr->src_dev_addr, 0, MAX_ADDR_LEN); 147331781Shselasky memset(dev_addr->broadcast, 0, MAX_ADDR_LEN); 148331781Shselasky memset(dev_addr->dst_dev_addr, 0, MAX_ADDR_LEN); 149331781Shselasky dev_addr->bound_dev_if = dev->if_index; 150331781Shselasky return (0); 151331781Shselasky } else if (dev->if_type == IFT_INFINIBAND) 152319974Shselasky dev_addr->dev_type = ARPHRD_INFINIBAND; 153319974Shselasky else if (dev->if_type == IFT_ETHER) 154319974Shselasky dev_addr->dev_type = ARPHRD_ETHER; 155319974Shselasky else 156319974Shselasky dev_addr->dev_type = 0; 157319974Shselasky rdma_copy_addr_sub(dev_addr->src_dev_addr, IF_LLADDR(dev), 158319974Shselasky dev->if_addrlen, MAX_ADDR_LEN); 159319974Shselasky rdma_copy_addr_sub(dev_addr->broadcast, dev->if_broadcastaddr, 160319974Shselasky dev->if_addrlen, MAX_ADDR_LEN); 161319974Shselasky if (dst_dev_addr != NULL) { 162319974Shselasky rdma_copy_addr_sub(dev_addr->dst_dev_addr, dst_dev_addr, 163319974Shselasky dev->if_addrlen, MAX_ADDR_LEN); 164319974Shselasky } 165319974Shselasky dev_addr->bound_dev_if = dev->if_index; 166319974Shselasky return 0; 167319974Shselasky} 168319974ShselaskyEXPORT_SYMBOL(rdma_copy_addr); 169319974Shselasky 170319974Shselaskyint rdma_translate_ip(const struct sockaddr *addr, 171331783Shselasky struct rdma_dev_addr *dev_addr) 172319974Shselasky{ 173337074Shselasky struct net_device *dev; 174337074Shselasky int ret; 175319974Shselasky 176319974Shselasky if (dev_addr->bound_dev_if) { 177319974Shselasky dev = dev_get_by_index(dev_addr->net, dev_addr->bound_dev_if); 178337074Shselasky } else switch (addr->sa_family) { 179319974Shselasky#ifdef INET 180319974Shselasky case AF_INET: 181319974Shselasky dev = ip_dev_find(dev_addr->net, 182319974Shselasky ((const struct sockaddr_in *)addr)->sin_addr.s_addr); 183319974Shselasky break; 184319974Shselasky#endif 185319974Shselasky#ifdef INET6 186323646Shselasky case AF_INET6: 187323646Shselasky dev = ip6_dev_find(dev_addr->net, 188341880Shselasky ((const struct sockaddr_in6 *)addr)->sin6_addr, 0); 189319974Shselasky break; 190319974Shselasky#endif 191319974Shselasky default: 192337074Shselasky dev = NULL; 193319974Shselasky break; 194319974Shselasky } 195319974Shselasky 196319974Shselasky if (dev != NULL) { 197337074Shselasky /* disallow connections through 127.0.0.1 itself */ 198337074Shselasky if (dev->if_flags & IFF_LOOPBACK) 199337074Shselasky ret = -EINVAL; 200337074Shselasky else 201337074Shselasky ret = rdma_copy_addr(dev_addr, dev, NULL); 202319974Shselasky dev_put(dev); 203337074Shselasky } else { 204337074Shselasky ret = -ENODEV; 205319974Shselasky } 206319974Shselasky return ret; 207319974Shselasky} 208319974ShselaskyEXPORT_SYMBOL(rdma_translate_ip); 209319974Shselasky 210337069Shselaskystatic void set_timeout(int time) 211319974Shselasky{ 212319974Shselasky int delay; /* under FreeBSD ticks are 32-bit */ 213319974Shselasky 214319974Shselasky delay = time - jiffies; 215319974Shselasky if (delay <= 0) 216319974Shselasky delay = 1; 217337069Shselasky else if (delay > hz) 218337069Shselasky delay = hz; 219319974Shselasky 220319974Shselasky mod_delayed_work(addr_wq, &work, delay); 221319974Shselasky} 222319974Shselasky 223319974Shselaskystatic void queue_req(struct addr_req *req) 224319974Shselasky{ 225319974Shselasky struct addr_req *temp_req; 226319974Shselasky 227319974Shselasky mutex_lock(&lock); 228319974Shselasky list_for_each_entry_reverse(temp_req, &req_list, list) { 229319974Shselasky if (time_after_eq(req->timeout, temp_req->timeout)) 230319974Shselasky break; 231319974Shselasky } 232319974Shselasky 233319974Shselasky list_add(&req->list, &temp_req->list); 234319974Shselasky 235319974Shselasky if (req_list.next == &req->list) 236319974Shselasky set_timeout(req->timeout); 237319974Shselasky mutex_unlock(&lock); 238319974Shselasky} 239319974Shselasky 240319974Shselasky#if defined(INET) || defined(INET6) 241319974Shselaskystatic int addr_resolve_multi(u8 *edst, struct ifnet *ifp, struct sockaddr *dst_in) 242319974Shselasky{ 243319974Shselasky struct sockaddr *llsa; 244319974Shselasky struct sockaddr_dl sdl; 245319974Shselasky int error; 246319974Shselasky 247319974Shselasky sdl.sdl_len = sizeof(sdl); 248319974Shselasky llsa = (struct sockaddr *)&sdl; 249319974Shselasky 250319974Shselasky if (ifp->if_resolvemulti == NULL) { 251319974Shselasky error = EOPNOTSUPP; 252319974Shselasky } else { 253319974Shselasky error = ifp->if_resolvemulti(ifp, &llsa, dst_in); 254319974Shselasky if (error == 0) { 255319974Shselasky rdma_copy_addr_sub(edst, LLADDR((struct sockaddr_dl *)llsa), 256319974Shselasky ifp->if_addrlen, MAX_ADDR_LEN); 257319974Shselasky } 258319974Shselasky } 259319974Shselasky return (error); 260319974Shselasky} 261319974Shselasky#endif 262319974Shselasky 263319974Shselasky#ifdef INET 264319974Shselaskystatic int addr4_resolve(struct sockaddr_in *src_in, 265319974Shselasky const struct sockaddr_in *dst_in, 266319974Shselasky struct rdma_dev_addr *addr, 267331781Shselasky u8 *edst, 268319974Shselasky struct ifnet **ifpp) 269319974Shselasky{ 270331790Shselasky enum { 271331790Shselasky ADDR_VALID = 0, 272331790Shselasky ADDR_SRC_ANY = 1, 273331790Shselasky ADDR_DST_ANY = 2, 274331790Shselasky }; 275319974Shselasky struct sockaddr_in dst_tmp = *dst_in; 276323646Shselasky in_port_t src_port; 277337070Shselasky struct sockaddr *saddr = NULL; 278319974Shselasky struct rtentry *rte; 279319974Shselasky struct ifnet *ifp; 280319974Shselasky int error; 281323646Shselasky int type; 282319974Shselasky 283323646Shselasky /* set VNET, if any */ 284323646Shselasky CURVNET_SET(addr->net); 285319974Shselasky 286319974Shselasky /* set default TTL limit */ 287319974Shselasky addr->hoplimit = V_ip_defttl; 288319974Shselasky 289331790Shselasky type = ADDR_VALID; 290323646Shselasky if (src_in->sin_addr.s_addr == INADDR_ANY) 291331790Shselasky type |= ADDR_SRC_ANY; 292323646Shselasky if (dst_tmp.sin_addr.s_addr == INADDR_ANY) 293331790Shselasky type |= ADDR_DST_ANY; 294319974Shselasky 295319974Shselasky /* 296323646Shselasky * Make sure the socket address length field 297323646Shselasky * is set, else rtalloc1() will fail. 298319974Shselasky */ 299323646Shselasky dst_tmp.sin_len = sizeof(dst_tmp); 300323646Shselasky 301323646Shselasky /* Step 1 - lookup destination route if any */ 302323646Shselasky switch (type) { 303331790Shselasky case ADDR_VALID: 304331790Shselasky case ADDR_SRC_ANY: 305323646Shselasky /* regular destination route lookup */ 306323646Shselasky rte = rtalloc1((struct sockaddr *)&dst_tmp, 1, 0); 307323646Shselasky if (rte == NULL) { 308323646Shselasky error = EHOSTUNREACH; 309323646Shselasky goto done; 310331781Shselasky } else if (rte->rt_ifp == NULL || RT_LINK_IS_UP(rte->rt_ifp) == 0) { 311323646Shselasky RTFREE_LOCKED(rte); 312323646Shselasky error = EHOSTUNREACH; 313323646Shselasky goto done; 314323646Shselasky } 315323646Shselasky RT_UNLOCK(rte); 316323646Shselasky break; 317323646Shselasky default: 318323646Shselasky error = ENETUNREACH; 319319974Shselasky goto done; 320323646Shselasky } 321319974Shselasky 322323646Shselasky /* Step 2 - find outgoing network interface */ 323323646Shselasky switch (type) { 324331790Shselasky case ADDR_VALID: 325337074Shselasky /* get source interface */ 326337074Shselasky if (addr->bound_dev_if != 0) { 327331783Shselasky ifp = dev_get_by_index(addr->net, addr->bound_dev_if); 328331781Shselasky } else { 329331781Shselasky ifp = ip_dev_find(addr->net, src_in->sin_addr.s_addr); 330331781Shselasky } 331337074Shselasky 332331781Shselasky /* check source interface */ 333319974Shselasky if (ifp == NULL) { 334319974Shselasky error = ENETUNREACH; 335323646Shselasky goto error_rt_free; 336337074Shselasky } else if (ifp->if_flags & IFF_LOOPBACK) { 337337074Shselasky /* 338337074Shselasky * Source address cannot be a loopback device. 339337074Shselasky */ 340337074Shselasky error = EHOSTUNREACH; 341337074Shselasky goto error_put_ifp; 342337074Shselasky } else if (rte->rt_ifp->if_flags & IFF_LOOPBACK) { 343337074Shselasky if (memcmp(&src_in->sin_addr, &dst_in->sin_addr, 344337074Shselasky sizeof(src_in->sin_addr))) { 345337074Shselasky /* 346337074Shselasky * Destination is loopback, but source 347337074Shselasky * and destination address is not the 348337074Shselasky * same. 349337074Shselasky */ 350337074Shselasky error = EHOSTUNREACH; 351337074Shselasky goto error_put_ifp; 352337074Shselasky } 353337089Shselasky /* get destination network interface from route */ 354337089Shselasky dev_put(ifp); 355337089Shselasky ifp = rte->rt_ifp; 356337089Shselasky dev_hold(ifp); 357319974Shselasky } else if (ifp != rte->rt_ifp) { 358337074Shselasky /* 359337074Shselasky * Source and destination interfaces are 360337074Shselasky * different. 361337074Shselasky */ 362319974Shselasky error = ENETUNREACH; 363323646Shselasky goto error_put_ifp; 364319974Shselasky } 365323646Shselasky break; 366331790Shselasky case ADDR_SRC_ANY: 367331781Shselasky /* check for loopback device */ 368331781Shselasky if (rte->rt_ifp->if_flags & IFF_LOOPBACK) 369331781Shselasky saddr = (struct sockaddr *)&dst_tmp; 370331781Shselasky else 371331781Shselasky saddr = rte->rt_ifa->ifa_addr; 372331781Shselasky 373323646Shselasky /* get destination network interface from route */ 374319974Shselasky ifp = rte->rt_ifp; 375319974Shselasky dev_hold(ifp); 376323646Shselasky break; 377323646Shselasky default: 378323646Shselasky break; 379319974Shselasky } 380319974Shselasky 381319974Shselasky /* 382323646Shselasky * Step 3 - resolve destination MAC address 383319974Shselasky */ 384319974Shselasky if (dst_tmp.sin_addr.s_addr == INADDR_BROADCAST) { 385319974Shselasky rdma_copy_addr_sub(edst, ifp->if_broadcastaddr, 386323646Shselasky ifp->if_addrlen, MAX_ADDR_LEN); 387331781Shselasky error = 0; 388319974Shselasky } else if (IN_MULTICAST(ntohl(dst_tmp.sin_addr.s_addr))) { 389337073Shselasky bool is_gw = (rte->rt_flags & RTF_GATEWAY) != 0; 390319974Shselasky error = addr_resolve_multi(edst, ifp, (struct sockaddr *)&dst_tmp); 391319974Shselasky if (error != 0) 392323646Shselasky goto error_put_ifp; 393337073Shselasky else if (is_gw) 394337073Shselasky addr->network = RDMA_NETWORK_IPV4; 395331781Shselasky } else if (ifp->if_flags & IFF_LOOPBACK) { 396331781Shselasky memset(edst, 0, MAX_ADDR_LEN); 397331781Shselasky error = 0; 398319974Shselasky } else { 399319974Shselasky bool is_gw = (rte->rt_flags & RTF_GATEWAY) != 0; 400331781Shselasky memset(edst, 0, MAX_ADDR_LEN); 401319974Shselasky error = arpresolve(ifp, is_gw, NULL, is_gw ? 402319974Shselasky rte->rt_gateway : (const struct sockaddr *)&dst_tmp, 403319974Shselasky edst, NULL, NULL); 404319974Shselasky if (error != 0) 405323646Shselasky goto error_put_ifp; 406337073Shselasky else if (is_gw) 407319974Shselasky addr->network = RDMA_NETWORK_IPV4; 408319974Shselasky } 409319974Shselasky 410337070Shselasky /* 411337070Shselasky * Step 4 - update source address, if any 412337070Shselasky */ 413337070Shselasky if (saddr != NULL) { 414337070Shselasky src_port = src_in->sin_port; 415337070Shselasky memcpy(src_in, saddr, rdma_addr_size(saddr)); 416337070Shselasky src_in->sin_port = src_port; /* preserve port number */ 417337070Shselasky } 418337070Shselasky 419323646Shselasky if (rte != NULL) 420323646Shselasky RTFREE(rte); 421323646Shselasky 422323646Shselasky *ifpp = ifp; 423323646Shselasky 424323646Shselasky goto done; 425323646Shselasky 426323646Shselaskyerror_put_ifp: 427323646Shselasky dev_put(ifp); 428323646Shselaskyerror_rt_free: 429319974Shselasky RTFREE(rte); 430319974Shselaskydone: 431323646Shselasky CURVNET_RESTORE(); 432323646Shselasky 433323646Shselasky if (error == EWOULDBLOCK || error == EAGAIN) 434323646Shselasky error = ENODATA; 435319974Shselasky return (-error); 436319974Shselasky} 437319974Shselasky#else 438319974Shselaskystatic int addr4_resolve(struct sockaddr_in *src_in, 439319974Shselasky const struct sockaddr_in *dst_in, 440319974Shselasky struct rdma_dev_addr *addr, 441331781Shselasky u8 *edst, 442319974Shselasky struct ifnet **ifpp) 443319974Shselasky{ 444319974Shselasky return -EADDRNOTAVAIL; 445319974Shselasky} 446319974Shselasky#endif 447319974Shselasky 448319974Shselasky#ifdef INET6 449319974Shselaskystatic int addr6_resolve(struct sockaddr_in6 *src_in, 450319974Shselasky const struct sockaddr_in6 *dst_in, 451319974Shselasky struct rdma_dev_addr *addr, 452331781Shselasky u8 *edst, 453319974Shselasky struct ifnet **ifpp) 454319974Shselasky{ 455331790Shselasky enum { 456331790Shselasky ADDR_VALID = 0, 457331790Shselasky ADDR_SRC_ANY = 1, 458331790Shselasky ADDR_DST_ANY = 2, 459331790Shselasky }; 460319974Shselasky struct sockaddr_in6 dst_tmp = *dst_in; 461323646Shselasky in_port_t src_port; 462337070Shselasky struct sockaddr *saddr = NULL; 463319974Shselasky struct rtentry *rte; 464319974Shselasky struct ifnet *ifp; 465319974Shselasky int error; 466323646Shselasky int type; 467319974Shselasky 468323646Shselasky /* set VNET, if any */ 469323646Shselasky CURVNET_SET(addr->net); 470319974Shselasky 471323646Shselasky /* set default TTL limit */ 472323646Shselasky addr->hoplimit = V_ip_defttl; 473323646Shselasky 474331790Shselasky type = ADDR_VALID; 475323646Shselasky if (ipv6_addr_any(&src_in->sin6_addr)) 476331790Shselasky type |= ADDR_SRC_ANY; 477323646Shselasky if (ipv6_addr_any(&dst_tmp.sin6_addr)) 478331790Shselasky type |= ADDR_DST_ANY; 479323646Shselasky 480319974Shselasky /* 481319974Shselasky * Make sure the socket address length field 482319974Shselasky * is set, else rtalloc1() will fail. 483319974Shselasky */ 484319974Shselasky dst_tmp.sin6_len = sizeof(dst_tmp); 485319974Shselasky 486331788Shselasky /* 487331788Shselasky * Make sure the scope ID gets embedded, else rtalloc1() will 488331788Shselasky * resolve to the loopback interface. 489331788Shselasky */ 490331788Shselasky dst_tmp.sin6_scope_id = addr->bound_dev_if; 491331788Shselasky sa6_embedscope(&dst_tmp, 0); 492331788Shselasky 493323646Shselasky /* Step 1 - lookup destination route if any */ 494323646Shselasky switch (type) { 495331790Shselasky case ADDR_VALID: 496323646Shselasky /* sanity check for IPv4 addresses */ 497323646Shselasky if (ipv6_addr_v4mapped(&src_in->sin6_addr) != 498323646Shselasky ipv6_addr_v4mapped(&dst_tmp.sin6_addr)) { 499323646Shselasky error = EAFNOSUPPORT; 500323646Shselasky goto done; 501323646Shselasky } 502323646Shselasky /* FALLTHROUGH */ 503331790Shselasky case ADDR_SRC_ANY: 504323646Shselasky /* regular destination route lookup */ 505323646Shselasky rte = rtalloc1((struct sockaddr *)&dst_tmp, 1, 0); 506323646Shselasky if (rte == NULL) { 507323646Shselasky error = EHOSTUNREACH; 508323646Shselasky goto done; 509331781Shselasky } else if (rte->rt_ifp == NULL || RT_LINK_IS_UP(rte->rt_ifp) == 0) { 510323646Shselasky RTFREE_LOCKED(rte); 511323646Shselasky error = EHOSTUNREACH; 512323646Shselasky goto done; 513323646Shselasky } 514323646Shselasky RT_UNLOCK(rte); 515323646Shselasky break; 516323646Shselasky default: 517323646Shselasky error = ENETUNREACH; 518319974Shselasky goto done; 519323646Shselasky } 520319974Shselasky 521323646Shselasky /* Step 2 - find outgoing network interface */ 522323646Shselasky switch (type) { 523331790Shselasky case ADDR_VALID: 524337074Shselasky /* get source interface */ 525337074Shselasky if (addr->bound_dev_if != 0) { 526331783Shselasky ifp = dev_get_by_index(addr->net, addr->bound_dev_if); 527331781Shselasky } else { 528341880Shselasky ifp = ip6_dev_find(addr->net, src_in->sin6_addr, 0); 529331781Shselasky } 530337074Shselasky 531331781Shselasky /* check source interface */ 532319974Shselasky if (ifp == NULL) { 533319974Shselasky error = ENETUNREACH; 534323646Shselasky goto error_rt_free; 535337074Shselasky } else if (ifp->if_flags & IFF_LOOPBACK) { 536337074Shselasky /* 537337074Shselasky * Source address cannot be a loopback device. 538337074Shselasky */ 539337074Shselasky error = EHOSTUNREACH; 540337074Shselasky goto error_put_ifp; 541337074Shselasky } else if (rte->rt_ifp->if_flags & IFF_LOOPBACK) { 542337074Shselasky if (memcmp(&src_in->sin6_addr, &dst_in->sin6_addr, 543337074Shselasky sizeof(src_in->sin6_addr))) { 544337074Shselasky /* 545337074Shselasky * Destination is loopback, but source 546337074Shselasky * and destination address is not the 547337074Shselasky * same. 548337074Shselasky */ 549337074Shselasky error = EHOSTUNREACH; 550337074Shselasky goto error_put_ifp; 551337074Shselasky } 552337089Shselasky /* get destination network interface from route */ 553337089Shselasky dev_put(ifp); 554337089Shselasky ifp = rte->rt_ifp; 555337089Shselasky dev_hold(ifp); 556319974Shselasky } else if (ifp != rte->rt_ifp) { 557337074Shselasky /* 558337074Shselasky * Source and destination interfaces are 559337074Shselasky * different. 560337074Shselasky */ 561319974Shselasky error = ENETUNREACH; 562323646Shselasky goto error_put_ifp; 563319974Shselasky } 564323646Shselasky break; 565331790Shselasky case ADDR_SRC_ANY: 566331781Shselasky /* check for loopback device */ 567331781Shselasky if (rte->rt_ifp->if_flags & IFF_LOOPBACK) 568331781Shselasky saddr = (struct sockaddr *)&dst_tmp; 569331781Shselasky else 570331781Shselasky saddr = rte->rt_ifa->ifa_addr; 571331781Shselasky 572323646Shselasky /* get destination network interface from route */ 573319974Shselasky ifp = rte->rt_ifp; 574319974Shselasky dev_hold(ifp); 575323646Shselasky break; 576323646Shselasky default: 577323646Shselasky break; 578319974Shselasky } 579319974Shselasky 580319974Shselasky /* 581323646Shselasky * Step 3 - resolve destination MAC address 582319974Shselasky */ 583319974Shselasky if (IN6_IS_ADDR_MULTICAST(&dst_tmp.sin6_addr)) { 584337073Shselasky bool is_gw = (rte->rt_flags & RTF_GATEWAY) != 0; 585323646Shselasky error = addr_resolve_multi(edst, ifp, 586323646Shselasky (struct sockaddr *)&dst_tmp); 587319974Shselasky if (error != 0) 588323646Shselasky goto error_put_ifp; 589337073Shselasky else if (is_gw) 590337073Shselasky addr->network = RDMA_NETWORK_IPV6; 591331781Shselasky } else if (rte->rt_ifp->if_flags & IFF_LOOPBACK) { 592331781Shselasky memset(edst, 0, MAX_ADDR_LEN); 593331781Shselasky error = 0; 594319974Shselasky } else { 595319974Shselasky bool is_gw = (rte->rt_flags & RTF_GATEWAY) != 0; 596331781Shselasky memset(edst, 0, MAX_ADDR_LEN); 597319974Shselasky error = nd6_resolve(ifp, is_gw, NULL, is_gw ? 598319974Shselasky rte->rt_gateway : (const struct sockaddr *)&dst_tmp, 599319974Shselasky edst, NULL, NULL); 600319974Shselasky if (error != 0) 601323646Shselasky goto error_put_ifp; 602337073Shselasky else if (is_gw) 603319974Shselasky addr->network = RDMA_NETWORK_IPV6; 604319974Shselasky } 605319974Shselasky 606337070Shselasky /* 607337070Shselasky * Step 4 - update source address, if any 608337070Shselasky */ 609337070Shselasky if (saddr != NULL) { 610337070Shselasky src_port = src_in->sin6_port; 611337070Shselasky memcpy(src_in, saddr, rdma_addr_size(saddr)); 612337070Shselasky src_in->sin6_port = src_port; /* preserve port number */ 613337070Shselasky } 614337070Shselasky 615323646Shselasky if (rte != NULL) 616323646Shselasky RTFREE(rte); 617323646Shselasky 618323646Shselasky *ifpp = ifp; 619323646Shselasky 620323646Shselasky goto done; 621323646Shselasky 622323646Shselaskyerror_put_ifp: 623323646Shselasky dev_put(ifp); 624323646Shselaskyerror_rt_free: 625319974Shselasky RTFREE(rte); 626319974Shselaskydone: 627323646Shselasky CURVNET_RESTORE(); 628319974Shselasky 629323646Shselasky if (error == EWOULDBLOCK || error == EAGAIN) 630323646Shselasky error = ENODATA; 631319974Shselasky return (-error); 632319974Shselasky} 633319974Shselasky#else 634319974Shselaskystatic int addr6_resolve(struct sockaddr_in6 *src_in, 635319974Shselasky const struct sockaddr_in6 *dst_in, 636319974Shselasky struct rdma_dev_addr *addr, 637331781Shselasky u8 *edst, 638319974Shselasky struct ifnet **ifpp) 639319974Shselasky{ 640319974Shselasky return -EADDRNOTAVAIL; 641319974Shselasky} 642319974Shselasky#endif 643319974Shselasky 644319974Shselaskystatic int addr_resolve_neigh(struct ifnet *dev, 645319974Shselasky const struct sockaddr *dst_in, 646331781Shselasky u8 *edst, 647319974Shselasky struct rdma_dev_addr *addr) 648319974Shselasky{ 649319974Shselasky if (dev->if_flags & IFF_LOOPBACK) { 650319974Shselasky int ret; 651319974Shselasky 652341882Shselasky /* 653341882Shselasky * Binding to a loopback device is not allowed. Make 654341882Shselasky * sure the destination device address is global by 655341882Shselasky * clearing the bound device interface: 656341882Shselasky */ 657341882Shselasky if (addr->bound_dev_if == dev->if_index) 658341882Shselasky addr->bound_dev_if = 0; 659337074Shselasky 660331783Shselasky ret = rdma_translate_ip(dst_in, addr); 661337074Shselasky if (ret == 0) { 662319974Shselasky memcpy(addr->dst_dev_addr, addr->src_dev_addr, 663319974Shselasky MAX_ADDR_LEN); 664337074Shselasky } 665319974Shselasky return ret; 666319974Shselasky } 667319974Shselasky 668319974Shselasky /* If the device doesn't do ARP internally */ 669319974Shselasky if (!(dev->if_flags & IFF_NOARP)) 670331781Shselasky return rdma_copy_addr(addr, dev, edst); 671319974Shselasky 672319974Shselasky return rdma_copy_addr(addr, dev, NULL); 673319974Shselasky} 674319974Shselasky 675319974Shselaskystatic int addr_resolve(struct sockaddr *src_in, 676319974Shselasky const struct sockaddr *dst_in, 677337074Shselasky struct rdma_dev_addr *addr) 678319974Shselasky{ 679319974Shselasky struct net_device *ndev = NULL; 680331781Shselasky u8 edst[MAX_ADDR_LEN]; 681319974Shselasky int ret; 682319974Shselasky 683319974Shselasky if (dst_in->sa_family != src_in->sa_family) 684319974Shselasky return -EINVAL; 685319974Shselasky 686337074Shselasky switch (src_in->sa_family) { 687337074Shselasky case AF_INET: 688319974Shselasky ret = addr4_resolve((struct sockaddr_in *)src_in, 689319974Shselasky (const struct sockaddr_in *)dst_in, 690331781Shselasky addr, edst, &ndev); 691337074Shselasky break; 692337074Shselasky case AF_INET6: 693319974Shselasky ret = addr6_resolve((struct sockaddr_in6 *)src_in, 694319974Shselasky (const struct sockaddr_in6 *)dst_in, addr, 695331781Shselasky edst, &ndev); 696337074Shselasky break; 697337074Shselasky default: 698337074Shselasky ret = -EADDRNOTAVAIL; 699337074Shselasky break; 700319974Shselasky } 701319974Shselasky 702337074Shselasky /* check for error */ 703337074Shselasky if (ret != 0) 704337074Shselasky return ret; 705337074Shselasky 706337074Shselasky /* store MAC addresses and check for loopback */ 707337074Shselasky ret = addr_resolve_neigh(ndev, dst_in, edst, addr); 708337074Shselasky 709337074Shselasky /* set belonging VNET, if any */ 710319974Shselasky addr->net = dev_net(ndev); 711319974Shselasky dev_put(ndev); 712319974Shselasky 713319974Shselasky return ret; 714319974Shselasky} 715319974Shselasky 716319974Shselaskystatic void process_req(struct work_struct *work) 717319974Shselasky{ 718319974Shselasky struct addr_req *req, *temp_req; 719319974Shselasky struct sockaddr *src_in, *dst_in; 720319974Shselasky struct list_head done_list; 721319974Shselasky 722319974Shselasky INIT_LIST_HEAD(&done_list); 723319974Shselasky 724319974Shselasky mutex_lock(&lock); 725319974Shselasky list_for_each_entry_safe(req, temp_req, &req_list, list) { 726319974Shselasky if (req->status == -ENODATA) { 727319974Shselasky src_in = (struct sockaddr *) &req->src_addr; 728319974Shselasky dst_in = (struct sockaddr *) &req->dst_addr; 729337074Shselasky req->status = addr_resolve(src_in, dst_in, req->addr); 730319974Shselasky if (req->status && time_after_eq(jiffies, req->timeout)) 731319974Shselasky req->status = -ETIMEDOUT; 732319974Shselasky else if (req->status == -ENODATA) 733319974Shselasky continue; 734319974Shselasky } 735319974Shselasky list_move_tail(&req->list, &done_list); 736319974Shselasky } 737319974Shselasky 738319974Shselasky if (!list_empty(&req_list)) { 739319974Shselasky req = list_entry(req_list.next, struct addr_req, list); 740319974Shselasky set_timeout(req->timeout); 741319974Shselasky } 742319974Shselasky mutex_unlock(&lock); 743319974Shselasky 744319974Shselasky list_for_each_entry_safe(req, temp_req, &done_list, list) { 745319974Shselasky list_del(&req->list); 746319974Shselasky req->callback(req->status, (struct sockaddr *) &req->src_addr, 747319974Shselasky req->addr, req->context); 748319974Shselasky put_client(req->client); 749319974Shselasky kfree(req); 750319974Shselasky } 751319974Shselasky} 752319974Shselasky 753319974Shselaskyint rdma_resolve_ip(struct rdma_addr_client *client, 754319974Shselasky struct sockaddr *src_addr, struct sockaddr *dst_addr, 755319974Shselasky struct rdma_dev_addr *addr, int timeout_ms, 756319974Shselasky void (*callback)(int status, struct sockaddr *src_addr, 757319974Shselasky struct rdma_dev_addr *addr, void *context), 758319974Shselasky void *context) 759319974Shselasky{ 760319974Shselasky struct sockaddr *src_in, *dst_in; 761319974Shselasky struct addr_req *req; 762319974Shselasky int ret = 0; 763319974Shselasky 764319974Shselasky req = kzalloc(sizeof *req, GFP_KERNEL); 765319974Shselasky if (!req) 766319974Shselasky return -ENOMEM; 767319974Shselasky 768319974Shselasky src_in = (struct sockaddr *) &req->src_addr; 769319974Shselasky dst_in = (struct sockaddr *) &req->dst_addr; 770319974Shselasky 771319974Shselasky if (src_addr) { 772319974Shselasky if (src_addr->sa_family != dst_addr->sa_family) { 773319974Shselasky ret = -EINVAL; 774319974Shselasky goto err; 775319974Shselasky } 776319974Shselasky 777319974Shselasky memcpy(src_in, src_addr, rdma_addr_size(src_addr)); 778319974Shselasky } else { 779319974Shselasky src_in->sa_family = dst_addr->sa_family; 780319974Shselasky } 781319974Shselasky 782319974Shselasky memcpy(dst_in, dst_addr, rdma_addr_size(dst_addr)); 783319974Shselasky req->addr = addr; 784319974Shselasky req->callback = callback; 785319974Shselasky req->context = context; 786319974Shselasky req->client = client; 787319974Shselasky atomic_inc(&client->refcount); 788319974Shselasky 789337074Shselasky req->status = addr_resolve(src_in, dst_in, addr); 790319974Shselasky switch (req->status) { 791319974Shselasky case 0: 792319974Shselasky req->timeout = jiffies; 793319974Shselasky queue_req(req); 794319974Shselasky break; 795319974Shselasky case -ENODATA: 796319974Shselasky req->timeout = msecs_to_jiffies(timeout_ms) + jiffies; 797319974Shselasky queue_req(req); 798319974Shselasky break; 799319974Shselasky default: 800319974Shselasky ret = req->status; 801319974Shselasky atomic_dec(&client->refcount); 802319974Shselasky goto err; 803319974Shselasky } 804319974Shselasky return ret; 805319974Shselaskyerr: 806319974Shselasky kfree(req); 807319974Shselasky return ret; 808319974Shselasky} 809319974ShselaskyEXPORT_SYMBOL(rdma_resolve_ip); 810319974Shselasky 811319974Shselaskyint rdma_resolve_ip_route(struct sockaddr *src_addr, 812319974Shselasky const struct sockaddr *dst_addr, 813319974Shselasky struct rdma_dev_addr *addr) 814319974Shselasky{ 815319974Shselasky struct sockaddr_storage ssrc_addr = {}; 816319974Shselasky struct sockaddr *src_in = (struct sockaddr *)&ssrc_addr; 817319974Shselasky 818319974Shselasky if (src_addr) { 819319974Shselasky if (src_addr->sa_family != dst_addr->sa_family) 820319974Shselasky return -EINVAL; 821319974Shselasky 822319974Shselasky memcpy(src_in, src_addr, rdma_addr_size(src_addr)); 823319974Shselasky } else { 824319974Shselasky src_in->sa_family = dst_addr->sa_family; 825319974Shselasky } 826319974Shselasky 827337074Shselasky return addr_resolve(src_in, dst_addr, addr); 828319974Shselasky} 829319974ShselaskyEXPORT_SYMBOL(rdma_resolve_ip_route); 830319974Shselasky 831319974Shselaskyvoid rdma_addr_cancel(struct rdma_dev_addr *addr) 832319974Shselasky{ 833319974Shselasky struct addr_req *req, *temp_req; 834319974Shselasky 835319974Shselasky mutex_lock(&lock); 836319974Shselasky list_for_each_entry_safe(req, temp_req, &req_list, list) { 837319974Shselasky if (req->addr == addr) { 838319974Shselasky req->status = -ECANCELED; 839319974Shselasky req->timeout = jiffies; 840319974Shselasky list_move(&req->list, &req_list); 841319974Shselasky set_timeout(req->timeout); 842319974Shselasky break; 843319974Shselasky } 844319974Shselasky } 845319974Shselasky mutex_unlock(&lock); 846319974Shselasky} 847319974ShselaskyEXPORT_SYMBOL(rdma_addr_cancel); 848319974Shselasky 849319974Shselaskystruct resolve_cb_context { 850319974Shselasky struct rdma_dev_addr *addr; 851319974Shselasky struct completion comp; 852319974Shselasky int status; 853319974Shselasky}; 854319974Shselasky 855319974Shselaskystatic void resolve_cb(int status, struct sockaddr *src_addr, 856319974Shselasky struct rdma_dev_addr *addr, void *context) 857319974Shselasky{ 858319974Shselasky if (!status) 859319974Shselasky memcpy(((struct resolve_cb_context *)context)->addr, 860319974Shselasky addr, sizeof(struct rdma_dev_addr)); 861319974Shselasky ((struct resolve_cb_context *)context)->status = status; 862319974Shselasky complete(&((struct resolve_cb_context *)context)->comp); 863319974Shselasky} 864319974Shselasky 865319974Shselaskyint rdma_addr_find_l2_eth_by_grh(const union ib_gid *sgid, 866319974Shselasky const union ib_gid *dgid, 867331783Shselasky u8 *dmac, struct net_device *dev, 868319974Shselasky int *hoplimit) 869319974Shselasky{ 870319974Shselasky int ret = 0; 871319974Shselasky struct rdma_dev_addr dev_addr; 872319974Shselasky struct resolve_cb_context ctx; 873319974Shselasky 874319974Shselasky union { 875319974Shselasky struct sockaddr _sockaddr; 876319974Shselasky struct sockaddr_in _sockaddr_in; 877319974Shselasky struct sockaddr_in6 _sockaddr_in6; 878319974Shselasky } sgid_addr, dgid_addr; 879319974Shselasky 880319974Shselasky rdma_gid2ip(&sgid_addr._sockaddr, sgid); 881319974Shselasky rdma_gid2ip(&dgid_addr._sockaddr, dgid); 882319974Shselasky 883319974Shselasky memset(&dev_addr, 0, sizeof(dev_addr)); 884319974Shselasky 885331783Shselasky dev_addr.bound_dev_if = dev->if_index; 886331783Shselasky dev_addr.net = dev_net(dev); 887331783Shselasky 888319974Shselasky ctx.addr = &dev_addr; 889319974Shselasky init_completion(&ctx.comp); 890319974Shselasky ret = rdma_resolve_ip(&self, &sgid_addr._sockaddr, &dgid_addr._sockaddr, 891319974Shselasky &dev_addr, 1000, resolve_cb, &ctx); 892319974Shselasky if (ret) 893319974Shselasky return ret; 894319974Shselasky 895319974Shselasky wait_for_completion(&ctx.comp); 896319974Shselasky 897319974Shselasky ret = ctx.status; 898319974Shselasky if (ret) 899319974Shselasky return ret; 900319974Shselasky 901319974Shselasky memcpy(dmac, dev_addr.dst_dev_addr, ETH_ALEN); 902319974Shselasky if (hoplimit) 903319974Shselasky *hoplimit = dev_addr.hoplimit; 904319974Shselasky return ret; 905319974Shselasky} 906319974ShselaskyEXPORT_SYMBOL(rdma_addr_find_l2_eth_by_grh); 907319974Shselasky 908319974Shselaskyint addr_init(void) 909319974Shselasky{ 910319974Shselasky addr_wq = alloc_workqueue("ib_addr", WQ_MEM_RECLAIM, 0); 911319974Shselasky if (!addr_wq) 912319974Shselasky return -ENOMEM; 913319974Shselasky 914319974Shselasky rdma_addr_register_client(&self); 915319974Shselasky 916319974Shselasky return 0; 917319974Shselasky} 918319974Shselasky 919319974Shselaskyvoid addr_cleanup(void) 920319974Shselasky{ 921319974Shselasky rdma_addr_unregister_client(&self); 922319974Shselasky destroy_workqueue(addr_wq); 923319974Shselasky} 924