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>
This commit is contained in:
parent
923c100ef0
commit
d14714df61
@ -176,34 +176,6 @@ static void queue_req(struct addr_req *req)
|
|||||||
mutex_unlock(&lock);
|
mutex_unlock(&lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void addr_send_arp(struct sockaddr *dst_in)
|
|
||||||
{
|
|
||||||
struct rtable *rt;
|
|
||||||
struct flowi fl;
|
|
||||||
|
|
||||||
memset(&fl, 0, sizeof fl);
|
|
||||||
|
|
||||||
switch (dst_in->sa_family) {
|
|
||||||
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
|
||||||
case AF_INET6:
|
|
||||||
{
|
|
||||||
struct dst_entry *dst;
|
|
||||||
|
|
||||||
fl.nl_u.ip6_u.daddr =
|
|
||||||
((struct sockaddr_in6 *) dst_in)->sin6_addr;
|
|
||||||
|
|
||||||
dst = ip6_route_output(&init_net, NULL, &fl);
|
|
||||||
if (!dst)
|
|
||||||
return;
|
|
||||||
|
|
||||||
neigh_event_send(dst->neighbour, NULL);
|
|
||||||
dst_release(dst);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int addr4_resolve(struct sockaddr_in *src_in,
|
static int addr4_resolve(struct sockaddr_in *src_in,
|
||||||
struct sockaddr_in *dst_in,
|
struct sockaddr_in *dst_in,
|
||||||
struct rdma_dev_addr *addr)
|
struct rdma_dev_addr *addr)
|
||||||
@ -259,39 +231,63 @@ out:
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||||
static int addr6_resolve_remote(struct sockaddr_in6 *src_in,
|
static int addr6_resolve(struct sockaddr_in6 *src_in,
|
||||||
struct sockaddr_in6 *dst_in,
|
struct sockaddr_in6 *dst_in,
|
||||||
struct rdma_dev_addr *addr)
|
struct rdma_dev_addr *addr)
|
||||||
{
|
{
|
||||||
struct flowi fl;
|
struct flowi fl;
|
||||||
struct neighbour *neigh;
|
struct neighbour *neigh;
|
||||||
struct dst_entry *dst;
|
struct dst_entry *dst;
|
||||||
int ret = -ENODATA;
|
int ret;
|
||||||
|
|
||||||
memset(&fl, 0, sizeof fl);
|
memset(&fl, 0, sizeof fl);
|
||||||
fl.nl_u.ip6_u.daddr = dst_in->sin6_addr;
|
ipv6_addr_copy(&fl.fl6_dst, &dst_in->sin6_addr);
|
||||||
fl.nl_u.ip6_u.saddr = src_in->sin6_addr;
|
ipv6_addr_copy(&fl.fl6_src, &src_in->sin6_addr);
|
||||||
fl.oif = addr->bound_dev_if;
|
fl.oif = addr->bound_dev_if;
|
||||||
|
|
||||||
dst = ip6_route_output(&init_net, NULL, &fl);
|
dst = ip6_route_output(&init_net, NULL, &fl);
|
||||||
if (!dst)
|
if ((ret = dst->error))
|
||||||
return ret;
|
goto put;
|
||||||
|
|
||||||
if (dst->dev->flags & IFF_NOARP) {
|
if (ipv6_addr_any(&fl.fl6_src)) {
|
||||||
ret = rdma_copy_addr(addr, dst->dev, NULL);
|
ret = ipv6_dev_get_saddr(&init_net, ip6_dst_idev(dst)->dev,
|
||||||
} else {
|
&fl.fl6_dst, 0, &fl.fl6_src);
|
||||||
neigh = dst->neighbour;
|
if (ret)
|
||||||
if (neigh && (neigh->nud_state & NUD_VALID))
|
goto put;
|
||||||
ret = rdma_copy_addr(addr, neigh->dev, neigh->ha);
|
|
||||||
|
src_in->sin6_family = AF_INET6;
|
||||||
|
ipv6_addr_copy(&src_in->sin6_addr, &fl.fl6_src);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dst->dev->flags & IFF_LOOPBACK) {
|
||||||
|
ret = rdma_translate_ip((struct sockaddr *) dst_in, addr);
|
||||||
|
if (!ret)
|
||||||
|
memcpy(addr->dst_dev_addr, addr->src_dev_addr, MAX_ADDR_LEN);
|
||||||
|
goto put;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the device does ARP internally, return 'done' */
|
||||||
|
if (dst->dev->flags & IFF_NOARP) {
|
||||||
|
ret = rdma_copy_addr(addr, dst->dev, NULL);
|
||||||
|
goto put;
|
||||||
|
}
|
||||||
|
|
||||||
|
neigh = dst->neighbour;
|
||||||
|
if (!neigh || !(neigh->nud_state & NUD_VALID)) {
|
||||||
|
neigh_event_send(dst->neighbour, NULL);
|
||||||
|
ret = -ENODATA;
|
||||||
|
goto put;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = rdma_copy_addr(addr, dst->dev, neigh->ha);
|
||||||
|
put:
|
||||||
dst_release(dst);
|
dst_release(dst);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static int addr6_resolve_remote(struct sockaddr_in6 *src_in,
|
static int addr6_resolve(struct sockaddr_in6 *src_in,
|
||||||
struct sockaddr_in6 *dst_in,
|
struct sockaddr_in6 *dst_in,
|
||||||
struct rdma_dev_addr *addr)
|
struct rdma_dev_addr *addr)
|
||||||
{
|
{
|
||||||
return -EADDRNOTAVAIL;
|
return -EADDRNOTAVAIL;
|
||||||
}
|
}
|
||||||
@ -305,7 +301,7 @@ static int addr_resolve(struct sockaddr *src_in,
|
|||||||
return addr4_resolve((struct sockaddr_in *) src_in,
|
return addr4_resolve((struct sockaddr_in *) src_in,
|
||||||
(struct sockaddr_in *) dst_in, addr);
|
(struct sockaddr_in *) dst_in, addr);
|
||||||
} else
|
} else
|
||||||
return addr6_resolve_remote((struct sockaddr_in6 *) src_in,
|
return addr6_resolve((struct sockaddr_in6 *) src_in,
|
||||||
(struct sockaddr_in6 *) dst_in, addr);
|
(struct sockaddr_in6 *) dst_in, addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -346,60 +342,6 @@ static void process_req(struct work_struct *work)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int addr_resolve_local(struct sockaddr *src_in,
|
|
||||||
struct sockaddr *dst_in,
|
|
||||||
struct rdma_dev_addr *addr)
|
|
||||||
{
|
|
||||||
struct net_device *dev;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
switch (dst_in->sa_family) {
|
|
||||||
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
|
||||||
case AF_INET6:
|
|
||||||
{
|
|
||||||
struct in6_addr *a;
|
|
||||||
|
|
||||||
read_lock(&dev_base_lock);
|
|
||||||
for_each_netdev(&init_net, dev)
|
|
||||||
if (ipv6_chk_addr(&init_net,
|
|
||||||
&((struct sockaddr_in6 *) dst_in)->sin6_addr,
|
|
||||||
dev, 1))
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (!dev) {
|
|
||||||
read_unlock(&dev_base_lock);
|
|
||||||
return -EADDRNOTAVAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
a = &((struct sockaddr_in6 *) src_in)->sin6_addr;
|
|
||||||
|
|
||||||
if (ipv6_addr_any(a)) {
|
|
||||||
src_in->sa_family = dst_in->sa_family;
|
|
||||||
((struct sockaddr_in6 *) src_in)->sin6_addr =
|
|
||||||
((struct sockaddr_in6 *) dst_in)->sin6_addr;
|
|
||||||
ret = rdma_copy_addr(addr, dev, dev->dev_addr);
|
|
||||||
} else if (ipv6_addr_loopback(a)) {
|
|
||||||
ret = rdma_translate_ip(dst_in, addr);
|
|
||||||
if (!ret)
|
|
||||||
memcpy(addr->dst_dev_addr, dev->dev_addr, MAX_ADDR_LEN);
|
|
||||||
} else {
|
|
||||||
ret = rdma_translate_ip(src_in, addr);
|
|
||||||
if (!ret)
|
|
||||||
memcpy(addr->dst_dev_addr, dev->dev_addr, MAX_ADDR_LEN);
|
|
||||||
}
|
|
||||||
read_unlock(&dev_base_lock);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
default:
|
|
||||||
ret = -EADDRNOTAVAIL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int rdma_resolve_ip(struct rdma_addr_client *client,
|
int rdma_resolve_ip(struct rdma_addr_client *client,
|
||||||
struct sockaddr *src_addr, struct sockaddr *dst_addr,
|
struct sockaddr *src_addr, struct sockaddr *dst_addr,
|
||||||
struct rdma_dev_addr *addr, int timeout_ms,
|
struct rdma_dev_addr *addr, int timeout_ms,
|
||||||
@ -436,10 +378,7 @@ int rdma_resolve_ip(struct rdma_addr_client *client,
|
|||||||
req->client = client;
|
req->client = client;
|
||||||
atomic_inc(&client->refcount);
|
atomic_inc(&client->refcount);
|
||||||
|
|
||||||
req->status = addr_resolve_local(src_in, dst_in, addr);
|
req->status = addr_resolve(src_in, dst_in, addr);
|
||||||
if (req->status == -EADDRNOTAVAIL)
|
|
||||||
req->status = addr_resolve(src_in, dst_in, addr);
|
|
||||||
|
|
||||||
switch (req->status) {
|
switch (req->status) {
|
||||||
case 0:
|
case 0:
|
||||||
req->timeout = jiffies;
|
req->timeout = jiffies;
|
||||||
@ -448,7 +387,6 @@ int rdma_resolve_ip(struct rdma_addr_client *client,
|
|||||||
case -ENODATA:
|
case -ENODATA:
|
||||||
req->timeout = msecs_to_jiffies(timeout_ms) + jiffies;
|
req->timeout = msecs_to_jiffies(timeout_ms) + jiffies;
|
||||||
queue_req(req);
|
queue_req(req);
|
||||||
addr_send_arp(dst_in);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ret = req->status;
|
ret = req->status;
|
||||||
|
@ -1472,15 +1472,6 @@ static void cma_listen_on_all(struct rdma_id_private *id_priv)
|
|||||||
mutex_unlock(&lock);
|
mutex_unlock(&lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cma_bind_any(struct rdma_cm_id *id, sa_family_t af)
|
|
||||||
{
|
|
||||||
struct sockaddr_storage addr_in;
|
|
||||||
|
|
||||||
memset(&addr_in, 0, sizeof addr_in);
|
|
||||||
addr_in.ss_family = af;
|
|
||||||
return rdma_bind_addr(id, (struct sockaddr *) &addr_in);
|
|
||||||
}
|
|
||||||
|
|
||||||
int rdma_listen(struct rdma_cm_id *id, int backlog)
|
int rdma_listen(struct rdma_cm_id *id, int backlog)
|
||||||
{
|
{
|
||||||
struct rdma_id_private *id_priv;
|
struct rdma_id_private *id_priv;
|
||||||
@ -1488,7 +1479,8 @@ int rdma_listen(struct rdma_cm_id *id, int backlog)
|
|||||||
|
|
||||||
id_priv = container_of(id, struct rdma_id_private, id);
|
id_priv = container_of(id, struct rdma_id_private, id);
|
||||||
if (id_priv->state == CMA_IDLE) {
|
if (id_priv->state == CMA_IDLE) {
|
||||||
ret = cma_bind_any(id, AF_INET);
|
((struct sockaddr *) &id->route.addr.src_addr)->sa_family = AF_INET;
|
||||||
|
ret = rdma_bind_addr(id, (struct sockaddr *) &id->route.addr.src_addr);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -1885,10 +1877,14 @@ err:
|
|||||||
static int cma_bind_addr(struct rdma_cm_id *id, struct sockaddr *src_addr,
|
static int cma_bind_addr(struct rdma_cm_id *id, struct sockaddr *src_addr,
|
||||||
struct sockaddr *dst_addr)
|
struct sockaddr *dst_addr)
|
||||||
{
|
{
|
||||||
if (src_addr && src_addr->sa_family)
|
if (!src_addr || !src_addr->sa_family) {
|
||||||
return rdma_bind_addr(id, src_addr);
|
src_addr = (struct sockaddr *) &id->route.addr.src_addr;
|
||||||
else
|
if ((src_addr->sa_family = dst_addr->sa_family) == AF_INET6) {
|
||||||
return cma_bind_any(id, dst_addr->sa_family);
|
((struct sockaddr_in6 *) src_addr)->sin6_scope_id =
|
||||||
|
((struct sockaddr_in6 *) dst_addr)->sin6_scope_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rdma_bind_addr(id, src_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
int rdma_resolve_addr(struct rdma_cm_id *id, struct sockaddr *src_addr,
|
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)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int cma_check_linklocal(struct rdma_dev_addr *dev_addr,
|
||||||
|
struct sockaddr *addr)
|
||||||
|
{
|
||||||
|
#if defined(CONFIG_IPv6) || defined(CONFIG_IPV6_MODULE)
|
||||||
|
struct sockaddr_in6 *sin6;
|
||||||
|
|
||||||
|
if (addr->sa_family != AF_INET6)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
sin6 = (struct sockaddr_in6 *) addr;
|
||||||
|
if ((ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) &&
|
||||||
|
!sin6->sin6_scope_id)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
dev_addr->bound_dev_if = sin6->sin6_scope_id;
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int rdma_bind_addr(struct rdma_cm_id *id, struct sockaddr *addr)
|
int rdma_bind_addr(struct rdma_cm_id *id, struct sockaddr *addr)
|
||||||
{
|
{
|
||||||
struct rdma_id_private *id_priv;
|
struct rdma_id_private *id_priv;
|
||||||
@ -2096,6 +2111,10 @@ int rdma_bind_addr(struct rdma_cm_id *id, struct sockaddr *addr)
|
|||||||
if (!cma_comp_exch(id_priv, CMA_IDLE, CMA_ADDR_BOUND))
|
if (!cma_comp_exch(id_priv, CMA_IDLE, CMA_ADDR_BOUND))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
ret = cma_check_linklocal(&id->route.addr.dev_addr, addr);
|
||||||
|
if (ret)
|
||||||
|
goto err1;
|
||||||
|
|
||||||
if (cma_loopback_addr(addr)) {
|
if (cma_loopback_addr(addr)) {
|
||||||
ret = cma_bind_loopback(id_priv);
|
ret = cma_bind_loopback(id_priv);
|
||||||
} else if (!cma_zero_addr(addr)) {
|
} else if (!cma_zero_addr(addr)) {
|
||||||
|
Loading…
Reference in New Issue
Block a user