]>
Commit | Line | Data |
---|---|---|
7025fcd3 SH |
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 | * | |
a9474917 SH |
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: | |
7025fcd3 | 12 | * |
a9474917 SH |
13 | * Redistribution and use in source and binary forms, with or |
14 | * without modification, are permitted provided that the following | |
15 | * conditions are met: | |
7025fcd3 | 16 | * |
a9474917 SH |
17 | * - Redistributions of source code must retain the above |
18 | * copyright notice, this list of conditions and the following | |
19 | * disclaimer. | |
7025fcd3 | 20 | * |
a9474917 SH |
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. | |
7025fcd3 | 25 | * |
a9474917 SH |
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. | |
7025fcd3 SH |
34 | */ |
35 | ||
36 | #include <linux/mutex.h> | |
37 | #include <linux/inetdevice.h> | |
5a0e3ad6 | 38 | #include <linux/slab.h> |
7025fcd3 | 39 | #include <linux/workqueue.h> |
e4dd23d7 | 40 | #include <linux/module.h> |
7025fcd3 SH |
41 | #include <net/arp.h> |
42 | #include <net/neighbour.h> | |
43 | #include <net/route.h> | |
e795d092 | 44 | #include <net/netevent.h> |
38617c64 AS |
45 | #include <net/addrconf.h> |
46 | #include <net/ip6_route.h> | |
7025fcd3 | 47 | #include <rdma/ib_addr.h> |
ef560861 | 48 | #include <rdma/ib.h> |
7025fcd3 SH |
49 | |
50 | MODULE_AUTHOR("Sean Hefty"); | |
51 | MODULE_DESCRIPTION("IB Address Translation"); | |
52 | MODULE_LICENSE("Dual BSD/GPL"); | |
53 | ||
54 | struct addr_req { | |
55 | struct list_head list; | |
38617c64 AS |
56 | struct sockaddr_storage src_addr; |
57 | struct sockaddr_storage dst_addr; | |
7025fcd3 | 58 | struct rdma_dev_addr *addr; |
7a118df3 | 59 | struct rdma_addr_client *client; |
7025fcd3 SH |
60 | void *context; |
61 | void (*callback)(int status, struct sockaddr *src_addr, | |
62 | struct rdma_dev_addr *addr, void *context); | |
63 | unsigned long timeout; | |
64 | int status; | |
65 | }; | |
66 | ||
c4028958 | 67 | static void process_req(struct work_struct *work); |
7025fcd3 SH |
68 | |
69 | static DEFINE_MUTEX(lock); | |
70 | static LIST_HEAD(req_list); | |
c4028958 | 71 | static DECLARE_DELAYED_WORK(work, process_req); |
7025fcd3 SH |
72 | static struct workqueue_struct *addr_wq; |
73 | ||
ef560861 SH |
74 | int rdma_addr_size(struct sockaddr *addr) |
75 | { | |
76 | switch (addr->sa_family) { | |
77 | case AF_INET: | |
78 | return sizeof(struct sockaddr_in); | |
79 | case AF_INET6: | |
80 | return sizeof(struct sockaddr_in6); | |
81 | case AF_IB: | |
82 | return sizeof(struct sockaddr_ib); | |
83 | default: | |
84 | return 0; | |
85 | } | |
86 | } | |
87 | EXPORT_SYMBOL(rdma_addr_size); | |
88 | ||
dd5f03be MB |
89 | static struct rdma_addr_client self; |
90 | ||
7a118df3 SH |
91 | void rdma_addr_register_client(struct rdma_addr_client *client) |
92 | { | |
93 | atomic_set(&client->refcount, 1); | |
94 | init_completion(&client->comp); | |
95 | } | |
96 | EXPORT_SYMBOL(rdma_addr_register_client); | |
97 | ||
98 | static inline void put_client(struct rdma_addr_client *client) | |
99 | { | |
100 | if (atomic_dec_and_test(&client->refcount)) | |
101 | complete(&client->comp); | |
102 | } | |
103 | ||
104 | void rdma_addr_unregister_client(struct rdma_addr_client *client) | |
105 | { | |
106 | put_client(client); | |
107 | wait_for_completion(&client->comp); | |
108 | } | |
109 | EXPORT_SYMBOL(rdma_addr_unregister_client); | |
110 | ||
07ebafba TT |
111 | int rdma_copy_addr(struct rdma_dev_addr *dev_addr, struct net_device *dev, |
112 | const unsigned char *dst_dev_addr) | |
7025fcd3 | 113 | { |
c4315d85 | 114 | dev_addr->dev_type = dev->type; |
7025fcd3 SH |
115 | memcpy(dev_addr->src_dev_addr, dev->dev_addr, MAX_ADDR_LEN); |
116 | memcpy(dev_addr->broadcast, dev->broadcast, MAX_ADDR_LEN); | |
117 | if (dst_dev_addr) | |
118 | memcpy(dev_addr->dst_dev_addr, dst_dev_addr, MAX_ADDR_LEN); | |
6266ed6e | 119 | dev_addr->bound_dev_if = dev->ifindex; |
7025fcd3 SH |
120 | return 0; |
121 | } | |
07ebafba | 122 | EXPORT_SYMBOL(rdma_copy_addr); |
7025fcd3 | 123 | |
20029832 MB |
124 | int rdma_translate_ip(const struct sockaddr *addr, |
125 | struct rdma_dev_addr *dev_addr, | |
dd5f03be | 126 | u16 *vlan_id) |
7025fcd3 SH |
127 | { |
128 | struct net_device *dev; | |
38617c64 | 129 | int ret = -EADDRNOTAVAIL; |
7025fcd3 | 130 | |
6266ed6e | 131 | if (dev_addr->bound_dev_if) { |
565edd1d | 132 | dev = dev_get_by_index(dev_addr->net, dev_addr->bound_dev_if); |
6266ed6e SH |
133 | if (!dev) |
134 | return -ENODEV; | |
135 | ret = rdma_copy_addr(dev_addr, dev, NULL); | |
136 | dev_put(dev); | |
137 | return ret; | |
138 | } | |
139 | ||
38617c64 AS |
140 | switch (addr->sa_family) { |
141 | case AF_INET: | |
565edd1d | 142 | dev = ip_dev_find(dev_addr->net, |
20029832 | 143 | ((const struct sockaddr_in *)addr)->sin_addr.s_addr); |
38617c64 AS |
144 | |
145 | if (!dev) | |
146 | return ret; | |
7025fcd3 | 147 | |
38617c64 | 148 | ret = rdma_copy_addr(dev_addr, dev, NULL); |
dd5f03be MB |
149 | if (vlan_id) |
150 | *vlan_id = rdma_vlan_dev_vlan_id(dev); | |
38617c64 AS |
151 | dev_put(dev); |
152 | break; | |
d90f9b35 | 153 | #if IS_ENABLED(CONFIG_IPV6) |
38617c64 | 154 | case AF_INET6: |
22f4fbd9 | 155 | rcu_read_lock(); |
565edd1d GS |
156 | for_each_netdev_rcu(dev_addr->net, dev) { |
157 | if (ipv6_chk_addr(dev_addr->net, | |
20029832 | 158 | &((const struct sockaddr_in6 *)addr)->sin6_addr, |
38617c64 AS |
159 | dev, 1)) { |
160 | ret = rdma_copy_addr(dev_addr, dev, NULL); | |
dd5f03be MB |
161 | if (vlan_id) |
162 | *vlan_id = rdma_vlan_dev_vlan_id(dev); | |
38617c64 AS |
163 | break; |
164 | } | |
165 | } | |
22f4fbd9 | 166 | rcu_read_unlock(); |
38617c64 | 167 | break; |
2c4ab624 | 168 | #endif |
38617c64 | 169 | } |
7025fcd3 SH |
170 | return ret; |
171 | } | |
172 | EXPORT_SYMBOL(rdma_translate_ip); | |
173 | ||
174 | static void set_timeout(unsigned long time) | |
175 | { | |
176 | unsigned long delay; | |
177 | ||
7025fcd3 | 178 | delay = time - jiffies; |
346f98b4 OK |
179 | if ((long)delay < 0) |
180 | delay = 0; | |
7025fcd3 | 181 | |
41f63c53 | 182 | mod_delayed_work(addr_wq, &work, delay); |
7025fcd3 SH |
183 | } |
184 | ||
185 | static void queue_req(struct addr_req *req) | |
186 | { | |
187 | struct addr_req *temp_req; | |
188 | ||
189 | mutex_lock(&lock); | |
190 | list_for_each_entry_reverse(temp_req, &req_list, list) { | |
f115db48 | 191 | if (time_after_eq(req->timeout, temp_req->timeout)) |
7025fcd3 SH |
192 | break; |
193 | } | |
194 | ||
195 | list_add(&req->list, &temp_req->list); | |
196 | ||
197 | if (req_list.next == &req->list) | |
198 | set_timeout(req->timeout); | |
199 | mutex_unlock(&lock); | |
200 | } | |
201 | ||
20029832 MB |
202 | static int dst_fetch_ha(struct dst_entry *dst, struct rdma_dev_addr *dev_addr, |
203 | const void *daddr) | |
51d45974 DM |
204 | { |
205 | struct neighbour *n; | |
206 | int ret; | |
207 | ||
02b61955 DM |
208 | n = dst_neigh_lookup(dst, daddr); |
209 | ||
51d45974 | 210 | rcu_read_lock(); |
51d45974 DM |
211 | if (!n || !(n->nud_state & NUD_VALID)) { |
212 | if (n) | |
213 | neigh_event_send(n, NULL); | |
214 | ret = -ENODATA; | |
215 | } else { | |
02b61955 | 216 | ret = rdma_copy_addr(dev_addr, dst->dev, n->ha); |
51d45974 DM |
217 | } |
218 | rcu_read_unlock(); | |
219 | ||
02b61955 DM |
220 | if (n) |
221 | neigh_release(n); | |
222 | ||
51d45974 DM |
223 | return ret; |
224 | } | |
225 | ||
923c100e | 226 | static int addr4_resolve(struct sockaddr_in *src_in, |
20029832 MB |
227 | const struct sockaddr_in *dst_in, |
228 | struct rdma_dev_addr *addr, | |
229 | struct rtable **prt) | |
7025fcd3 | 230 | { |
1b90c137 AV |
231 | __be32 src_ip = src_in->sin_addr.s_addr; |
232 | __be32 dst_ip = dst_in->sin_addr.s_addr; | |
7025fcd3 | 233 | struct rtable *rt; |
5fc3590c | 234 | struct flowi4 fl4; |
7025fcd3 SH |
235 | int ret; |
236 | ||
5fc3590c DM |
237 | memset(&fl4, 0, sizeof(fl4)); |
238 | fl4.daddr = dst_ip; | |
239 | fl4.saddr = src_ip; | |
240 | fl4.flowi4_oif = addr->bound_dev_if; | |
565edd1d | 241 | rt = ip_route_output_key(addr->net, &fl4); |
b23dd4fe DM |
242 | if (IS_ERR(rt)) { |
243 | ret = PTR_ERR(rt); | |
7025fcd3 | 244 | goto out; |
b23dd4fe | 245 | } |
923c100e | 246 | src_in->sin_family = AF_INET; |
5fc3590c | 247 | src_in->sin_addr.s_addr = fl4.saddr; |
923c100e | 248 | |
c865f246 SK |
249 | /* If there's a gateway, we're definitely in RoCE v2 (as RoCE v1 isn't |
250 | * routable) and we could set the network type accordingly. | |
251 | */ | |
252 | if (rt->rt_uses_gateway) | |
253 | addr->network = RDMA_NETWORK_IPV4; | |
254 | ||
20029832 MB |
255 | *prt = rt; |
256 | return 0; | |
7025fcd3 SH |
257 | out: |
258 | return ret; | |
259 | } | |
260 | ||
d90f9b35 | 261 | #if IS_ENABLED(CONFIG_IPV6) |
d14714df | 262 | static int addr6_resolve(struct sockaddr_in6 *src_in, |
20029832 MB |
263 | const struct sockaddr_in6 *dst_in, |
264 | struct rdma_dev_addr *addr, | |
265 | struct dst_entry **pdst) | |
38617c64 | 266 | { |
4c9483b2 | 267 | struct flowi6 fl6; |
38617c64 | 268 | struct dst_entry *dst; |
c865f246 | 269 | struct rt6_info *rt; |
d14714df | 270 | int ret; |
38617c64 | 271 | |
4c9483b2 | 272 | memset(&fl6, 0, sizeof fl6); |
4e3fd7a0 AD |
273 | fl6.daddr = dst_in->sin6_addr; |
274 | fl6.saddr = src_in->sin6_addr; | |
4c9483b2 | 275 | fl6.flowi6_oif = addr->bound_dev_if; |
38617c64 | 276 | |
565edd1d | 277 | dst = ip6_route_output(addr->net, NULL, &fl6); |
d14714df SH |
278 | if ((ret = dst->error)) |
279 | goto put; | |
280 | ||
c865f246 | 281 | rt = (struct rt6_info *)dst; |
4c9483b2 | 282 | if (ipv6_addr_any(&fl6.saddr)) { |
565edd1d | 283 | ret = ipv6_dev_get_saddr(addr->net, ip6_dst_idev(dst)->dev, |
4c9483b2 | 284 | &fl6.daddr, 0, &fl6.saddr); |
d14714df SH |
285 | if (ret) |
286 | goto put; | |
38617c64 | 287 | |
d14714df | 288 | src_in->sin6_family = AF_INET6; |
4e3fd7a0 | 289 | src_in->sin6_addr = fl6.saddr; |
d14714df SH |
290 | } |
291 | ||
c865f246 SK |
292 | /* If there's a gateway, we're definitely in RoCE v2 (as RoCE v1 isn't |
293 | * routable) and we could set the network type accordingly. | |
294 | */ | |
295 | if (rt->rt6i_flags & RTF_GATEWAY) | |
296 | addr->network = RDMA_NETWORK_IPV6; | |
297 | ||
20029832 MB |
298 | *pdst = dst; |
299 | return 0; | |
d14714df | 300 | put: |
38617c64 AS |
301 | dst_release(dst); |
302 | return ret; | |
303 | } | |
2c4ab624 | 304 | #else |
d14714df | 305 | static int addr6_resolve(struct sockaddr_in6 *src_in, |
20029832 MB |
306 | const struct sockaddr_in6 *dst_in, |
307 | struct rdma_dev_addr *addr, | |
308 | struct dst_entry **pdst) | |
2c4ab624 RD |
309 | { |
310 | return -EADDRNOTAVAIL; | |
311 | } | |
312 | #endif | |
38617c64 | 313 | |
20029832 MB |
314 | static int addr_resolve_neigh(struct dst_entry *dst, |
315 | const struct sockaddr *dst_in, | |
316 | struct rdma_dev_addr *addr) | |
317 | { | |
318 | if (dst->dev->flags & IFF_LOOPBACK) { | |
319 | int ret; | |
320 | ||
321 | ret = rdma_translate_ip(dst_in, addr, NULL); | |
322 | if (!ret) | |
323 | memcpy(addr->dst_dev_addr, addr->src_dev_addr, | |
324 | MAX_ADDR_LEN); | |
325 | ||
326 | return ret; | |
327 | } | |
328 | ||
329 | /* If the device doesn't do ARP internally */ | |
330 | if (!(dst->dev->flags & IFF_NOARP)) { | |
331 | const struct sockaddr_in *dst_in4 = | |
332 | (const struct sockaddr_in *)dst_in; | |
333 | const struct sockaddr_in6 *dst_in6 = | |
334 | (const struct sockaddr_in6 *)dst_in; | |
335 | ||
336 | return dst_fetch_ha(dst, addr, | |
337 | dst_in->sa_family == AF_INET ? | |
338 | (const void *)&dst_in4->sin_addr.s_addr : | |
339 | (const void *)&dst_in6->sin6_addr); | |
340 | } | |
341 | ||
342 | return rdma_copy_addr(addr, dst->dev, NULL); | |
343 | } | |
344 | ||
923c100e | 345 | static int addr_resolve(struct sockaddr *src_in, |
20029832 MB |
346 | const struct sockaddr *dst_in, |
347 | struct rdma_dev_addr *addr, | |
348 | bool resolve_neigh) | |
38617c64 | 349 | { |
20029832 MB |
350 | struct net_device *ndev; |
351 | struct dst_entry *dst; | |
352 | int ret; | |
353 | ||
38617c64 | 354 | if (src_in->sa_family == AF_INET) { |
20029832 MB |
355 | struct rtable *rt = NULL; |
356 | const struct sockaddr_in *dst_in4 = | |
357 | (const struct sockaddr_in *)dst_in; | |
358 | ||
359 | ret = addr4_resolve((struct sockaddr_in *)src_in, | |
360 | dst_in4, addr, &rt); | |
361 | if (ret) | |
362 | return ret; | |
363 | ||
364 | if (resolve_neigh) | |
365 | ret = addr_resolve_neigh(&rt->dst, dst_in, addr); | |
366 | ||
367 | ndev = rt->dst.dev; | |
368 | dev_hold(ndev); | |
369 | ||
370 | ip_rt_put(rt); | |
371 | } else { | |
372 | const struct sockaddr_in6 *dst_in6 = | |
373 | (const struct sockaddr_in6 *)dst_in; | |
374 | ||
375 | ret = addr6_resolve((struct sockaddr_in6 *)src_in, | |
376 | dst_in6, addr, | |
377 | &dst); | |
378 | if (ret) | |
379 | return ret; | |
380 | ||
381 | if (resolve_neigh) | |
382 | ret = addr_resolve_neigh(dst, dst_in, addr); | |
383 | ||
384 | ndev = dst->dev; | |
385 | dev_hold(ndev); | |
386 | ||
387 | dst_release(dst); | |
388 | } | |
389 | ||
390 | addr->bound_dev_if = ndev->ifindex; | |
391 | addr->net = dev_net(ndev); | |
392 | dev_put(ndev); | |
393 | ||
394 | return ret; | |
38617c64 AS |
395 | } |
396 | ||
c4028958 | 397 | static void process_req(struct work_struct *work) |
7025fcd3 SH |
398 | { |
399 | struct addr_req *req, *temp_req; | |
38617c64 | 400 | struct sockaddr *src_in, *dst_in; |
7025fcd3 SH |
401 | struct list_head done_list; |
402 | ||
403 | INIT_LIST_HEAD(&done_list); | |
404 | ||
405 | mutex_lock(&lock); | |
406 | list_for_each_entry_safe(req, temp_req, &req_list, list) { | |
c78bb844 | 407 | if (req->status == -ENODATA) { |
38617c64 AS |
408 | src_in = (struct sockaddr *) &req->src_addr; |
409 | dst_in = (struct sockaddr *) &req->dst_addr; | |
20029832 MB |
410 | req->status = addr_resolve(src_in, dst_in, req->addr, |
411 | true); | |
c78bb844 KK |
412 | if (req->status && time_after_eq(jiffies, req->timeout)) |
413 | req->status = -ETIMEDOUT; | |
414 | else if (req->status == -ENODATA) | |
415 | continue; | |
7025fcd3 | 416 | } |
04699a1f | 417 | list_move_tail(&req->list, &done_list); |
7025fcd3 SH |
418 | } |
419 | ||
420 | if (!list_empty(&req_list)) { | |
421 | req = list_entry(req_list.next, struct addr_req, list); | |
422 | set_timeout(req->timeout); | |
423 | } | |
424 | mutex_unlock(&lock); | |
425 | ||
426 | list_for_each_entry_safe(req, temp_req, &done_list, list) { | |
427 | list_del(&req->list); | |
38617c64 AS |
428 | req->callback(req->status, (struct sockaddr *) &req->src_addr, |
429 | req->addr, req->context); | |
7a118df3 | 430 | put_client(req->client); |
7025fcd3 SH |
431 | kfree(req); |
432 | } | |
433 | } | |
434 | ||
7a118df3 SH |
435 | int rdma_resolve_ip(struct rdma_addr_client *client, |
436 | struct sockaddr *src_addr, struct sockaddr *dst_addr, | |
7025fcd3 SH |
437 | struct rdma_dev_addr *addr, int timeout_ms, |
438 | void (*callback)(int status, struct sockaddr *src_addr, | |
439 | struct rdma_dev_addr *addr, void *context), | |
440 | void *context) | |
441 | { | |
38617c64 | 442 | struct sockaddr *src_in, *dst_in; |
7025fcd3 SH |
443 | struct addr_req *req; |
444 | int ret = 0; | |
445 | ||
dd00cc48 | 446 | req = kzalloc(sizeof *req, GFP_KERNEL); |
7025fcd3 SH |
447 | if (!req) |
448 | return -ENOMEM; | |
7025fcd3 | 449 | |
d2e08862 SH |
450 | src_in = (struct sockaddr *) &req->src_addr; |
451 | dst_in = (struct sockaddr *) &req->dst_addr; | |
452 | ||
453 | if (src_addr) { | |
454 | if (src_addr->sa_family != dst_addr->sa_family) { | |
455 | ret = -EINVAL; | |
456 | goto err; | |
457 | } | |
458 | ||
ef560861 | 459 | memcpy(src_in, src_addr, rdma_addr_size(src_addr)); |
d2e08862 SH |
460 | } else { |
461 | src_in->sa_family = dst_addr->sa_family; | |
462 | } | |
463 | ||
ef560861 | 464 | memcpy(dst_in, dst_addr, rdma_addr_size(dst_addr)); |
7025fcd3 SH |
465 | req->addr = addr; |
466 | req->callback = callback; | |
467 | req->context = context; | |
7a118df3 SH |
468 | req->client = client; |
469 | atomic_inc(&client->refcount); | |
7025fcd3 | 470 | |
20029832 | 471 | req->status = addr_resolve(src_in, dst_in, addr, true); |
7025fcd3 SH |
472 | switch (req->status) { |
473 | case 0: | |
474 | req->timeout = jiffies; | |
475 | queue_req(req); | |
476 | break; | |
477 | case -ENODATA: | |
478 | req->timeout = msecs_to_jiffies(timeout_ms) + jiffies; | |
479 | queue_req(req); | |
7025fcd3 SH |
480 | break; |
481 | default: | |
482 | ret = req->status; | |
7a118df3 | 483 | atomic_dec(&client->refcount); |
d2e08862 | 484 | goto err; |
7025fcd3 SH |
485 | } |
486 | return ret; | |
d2e08862 SH |
487 | err: |
488 | kfree(req); | |
489 | return ret; | |
7025fcd3 SH |
490 | } |
491 | EXPORT_SYMBOL(rdma_resolve_ip); | |
492 | ||
20029832 MB |
493 | int rdma_resolve_ip_route(struct sockaddr *src_addr, |
494 | const struct sockaddr *dst_addr, | |
495 | struct rdma_dev_addr *addr) | |
496 | { | |
497 | struct sockaddr_storage ssrc_addr = {}; | |
498 | struct sockaddr *src_in = (struct sockaddr *)&ssrc_addr; | |
499 | ||
9506902b MB |
500 | if (src_addr) { |
501 | if (src_addr->sa_family != dst_addr->sa_family) | |
502 | return -EINVAL; | |
20029832 | 503 | |
20029832 | 504 | memcpy(src_in, src_addr, rdma_addr_size(src_addr)); |
9506902b | 505 | } else { |
20029832 | 506 | src_in->sa_family = dst_addr->sa_family; |
9506902b | 507 | } |
20029832 MB |
508 | |
509 | return addr_resolve(src_in, dst_addr, addr, false); | |
510 | } | |
511 | EXPORT_SYMBOL(rdma_resolve_ip_route); | |
512 | ||
7025fcd3 SH |
513 | void rdma_addr_cancel(struct rdma_dev_addr *addr) |
514 | { | |
515 | struct addr_req *req, *temp_req; | |
516 | ||
517 | mutex_lock(&lock); | |
518 | list_for_each_entry_safe(req, temp_req, &req_list, list) { | |
519 | if (req->addr == addr) { | |
520 | req->status = -ECANCELED; | |
521 | req->timeout = jiffies; | |
04699a1f | 522 | list_move(&req->list, &req_list); |
7025fcd3 SH |
523 | set_timeout(req->timeout); |
524 | break; | |
525 | } | |
526 | } | |
527 | mutex_unlock(&lock); | |
528 | } | |
529 | EXPORT_SYMBOL(rdma_addr_cancel); | |
530 | ||
dd5f03be MB |
531 | struct resolve_cb_context { |
532 | struct rdma_dev_addr *addr; | |
533 | struct completion comp; | |
534 | }; | |
535 | ||
536 | static void resolve_cb(int status, struct sockaddr *src_addr, | |
537 | struct rdma_dev_addr *addr, void *context) | |
538 | { | |
539 | memcpy(((struct resolve_cb_context *)context)->addr, addr, sizeof(struct | |
540 | rdma_dev_addr)); | |
541 | complete(&((struct resolve_cb_context *)context)->comp); | |
542 | } | |
543 | ||
73cdaaee | 544 | int rdma_addr_find_dmac_by_grh(const union ib_gid *sgid, const union ib_gid *dgid, |
20029832 | 545 | u8 *dmac, u16 *vlan_id, int *if_index) |
dd5f03be MB |
546 | { |
547 | int ret = 0; | |
548 | struct rdma_dev_addr dev_addr; | |
549 | struct resolve_cb_context ctx; | |
550 | struct net_device *dev; | |
551 | ||
552 | union { | |
553 | struct sockaddr _sockaddr; | |
554 | struct sockaddr_in _sockaddr_in; | |
555 | struct sockaddr_in6 _sockaddr_in6; | |
556 | } sgid_addr, dgid_addr; | |
557 | ||
558 | ||
471e7058 HL |
559 | rdma_gid2ip(&sgid_addr._sockaddr, sgid); |
560 | rdma_gid2ip(&dgid_addr._sockaddr, dgid); | |
dd5f03be MB |
561 | |
562 | memset(&dev_addr, 0, sizeof(dev_addr)); | |
20029832 MB |
563 | if (if_index) |
564 | dev_addr.bound_dev_if = *if_index; | |
565edd1d | 565 | dev_addr.net = &init_net; |
dd5f03be MB |
566 | |
567 | ctx.addr = &dev_addr; | |
568 | init_completion(&ctx.comp); | |
569 | ret = rdma_resolve_ip(&self, &sgid_addr._sockaddr, &dgid_addr._sockaddr, | |
570 | &dev_addr, 1000, resolve_cb, &ctx); | |
571 | if (ret) | |
572 | return ret; | |
573 | ||
574 | wait_for_completion(&ctx.comp); | |
575 | ||
576 | memcpy(dmac, dev_addr.dst_dev_addr, ETH_ALEN); | |
577 | dev = dev_get_by_index(&init_net, dev_addr.bound_dev_if); | |
578 | if (!dev) | |
579 | return -ENODEV; | |
20029832 MB |
580 | if (if_index) |
581 | *if_index = dev_addr.bound_dev_if; | |
dd5f03be MB |
582 | if (vlan_id) |
583 | *vlan_id = rdma_vlan_dev_vlan_id(dev); | |
584 | dev_put(dev); | |
585 | return ret; | |
586 | } | |
587 | EXPORT_SYMBOL(rdma_addr_find_dmac_by_grh); | |
588 | ||
589 | int rdma_addr_find_smac_by_sgid(union ib_gid *sgid, u8 *smac, u16 *vlan_id) | |
590 | { | |
591 | int ret = 0; | |
592 | struct rdma_dev_addr dev_addr; | |
593 | union { | |
594 | struct sockaddr _sockaddr; | |
595 | struct sockaddr_in _sockaddr_in; | |
596 | struct sockaddr_in6 _sockaddr_in6; | |
597 | } gid_addr; | |
598 | ||
471e7058 | 599 | rdma_gid2ip(&gid_addr._sockaddr, sgid); |
dd5f03be | 600 | |
dd5f03be | 601 | memset(&dev_addr, 0, sizeof(dev_addr)); |
565edd1d | 602 | dev_addr.net = &init_net; |
dd5f03be MB |
603 | ret = rdma_translate_ip(&gid_addr._sockaddr, &dev_addr, vlan_id); |
604 | if (ret) | |
605 | return ret; | |
606 | ||
607 | memcpy(smac, dev_addr.src_dev_addr, ETH_ALEN); | |
608 | return ret; | |
609 | } | |
610 | EXPORT_SYMBOL(rdma_addr_find_smac_by_sgid); | |
611 | ||
3cd96564 | 612 | static int netevent_callback(struct notifier_block *self, unsigned long event, |
e795d092 | 613 | void *ctx) |
7025fcd3 | 614 | { |
3cd96564 | 615 | if (event == NETEVENT_NEIGH_UPDATE) { |
e795d092 | 616 | struct neighbour *neigh = ctx; |
7025fcd3 | 617 | |
1f126670 | 618 | if (neigh->nud_state & NUD_VALID) { |
e795d092 TT |
619 | set_timeout(jiffies); |
620 | } | |
621 | } | |
7025fcd3 SH |
622 | return 0; |
623 | } | |
624 | ||
e795d092 TT |
625 | static struct notifier_block nb = { |
626 | .notifier_call = netevent_callback | |
7025fcd3 SH |
627 | }; |
628 | ||
716abb1f | 629 | static int __init addr_init(void) |
7025fcd3 | 630 | { |
c7f743a6 | 631 | addr_wq = create_singlethread_workqueue("ib_addr"); |
7025fcd3 SH |
632 | if (!addr_wq) |
633 | return -ENOMEM; | |
634 | ||
e795d092 | 635 | register_netevent_notifier(&nb); |
dd5f03be | 636 | rdma_addr_register_client(&self); |
7025fcd3 SH |
637 | return 0; |
638 | } | |
639 | ||
716abb1f | 640 | static void __exit addr_cleanup(void) |
7025fcd3 | 641 | { |
dd5f03be | 642 | rdma_addr_unregister_client(&self); |
e795d092 | 643 | unregister_netevent_notifier(&nb); |
7025fcd3 SH |
644 | destroy_workqueue(addr_wq); |
645 | } | |
646 | ||
647 | module_init(addr_init); | |
648 | module_exit(addr_cleanup); |