blob: 09a2bec7fd3207b65abac3f93339b7cd46a66c8e [file] [log] [blame]
Sean Hefty7025fcd2006-06-17 20:37:28 -07001/*
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 *
Sean Heftya9474912008-07-14 23:48:43 -07007 * 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:
Sean Hefty7025fcd2006-06-17 20:37:28 -070012 *
Sean Heftya9474912008-07-14 23:48:43 -070013 * Redistribution and use in source and binary forms, with or
14 * without modification, are permitted provided that the following
15 * conditions are met:
Sean Hefty7025fcd2006-06-17 20:37:28 -070016 *
Sean Heftya9474912008-07-14 23:48:43 -070017 * - Redistributions of source code must retain the above
18 * copyright notice, this list of conditions and the following
19 * disclaimer.
Sean Hefty7025fcd2006-06-17 20:37:28 -070020 *
Sean Heftya9474912008-07-14 23:48:43 -070021 * - 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.
Sean Hefty7025fcd2006-06-17 20:37:28 -070025 *
Sean Heftya9474912008-07-14 23:48:43 -070026 * 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.
Sean Hefty7025fcd2006-06-17 20:37:28 -070034 */
35
36#include <linux/mutex.h>
37#include <linux/inetdevice.h>
38#include <linux/workqueue.h>
39#include <linux/if_arp.h>
40#include <net/arp.h>
41#include <net/neighbour.h>
42#include <net/route.h>
Tom Tuckere795d092006-07-30 20:44:19 -070043#include <net/netevent.h>
Sean Hefty7025fcd2006-06-17 20:37:28 -070044#include <rdma/ib_addr.h>
45
46MODULE_AUTHOR("Sean Hefty");
47MODULE_DESCRIPTION("IB Address Translation");
48MODULE_LICENSE("Dual BSD/GPL");
49
50struct addr_req {
51 struct list_head list;
52 struct sockaddr src_addr;
53 struct sockaddr dst_addr;
54 struct rdma_dev_addr *addr;
Sean Hefty7a118df2006-10-31 11:12:59 -080055 struct rdma_addr_client *client;
Sean Hefty7025fcd2006-06-17 20:37:28 -070056 void *context;
57 void (*callback)(int status, struct sockaddr *src_addr,
58 struct rdma_dev_addr *addr, void *context);
59 unsigned long timeout;
60 int status;
61};
62
David Howellsc4028952006-11-22 14:57:56 +000063static void process_req(struct work_struct *work);
Sean Hefty7025fcd2006-06-17 20:37:28 -070064
65static DEFINE_MUTEX(lock);
66static LIST_HEAD(req_list);
David Howellsc4028952006-11-22 14:57:56 +000067static DECLARE_DELAYED_WORK(work, process_req);
Sean Hefty7025fcd2006-06-17 20:37:28 -070068static struct workqueue_struct *addr_wq;
69
Sean Hefty7a118df2006-10-31 11:12:59 -080070void rdma_addr_register_client(struct rdma_addr_client *client)
71{
72 atomic_set(&client->refcount, 1);
73 init_completion(&client->comp);
74}
75EXPORT_SYMBOL(rdma_addr_register_client);
76
77static inline void put_client(struct rdma_addr_client *client)
78{
79 if (atomic_dec_and_test(&client->refcount))
80 complete(&client->comp);
81}
82
83void rdma_addr_unregister_client(struct rdma_addr_client *client)
84{
85 put_client(client);
86 wait_for_completion(&client->comp);
87}
88EXPORT_SYMBOL(rdma_addr_unregister_client);
89
Tom Tucker07ebafb2006-08-03 16:02:42 -050090int rdma_copy_addr(struct rdma_dev_addr *dev_addr, struct net_device *dev,
91 const unsigned char *dst_dev_addr)
Sean Hefty7025fcd2006-06-17 20:37:28 -070092{
93 switch (dev->type) {
94 case ARPHRD_INFINIBAND:
Tom Tucker07ebafb2006-08-03 16:02:42 -050095 dev_addr->dev_type = RDMA_NODE_IB_CA;
96 break;
97 case ARPHRD_ETHER:
98 dev_addr->dev_type = RDMA_NODE_RNIC;
Sean Hefty7025fcd2006-06-17 20:37:28 -070099 break;
100 default:
101 return -EADDRNOTAVAIL;
102 }
103
104 memcpy(dev_addr->src_dev_addr, dev->dev_addr, MAX_ADDR_LEN);
105 memcpy(dev_addr->broadcast, dev->broadcast, MAX_ADDR_LEN);
106 if (dst_dev_addr)
107 memcpy(dev_addr->dst_dev_addr, dst_dev_addr, MAX_ADDR_LEN);
Or Gerlitz64c5e612008-07-14 23:48:53 -0700108 dev_addr->src_dev = dev;
Sean Hefty7025fcd2006-06-17 20:37:28 -0700109 return 0;
110}
Tom Tucker07ebafb2006-08-03 16:02:42 -0500111EXPORT_SYMBOL(rdma_copy_addr);
Sean Hefty7025fcd2006-06-17 20:37:28 -0700112
113int rdma_translate_ip(struct sockaddr *addr, struct rdma_dev_addr *dev_addr)
114{
115 struct net_device *dev;
Al Viro60cad5d2006-09-26 22:17:09 -0700116 __be32 ip = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
Sean Hefty7025fcd2006-06-17 20:37:28 -0700117 int ret;
118
Denis V. Lunev1ab35272008-01-22 22:04:30 -0800119 dev = ip_dev_find(&init_net, ip);
Sean Hefty7025fcd2006-06-17 20:37:28 -0700120 if (!dev)
121 return -EADDRNOTAVAIL;
122
Tom Tucker07ebafb2006-08-03 16:02:42 -0500123 ret = rdma_copy_addr(dev_addr, dev, NULL);
Sean Hefty7025fcd2006-06-17 20:37:28 -0700124 dev_put(dev);
125 return ret;
126}
127EXPORT_SYMBOL(rdma_translate_ip);
128
129static void set_timeout(unsigned long time)
130{
131 unsigned long delay;
132
133 cancel_delayed_work(&work);
134
135 delay = time - jiffies;
136 if ((long)delay <= 0)
137 delay = 1;
138
139 queue_delayed_work(addr_wq, &work, delay);
140}
141
142static void queue_req(struct addr_req *req)
143{
144 struct addr_req *temp_req;
145
146 mutex_lock(&lock);
147 list_for_each_entry_reverse(temp_req, &req_list, list) {
Krishna Kumarf115db42006-10-17 10:09:09 +0530148 if (time_after_eq(req->timeout, temp_req->timeout))
Sean Hefty7025fcd2006-06-17 20:37:28 -0700149 break;
150 }
151
152 list_add(&req->list, &temp_req->list);
153
154 if (req_list.next == &req->list)
155 set_timeout(req->timeout);
156 mutex_unlock(&lock);
157}
158
159static void addr_send_arp(struct sockaddr_in *dst_in)
160{
161 struct rtable *rt;
162 struct flowi fl;
Al Viro1b90c132008-03-29 03:10:28 +0000163 __be32 dst_ip = dst_in->sin_addr.s_addr;
Sean Hefty7025fcd2006-06-17 20:37:28 -0700164
165 memset(&fl, 0, sizeof fl);
166 fl.nl_u.ip4_u.daddr = dst_ip;
Denis V. Lunevf2063512008-01-22 22:07:34 -0800167 if (ip_route_output_key(&init_net, &rt, &fl))
Sean Hefty7025fcd2006-06-17 20:37:28 -0700168 return;
169
Steve Wise935ef2d2007-09-12 05:00:25 -0500170 neigh_event_send(rt->u.dst.neighbour, NULL);
Sean Hefty7025fcd2006-06-17 20:37:28 -0700171 ip_rt_put(rt);
172}
173
174static int addr_resolve_remote(struct sockaddr_in *src_in,
175 struct sockaddr_in *dst_in,
176 struct rdma_dev_addr *addr)
177{
Al Viro1b90c132008-03-29 03:10:28 +0000178 __be32 src_ip = src_in->sin_addr.s_addr;
179 __be32 dst_ip = dst_in->sin_addr.s_addr;
Sean Hefty7025fcd2006-06-17 20:37:28 -0700180 struct flowi fl;
181 struct rtable *rt;
182 struct neighbour *neigh;
183 int ret;
184
185 memset(&fl, 0, sizeof fl);
186 fl.nl_u.ip4_u.daddr = dst_ip;
187 fl.nl_u.ip4_u.saddr = src_ip;
Denis V. Lunevf2063512008-01-22 22:07:34 -0800188 ret = ip_route_output_key(&init_net, &rt, &fl);
Sean Hefty7025fcd2006-06-17 20:37:28 -0700189 if (ret)
190 goto out;
191
192 /* If the device does ARP internally, return 'done' */
193 if (rt->idev->dev->flags & IFF_NOARP) {
Tom Tucker07ebafb2006-08-03 16:02:42 -0500194 rdma_copy_addr(addr, rt->idev->dev, NULL);
Sean Hefty7025fcd2006-06-17 20:37:28 -0700195 goto put;
196 }
197
198 neigh = neigh_lookup(&arp_tbl, &rt->rt_gateway, rt->idev->dev);
199 if (!neigh) {
200 ret = -ENODATA;
201 goto put;
202 }
203
204 if (!(neigh->nud_state & NUD_VALID)) {
205 ret = -ENODATA;
206 goto release;
207 }
208
209 if (!src_ip) {
210 src_in->sin_family = dst_in->sin_family;
211 src_in->sin_addr.s_addr = rt->rt_src;
212 }
213
Tom Tucker07ebafb2006-08-03 16:02:42 -0500214 ret = rdma_copy_addr(addr, neigh->dev, neigh->ha);
Sean Hefty7025fcd2006-06-17 20:37:28 -0700215release:
216 neigh_release(neigh);
217put:
218 ip_rt_put(rt);
219out:
220 return ret;
221}
222
David Howellsc4028952006-11-22 14:57:56 +0000223static void process_req(struct work_struct *work)
Sean Hefty7025fcd2006-06-17 20:37:28 -0700224{
225 struct addr_req *req, *temp_req;
226 struct sockaddr_in *src_in, *dst_in;
227 struct list_head done_list;
228
229 INIT_LIST_HEAD(&done_list);
230
231 mutex_lock(&lock);
232 list_for_each_entry_safe(req, temp_req, &req_list, list) {
Krishna Kumarc78bb842006-11-24 16:02:34 +0530233 if (req->status == -ENODATA) {
Sean Hefty7025fcd2006-06-17 20:37:28 -0700234 src_in = (struct sockaddr_in *) &req->src_addr;
235 dst_in = (struct sockaddr_in *) &req->dst_addr;
236 req->status = addr_resolve_remote(src_in, dst_in,
237 req->addr);
Krishna Kumarc78bb842006-11-24 16:02:34 +0530238 if (req->status && time_after_eq(jiffies, req->timeout))
239 req->status = -ETIMEDOUT;
240 else if (req->status == -ENODATA)
241 continue;
Sean Hefty7025fcd2006-06-17 20:37:28 -0700242 }
Roland Dreier04699a12006-11-29 15:33:09 -0800243 list_move_tail(&req->list, &done_list);
Sean Hefty7025fcd2006-06-17 20:37:28 -0700244 }
245
246 if (!list_empty(&req_list)) {
247 req = list_entry(req_list.next, struct addr_req, list);
248 set_timeout(req->timeout);
249 }
250 mutex_unlock(&lock);
251
252 list_for_each_entry_safe(req, temp_req, &done_list, list) {
253 list_del(&req->list);
254 req->callback(req->status, &req->src_addr, req->addr,
255 req->context);
Sean Hefty7a118df2006-10-31 11:12:59 -0800256 put_client(req->client);
Sean Hefty7025fcd2006-06-17 20:37:28 -0700257 kfree(req);
258 }
259}
260
261static int addr_resolve_local(struct sockaddr_in *src_in,
262 struct sockaddr_in *dst_in,
263 struct rdma_dev_addr *addr)
264{
265 struct net_device *dev;
Al Viro1b90c132008-03-29 03:10:28 +0000266 __be32 src_ip = src_in->sin_addr.s_addr;
Al Viro60cad5d2006-09-26 22:17:09 -0700267 __be32 dst_ip = dst_in->sin_addr.s_addr;
Sean Hefty7025fcd2006-06-17 20:37:28 -0700268 int ret;
269
Denis V. Lunev1ab35272008-01-22 22:04:30 -0800270 dev = ip_dev_find(&init_net, dst_ip);
Sean Hefty7025fcd2006-06-17 20:37:28 -0700271 if (!dev)
272 return -EADDRNOTAVAIL;
273
Joe Perches6360a022007-12-16 13:47:33 -0800274 if (ipv4_is_zeronet(src_ip)) {
Sean Hefty7025fcd2006-06-17 20:37:28 -0700275 src_in->sin_family = dst_in->sin_family;
276 src_in->sin_addr.s_addr = dst_ip;
Tom Tucker07ebafb2006-08-03 16:02:42 -0500277 ret = rdma_copy_addr(addr, dev, dev->dev_addr);
Joe Perches6360a022007-12-16 13:47:33 -0800278 } else if (ipv4_is_loopback(src_ip)) {
Sean Hefty7025fcd2006-06-17 20:37:28 -0700279 ret = rdma_translate_ip((struct sockaddr *)dst_in, addr);
280 if (!ret)
281 memcpy(addr->dst_dev_addr, dev->dev_addr, MAX_ADDR_LEN);
282 } else {
283 ret = rdma_translate_ip((struct sockaddr *)src_in, addr);
284 if (!ret)
285 memcpy(addr->dst_dev_addr, dev->dev_addr, MAX_ADDR_LEN);
286 }
287
288 dev_put(dev);
289 return ret;
290}
291
Sean Hefty7a118df2006-10-31 11:12:59 -0800292int rdma_resolve_ip(struct rdma_addr_client *client,
293 struct sockaddr *src_addr, struct sockaddr *dst_addr,
Sean Hefty7025fcd2006-06-17 20:37:28 -0700294 struct rdma_dev_addr *addr, int timeout_ms,
295 void (*callback)(int status, struct sockaddr *src_addr,
296 struct rdma_dev_addr *addr, void *context),
297 void *context)
298{
299 struct sockaddr_in *src_in, *dst_in;
300 struct addr_req *req;
301 int ret = 0;
302
Yoann Padioleaudd00cc42007-07-19 01:49:03 -0700303 req = kzalloc(sizeof *req, GFP_KERNEL);
Sean Hefty7025fcd2006-06-17 20:37:28 -0700304 if (!req)
305 return -ENOMEM;
Sean Hefty7025fcd2006-06-17 20:37:28 -0700306
307 if (src_addr)
308 memcpy(&req->src_addr, src_addr, ip_addr_size(src_addr));
309 memcpy(&req->dst_addr, dst_addr, ip_addr_size(dst_addr));
310 req->addr = addr;
311 req->callback = callback;
312 req->context = context;
Sean Hefty7a118df2006-10-31 11:12:59 -0800313 req->client = client;
314 atomic_inc(&client->refcount);
Sean Hefty7025fcd2006-06-17 20:37:28 -0700315
316 src_in = (struct sockaddr_in *) &req->src_addr;
317 dst_in = (struct sockaddr_in *) &req->dst_addr;
318
319 req->status = addr_resolve_local(src_in, dst_in, addr);
320 if (req->status == -EADDRNOTAVAIL)
321 req->status = addr_resolve_remote(src_in, dst_in, addr);
322
323 switch (req->status) {
324 case 0:
325 req->timeout = jiffies;
326 queue_req(req);
327 break;
328 case -ENODATA:
329 req->timeout = msecs_to_jiffies(timeout_ms) + jiffies;
330 queue_req(req);
331 addr_send_arp(dst_in);
332 break;
333 default:
334 ret = req->status;
Sean Hefty7a118df2006-10-31 11:12:59 -0800335 atomic_dec(&client->refcount);
Sean Hefty7025fcd2006-06-17 20:37:28 -0700336 kfree(req);
337 break;
338 }
339 return ret;
340}
341EXPORT_SYMBOL(rdma_resolve_ip);
342
343void rdma_addr_cancel(struct rdma_dev_addr *addr)
344{
345 struct addr_req *req, *temp_req;
346
347 mutex_lock(&lock);
348 list_for_each_entry_safe(req, temp_req, &req_list, list) {
349 if (req->addr == addr) {
350 req->status = -ECANCELED;
351 req->timeout = jiffies;
Roland Dreier04699a12006-11-29 15:33:09 -0800352 list_move(&req->list, &req_list);
Sean Hefty7025fcd2006-06-17 20:37:28 -0700353 set_timeout(req->timeout);
354 break;
355 }
356 }
357 mutex_unlock(&lock);
358}
359EXPORT_SYMBOL(rdma_addr_cancel);
360
Roland Dreier3cd96562006-09-22 15:22:46 -0700361static int netevent_callback(struct notifier_block *self, unsigned long event,
Tom Tuckere795d092006-07-30 20:44:19 -0700362 void *ctx)
Sean Hefty7025fcd2006-06-17 20:37:28 -0700363{
Roland Dreier3cd96562006-09-22 15:22:46 -0700364 if (event == NETEVENT_NEIGH_UPDATE) {
Tom Tuckere795d092006-07-30 20:44:19 -0700365 struct neighbour *neigh = ctx;
Sean Hefty7025fcd2006-06-17 20:37:28 -0700366
Steve Wise1f126672007-01-23 19:03:17 -0600367 if (neigh->nud_state & NUD_VALID) {
Tom Tuckere795d092006-07-30 20:44:19 -0700368 set_timeout(jiffies);
369 }
370 }
Sean Hefty7025fcd2006-06-17 20:37:28 -0700371 return 0;
372}
373
Tom Tuckere795d092006-07-30 20:44:19 -0700374static struct notifier_block nb = {
375 .notifier_call = netevent_callback
Sean Hefty7025fcd2006-06-17 20:37:28 -0700376};
377
378static int addr_init(void)
379{
Sean Heftyc7f743a2007-02-01 12:23:37 -0800380 addr_wq = create_singlethread_workqueue("ib_addr");
Sean Hefty7025fcd2006-06-17 20:37:28 -0700381 if (!addr_wq)
382 return -ENOMEM;
383
Tom Tuckere795d092006-07-30 20:44:19 -0700384 register_netevent_notifier(&nb);
Sean Hefty7025fcd2006-06-17 20:37:28 -0700385 return 0;
386}
387
388static void addr_cleanup(void)
389{
Tom Tuckere795d092006-07-30 20:44:19 -0700390 unregister_netevent_notifier(&nb);
Sean Hefty7025fcd2006-06-17 20:37:28 -0700391 destroy_workqueue(addr_wq);
392}
393
394module_init(addr_init);
395module_exit(addr_cleanup);