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