diff options
author | Sean Hefty <sean.hefty@intel.com> | 2009-11-19 19:46:25 -0500 |
---|---|---|
committer | Roland Dreier <rolandd@cisco.com> | 2009-11-19 19:46:25 -0500 |
commit | d14714df61681cfecf945a58436edf197327e87f (patch) | |
tree | 1dc232e98f17c531d75b0646ea519264e49a63b1 /drivers | |
parent | 923c100ef019bf15fb89b6fa3d3ad0485d25d59b (diff) |
IB/addr: Fix IPv6 routing lookup
Include link scope as part of address resolution. Combine local
and remote address resolution into a single, simpler code path.
Fix error checking in the IPv6 routing lookups.
Based on work from:
David Wilder <dwilder@us.ibm.com>
Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
Signed-off-by: Sean Hefty <sean.hefty@intel.com>
[ Fix up cma_check_linklocal() for !IPV6 case. - Roland ]
Signed-off-by: Roland Dreier <rolandd@cisco.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/infiniband/core/addr.c | 144 | ||||
-rw-r--r-- | drivers/infiniband/core/cma.c | 47 |
2 files changed, 74 insertions, 117 deletions
diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c index 38a7184ea745..abbb06996f9e 100644 --- a/drivers/infiniband/core/addr.c +++ b/drivers/infiniband/core/addr.c | |||
@@ -176,34 +176,6 @@ static void queue_req(struct addr_req *req) | |||
176 | mutex_unlock(&lock); | 176 | mutex_unlock(&lock); |
177 | } | 177 | } |
178 | 178 | ||
179 | static void addr_send_arp(struct sockaddr *dst_in) | ||
180 | { | ||
181 | struct rtable *rt; | ||
182 | struct flowi fl; | ||
183 | |||
184 | memset(&fl, 0, sizeof fl); | ||
185 | |||
186 | switch (dst_in->sa_family) { | ||
187 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | ||
188 | case AF_INET6: | ||
189 | { | ||
190 | struct dst_entry *dst; | ||
191 | |||
192 | fl.nl_u.ip6_u.daddr = | ||
193 | ((struct sockaddr_in6 *) dst_in)->sin6_addr; | ||
194 | |||
195 | dst = ip6_route_output(&init_net, NULL, &fl); | ||
196 | if (!dst) | ||
197 | return; | ||
198 | |||
199 | neigh_event_send(dst->neighbour, NULL); | ||
200 | dst_release(dst); | ||
201 | break; | ||
202 | } | ||
203 | #endif | ||
204 | } | ||
205 | } | ||
206 | |||
207 | static int addr4_resolve(struct sockaddr_in *src_in, | 179 | static int addr4_resolve(struct sockaddr_in *src_in, |
208 | struct sockaddr_in *dst_in, | 180 | struct sockaddr_in *dst_in, |
209 | struct rdma_dev_addr *addr) | 181 | struct rdma_dev_addr *addr) |
@@ -259,39 +231,63 @@ out: | |||
259 | } | 231 | } |
260 | 232 | ||
261 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | 233 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
262 | static int addr6_resolve_remote(struct sockaddr_in6 *src_in, | 234 | static int addr6_resolve(struct sockaddr_in6 *src_in, |
263 | struct sockaddr_in6 *dst_in, | 235 | struct sockaddr_in6 *dst_in, |
264 | struct rdma_dev_addr *addr) | 236 | struct rdma_dev_addr *addr) |
265 | { | 237 | { |
266 | struct flowi fl; | 238 | struct flowi fl; |
267 | struct neighbour *neigh; | 239 | struct neighbour *neigh; |
268 | struct dst_entry *dst; | 240 | struct dst_entry *dst; |
269 | int ret = -ENODATA; | 241 | int ret; |
270 | 242 | ||
271 | memset(&fl, 0, sizeof fl); | 243 | memset(&fl, 0, sizeof fl); |
272 | fl.nl_u.ip6_u.daddr = dst_in->sin6_addr; | 244 | ipv6_addr_copy(&fl.fl6_dst, &dst_in->sin6_addr); |
273 | fl.nl_u.ip6_u.saddr = src_in->sin6_addr; | 245 | ipv6_addr_copy(&fl.fl6_src, &src_in->sin6_addr); |
274 | fl.oif = addr->bound_dev_if; | 246 | fl.oif = addr->bound_dev_if; |
275 | 247 | ||
276 | dst = ip6_route_output(&init_net, NULL, &fl); | 248 | dst = ip6_route_output(&init_net, NULL, &fl); |
277 | if (!dst) | 249 | if ((ret = dst->error)) |
278 | return ret; | 250 | goto put; |
251 | |||
252 | if (ipv6_addr_any(&fl.fl6_src)) { | ||
253 | ret = ipv6_dev_get_saddr(&init_net, ip6_dst_idev(dst)->dev, | ||
254 | &fl.fl6_dst, 0, &fl.fl6_src); | ||
255 | if (ret) | ||
256 | goto put; | ||
279 | 257 | ||
258 | src_in->sin6_family = AF_INET6; | ||
259 | ipv6_addr_copy(&src_in->sin6_addr, &fl.fl6_src); | ||
260 | } | ||
261 | |||
262 | if (dst->dev->flags & IFF_LOOPBACK) { | ||
263 | ret = rdma_translate_ip((struct sockaddr *) dst_in, addr); | ||
264 | if (!ret) | ||
265 | memcpy(addr->dst_dev_addr, addr->src_dev_addr, MAX_ADDR_LEN); | ||
266 | goto put; | ||
267 | } | ||
268 | |||
269 | /* If the device does ARP internally, return 'done' */ | ||
280 | if (dst->dev->flags & IFF_NOARP) { | 270 | if (dst->dev->flags & IFF_NOARP) { |
281 | ret = rdma_copy_addr(addr, dst->dev, NULL); | 271 | ret = rdma_copy_addr(addr, dst->dev, NULL); |
282 | } else { | 272 | goto put; |
283 | neigh = dst->neighbour; | ||
284 | if (neigh && (neigh->nud_state & NUD_VALID)) | ||
285 | ret = rdma_copy_addr(addr, neigh->dev, neigh->ha); | ||
286 | } | 273 | } |
287 | 274 | ||
275 | neigh = dst->neighbour; | ||
276 | if (!neigh || !(neigh->nud_state & NUD_VALID)) { | ||
277 | neigh_event_send(dst->neighbour, NULL); | ||
278 | ret = -ENODATA; | ||
279 | goto put; | ||
280 | } | ||
281 | |||
282 | ret = rdma_copy_addr(addr, dst->dev, neigh->ha); | ||
283 | put: | ||
288 | dst_release(dst); | 284 | dst_release(dst); |
289 | return ret; | 285 | return ret; |
290 | } | 286 | } |
291 | #else | 287 | #else |
292 | static int addr6_resolve_remote(struct sockaddr_in6 *src_in, | 288 | static int addr6_resolve(struct sockaddr_in6 *src_in, |
293 | struct sockaddr_in6 *dst_in, | 289 | struct sockaddr_in6 *dst_in, |
294 | struct rdma_dev_addr *addr) | 290 | struct rdma_dev_addr *addr) |
295 | { | 291 | { |
296 | return -EADDRNOTAVAIL; | 292 | return -EADDRNOTAVAIL; |
297 | } | 293 | } |
@@ -305,7 +301,7 @@ static int addr_resolve(struct sockaddr *src_in, | |||
305 | return addr4_resolve((struct sockaddr_in *) src_in, | 301 | return addr4_resolve((struct sockaddr_in *) src_in, |
306 | (struct sockaddr_in *) dst_in, addr); | 302 | (struct sockaddr_in *) dst_in, addr); |
307 | } else | 303 | } else |
308 | return addr6_resolve_remote((struct sockaddr_in6 *) src_in, | 304 | return addr6_resolve((struct sockaddr_in6 *) src_in, |
309 | (struct sockaddr_in6 *) dst_in, addr); | 305 | (struct sockaddr_in6 *) dst_in, addr); |
310 | } | 306 | } |
311 | 307 | ||
@@ -346,60 +342,6 @@ static void process_req(struct work_struct *work) | |||
346 | } | 342 | } |
347 | } | 343 | } |
348 | 344 | ||
349 | static int addr_resolve_local(struct sockaddr *src_in, | ||
350 | struct sockaddr *dst_in, | ||
351 | struct rdma_dev_addr *addr) | ||
352 | { | ||
353 | struct net_device *dev; | ||
354 | int ret; | ||
355 | |||
356 | switch (dst_in->sa_family) { | ||
357 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | ||
358 | case AF_INET6: | ||
359 | { | ||
360 | struct in6_addr *a; | ||
361 | |||
362 | read_lock(&dev_base_lock); | ||
363 | for_each_netdev(&init_net, dev) | ||
364 | if (ipv6_chk_addr(&init_net, | ||
365 | &((struct sockaddr_in6 *) dst_in)->sin6_addr, | ||
366 | dev, 1)) | ||
367 | break; | ||
368 | |||
369 | if (!dev) { | ||
370 | read_unlock(&dev_base_lock); | ||
371 | return -EADDRNOTAVAIL; | ||
372 | } | ||
373 | |||
374 | a = &((struct sockaddr_in6 *) src_in)->sin6_addr; | ||
375 | |||
376 | if (ipv6_addr_any(a)) { | ||
377 | src_in->sa_family = dst_in->sa_family; | ||
378 | ((struct sockaddr_in6 *) src_in)->sin6_addr = | ||
379 | ((struct sockaddr_in6 *) dst_in)->sin6_addr; | ||
380 | ret = rdma_copy_addr(addr, dev, dev->dev_addr); | ||
381 | } else if (ipv6_addr_loopback(a)) { | ||
382 | ret = rdma_translate_ip(dst_in, addr); | ||
383 | if (!ret) | ||
384 | memcpy(addr->dst_dev_addr, dev->dev_addr, MAX_ADDR_LEN); | ||
385 | } else { | ||
386 | ret = rdma_translate_ip(src_in, addr); | ||
387 | if (!ret) | ||
388 | memcpy(addr->dst_dev_addr, dev->dev_addr, MAX_ADDR_LEN); | ||
389 | } | ||
390 | read_unlock(&dev_base_lock); | ||
391 | break; | ||
392 | } | ||
393 | #endif | ||
394 | |||
395 | default: | ||
396 | ret = -EADDRNOTAVAIL; | ||
397 | break; | ||
398 | } | ||
399 | |||
400 | return ret; | ||
401 | } | ||
402 | |||
403 | int rdma_resolve_ip(struct rdma_addr_client *client, | 345 | int rdma_resolve_ip(struct rdma_addr_client *client, |
404 | struct sockaddr *src_addr, struct sockaddr *dst_addr, | 346 | struct sockaddr *src_addr, struct sockaddr *dst_addr, |
405 | struct rdma_dev_addr *addr, int timeout_ms, | 347 | struct rdma_dev_addr *addr, int timeout_ms, |
@@ -436,10 +378,7 @@ int rdma_resolve_ip(struct rdma_addr_client *client, | |||
436 | req->client = client; | 378 | req->client = client; |
437 | atomic_inc(&client->refcount); | 379 | atomic_inc(&client->refcount); |
438 | 380 | ||
439 | req->status = addr_resolve_local(src_in, dst_in, addr); | 381 | req->status = addr_resolve(src_in, dst_in, addr); |
440 | if (req->status == -EADDRNOTAVAIL) | ||
441 | req->status = addr_resolve(src_in, dst_in, addr); | ||
442 | |||
443 | switch (req->status) { | 382 | switch (req->status) { |
444 | case 0: | 383 | case 0: |
445 | req->timeout = jiffies; | 384 | req->timeout = jiffies; |
@@ -448,7 +387,6 @@ int rdma_resolve_ip(struct rdma_addr_client *client, | |||
448 | case -ENODATA: | 387 | case -ENODATA: |
449 | req->timeout = msecs_to_jiffies(timeout_ms) + jiffies; | 388 | req->timeout = msecs_to_jiffies(timeout_ms) + jiffies; |
450 | queue_req(req); | 389 | queue_req(req); |
451 | addr_send_arp(dst_in); | ||
452 | break; | 390 | break; |
453 | default: | 391 | default: |
454 | ret = req->status; | 392 | ret = req->status; |
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 38867a46d39e..fbdd73106000 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c | |||
@@ -1472,15 +1472,6 @@ static void cma_listen_on_all(struct rdma_id_private *id_priv) | |||
1472 | mutex_unlock(&lock); | 1472 | mutex_unlock(&lock); |
1473 | } | 1473 | } |
1474 | 1474 | ||
1475 | static int cma_bind_any(struct rdma_cm_id *id, sa_family_t af) | ||
1476 | { | ||
1477 | struct sockaddr_storage addr_in; | ||
1478 | |||
1479 | memset(&addr_in, 0, sizeof addr_in); | ||
1480 | addr_in.ss_family = af; | ||
1481 | return rdma_bind_addr(id, (struct sockaddr *) &addr_in); | ||
1482 | } | ||
1483 | |||
1484 | int rdma_listen(struct rdma_cm_id *id, int backlog) | 1475 | int rdma_listen(struct rdma_cm_id *id, int backlog) |
1485 | { | 1476 | { |
1486 | struct rdma_id_private *id_priv; | 1477 | struct rdma_id_private *id_priv; |
@@ -1488,7 +1479,8 @@ int rdma_listen(struct rdma_cm_id *id, int backlog) | |||
1488 | 1479 | ||
1489 | id_priv = container_of(id, struct rdma_id_private, id); | 1480 | id_priv = container_of(id, struct rdma_id_private, id); |
1490 | if (id_priv->state == CMA_IDLE) { | 1481 | if (id_priv->state == CMA_IDLE) { |
1491 | ret = cma_bind_any(id, AF_INET); | 1482 | ((struct sockaddr *) &id->route.addr.src_addr)->sa_family = AF_INET; |
1483 | ret = rdma_bind_addr(id, (struct sockaddr *) &id->route.addr.src_addr); | ||
1492 | if (ret) | 1484 | if (ret) |
1493 | return ret; | 1485 | return ret; |
1494 | } | 1486 | } |
@@ -1885,10 +1877,14 @@ err: | |||
1885 | static int cma_bind_addr(struct rdma_cm_id *id, struct sockaddr *src_addr, | 1877 | static int cma_bind_addr(struct rdma_cm_id *id, struct sockaddr *src_addr, |
1886 | struct sockaddr *dst_addr) | 1878 | struct sockaddr *dst_addr) |
1887 | { | 1879 | { |
1888 | if (src_addr && src_addr->sa_family) | 1880 | if (!src_addr || !src_addr->sa_family) { |
1889 | return rdma_bind_addr(id, src_addr); | 1881 | src_addr = (struct sockaddr *) &id->route.addr.src_addr; |
1890 | else | 1882 | if ((src_addr->sa_family = dst_addr->sa_family) == AF_INET6) { |
1891 | return cma_bind_any(id, dst_addr->sa_family); | 1883 | ((struct sockaddr_in6 *) src_addr)->sin6_scope_id = |
1884 | ((struct sockaddr_in6 *) dst_addr)->sin6_scope_id; | ||
1885 | } | ||
1886 | } | ||
1887 | return rdma_bind_addr(id, src_addr); | ||
1892 | } | 1888 | } |
1893 | 1889 | ||
1894 | int rdma_resolve_addr(struct rdma_cm_id *id, struct sockaddr *src_addr, | 1890 | int rdma_resolve_addr(struct rdma_cm_id *id, struct sockaddr *src_addr, |
@@ -2084,6 +2080,25 @@ static int cma_get_port(struct rdma_id_private *id_priv) | |||
2084 | return ret; | 2080 | return ret; |
2085 | } | 2081 | } |
2086 | 2082 | ||
2083 | static int cma_check_linklocal(struct rdma_dev_addr *dev_addr, | ||
2084 | struct sockaddr *addr) | ||
2085 | { | ||
2086 | #if defined(CONFIG_IPv6) || defined(CONFIG_IPV6_MODULE) | ||
2087 | struct sockaddr_in6 *sin6; | ||
2088 | |||
2089 | if (addr->sa_family != AF_INET6) | ||
2090 | return 0; | ||
2091 | |||
2092 | sin6 = (struct sockaddr_in6 *) addr; | ||
2093 | if ((ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) && | ||
2094 | !sin6->sin6_scope_id) | ||
2095 | return -EINVAL; | ||
2096 | |||
2097 | dev_addr->bound_dev_if = sin6->sin6_scope_id; | ||
2098 | #endif | ||
2099 | return 0; | ||
2100 | } | ||
2101 | |||
2087 | int rdma_bind_addr(struct rdma_cm_id *id, struct sockaddr *addr) | 2102 | int rdma_bind_addr(struct rdma_cm_id *id, struct sockaddr *addr) |
2088 | { | 2103 | { |
2089 | struct rdma_id_private *id_priv; | 2104 | struct rdma_id_private *id_priv; |
@@ -2096,6 +2111,10 @@ int rdma_bind_addr(struct rdma_cm_id *id, struct sockaddr *addr) | |||
2096 | if (!cma_comp_exch(id_priv, CMA_IDLE, CMA_ADDR_BOUND)) | 2111 | if (!cma_comp_exch(id_priv, CMA_IDLE, CMA_ADDR_BOUND)) |
2097 | return -EINVAL; | 2112 | return -EINVAL; |
2098 | 2113 | ||
2114 | ret = cma_check_linklocal(&id->route.addr.dev_addr, addr); | ||
2115 | if (ret) | ||
2116 | goto err1; | ||
2117 | |||
2099 | if (cma_loopback_addr(addr)) { | 2118 | if (cma_loopback_addr(addr)) { |
2100 | ret = cma_bind_loopback(id_priv); | 2119 | ret = cma_bind_loopback(id_priv); |
2101 | } else if (!cma_zero_addr(addr)) { | 2120 | } else if (!cma_zero_addr(addr)) { |