Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d14714df authored by Sean Hefty's avatar Sean Hefty Committed by Roland Dreier
Browse files

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: default avatarSean Hefty <sean.hefty@intel.com>

[ Fix up cma_check_linklocal() for !IPV6 case.  - Roland ]

Signed-off-by: default avatarRoland Dreier <rolandd@cisco.com>
parent 923c100e
Loading
Loading
Loading
Loading
+41 −103
Original line number Diff line number Diff line
@@ -176,34 +176,6 @@ static void queue_req(struct addr_req *req)
	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,
			 struct sockaddr_in *dst_in,
			 struct rdma_dev_addr *addr)
@@ -259,37 +231,61 @@ static int addr4_resolve(struct sockaddr_in *src_in,
}

#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 rdma_dev_addr *addr)
{
	struct flowi fl;
	struct neighbour *neigh;
	struct dst_entry *dst;
	int ret = -ENODATA;
	int ret;

	memset(&fl, 0, sizeof fl);
	fl.nl_u.ip6_u.daddr = dst_in->sin6_addr;
	fl.nl_u.ip6_u.saddr = src_in->sin6_addr;
	ipv6_addr_copy(&fl.fl6_dst, &dst_in->sin6_addr);
	ipv6_addr_copy(&fl.fl6_src, &src_in->sin6_addr);
	fl.oif = addr->bound_dev_if;

	dst = ip6_route_output(&init_net, NULL, &fl);
	if (!dst)
		return ret;
	if ((ret = dst->error))
		goto put;

	if (ipv6_addr_any(&fl.fl6_src)) {
		ret = ipv6_dev_get_saddr(&init_net, ip6_dst_idev(dst)->dev,
					 &fl.fl6_dst, 0, &fl.fl6_src);
		if (ret)
			goto put;

		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);
	} else {
		goto put;
	}

	neigh = dst->neighbour;
		if (neigh && (neigh->nud_state & NUD_VALID))
			ret = rdma_copy_addr(addr, neigh->dev, neigh->ha);
	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);
	return ret;
}
#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 rdma_dev_addr *addr)
{
@@ -305,7 +301,7 @@ static int addr_resolve(struct sockaddr *src_in,
		return addr4_resolve((struct sockaddr_in *) src_in,
			(struct sockaddr_in *) dst_in, addr);
	} else
		return addr6_resolve_remote((struct sockaddr_in6 *) src_in,
		return addr6_resolve((struct sockaddr_in6 *) src_in,
			(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,
		    struct sockaddr *src_addr, struct sockaddr *dst_addr,
		    struct rdma_dev_addr *addr, int timeout_ms,
@@ -436,10 +378,7 @@ int rdma_resolve_ip(struct rdma_addr_client *client,
	req->client = client;
	atomic_inc(&client->refcount);

	req->status = addr_resolve_local(src_in, dst_in, addr);
	if (req->status == -EADDRNOTAVAIL)
	req->status = addr_resolve(src_in, dst_in, addr);

	switch (req->status) {
	case 0:
		req->timeout = jiffies;
@@ -448,7 +387,6 @@ int rdma_resolve_ip(struct rdma_addr_client *client,
	case -ENODATA:
		req->timeout = msecs_to_jiffies(timeout_ms) + jiffies;
		queue_req(req);
		addr_send_arp(dst_in);
		break;
	default:
		ret = req->status;
+33 −14
Original line number Diff line number Diff line
@@ -1472,15 +1472,6 @@ static void cma_listen_on_all(struct rdma_id_private *id_priv)
	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)
{
	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);
	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)
			return ret;
	}
@@ -1885,10 +1877,14 @@ static int cma_resolve_loopback(struct rdma_id_private *id_priv)
static int cma_bind_addr(struct rdma_cm_id *id, struct sockaddr *src_addr,
			 struct sockaddr *dst_addr)
{
	if (src_addr && src_addr->sa_family)
	if (!src_addr || !src_addr->sa_family) {
		src_addr = (struct sockaddr *) &id->route.addr.src_addr;
		if ((src_addr->sa_family = dst_addr->sa_family) == AF_INET6) {
			((struct sockaddr_in6 *) src_addr)->sin6_scope_id =
				((struct sockaddr_in6 *) dst_addr)->sin6_scope_id;
		}
	}
	return rdma_bind_addr(id, src_addr);
	else
		return cma_bind_any(id, dst_addr->sa_family);
}

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;
}

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)
{
	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))
		return -EINVAL;

	ret = cma_check_linklocal(&id->route.addr.dev_addr, addr);
	if (ret)
		goto err1;

	if (cma_loopback_addr(addr)) {
		ret = cma_bind_loopback(id_priv);
	} else if (!cma_zero_addr(addr)) {