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> |
ae43f828 MB |
49 | #include <rdma/rdma_netlink.h> |
50 | #include <net/netlink.h> | |
51 | ||
52 | #include "core_priv.h" | |
7025fcd3 | 53 | |
7025fcd3 SH |
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; | |
ae43f828 | 65 | u32 seq; |
7025fcd3 SH |
66 | }; |
67 | ||
ae43f828 MB |
68 | static atomic_t ib_nl_addr_request_seq = ATOMIC_INIT(0); |
69 | ||
c4028958 | 70 | static void process_req(struct work_struct *work); |
7025fcd3 SH |
71 | |
72 | static DEFINE_MUTEX(lock); | |
73 | static LIST_HEAD(req_list); | |
c4028958 | 74 | static DECLARE_DELAYED_WORK(work, process_req); |
7025fcd3 SH |
75 | static struct workqueue_struct *addr_wq; |
76 | ||
ae43f828 MB |
77 | static const struct nla_policy ib_nl_addr_policy[LS_NLA_TYPE_MAX] = { |
78 | [LS_NLA_TYPE_DGID] = {.type = NLA_BINARY, | |
79 | .len = sizeof(struct rdma_nla_ls_gid)}, | |
80 | }; | |
81 | ||
82 | static inline bool ib_nl_is_good_ip_resp(const struct nlmsghdr *nlh) | |
83 | { | |
84 | struct nlattr *tb[LS_NLA_TYPE_MAX] = {}; | |
85 | int ret; | |
86 | ||
87 | if (nlh->nlmsg_flags & RDMA_NL_LS_F_ERR) | |
88 | return false; | |
89 | ||
90 | ret = nla_parse(tb, LS_NLA_TYPE_MAX - 1, nlmsg_data(nlh), | |
91 | nlmsg_len(nlh), ib_nl_addr_policy); | |
92 | if (ret) | |
93 | return false; | |
94 | ||
95 | return true; | |
96 | } | |
97 | ||
98 | static void ib_nl_process_good_ip_rsep(const struct nlmsghdr *nlh) | |
99 | { | |
100 | const struct nlattr *head, *curr; | |
101 | union ib_gid gid; | |
102 | struct addr_req *req; | |
103 | int len, rem; | |
104 | int found = 0; | |
105 | ||
106 | head = (const struct nlattr *)nlmsg_data(nlh); | |
107 | len = nlmsg_len(nlh); | |
108 | ||
109 | nla_for_each_attr(curr, head, len, rem) { | |
110 | if (curr->nla_type == LS_NLA_TYPE_DGID) | |
111 | memcpy(&gid, nla_data(curr), nla_len(curr)); | |
112 | } | |
113 | ||
114 | mutex_lock(&lock); | |
115 | list_for_each_entry(req, &req_list, list) { | |
116 | if (nlh->nlmsg_seq != req->seq) | |
117 | continue; | |
118 | /* We set the DGID part, the rest was set earlier */ | |
119 | rdma_addr_set_dgid(req->addr, &gid); | |
120 | req->status = 0; | |
121 | found = 1; | |
122 | break; | |
123 | } | |
124 | mutex_unlock(&lock); | |
125 | ||
126 | if (!found) | |
127 | pr_info("Couldn't find request waiting for DGID: %pI6\n", | |
128 | &gid); | |
129 | } | |
130 | ||
131 | int ib_nl_handle_ip_res_resp(struct sk_buff *skb, | |
132 | struct netlink_callback *cb) | |
133 | { | |
134 | const struct nlmsghdr *nlh = (struct nlmsghdr *)cb->nlh; | |
135 | ||
136 | if ((nlh->nlmsg_flags & NLM_F_REQUEST) || | |
137 | !(NETLINK_CB(skb).sk) || | |
138 | !netlink_capable(skb, CAP_NET_ADMIN)) | |
139 | return -EPERM; | |
140 | ||
141 | if (ib_nl_is_good_ip_resp(nlh)) | |
142 | ib_nl_process_good_ip_rsep(nlh); | |
143 | ||
144 | return skb->len; | |
145 | } | |
146 | ||
147 | static int ib_nl_ip_send_msg(struct rdma_dev_addr *dev_addr, | |
148 | const void *daddr, | |
149 | u32 seq, u16 family) | |
150 | { | |
151 | struct sk_buff *skb = NULL; | |
152 | struct nlmsghdr *nlh; | |
153 | struct rdma_ls_ip_resolve_header *header; | |
154 | void *data; | |
155 | size_t size; | |
156 | int attrtype; | |
157 | int len; | |
158 | ||
159 | if (family == AF_INET) { | |
160 | size = sizeof(struct in_addr); | |
161 | attrtype = RDMA_NLA_F_MANDATORY | LS_NLA_TYPE_IPV4; | |
162 | } else { | |
163 | size = sizeof(struct in6_addr); | |
164 | attrtype = RDMA_NLA_F_MANDATORY | LS_NLA_TYPE_IPV6; | |
165 | } | |
166 | ||
167 | len = nla_total_size(sizeof(size)); | |
168 | len += NLMSG_ALIGN(sizeof(*header)); | |
169 | ||
170 | skb = nlmsg_new(len, GFP_KERNEL); | |
171 | if (!skb) | |
172 | return -ENOMEM; | |
173 | ||
174 | data = ibnl_put_msg(skb, &nlh, seq, 0, RDMA_NL_LS, | |
175 | RDMA_NL_LS_OP_IP_RESOLVE, NLM_F_REQUEST); | |
176 | if (!data) { | |
177 | nlmsg_free(skb); | |
178 | return -ENODATA; | |
179 | } | |
180 | ||
181 | /* Construct the family header first */ | |
182 | header = (struct rdma_ls_ip_resolve_header *) | |
183 | skb_put(skb, NLMSG_ALIGN(sizeof(*header))); | |
184 | header->ifindex = dev_addr->bound_dev_if; | |
185 | nla_put(skb, attrtype, size, daddr); | |
186 | ||
187 | /* Repair the nlmsg header length */ | |
188 | nlmsg_end(skb, nlh); | |
189 | ibnl_multicast(skb, nlh, RDMA_NL_GROUP_LS, GFP_KERNEL); | |
190 | ||
191 | /* Make the request retry, so when we get the response from userspace | |
192 | * we will have something. | |
193 | */ | |
194 | return -ENODATA; | |
195 | } | |
196 | ||
ef560861 SH |
197 | int rdma_addr_size(struct sockaddr *addr) |
198 | { | |
199 | switch (addr->sa_family) { | |
200 | case AF_INET: | |
201 | return sizeof(struct sockaddr_in); | |
202 | case AF_INET6: | |
203 | return sizeof(struct sockaddr_in6); | |
204 | case AF_IB: | |
205 | return sizeof(struct sockaddr_ib); | |
206 | default: | |
207 | return 0; | |
208 | } | |
209 | } | |
210 | EXPORT_SYMBOL(rdma_addr_size); | |
211 | ||
dd5f03be MB |
212 | static struct rdma_addr_client self; |
213 | ||
7a118df3 SH |
214 | void rdma_addr_register_client(struct rdma_addr_client *client) |
215 | { | |
216 | atomic_set(&client->refcount, 1); | |
217 | init_completion(&client->comp); | |
218 | } | |
219 | EXPORT_SYMBOL(rdma_addr_register_client); | |
220 | ||
221 | static inline void put_client(struct rdma_addr_client *client) | |
222 | { | |
223 | if (atomic_dec_and_test(&client->refcount)) | |
224 | complete(&client->comp); | |
225 | } | |
226 | ||
227 | void rdma_addr_unregister_client(struct rdma_addr_client *client) | |
228 | { | |
229 | put_client(client); | |
230 | wait_for_completion(&client->comp); | |
231 | } | |
232 | EXPORT_SYMBOL(rdma_addr_unregister_client); | |
233 | ||
07ebafba TT |
234 | int rdma_copy_addr(struct rdma_dev_addr *dev_addr, struct net_device *dev, |
235 | const unsigned char *dst_dev_addr) | |
7025fcd3 | 236 | { |
c4315d85 | 237 | dev_addr->dev_type = dev->type; |
7025fcd3 SH |
238 | memcpy(dev_addr->src_dev_addr, dev->dev_addr, MAX_ADDR_LEN); |
239 | memcpy(dev_addr->broadcast, dev->broadcast, MAX_ADDR_LEN); | |
240 | if (dst_dev_addr) | |
241 | memcpy(dev_addr->dst_dev_addr, dst_dev_addr, MAX_ADDR_LEN); | |
6266ed6e | 242 | dev_addr->bound_dev_if = dev->ifindex; |
7025fcd3 SH |
243 | return 0; |
244 | } | |
07ebafba | 245 | EXPORT_SYMBOL(rdma_copy_addr); |
7025fcd3 | 246 | |
20029832 MB |
247 | int rdma_translate_ip(const struct sockaddr *addr, |
248 | struct rdma_dev_addr *dev_addr, | |
dd5f03be | 249 | u16 *vlan_id) |
7025fcd3 SH |
250 | { |
251 | struct net_device *dev; | |
38617c64 | 252 | int ret = -EADDRNOTAVAIL; |
7025fcd3 | 253 | |
6266ed6e | 254 | if (dev_addr->bound_dev_if) { |
565edd1d | 255 | dev = dev_get_by_index(dev_addr->net, dev_addr->bound_dev_if); |
6266ed6e SH |
256 | if (!dev) |
257 | return -ENODEV; | |
258 | ret = rdma_copy_addr(dev_addr, dev, NULL); | |
259 | dev_put(dev); | |
260 | return ret; | |
261 | } | |
262 | ||
38617c64 AS |
263 | switch (addr->sa_family) { |
264 | case AF_INET: | |
565edd1d | 265 | dev = ip_dev_find(dev_addr->net, |
20029832 | 266 | ((const struct sockaddr_in *)addr)->sin_addr.s_addr); |
38617c64 AS |
267 | |
268 | if (!dev) | |
269 | return ret; | |
7025fcd3 | 270 | |
38617c64 | 271 | ret = rdma_copy_addr(dev_addr, dev, NULL); |
dd5f03be MB |
272 | if (vlan_id) |
273 | *vlan_id = rdma_vlan_dev_vlan_id(dev); | |
38617c64 AS |
274 | dev_put(dev); |
275 | break; | |
d90f9b35 | 276 | #if IS_ENABLED(CONFIG_IPV6) |
38617c64 | 277 | case AF_INET6: |
22f4fbd9 | 278 | rcu_read_lock(); |
565edd1d GS |
279 | for_each_netdev_rcu(dev_addr->net, dev) { |
280 | if (ipv6_chk_addr(dev_addr->net, | |
20029832 | 281 | &((const struct sockaddr_in6 *)addr)->sin6_addr, |
38617c64 AS |
282 | dev, 1)) { |
283 | ret = rdma_copy_addr(dev_addr, dev, NULL); | |
dd5f03be MB |
284 | if (vlan_id) |
285 | *vlan_id = rdma_vlan_dev_vlan_id(dev); | |
38617c64 AS |
286 | break; |
287 | } | |
288 | } | |
22f4fbd9 | 289 | rcu_read_unlock(); |
38617c64 | 290 | break; |
2c4ab624 | 291 | #endif |
38617c64 | 292 | } |
7025fcd3 SH |
293 | return ret; |
294 | } | |
295 | EXPORT_SYMBOL(rdma_translate_ip); | |
296 | ||
297 | static void set_timeout(unsigned long time) | |
298 | { | |
299 | unsigned long delay; | |
300 | ||
7025fcd3 | 301 | delay = time - jiffies; |
346f98b4 OK |
302 | if ((long)delay < 0) |
303 | delay = 0; | |
7025fcd3 | 304 | |
41f63c53 | 305 | mod_delayed_work(addr_wq, &work, delay); |
7025fcd3 SH |
306 | } |
307 | ||
308 | static void queue_req(struct addr_req *req) | |
309 | { | |
310 | struct addr_req *temp_req; | |
311 | ||
312 | mutex_lock(&lock); | |
313 | list_for_each_entry_reverse(temp_req, &req_list, list) { | |
f115db48 | 314 | if (time_after_eq(req->timeout, temp_req->timeout)) |
7025fcd3 SH |
315 | break; |
316 | } | |
317 | ||
318 | list_add(&req->list, &temp_req->list); | |
319 | ||
320 | if (req_list.next == &req->list) | |
321 | set_timeout(req->timeout); | |
322 | mutex_unlock(&lock); | |
323 | } | |
324 | ||
ae43f828 MB |
325 | static int ib_nl_fetch_ha(struct dst_entry *dst, struct rdma_dev_addr *dev_addr, |
326 | const void *daddr, u32 seq, u16 family) | |
327 | { | |
328 | if (ibnl_chk_listeners(RDMA_NL_GROUP_LS)) | |
329 | return -EADDRNOTAVAIL; | |
330 | ||
331 | /* We fill in what we can, the response will fill the rest */ | |
332 | rdma_copy_addr(dev_addr, dst->dev, NULL); | |
333 | return ib_nl_ip_send_msg(dev_addr, daddr, seq, family); | |
334 | } | |
335 | ||
20029832 MB |
336 | static int dst_fetch_ha(struct dst_entry *dst, struct rdma_dev_addr *dev_addr, |
337 | const void *daddr) | |
51d45974 DM |
338 | { |
339 | struct neighbour *n; | |
340 | int ret; | |
341 | ||
02b61955 DM |
342 | n = dst_neigh_lookup(dst, daddr); |
343 | ||
51d45974 | 344 | rcu_read_lock(); |
51d45974 DM |
345 | if (!n || !(n->nud_state & NUD_VALID)) { |
346 | if (n) | |
347 | neigh_event_send(n, NULL); | |
348 | ret = -ENODATA; | |
349 | } else { | |
02b61955 | 350 | ret = rdma_copy_addr(dev_addr, dst->dev, n->ha); |
51d45974 DM |
351 | } |
352 | rcu_read_unlock(); | |
353 | ||
02b61955 DM |
354 | if (n) |
355 | neigh_release(n); | |
356 | ||
51d45974 DM |
357 | return ret; |
358 | } | |
359 | ||
ae43f828 MB |
360 | static bool has_gateway(struct dst_entry *dst, sa_family_t family) |
361 | { | |
362 | struct rtable *rt; | |
363 | struct rt6_info *rt6; | |
364 | ||
365 | if (family == AF_INET) { | |
366 | rt = container_of(dst, struct rtable, dst); | |
367 | return rt->rt_uses_gateway; | |
368 | } | |
369 | ||
370 | rt6 = container_of(dst, struct rt6_info, dst); | |
371 | return rt6->rt6i_flags & RTF_GATEWAY; | |
372 | } | |
373 | ||
374 | static int fetch_ha(struct dst_entry *dst, struct rdma_dev_addr *dev_addr, | |
375 | const struct sockaddr *dst_in, u32 seq) | |
376 | { | |
377 | const struct sockaddr_in *dst_in4 = | |
378 | (const struct sockaddr_in *)dst_in; | |
379 | const struct sockaddr_in6 *dst_in6 = | |
380 | (const struct sockaddr_in6 *)dst_in; | |
381 | const void *daddr = (dst_in->sa_family == AF_INET) ? | |
382 | (const void *)&dst_in4->sin_addr.s_addr : | |
383 | (const void *)&dst_in6->sin6_addr; | |
384 | sa_family_t family = dst_in->sa_family; | |
385 | ||
386 | /* Gateway + ARPHRD_INFINIBAND -> IB router */ | |
387 | if (has_gateway(dst, family) && dst->dev->type == ARPHRD_INFINIBAND) | |
388 | return ib_nl_fetch_ha(dst, dev_addr, daddr, seq, family); | |
389 | else | |
390 | return dst_fetch_ha(dst, dev_addr, daddr); | |
391 | } | |
392 | ||
923c100e | 393 | static int addr4_resolve(struct sockaddr_in *src_in, |
20029832 MB |
394 | const struct sockaddr_in *dst_in, |
395 | struct rdma_dev_addr *addr, | |
396 | struct rtable **prt) | |
7025fcd3 | 397 | { |
1b90c137 AV |
398 | __be32 src_ip = src_in->sin_addr.s_addr; |
399 | __be32 dst_ip = dst_in->sin_addr.s_addr; | |
7025fcd3 | 400 | struct rtable *rt; |
5fc3590c | 401 | struct flowi4 fl4; |
7025fcd3 SH |
402 | int ret; |
403 | ||
5fc3590c DM |
404 | memset(&fl4, 0, sizeof(fl4)); |
405 | fl4.daddr = dst_ip; | |
406 | fl4.saddr = src_ip; | |
407 | fl4.flowi4_oif = addr->bound_dev_if; | |
565edd1d | 408 | rt = ip_route_output_key(addr->net, &fl4); |
b23dd4fe DM |
409 | if (IS_ERR(rt)) { |
410 | ret = PTR_ERR(rt); | |
7025fcd3 | 411 | goto out; |
b23dd4fe | 412 | } |
923c100e | 413 | src_in->sin_family = AF_INET; |
5fc3590c | 414 | src_in->sin_addr.s_addr = fl4.saddr; |
923c100e | 415 | |
ae43f828 MB |
416 | /* If there's a gateway and type of device not ARPHRD_INFINIBAND, we're |
417 | * definitely in RoCE v2 (as RoCE v1 isn't routable) set the network | |
418 | * type accordingly. | |
c865f246 | 419 | */ |
ae43f828 | 420 | if (rt->rt_uses_gateway && rt->dst.dev->type != ARPHRD_INFINIBAND) |
c865f246 SK |
421 | addr->network = RDMA_NETWORK_IPV4; |
422 | ||
c3efe750 MB |
423 | addr->hoplimit = ip4_dst_hoplimit(&rt->dst); |
424 | ||
20029832 MB |
425 | *prt = rt; |
426 | return 0; | |
7025fcd3 SH |
427 | out: |
428 | return ret; | |
429 | } | |
430 | ||
d90f9b35 | 431 | #if IS_ENABLED(CONFIG_IPV6) |
d14714df | 432 | static int addr6_resolve(struct sockaddr_in6 *src_in, |
20029832 MB |
433 | const struct sockaddr_in6 *dst_in, |
434 | struct rdma_dev_addr *addr, | |
435 | struct dst_entry **pdst) | |
38617c64 | 436 | { |
4c9483b2 | 437 | struct flowi6 fl6; |
38617c64 | 438 | struct dst_entry *dst; |
c865f246 | 439 | struct rt6_info *rt; |
d14714df | 440 | int ret; |
38617c64 | 441 | |
4c9483b2 | 442 | memset(&fl6, 0, sizeof fl6); |
4e3fd7a0 AD |
443 | fl6.daddr = dst_in->sin6_addr; |
444 | fl6.saddr = src_in->sin6_addr; | |
4c9483b2 | 445 | fl6.flowi6_oif = addr->bound_dev_if; |
38617c64 | 446 | |
565edd1d | 447 | dst = ip6_route_output(addr->net, NULL, &fl6); |
d14714df SH |
448 | if ((ret = dst->error)) |
449 | goto put; | |
450 | ||
c865f246 | 451 | rt = (struct rt6_info *)dst; |
4c9483b2 | 452 | if (ipv6_addr_any(&fl6.saddr)) { |
565edd1d | 453 | ret = ipv6_dev_get_saddr(addr->net, ip6_dst_idev(dst)->dev, |
4c9483b2 | 454 | &fl6.daddr, 0, &fl6.saddr); |
d14714df SH |
455 | if (ret) |
456 | goto put; | |
38617c64 | 457 | |
d14714df | 458 | src_in->sin6_family = AF_INET6; |
4e3fd7a0 | 459 | src_in->sin6_addr = fl6.saddr; |
d14714df SH |
460 | } |
461 | ||
ae43f828 MB |
462 | /* If there's a gateway and type of device not ARPHRD_INFINIBAND, we're |
463 | * definitely in RoCE v2 (as RoCE v1 isn't routable) set the network | |
464 | * type accordingly. | |
c865f246 | 465 | */ |
ae43f828 MB |
466 | if (rt->rt6i_flags & RTF_GATEWAY && |
467 | ip6_dst_idev(dst)->dev->type != ARPHRD_INFINIBAND) | |
c865f246 SK |
468 | addr->network = RDMA_NETWORK_IPV6; |
469 | ||
c3efe750 MB |
470 | addr->hoplimit = ip6_dst_hoplimit(dst); |
471 | ||
20029832 MB |
472 | *pdst = dst; |
473 | return 0; | |
d14714df | 474 | put: |
38617c64 AS |
475 | dst_release(dst); |
476 | return ret; | |
477 | } | |
2c4ab624 | 478 | #else |
d14714df | 479 | static int addr6_resolve(struct sockaddr_in6 *src_in, |
20029832 MB |
480 | const struct sockaddr_in6 *dst_in, |
481 | struct rdma_dev_addr *addr, | |
482 | struct dst_entry **pdst) | |
2c4ab624 RD |
483 | { |
484 | return -EADDRNOTAVAIL; | |
485 | } | |
486 | #endif | |
38617c64 | 487 | |
20029832 MB |
488 | static int addr_resolve_neigh(struct dst_entry *dst, |
489 | const struct sockaddr *dst_in, | |
ae43f828 MB |
490 | struct rdma_dev_addr *addr, |
491 | u32 seq) | |
20029832 MB |
492 | { |
493 | if (dst->dev->flags & IFF_LOOPBACK) { | |
494 | int ret; | |
495 | ||
496 | ret = rdma_translate_ip(dst_in, addr, NULL); | |
497 | if (!ret) | |
498 | memcpy(addr->dst_dev_addr, addr->src_dev_addr, | |
499 | MAX_ADDR_LEN); | |
500 | ||
501 | return ret; | |
502 | } | |
503 | ||
504 | /* If the device doesn't do ARP internally */ | |
ae43f828 MB |
505 | if (!(dst->dev->flags & IFF_NOARP)) |
506 | return fetch_ha(dst, addr, dst_in, seq); | |
20029832 MB |
507 | |
508 | return rdma_copy_addr(addr, dst->dev, NULL); | |
509 | } | |
510 | ||
923c100e | 511 | static int addr_resolve(struct sockaddr *src_in, |
20029832 MB |
512 | const struct sockaddr *dst_in, |
513 | struct rdma_dev_addr *addr, | |
ae43f828 MB |
514 | bool resolve_neigh, |
515 | u32 seq) | |
38617c64 | 516 | { |
20029832 MB |
517 | struct net_device *ndev; |
518 | struct dst_entry *dst; | |
519 | int ret; | |
520 | ||
38617c64 | 521 | if (src_in->sa_family == AF_INET) { |
20029832 MB |
522 | struct rtable *rt = NULL; |
523 | const struct sockaddr_in *dst_in4 = | |
524 | (const struct sockaddr_in *)dst_in; | |
525 | ||
526 | ret = addr4_resolve((struct sockaddr_in *)src_in, | |
527 | dst_in4, addr, &rt); | |
528 | if (ret) | |
529 | return ret; | |
530 | ||
531 | if (resolve_neigh) | |
ae43f828 | 532 | ret = addr_resolve_neigh(&rt->dst, dst_in, addr, seq); |
20029832 MB |
533 | |
534 | ndev = rt->dst.dev; | |
535 | dev_hold(ndev); | |
536 | ||
537 | ip_rt_put(rt); | |
538 | } else { | |
539 | const struct sockaddr_in6 *dst_in6 = | |
540 | (const struct sockaddr_in6 *)dst_in; | |
541 | ||
542 | ret = addr6_resolve((struct sockaddr_in6 *)src_in, | |
543 | dst_in6, addr, | |
544 | &dst); | |
545 | if (ret) | |
546 | return ret; | |
547 | ||
548 | if (resolve_neigh) | |
ae43f828 | 549 | ret = addr_resolve_neigh(dst, dst_in, addr, seq); |
20029832 MB |
550 | |
551 | ndev = dst->dev; | |
552 | dev_hold(ndev); | |
553 | ||
554 | dst_release(dst); | |
555 | } | |
556 | ||
557 | addr->bound_dev_if = ndev->ifindex; | |
558 | addr->net = dev_net(ndev); | |
559 | dev_put(ndev); | |
560 | ||
561 | return ret; | |
38617c64 AS |
562 | } |
563 | ||
c4028958 | 564 | static void process_req(struct work_struct *work) |
7025fcd3 SH |
565 | { |
566 | struct addr_req *req, *temp_req; | |
38617c64 | 567 | struct sockaddr *src_in, *dst_in; |
7025fcd3 SH |
568 | struct list_head done_list; |
569 | ||
570 | INIT_LIST_HEAD(&done_list); | |
571 | ||
572 | mutex_lock(&lock); | |
573 | list_for_each_entry_safe(req, temp_req, &req_list, list) { | |
c78bb844 | 574 | if (req->status == -ENODATA) { |
38617c64 AS |
575 | src_in = (struct sockaddr *) &req->src_addr; |
576 | dst_in = (struct sockaddr *) &req->dst_addr; | |
20029832 | 577 | req->status = addr_resolve(src_in, dst_in, req->addr, |
ae43f828 | 578 | true, req->seq); |
c78bb844 KK |
579 | if (req->status && time_after_eq(jiffies, req->timeout)) |
580 | req->status = -ETIMEDOUT; | |
581 | else if (req->status == -ENODATA) | |
582 | continue; | |
7025fcd3 | 583 | } |
04699a1f | 584 | list_move_tail(&req->list, &done_list); |
7025fcd3 SH |
585 | } |
586 | ||
587 | if (!list_empty(&req_list)) { | |
588 | req = list_entry(req_list.next, struct addr_req, list); | |
589 | set_timeout(req->timeout); | |
590 | } | |
591 | mutex_unlock(&lock); | |
592 | ||
593 | list_for_each_entry_safe(req, temp_req, &done_list, list) { | |
594 | list_del(&req->list); | |
38617c64 AS |
595 | req->callback(req->status, (struct sockaddr *) &req->src_addr, |
596 | req->addr, req->context); | |
7a118df3 | 597 | put_client(req->client); |
7025fcd3 SH |
598 | kfree(req); |
599 | } | |
600 | } | |
601 | ||
7a118df3 SH |
602 | int rdma_resolve_ip(struct rdma_addr_client *client, |
603 | struct sockaddr *src_addr, struct sockaddr *dst_addr, | |
7025fcd3 SH |
604 | struct rdma_dev_addr *addr, int timeout_ms, |
605 | void (*callback)(int status, struct sockaddr *src_addr, | |
606 | struct rdma_dev_addr *addr, void *context), | |
607 | void *context) | |
608 | { | |
38617c64 | 609 | struct sockaddr *src_in, *dst_in; |
7025fcd3 SH |
610 | struct addr_req *req; |
611 | int ret = 0; | |
612 | ||
dd00cc48 | 613 | req = kzalloc(sizeof *req, GFP_KERNEL); |
7025fcd3 SH |
614 | if (!req) |
615 | return -ENOMEM; | |
7025fcd3 | 616 | |
d2e08862 SH |
617 | src_in = (struct sockaddr *) &req->src_addr; |
618 | dst_in = (struct sockaddr *) &req->dst_addr; | |
619 | ||
620 | if (src_addr) { | |
621 | if (src_addr->sa_family != dst_addr->sa_family) { | |
622 | ret = -EINVAL; | |
623 | goto err; | |
624 | } | |
625 | ||
ef560861 | 626 | memcpy(src_in, src_addr, rdma_addr_size(src_addr)); |
d2e08862 SH |
627 | } else { |
628 | src_in->sa_family = dst_addr->sa_family; | |
629 | } | |
630 | ||
ef560861 | 631 | memcpy(dst_in, dst_addr, rdma_addr_size(dst_addr)); |
7025fcd3 SH |
632 | req->addr = addr; |
633 | req->callback = callback; | |
634 | req->context = context; | |
7a118df3 SH |
635 | req->client = client; |
636 | atomic_inc(&client->refcount); | |
ae43f828 | 637 | req->seq = (u32)atomic_inc_return(&ib_nl_addr_request_seq); |
7025fcd3 | 638 | |
ae43f828 | 639 | req->status = addr_resolve(src_in, dst_in, addr, true, req->seq); |
7025fcd3 SH |
640 | switch (req->status) { |
641 | case 0: | |
642 | req->timeout = jiffies; | |
643 | queue_req(req); | |
644 | break; | |
645 | case -ENODATA: | |
646 | req->timeout = msecs_to_jiffies(timeout_ms) + jiffies; | |
647 | queue_req(req); | |
7025fcd3 SH |
648 | break; |
649 | default: | |
650 | ret = req->status; | |
7a118df3 | 651 | atomic_dec(&client->refcount); |
d2e08862 | 652 | goto err; |
7025fcd3 SH |
653 | } |
654 | return ret; | |
d2e08862 SH |
655 | err: |
656 | kfree(req); | |
657 | return ret; | |
7025fcd3 SH |
658 | } |
659 | EXPORT_SYMBOL(rdma_resolve_ip); | |
660 | ||
20029832 MB |
661 | int rdma_resolve_ip_route(struct sockaddr *src_addr, |
662 | const struct sockaddr *dst_addr, | |
663 | struct rdma_dev_addr *addr) | |
664 | { | |
665 | struct sockaddr_storage ssrc_addr = {}; | |
666 | struct sockaddr *src_in = (struct sockaddr *)&ssrc_addr; | |
667 | ||
9506902b MB |
668 | if (src_addr) { |
669 | if (src_addr->sa_family != dst_addr->sa_family) | |
670 | return -EINVAL; | |
20029832 | 671 | |
20029832 | 672 | memcpy(src_in, src_addr, rdma_addr_size(src_addr)); |
9506902b | 673 | } else { |
20029832 | 674 | src_in->sa_family = dst_addr->sa_family; |
9506902b | 675 | } |
20029832 | 676 | |
ae43f828 | 677 | return addr_resolve(src_in, dst_addr, addr, false, 0); |
20029832 MB |
678 | } |
679 | EXPORT_SYMBOL(rdma_resolve_ip_route); | |
680 | ||
7025fcd3 SH |
681 | void rdma_addr_cancel(struct rdma_dev_addr *addr) |
682 | { | |
683 | struct addr_req *req, *temp_req; | |
684 | ||
685 | mutex_lock(&lock); | |
686 | list_for_each_entry_safe(req, temp_req, &req_list, list) { | |
687 | if (req->addr == addr) { | |
688 | req->status = -ECANCELED; | |
689 | req->timeout = jiffies; | |
04699a1f | 690 | list_move(&req->list, &req_list); |
7025fcd3 SH |
691 | set_timeout(req->timeout); |
692 | break; | |
693 | } | |
694 | } | |
695 | mutex_unlock(&lock); | |
696 | } | |
697 | EXPORT_SYMBOL(rdma_addr_cancel); | |
698 | ||
dd5f03be MB |
699 | struct resolve_cb_context { |
700 | struct rdma_dev_addr *addr; | |
701 | struct completion comp; | |
702 | }; | |
703 | ||
704 | static void resolve_cb(int status, struct sockaddr *src_addr, | |
705 | struct rdma_dev_addr *addr, void *context) | |
706 | { | |
707 | memcpy(((struct resolve_cb_context *)context)->addr, addr, sizeof(struct | |
708 | rdma_dev_addr)); | |
709 | complete(&((struct resolve_cb_context *)context)->comp); | |
710 | } | |
711 | ||
f7f4b23e MB |
712 | int rdma_addr_find_l2_eth_by_grh(const union ib_gid *sgid, |
713 | const union ib_gid *dgid, | |
c3efe750 MB |
714 | u8 *dmac, u16 *vlan_id, int *if_index, |
715 | int *hoplimit) | |
dd5f03be MB |
716 | { |
717 | int ret = 0; | |
718 | struct rdma_dev_addr dev_addr; | |
719 | struct resolve_cb_context ctx; | |
720 | struct net_device *dev; | |
721 | ||
722 | union { | |
723 | struct sockaddr _sockaddr; | |
724 | struct sockaddr_in _sockaddr_in; | |
725 | struct sockaddr_in6 _sockaddr_in6; | |
726 | } sgid_addr, dgid_addr; | |
727 | ||
728 | ||
471e7058 HL |
729 | rdma_gid2ip(&sgid_addr._sockaddr, sgid); |
730 | rdma_gid2ip(&dgid_addr._sockaddr, dgid); | |
dd5f03be MB |
731 | |
732 | memset(&dev_addr, 0, sizeof(dev_addr)); | |
20029832 MB |
733 | if (if_index) |
734 | dev_addr.bound_dev_if = *if_index; | |
565edd1d | 735 | dev_addr.net = &init_net; |
dd5f03be MB |
736 | |
737 | ctx.addr = &dev_addr; | |
738 | init_completion(&ctx.comp); | |
739 | ret = rdma_resolve_ip(&self, &sgid_addr._sockaddr, &dgid_addr._sockaddr, | |
740 | &dev_addr, 1000, resolve_cb, &ctx); | |
741 | if (ret) | |
742 | return ret; | |
743 | ||
744 | wait_for_completion(&ctx.comp); | |
745 | ||
746 | memcpy(dmac, dev_addr.dst_dev_addr, ETH_ALEN); | |
747 | dev = dev_get_by_index(&init_net, dev_addr.bound_dev_if); | |
748 | if (!dev) | |
749 | return -ENODEV; | |
20029832 MB |
750 | if (if_index) |
751 | *if_index = dev_addr.bound_dev_if; | |
dd5f03be MB |
752 | if (vlan_id) |
753 | *vlan_id = rdma_vlan_dev_vlan_id(dev); | |
c3efe750 MB |
754 | if (hoplimit) |
755 | *hoplimit = dev_addr.hoplimit; | |
dd5f03be MB |
756 | dev_put(dev); |
757 | return ret; | |
758 | } | |
f7f4b23e | 759 | EXPORT_SYMBOL(rdma_addr_find_l2_eth_by_grh); |
dd5f03be MB |
760 | |
761 | int rdma_addr_find_smac_by_sgid(union ib_gid *sgid, u8 *smac, u16 *vlan_id) | |
762 | { | |
763 | int ret = 0; | |
764 | struct rdma_dev_addr dev_addr; | |
765 | union { | |
766 | struct sockaddr _sockaddr; | |
767 | struct sockaddr_in _sockaddr_in; | |
768 | struct sockaddr_in6 _sockaddr_in6; | |
769 | } gid_addr; | |
770 | ||
471e7058 | 771 | rdma_gid2ip(&gid_addr._sockaddr, sgid); |
dd5f03be | 772 | |
dd5f03be | 773 | memset(&dev_addr, 0, sizeof(dev_addr)); |
565edd1d | 774 | dev_addr.net = &init_net; |
dd5f03be MB |
775 | ret = rdma_translate_ip(&gid_addr._sockaddr, &dev_addr, vlan_id); |
776 | if (ret) | |
777 | return ret; | |
778 | ||
779 | memcpy(smac, dev_addr.src_dev_addr, ETH_ALEN); | |
780 | return ret; | |
781 | } | |
782 | EXPORT_SYMBOL(rdma_addr_find_smac_by_sgid); | |
783 | ||
3cd96564 | 784 | static int netevent_callback(struct notifier_block *self, unsigned long event, |
e795d092 | 785 | void *ctx) |
7025fcd3 | 786 | { |
3cd96564 | 787 | if (event == NETEVENT_NEIGH_UPDATE) { |
e795d092 | 788 | struct neighbour *neigh = ctx; |
7025fcd3 | 789 | |
1f126670 | 790 | if (neigh->nud_state & NUD_VALID) { |
e795d092 TT |
791 | set_timeout(jiffies); |
792 | } | |
793 | } | |
7025fcd3 SH |
794 | return 0; |
795 | } | |
796 | ||
e795d092 TT |
797 | static struct notifier_block nb = { |
798 | .notifier_call = netevent_callback | |
7025fcd3 SH |
799 | }; |
800 | ||
e3f20f02 | 801 | int addr_init(void) |
7025fcd3 | 802 | { |
c7f743a6 | 803 | addr_wq = create_singlethread_workqueue("ib_addr"); |
7025fcd3 SH |
804 | if (!addr_wq) |
805 | return -ENOMEM; | |
806 | ||
e795d092 | 807 | register_netevent_notifier(&nb); |
dd5f03be | 808 | rdma_addr_register_client(&self); |
ae43f828 | 809 | |
7025fcd3 SH |
810 | return 0; |
811 | } | |
812 | ||
e3f20f02 | 813 | void addr_cleanup(void) |
7025fcd3 | 814 | { |
dd5f03be | 815 | rdma_addr_unregister_client(&self); |
e795d092 | 816 | unregister_netevent_notifier(&nb); |
7025fcd3 SH |
817 | destroy_workqueue(addr_wq); |
818 | } |