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
					
				
			
		
					 2 changed files with 76 additions and 119 deletions
				
			
		|  | @ -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,37 +231,61 @@ 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) | ||||||
| { | { | ||||||
|  | @ -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); |  | ||||||
| 	if (req->status == -EADDRNOTAVAIL) |  | ||||||
| 	req->status = addr_resolve(src_in, dst_in, addr); | 	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) { | ||||||
|  | 		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); | 	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, | 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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Sean Hefty
				Sean Hefty