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 | |
| 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>
| -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)) { |
