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