linux/drivers/infiniband/core/addr.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2005 Voltaire Inc.  All rights reserved.
   3 * Copyright (c) 2002-2005, Network Appliance, Inc. All rights reserved.
   4 * Copyright (c) 1999-2005, Mellanox Technologies, Inc. All rights reserved.
   5 * Copyright (c) 2005 Intel Corporation.  All rights reserved.
   6 *
   7 * This software is available to you under a choice of one of two
   8 * licenses.  You may choose to be licensed under the terms of the GNU
   9 * General Public License (GPL) Version 2, available from the file
  10 * COPYING in the main directory of this source tree, or the
  11 * OpenIB.org BSD license below:
  12 *
  13 *     Redistribution and use in source and binary forms, with or
  14 *     without modification, are permitted provided that the following
  15 *     conditions are met:
  16 *
  17 *      - Redistributions of source code must retain the above
  18 *        copyright notice, this list of conditions and the following
  19 *        disclaimer.
  20 *
  21 *      - Redistributions in binary form must reproduce the above
  22 *        copyright notice, this list of conditions and the following
  23 *        disclaimer in the documentation and/or other materials
  24 *        provided with the distribution.
  25 *
  26 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  27 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  28 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  29 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  30 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  31 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  32 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  33 * SOFTWARE.
  34 */
  35
  36#include <linux/mutex.h>
  37#include <linux/inetdevice.h>
  38#include <linux/slab.h>
  39#include <linux/workqueue.h>
  40#include <net/arp.h>
  41#include <net/neighbour.h>
  42#include <net/route.h>
  43#include <net/netevent.h>
  44#include <net/addrconf.h>
  45#include <net/ip6_route.h>
  46#include <rdma/ib_addr.h>
  47
  48MODULE_AUTHOR("Sean Hefty");
  49MODULE_DESCRIPTION("IB Address Translation");
  50MODULE_LICENSE("Dual BSD/GPL");
  51
  52struct addr_req {
  53        struct list_head list;
  54        struct sockaddr_storage src_addr;
  55        struct sockaddr_storage dst_addr;
  56        struct rdma_dev_addr *addr;
  57        struct rdma_addr_client *client;
  58        void *context;
  59        void (*callback)(int status, struct sockaddr *src_addr,
  60                         struct rdma_dev_addr *addr, void *context);
  61        unsigned long timeout;
  62        int status;
  63};
  64
  65static void process_req(struct work_struct *work);
  66
  67static DEFINE_MUTEX(lock);
  68static LIST_HEAD(req_list);
  69static DECLARE_DELAYED_WORK(work, process_req);
  70static struct workqueue_struct *addr_wq;
  71
  72void rdma_addr_register_client(struct rdma_addr_client *client)
  73{
  74        atomic_set(&client->refcount, 1);
  75        init_completion(&client->comp);
  76}
  77EXPORT_SYMBOL(rdma_addr_register_client);
  78
  79static inline void put_client(struct rdma_addr_client *client)
  80{
  81        if (atomic_dec_and_test(&client->refcount))
  82                complete(&client->comp);
  83}
  84
  85void rdma_addr_unregister_client(struct rdma_addr_client *client)
  86{
  87        put_client(client);
  88        wait_for_completion(&client->comp);
  89}
  90EXPORT_SYMBOL(rdma_addr_unregister_client);
  91
  92int rdma_copy_addr(struct rdma_dev_addr *dev_addr, struct net_device *dev,
  93                     const unsigned char *dst_dev_addr)
  94{
  95        dev_addr->dev_type = dev->type;
  96        memcpy(dev_addr->src_dev_addr, dev->dev_addr, MAX_ADDR_LEN);
  97        memcpy(dev_addr->broadcast, dev->broadcast, MAX_ADDR_LEN);
  98        if (dst_dev_addr)
  99                memcpy(dev_addr->dst_dev_addr, dst_dev_addr, MAX_ADDR_LEN);
 100        dev_addr->bound_dev_if = dev->ifindex;
 101        return 0;
 102}
 103EXPORT_SYMBOL(rdma_copy_addr);
 104
 105int rdma_translate_ip(struct sockaddr *addr, struct rdma_dev_addr *dev_addr)
 106{
 107        struct net_device *dev;
 108        int ret = -EADDRNOTAVAIL;
 109
 110        if (dev_addr->bound_dev_if) {
 111                dev = dev_get_by_index(&init_net, dev_addr->bound_dev_if);
 112                if (!dev)
 113                        return -ENODEV;
 114                ret = rdma_copy_addr(dev_addr, dev, NULL);
 115                dev_put(dev);
 116                return ret;
 117        }
 118
 119        switch (addr->sa_family) {
 120        case AF_INET:
 121                dev = ip_dev_find(&init_net,
 122                        ((struct sockaddr_in *) addr)->sin_addr.s_addr);
 123
 124                if (!dev)
 125                        return ret;
 126
 127                ret = rdma_copy_addr(dev_addr, dev, NULL);
 128                dev_put(dev);
 129                break;
 130
 131#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 132        case AF_INET6:
 133                rcu_read_lock();
 134                for_each_netdev_rcu(&init_net, dev) {
 135                        if (ipv6_chk_addr(&init_net,
 136                                          &((struct sockaddr_in6 *) addr)->sin6_addr,
 137                                          dev, 1)) {
 138                                ret = rdma_copy_addr(dev_addr, dev, NULL);
 139                                break;
 140                        }
 141                }
 142                rcu_read_unlock();
 143                break;
 144#endif
 145        }
 146        return ret;
 147}
 148EXPORT_SYMBOL(rdma_translate_ip);
 149
 150static void set_timeout(unsigned long time)
 151{
 152        unsigned long delay;
 153
 154        cancel_delayed_work(&work);
 155
 156        delay = time - jiffies;
 157        if ((long)delay <= 0)
 158                delay = 1;
 159
 160        queue_delayed_work(addr_wq, &work, delay);
 161}
 162
 163static void queue_req(struct addr_req *req)
 164{
 165        struct addr_req *temp_req;
 166
 167        mutex_lock(&lock);
 168        list_for_each_entry_reverse(temp_req, &req_list, list) {
 169                if (time_after_eq(req->timeout, temp_req->timeout))
 170                        break;
 171        }
 172
 173        list_add(&req->list, &temp_req->list);
 174
 175        if (req_list.next == &req->list)
 176                set_timeout(req->timeout);
 177        mutex_unlock(&lock);
 178}
 179
 180static int addr4_resolve(struct sockaddr_in *src_in,
 181                         struct sockaddr_in *dst_in,
 182                         struct rdma_dev_addr *addr)
 183{
 184        __be32 src_ip = src_in->sin_addr.s_addr;
 185        __be32 dst_ip = dst_in->sin_addr.s_addr;
 186        struct flowi fl;
 187        struct rtable *rt;
 188        struct neighbour *neigh;
 189        int ret;
 190
 191        memset(&fl, 0, sizeof fl);
 192        fl.nl_u.ip4_u.daddr = dst_ip;
 193        fl.nl_u.ip4_u.saddr = src_ip;
 194        fl.oif = addr->bound_dev_if;
 195
 196        ret = ip_route_output_key(&init_net, &rt, &fl);
 197        if (ret)
 198                goto out;
 199
 200        src_in->sin_family = AF_INET;
 201        src_in->sin_addr.s_addr = rt->rt_src;
 202
 203        if (rt->dst.dev->flags & IFF_LOOPBACK) {
 204                ret = rdma_translate_ip((struct sockaddr *) dst_in, addr);
 205                if (!ret)
 206                        memcpy(addr->dst_dev_addr, addr->src_dev_addr, MAX_ADDR_LEN);
 207                goto put;
 208        }
 209
 210        /* If the device does ARP internally, return 'done' */
 211        if (rt->dst.dev->flags & IFF_NOARP) {
 212                rdma_copy_addr(addr, rt->dst.dev, NULL);
 213                goto put;
 214        }
 215
 216        neigh = neigh_lookup(&arp_tbl, &rt->rt_gateway, rt->dst.dev);
 217        if (!neigh || !(neigh->nud_state & NUD_VALID)) {
 218                neigh_event_send(rt->dst.neighbour, NULL);
 219                ret = -ENODATA;
 220                if (neigh)
 221                        goto release;
 222                goto put;
 223        }
 224
 225        ret = rdma_copy_addr(addr, neigh->dev, neigh->ha);
 226release:
 227        neigh_release(neigh);
 228put:
 229        ip_rt_put(rt);
 230out:
 231        return ret;
 232}
 233
 234#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 235static int addr6_resolve(struct sockaddr_in6 *src_in,
 236                         struct sockaddr_in6 *dst_in,
 237                         struct rdma_dev_addr *addr)
 238{
 239        struct flowi fl;
 240        struct neighbour *neigh;
 241        struct dst_entry *dst;
 242        int ret;
 243
 244        memset(&fl, 0, sizeof fl);
 245        ipv6_addr_copy(&fl.fl6_dst, &dst_in->sin6_addr);
 246        ipv6_addr_copy(&fl.fl6_src, &src_in->sin6_addr);
 247        fl.oif = addr->bound_dev_if;
 248
 249        dst = ip6_route_output(&init_net, NULL, &fl);
 250        if ((ret = dst->error))
 251                goto put;
 252
 253        if (ipv6_addr_any(&fl.fl6_src)) {
 254                ret = ipv6_dev_get_saddr(&init_net, ip6_dst_idev(dst)->dev,
 255                                         &fl.fl6_dst, 0, &fl.fl6_src);
 256                if (ret)
 257                        goto put;
 258
 259                src_in->sin6_family = AF_INET6;
 260                ipv6_addr_copy(&src_in->sin6_addr, &fl.fl6_src);
 261        }
 262
 263        if (dst->dev->flags & IFF_LOOPBACK) {
 264                ret = rdma_translate_ip((struct sockaddr *) dst_in, addr);
 265                if (!ret)
 266                        memcpy(addr->dst_dev_addr, addr->src_dev_addr, MAX_ADDR_LEN);
 267                goto put;
 268        }
 269
 270        /* If the device does ARP internally, return 'done' */
 271        if (dst->dev->flags & IFF_NOARP) {
 272                ret = rdma_copy_addr(addr, dst->dev, NULL);
 273                goto put;
 274        }
 275
 276        neigh = dst->neighbour;
 277        if (!neigh || !(neigh->nud_state & NUD_VALID)) {
 278                neigh_event_send(dst->neighbour, NULL);
 279                ret = -ENODATA;
 280                goto put;
 281        }
 282
 283        ret = rdma_copy_addr(addr, dst->dev, neigh->ha);
 284put:
 285        dst_release(dst);
 286        return ret;
 287}
 288#else
 289static int addr6_resolve(struct sockaddr_in6 *src_in,
 290                         struct sockaddr_in6 *dst_in,
 291                         struct rdma_dev_addr *addr)
 292{
 293        return -EADDRNOTAVAIL;
 294}
 295#endif
 296
 297static int addr_resolve(struct sockaddr *src_in,
 298                        struct sockaddr *dst_in,
 299                        struct rdma_dev_addr *addr)
 300{
 301        if (src_in->sa_family == AF_INET) {
 302                return addr4_resolve((struct sockaddr_in *) src_in,
 303                        (struct sockaddr_in *) dst_in, addr);
 304        } else
 305                return addr6_resolve((struct sockaddr_in6 *) src_in,
 306                        (struct sockaddr_in6 *) dst_in, addr);
 307}
 308
 309static void process_req(struct work_struct *work)
 310{
 311        struct addr_req *req, *temp_req;
 312        struct sockaddr *src_in, *dst_in;
 313        struct list_head done_list;
 314
 315        INIT_LIST_HEAD(&done_list);
 316
 317        mutex_lock(&lock);
 318        list_for_each_entry_safe(req, temp_req, &req_list, list) {
 319                if (req->status == -ENODATA) {
 320                        src_in = (struct sockaddr *) &req->src_addr;
 321                        dst_in = (struct sockaddr *) &req->dst_addr;
 322                        req->status = addr_resolve(src_in, dst_in, req->addr);
 323                        if (req->status && time_after_eq(jiffies, req->timeout))
 324                                req->status = -ETIMEDOUT;
 325                        else if (req->status == -ENODATA)
 326                                continue;
 327                }
 328                list_move_tail(&req->list, &done_list);
 329        }
 330
 331        if (!list_empty(&req_list)) {
 332                req = list_entry(req_list.next, struct addr_req, list);
 333                set_timeout(req->timeout);
 334        }
 335        mutex_unlock(&lock);
 336
 337        list_for_each_entry_safe(req, temp_req, &done_list, list) {
 338                list_del(&req->list);
 339                req->callback(req->status, (struct sockaddr *) &req->src_addr,
 340                        req->addr, req->context);
 341                put_client(req->client);
 342                kfree(req);
 343        }
 344}
 345
 346int rdma_resolve_ip(struct rdma_addr_client *client,
 347                    struct sockaddr *src_addr, struct sockaddr *dst_addr,
 348                    struct rdma_dev_addr *addr, int timeout_ms,
 349                    void (*callback)(int status, struct sockaddr *src_addr,
 350                                     struct rdma_dev_addr *addr, void *context),
 351                    void *context)
 352{
 353        struct sockaddr *src_in, *dst_in;
 354        struct addr_req *req;
 355        int ret = 0;
 356
 357        req = kzalloc(sizeof *req, GFP_KERNEL);
 358        if (!req)
 359                return -ENOMEM;
 360
 361        src_in = (struct sockaddr *) &req->src_addr;
 362        dst_in = (struct sockaddr *) &req->dst_addr;
 363
 364        if (src_addr) {
 365                if (src_addr->sa_family != dst_addr->sa_family) {
 366                        ret = -EINVAL;
 367                        goto err;
 368                }
 369
 370                memcpy(src_in, src_addr, ip_addr_size(src_addr));
 371        } else {
 372                src_in->sa_family = dst_addr->sa_family;
 373        }
 374
 375        memcpy(dst_in, dst_addr, ip_addr_size(dst_addr));
 376        req->addr = addr;
 377        req->callback = callback;
 378        req->context = context;
 379        req->client = client;
 380        atomic_inc(&client->refcount);
 381
 382        req->status = addr_resolve(src_in, dst_in, addr);
 383        switch (req->status) {
 384        case 0:
 385                req->timeout = jiffies;
 386                queue_req(req);
 387                break;
 388        case -ENODATA:
 389                req->timeout = msecs_to_jiffies(timeout_ms) + jiffies;
 390                queue_req(req);
 391                break;
 392        default:
 393                ret = req->status;
 394                atomic_dec(&client->refcount);
 395                goto err;
 396        }
 397        return ret;
 398err:
 399        kfree(req);
 400        return ret;
 401}
 402EXPORT_SYMBOL(rdma_resolve_ip);
 403
 404void rdma_addr_cancel(struct rdma_dev_addr *addr)
 405{
 406        struct addr_req *req, *temp_req;
 407
 408        mutex_lock(&lock);
 409        list_for_each_entry_safe(req, temp_req, &req_list, list) {
 410                if (req->addr == addr) {
 411                        req->status = -ECANCELED;
 412                        req->timeout = jiffies;
 413                        list_move(&req->list, &req_list);
 414                        set_timeout(req->timeout);
 415                        break;
 416                }
 417        }
 418        mutex_unlock(&lock);
 419}
 420EXPORT_SYMBOL(rdma_addr_cancel);
 421
 422static int netevent_callback(struct notifier_block *self, unsigned long event,
 423        void *ctx)
 424{
 425        if (event == NETEVENT_NEIGH_UPDATE) {
 426                struct neighbour *neigh = ctx;
 427
 428                if (neigh->nud_state & NUD_VALID) {
 429                        set_timeout(jiffies);
 430                }
 431        }
 432        return 0;
 433}
 434
 435static struct notifier_block nb = {
 436        .notifier_call = netevent_callback
 437};
 438
 439static int __init addr_init(void)
 440{
 441        addr_wq = create_singlethread_workqueue("ib_addr");
 442        if (!addr_wq)
 443                return -ENOMEM;
 444
 445        register_netevent_notifier(&nb);
 446        return 0;
 447}
 448
 449static void __exit addr_cleanup(void)
 450{
 451        unregister_netevent_notifier(&nb);
 452        destroy_workqueue(addr_wq);
 453}
 454
 455module_init(addr_init);
 456module_exit(addr_cleanup);
 457