addr.c revision 07ebafbaaa72aa6a35472879008f5a1d1d469a0c
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 licensed under one of the following licenses:
8 *
9 * 1) under the terms of the "Common Public License 1.0" a copy of which is
10 *    available from the Open Source Initiative, see
11 *    http://www.opensource.org/licenses/cpl.php.
12 *
13 * 2) under the terms of the "The BSD License" a copy of which is
14 *    available from the Open Source Initiative, see
15 *    http://www.opensource.org/licenses/bsd-license.php.
16 *
17 * 3) under the terms of the "GNU General Public License (GPL) Version 2" a
18 *    copy of which is available from the Open Source Initiative, see
19 *    http://www.opensource.org/licenses/gpl-license.php.
20 *
21 * Licensee has the right to choose one of the above licenses.
22 *
23 * Redistributions of source code must retain the above copyright
24 * notice and one of the license notices.
25 *
26 * Redistributions in binary form must reproduce both the above copyright
27 * notice, one of the license notices in the documentation
28 * and/or other materials provided with the distribution.
29 */
30
31#include <linux/mutex.h>
32#include <linux/inetdevice.h>
33#include <linux/workqueue.h>
34#include <linux/if_arp.h>
35#include <net/arp.h>
36#include <net/neighbour.h>
37#include <net/route.h>
38#include <net/netevent.h>
39#include <rdma/ib_addr.h>
40
41MODULE_AUTHOR("Sean Hefty");
42MODULE_DESCRIPTION("IB Address Translation");
43MODULE_LICENSE("Dual BSD/GPL");
44
45struct addr_req {
46	struct list_head list;
47	struct sockaddr src_addr;
48	struct sockaddr dst_addr;
49	struct rdma_dev_addr *addr;
50	void *context;
51	void (*callback)(int status, struct sockaddr *src_addr,
52			 struct rdma_dev_addr *addr, void *context);
53	unsigned long timeout;
54	int status;
55};
56
57static void process_req(void *data);
58
59static DEFINE_MUTEX(lock);
60static LIST_HEAD(req_list);
61static DECLARE_WORK(work, process_req, NULL);
62static struct workqueue_struct *addr_wq;
63
64int rdma_copy_addr(struct rdma_dev_addr *dev_addr, struct net_device *dev,
65		     const unsigned char *dst_dev_addr)
66{
67	switch (dev->type) {
68	case ARPHRD_INFINIBAND:
69		dev_addr->dev_type = RDMA_NODE_IB_CA;
70		break;
71	case ARPHRD_ETHER:
72		dev_addr->dev_type = RDMA_NODE_RNIC;
73		break;
74	default:
75		return -EADDRNOTAVAIL;
76	}
77
78	memcpy(dev_addr->src_dev_addr, dev->dev_addr, MAX_ADDR_LEN);
79	memcpy(dev_addr->broadcast, dev->broadcast, MAX_ADDR_LEN);
80	if (dst_dev_addr)
81		memcpy(dev_addr->dst_dev_addr, dst_dev_addr, MAX_ADDR_LEN);
82	return 0;
83}
84EXPORT_SYMBOL(rdma_copy_addr);
85
86int rdma_translate_ip(struct sockaddr *addr, struct rdma_dev_addr *dev_addr)
87{
88	struct net_device *dev;
89	u32 ip = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
90	int ret;
91
92	dev = ip_dev_find(ip);
93	if (!dev)
94		return -EADDRNOTAVAIL;
95
96	ret = rdma_copy_addr(dev_addr, dev, NULL);
97	dev_put(dev);
98	return ret;
99}
100EXPORT_SYMBOL(rdma_translate_ip);
101
102static void set_timeout(unsigned long time)
103{
104	unsigned long delay;
105
106	cancel_delayed_work(&work);
107
108	delay = time - jiffies;
109	if ((long)delay <= 0)
110		delay = 1;
111
112	queue_delayed_work(addr_wq, &work, delay);
113}
114
115static void queue_req(struct addr_req *req)
116{
117	struct addr_req *temp_req;
118
119	mutex_lock(&lock);
120	list_for_each_entry_reverse(temp_req, &req_list, list) {
121		if (time_after(req->timeout, temp_req->timeout))
122			break;
123	}
124
125	list_add(&req->list, &temp_req->list);
126
127	if (req_list.next == &req->list)
128		set_timeout(req->timeout);
129	mutex_unlock(&lock);
130}
131
132static void addr_send_arp(struct sockaddr_in *dst_in)
133{
134	struct rtable *rt;
135	struct flowi fl;
136	u32 dst_ip = dst_in->sin_addr.s_addr;
137
138	memset(&fl, 0, sizeof fl);
139	fl.nl_u.ip4_u.daddr = dst_ip;
140	if (ip_route_output_key(&rt, &fl))
141		return;
142
143	arp_send(ARPOP_REQUEST, ETH_P_ARP, rt->rt_gateway, rt->idev->dev,
144		 rt->rt_src, NULL, rt->idev->dev->dev_addr, NULL);
145	ip_rt_put(rt);
146}
147
148static int addr_resolve_remote(struct sockaddr_in *src_in,
149			       struct sockaddr_in *dst_in,
150			       struct rdma_dev_addr *addr)
151{
152	u32 src_ip = src_in->sin_addr.s_addr;
153	u32 dst_ip = dst_in->sin_addr.s_addr;
154	struct flowi fl;
155	struct rtable *rt;
156	struct neighbour *neigh;
157	int ret;
158
159	memset(&fl, 0, sizeof fl);
160	fl.nl_u.ip4_u.daddr = dst_ip;
161	fl.nl_u.ip4_u.saddr = src_ip;
162	ret = ip_route_output_key(&rt, &fl);
163	if (ret)
164		goto out;
165
166	/* If the device does ARP internally, return 'done' */
167	if (rt->idev->dev->flags & IFF_NOARP) {
168		rdma_copy_addr(addr, rt->idev->dev, NULL);
169		goto put;
170	}
171
172	neigh = neigh_lookup(&arp_tbl, &rt->rt_gateway, rt->idev->dev);
173	if (!neigh) {
174		ret = -ENODATA;
175		goto put;
176	}
177
178	if (!(neigh->nud_state & NUD_VALID)) {
179		ret = -ENODATA;
180		goto release;
181	}
182
183	if (!src_ip) {
184		src_in->sin_family = dst_in->sin_family;
185		src_in->sin_addr.s_addr = rt->rt_src;
186	}
187
188	ret = rdma_copy_addr(addr, neigh->dev, neigh->ha);
189release:
190	neigh_release(neigh);
191put:
192	ip_rt_put(rt);
193out:
194	return ret;
195}
196
197static void process_req(void *data)
198{
199	struct addr_req *req, *temp_req;
200	struct sockaddr_in *src_in, *dst_in;
201	struct list_head done_list;
202
203	INIT_LIST_HEAD(&done_list);
204
205	mutex_lock(&lock);
206	list_for_each_entry_safe(req, temp_req, &req_list, list) {
207		if (req->status) {
208			src_in = (struct sockaddr_in *) &req->src_addr;
209			dst_in = (struct sockaddr_in *) &req->dst_addr;
210			req->status = addr_resolve_remote(src_in, dst_in,
211							  req->addr);
212		}
213		if (req->status && time_after(jiffies, req->timeout))
214			req->status = -ETIMEDOUT;
215		else if (req->status == -ENODATA)
216			continue;
217
218		list_del(&req->list);
219		list_add_tail(&req->list, &done_list);
220	}
221
222	if (!list_empty(&req_list)) {
223		req = list_entry(req_list.next, struct addr_req, list);
224		set_timeout(req->timeout);
225	}
226	mutex_unlock(&lock);
227
228	list_for_each_entry_safe(req, temp_req, &done_list, list) {
229		list_del(&req->list);
230		req->callback(req->status, &req->src_addr, req->addr,
231			      req->context);
232		kfree(req);
233	}
234}
235
236static int addr_resolve_local(struct sockaddr_in *src_in,
237			      struct sockaddr_in *dst_in,
238			      struct rdma_dev_addr *addr)
239{
240	struct net_device *dev;
241	u32 src_ip = src_in->sin_addr.s_addr;
242	u32 dst_ip = dst_in->sin_addr.s_addr;
243	int ret;
244
245	dev = ip_dev_find(dst_ip);
246	if (!dev)
247		return -EADDRNOTAVAIL;
248
249	if (ZERONET(src_ip)) {
250		src_in->sin_family = dst_in->sin_family;
251		src_in->sin_addr.s_addr = dst_ip;
252		ret = rdma_copy_addr(addr, dev, dev->dev_addr);
253	} else if (LOOPBACK(src_ip)) {
254		ret = rdma_translate_ip((struct sockaddr *)dst_in, addr);
255		if (!ret)
256			memcpy(addr->dst_dev_addr, dev->dev_addr, MAX_ADDR_LEN);
257	} else {
258		ret = rdma_translate_ip((struct sockaddr *)src_in, addr);
259		if (!ret)
260			memcpy(addr->dst_dev_addr, dev->dev_addr, MAX_ADDR_LEN);
261	}
262
263	dev_put(dev);
264	return ret;
265}
266
267int rdma_resolve_ip(struct sockaddr *src_addr, struct sockaddr *dst_addr,
268		    struct rdma_dev_addr *addr, int timeout_ms,
269		    void (*callback)(int status, struct sockaddr *src_addr,
270				     struct rdma_dev_addr *addr, void *context),
271		    void *context)
272{
273	struct sockaddr_in *src_in, *dst_in;
274	struct addr_req *req;
275	int ret = 0;
276
277	req = kmalloc(sizeof *req, GFP_KERNEL);
278	if (!req)
279		return -ENOMEM;
280	memset(req, 0, sizeof *req);
281
282	if (src_addr)
283		memcpy(&req->src_addr, src_addr, ip_addr_size(src_addr));
284	memcpy(&req->dst_addr, dst_addr, ip_addr_size(dst_addr));
285	req->addr = addr;
286	req->callback = callback;
287	req->context = context;
288
289	src_in = (struct sockaddr_in *) &req->src_addr;
290	dst_in = (struct sockaddr_in *) &req->dst_addr;
291
292	req->status = addr_resolve_local(src_in, dst_in, addr);
293	if (req->status == -EADDRNOTAVAIL)
294		req->status = addr_resolve_remote(src_in, dst_in, addr);
295
296	switch (req->status) {
297	case 0:
298		req->timeout = jiffies;
299		queue_req(req);
300		break;
301	case -ENODATA:
302		req->timeout = msecs_to_jiffies(timeout_ms) + jiffies;
303		queue_req(req);
304		addr_send_arp(dst_in);
305		break;
306	default:
307		ret = req->status;
308		kfree(req);
309		break;
310	}
311	return ret;
312}
313EXPORT_SYMBOL(rdma_resolve_ip);
314
315void rdma_addr_cancel(struct rdma_dev_addr *addr)
316{
317	struct addr_req *req, *temp_req;
318
319	mutex_lock(&lock);
320	list_for_each_entry_safe(req, temp_req, &req_list, list) {
321		if (req->addr == addr) {
322			req->status = -ECANCELED;
323			req->timeout = jiffies;
324			list_del(&req->list);
325			list_add(&req->list, &req_list);
326			set_timeout(req->timeout);
327			break;
328		}
329	}
330	mutex_unlock(&lock);
331}
332EXPORT_SYMBOL(rdma_addr_cancel);
333
334static int netevent_callback(struct notifier_block *self, unsigned long event,
335	void *ctx)
336{
337	if (event == NETEVENT_NEIGH_UPDATE) {
338		struct neighbour *neigh = ctx;
339
340		if (neigh->dev->type == ARPHRD_INFINIBAND &&
341		    (neigh->nud_state & NUD_VALID)) {
342			set_timeout(jiffies);
343		}
344	}
345	return 0;
346}
347
348static struct notifier_block nb = {
349	.notifier_call = netevent_callback
350};
351
352static int addr_init(void)
353{
354	addr_wq = create_singlethread_workqueue("ib_addr_wq");
355	if (!addr_wq)
356		return -ENOMEM;
357
358	register_netevent_notifier(&nb);
359	return 0;
360}
361
362static void addr_cleanup(void)
363{
364	unregister_netevent_notifier(&nb);
365	destroy_workqueue(addr_wq);
366}
367
368module_init(addr_init);
369module_exit(addr_cleanup);
370