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
32#include <sys/cdefs.h>
33__FBSDID("$FreeBSD$");
34
35#include <sys/param.h>
36#include <sys/condvar.h>
37#include <sys/systm.h>
38#include <sys/kernel.h>
39#include <sys/socket.h>
40#include <sys/module.h>
41
42#include <sys/lock.h>
43#include <sys/condvar.h>
44#include <sys/mutex.h>
45#include <sys/rwlock.h>
46#include <sys/queue.h>
47#include <sys/taskqueue.h>
48
49#include <net/if.h>
50#include <net/if_dl.h>
51#include <net/if_var.h>
52#include <net/if_arp.h>
53#include <net/route.h>
54
55#include <net80211/ieee80211_freebsd.h>
56
57#include <netinet/in.h>
58#include <netinet/if_ether.h>
59
60#include <contrib/rdma/ib_addr.h>
61
62struct addr_req {
63	TAILQ_ENTRY(addr_req) entry;
64	struct sockaddr src_addr;
65	struct sockaddr dst_addr;
66	struct rdma_dev_addr *addr;
67	struct rdma_addr_client *client;
68	void *context;
69	void (*callback)(int status, struct sockaddr *src_addr,
70			 struct rdma_dev_addr *addr, void *context);
71	unsigned long timeout;
72	int status;
73};
74
75static void process_req(void *ctx, int pending);
76
77static struct mtx lock;
78
79static TAILQ_HEAD(addr_req_list, addr_req) req_list;
80static struct task addr_task;
81static struct taskqueue *addr_taskq;
82static struct callout addr_ch;
83static eventhandler_tag route_event_tag;
84
85static void addr_timeout(void *arg)
86{
87	taskqueue_enqueue(addr_taskq, &addr_task);
88}
89
90void rdma_addr_register_client(struct rdma_addr_client *client)
91{
92	mtx_init(&client->lock, "rdma_addr client lock", NULL, MTX_DUPOK|MTX_DEF);
93	cv_init(&client->comp, "rdma_addr cv");
94	client->refcount = 1;
95}
96
97static inline void put_client(struct rdma_addr_client *client)
98{
99	mtx_lock(&client->lock);
100	if (--client->refcount == 0) {
101		cv_broadcast(&client->comp);
102	}
103	mtx_unlock(&client->lock);
104}
105
106void rdma_addr_unregister_client(struct rdma_addr_client *client)
107{
108	put_client(client);
109	mtx_lock(&client->lock);
110	if (client->refcount) {
111		cv_wait(&client->comp, &client->lock);
112	}
113	mtx_unlock(&client->lock);
114}
115
116int rdma_copy_addr(struct rdma_dev_addr *dev_addr, struct ifnet *dev,
117		     const unsigned char *dst_dev_addr)
118{
119	dev_addr->dev_type = RDMA_NODE_RNIC;
120	memset(dev_addr->src_dev_addr, 0, MAX_ADDR_LEN);
121	memcpy(dev_addr->src_dev_addr, IF_LLADDR(dev), dev->if_addrlen);
122	memcpy(dev_addr->broadcast, dev->if_broadcastaddr, MAX_ADDR_LEN);
123	if (dst_dev_addr)
124		memcpy(dev_addr->dst_dev_addr, dst_dev_addr, MAX_ADDR_LEN);
125	return 0;
126}
127
128int rdma_translate_ip(struct sockaddr *addr, struct rdma_dev_addr *dev_addr)
129{
130	struct ifaddr *ifa;
131	struct sockaddr_in *sin = (struct sockaddr_in *)addr;
132	uint16_t port = sin->sin_port;
133	int ret;
134
135	sin->sin_port = 0;
136	ifa = ifa_ifwithaddr(addr);
137	sin->sin_port = port;
138	if (!ifa)
139		return (EADDRNOTAVAIL);
140	ret = rdma_copy_addr(dev_addr, ifa->ifa_ifp, NULL);
141	ifa_free(ifa);
142	return (ret);
143}
144
145static void queue_req(struct addr_req *req)
146{
147	struct addr_req *tmp_req = NULL;
148
149	mtx_lock(&lock);
150	TAILQ_FOREACH_REVERSE(tmp_req, &req_list, addr_req_list, entry)
151	    if (time_after_eq(req->timeout, tmp_req->timeout))
152		    break;
153
154	if (tmp_req)
155		TAILQ_INSERT_AFTER(&req_list, tmp_req, req, entry);
156	else
157		TAILQ_INSERT_TAIL(&req_list, req, entry);
158
159	if (TAILQ_FIRST(&req_list) == req)
160		callout_reset(&addr_ch, req->timeout - ticks, addr_timeout, NULL);
161	mtx_unlock(&lock);
162}
163
164#ifdef needed
165static void addr_send_arp(struct sockaddr_in *dst_in)
166{
167	struct route iproute;
168	struct sockaddr_in *dst = (struct sockaddr_in *)&iproute.ro_dst;
169	char dmac[ETHER_ADDR_LEN];
170	struct llentry *lle;
171
172	bzero(&iproute, sizeof iproute);
173	*dst = *dst_in;
174
175	rtalloc(&iproute);
176	if (iproute.ro_rt == NULL);
177		return;
178
179	arpresolve(iproute.ro_rt->rt_ifp, iproute.ro_rt, NULL,
180		   rt_key(iproute.ro_rt), dmac, &lle);
181
182	RTFREE(iproute.ro_rt);
183}
184#endif
185
186static int addr_resolve_remote(struct sockaddr_in *src_in,
187			       struct sockaddr_in *dst_in,
188			       struct rdma_dev_addr *addr)
189{
190	int ret = 0;
191	struct route iproute;
192	struct sockaddr_in *dst = (struct sockaddr_in *)&iproute.ro_dst;
193	char dmac[ETHER_ADDR_LEN];
194	struct llentry *lle;
195
196	bzero(&iproute, sizeof iproute);
197	*dst = *dst_in;
198
199	rtalloc(&iproute);
200	if (iproute.ro_rt == NULL) {
201		ret = EHOSTUNREACH;
202		goto out;
203	}
204
205	/* If the device does ARP internally, return 'done' */
206	if (iproute.ro_rt->rt_ifp->if_flags & IFF_NOARP) {
207		rdma_copy_addr(addr, iproute.ro_rt->rt_ifp, NULL);
208		goto put;
209	}
210 	ret = arpresolve(iproute.ro_rt->rt_ifp, iproute.ro_rt, NULL,
211		(struct sockaddr *)dst_in, dmac, &lle);
212	if (ret) {
213		goto put;
214	}
215
216	if (!src_in->sin_addr.s_addr) {
217		src_in->sin_len = sizeof *src_in;
218		src_in->sin_family = dst_in->sin_family;
219		src_in->sin_addr.s_addr = ((struct sockaddr_in *)iproute.ro_rt->rt_ifa->ifa_addr)->sin_addr.s_addr;
220	}
221
222	ret = rdma_copy_addr(addr, iproute.ro_rt->rt_ifp, dmac);
223put:
224	RTFREE(iproute.ro_rt);
225out:
226	return ret;
227}
228
229static void process_req(void *ctx, int pending)
230{
231	struct addr_req *req, *tmp_req;
232	struct sockaddr_in *src_in, *dst_in;
233	TAILQ_HEAD(, addr_req) done_list;
234
235	TAILQ_INIT(&done_list);
236
237	mtx_lock(&lock);
238	TAILQ_FOREACH_SAFE(req, &req_list, entry, tmp_req) {
239		if (req->status == EWOULDBLOCK) {
240			src_in = (struct sockaddr_in *) &req->src_addr;
241			dst_in = (struct sockaddr_in *) &req->dst_addr;
242			req->status = addr_resolve_remote(src_in, dst_in,
243							  req->addr);
244			if (req->status && time_after_eq(ticks, req->timeout))
245				req->status = ETIMEDOUT;
246			else if (req->status == EWOULDBLOCK)
247				continue;
248		}
249		TAILQ_REMOVE(&req_list, req, entry);
250		TAILQ_INSERT_TAIL(&done_list, req, entry);
251	}
252
253	if (!TAILQ_EMPTY(&req_list)) {
254		req = TAILQ_FIRST(&req_list);
255		callout_reset(&addr_ch, req->timeout - ticks, addr_timeout,
256			NULL);
257	}
258	mtx_unlock(&lock);
259
260	TAILQ_FOREACH_SAFE(req, &done_list, entry, tmp_req) {
261		TAILQ_REMOVE(&done_list, req, entry);
262		req->callback(req->status, &req->src_addr, req->addr,
263			      req->context);
264		put_client(req->client);
265		free(req, M_DEVBUF);
266	}
267}
268
269int rdma_resolve_ip(struct rdma_addr_client *client,
270		    struct sockaddr *src_addr, struct sockaddr *dst_addr,
271		    struct rdma_dev_addr *addr, int timeout_ms,
272		    void (*callback)(int status, struct sockaddr *src_addr,
273				     struct rdma_dev_addr *addr, void *context),
274		    void *context)
275{
276	struct sockaddr_in *src_in, *dst_in;
277	struct addr_req *req;
278	int ret = 0;
279
280	req = malloc(sizeof *req, M_DEVBUF, M_NOWAIT);
281	if (!req)
282		return (ENOMEM);
283	memset(req, 0, sizeof *req);
284
285	if (src_addr)
286		memcpy(&req->src_addr, src_addr, ip_addr_size(src_addr));
287	memcpy(&req->dst_addr, dst_addr, ip_addr_size(dst_addr));
288	req->addr = addr;
289	req->callback = callback;
290	req->context = context;
291	req->client = client;
292	mtx_lock(&client->lock);
293	client->refcount++;
294	mtx_unlock(&client->lock);
295
296	src_in = (struct sockaddr_in *) &req->src_addr;
297	dst_in = (struct sockaddr_in *) &req->dst_addr;
298
299	req->status = addr_resolve_remote(src_in, dst_in, addr);
300
301	switch (req->status) {
302	case 0:
303		req->timeout = ticks;
304		queue_req(req);
305		break;
306	case EWOULDBLOCK:
307		req->timeout = msecs_to_ticks(timeout_ms) + ticks;
308		queue_req(req);
309#ifdef needed
310		addr_send_arp(dst_in);
311#endif
312		break;
313	default:
314		ret = req->status;
315		mtx_lock(&client->lock);
316		client->refcount--;
317		mtx_unlock(&client->lock);
318		free(req, M_DEVBUF);
319		break;
320	}
321	return ret;
322}
323
324void rdma_addr_cancel(struct rdma_dev_addr *addr)
325{
326	struct addr_req *req, *tmp_req;
327
328	mtx_lock(&lock);
329	TAILQ_FOREACH_SAFE(req, &req_list, entry, tmp_req) {
330		if (req->addr == addr) {
331			req->status = ECANCELED;
332			req->timeout = ticks;
333			TAILQ_REMOVE(&req_list, req, entry);
334			TAILQ_INSERT_HEAD(&req_list, req, entry);
335			callout_reset(&addr_ch, req->timeout - ticks, addr_timeout, NULL);
336			break;
337		}
338	}
339	mtx_unlock(&lock);
340}
341
342static void
343route_event_arp_update(void *unused, struct rtentry *rt0, uint8_t *enaddr,
344	struct sockaddr *sa)
345{
346		callout_stop(&addr_ch);
347		taskqueue_enqueue(addr_taskq, &addr_task);
348}
349
350static int addr_init(void)
351{
352	TAILQ_INIT(&req_list);
353	mtx_init(&lock, "rdma_addr req_list lock", NULL, MTX_DEF);
354
355	addr_taskq = taskqueue_create("rdma_addr_taskq", M_NOWAIT,
356		taskqueue_thread_enqueue, &addr_taskq);
357        if (addr_taskq == NULL) {
358                printf("failed to allocate rdma_addr taskqueue\n");
359                return (ENOMEM);
360        }
361        taskqueue_start_threads(&addr_taskq, 1, PI_NET, "rdma_addr taskq");
362        TASK_INIT(&addr_task, 0, process_req, NULL);
363
364	callout_init(&addr_ch, TRUE);
365
366	route_event_tag = EVENTHANDLER_REGISTER(route_arp_update_event,
367		route_event_arp_update, NULL, EVENTHANDLER_PRI_ANY);
368
369	return 0;
370}
371
372static void addr_cleanup(void)
373{
374	EVENTHANDLER_DEREGISTER(route_event_arp_update, route_event_tag);
375	callout_stop(&addr_ch);
376	taskqueue_drain(addr_taskq, &addr_task);
377	taskqueue_free(addr_taskq);
378}
379
380static int
381addr_load(module_t mod, int cmd, void *arg)
382{
383        int err = 0;
384
385        switch (cmd) {
386        case MOD_LOAD:
387                printf("Loading rdma_addr.\n");
388
389                addr_init();
390                break;
391        case MOD_QUIESCE:
392                break;
393        case MOD_UNLOAD:
394                printf("Unloading rdma_addr.\n");
395		addr_cleanup();
396                break;
397        case MOD_SHUTDOWN:
398                break;
399        default:
400                err = EOPNOTSUPP;
401                break;
402        }
403
404        return (err);
405}
406
407static moduledata_t mod_data = {
408	"rdma_addr",
409	addr_load,
410	0
411};
412
413MODULE_VERSION(rdma_addr, 1);
414DECLARE_MODULE(rdma_addr, mod_data, SI_SUB_EXEC, SI_ORDER_ANY);
415