RDMA/core: Protect against changing dst->dev during destination resolve
During resolving address process, during route lookup and while performing src address translation in case of loopback mode, hold the rcu lock so that if netdevice is moving to different net namespace, or being unregistered, it can be synchronized with net/core/dev.c, ie change_net_namespace() ->dev_close_many() ->rt6_uncached_list_flush_dev() who would change dst->dev to loopback device of the given net namespace. Therefore, hold the rcu lock and sync with synchronize_net() of change_net_namespace() to ensure that netdevice cannot get freed while dst->dev is being used. Signed-off-by: Parav Pandit <parav@mellanox.com> Reviewed-by: Daniel Jurgens <danielj@mellanox.com> Signed-off-by: Leon Romanovsky <leonro@mellanox.com> Signed-off-by: Jason Gunthorpe <jgg@mellanox.com>
This commit is contained in:
parent
307edde8ef
commit
c31d4b2ddf
|
@ -450,23 +450,26 @@ static int addr6_resolve(struct sockaddr *src_sock,
|
||||||
static int addr_resolve_neigh(const struct dst_entry *dst,
|
static int addr_resolve_neigh(const struct dst_entry *dst,
|
||||||
const struct sockaddr *dst_in,
|
const struct sockaddr *dst_in,
|
||||||
struct rdma_dev_addr *addr,
|
struct rdma_dev_addr *addr,
|
||||||
|
unsigned int ndev_flags,
|
||||||
u32 seq)
|
u32 seq)
|
||||||
{
|
{
|
||||||
if (dst->dev->flags & IFF_LOOPBACK) {
|
int ret = 0;
|
||||||
|
|
||||||
|
if (ndev_flags & IFF_LOOPBACK) {
|
||||||
memcpy(addr->dst_dev_addr, addr->src_dev_addr, MAX_ADDR_LEN);
|
memcpy(addr->dst_dev_addr, addr->src_dev_addr, MAX_ADDR_LEN);
|
||||||
return 0;
|
} else {
|
||||||
}
|
if (!(ndev_flags & IFF_NOARP)) {
|
||||||
|
|
||||||
/* If the device doesn't do ARP internally */
|
/* If the device doesn't do ARP internally */
|
||||||
if (!(dst->dev->flags & IFF_NOARP))
|
ret = fetch_ha(dst, addr, dst_in, seq);
|
||||||
return fetch_ha(dst, addr, dst_in, seq);
|
}
|
||||||
|
}
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rdma_set_src_addr(const struct dst_entry *dst,
|
static void copy_src_l2_addr(struct rdma_dev_addr *dev_addr,
|
||||||
const struct sockaddr *dst_in,
|
const struct sockaddr *dst_in,
|
||||||
struct rdma_dev_addr *dev_addr)
|
const struct dst_entry *dst,
|
||||||
|
const struct net_device *ndev)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
|
@ -481,14 +484,37 @@ static int rdma_set_src_addr(const struct dst_entry *dst,
|
||||||
* network type accordingly.
|
* network type accordingly.
|
||||||
*/
|
*/
|
||||||
if (has_gateway(dst, dst_in->sa_family) &&
|
if (has_gateway(dst, dst_in->sa_family) &&
|
||||||
dst->dev->type != ARPHRD_INFINIBAND)
|
ndev->type != ARPHRD_INFINIBAND)
|
||||||
dev_addr->network = dst_in->sa_family == AF_INET ?
|
dev_addr->network = dst_in->sa_family == AF_INET ?
|
||||||
RDMA_NETWORK_IPV4 :
|
RDMA_NETWORK_IPV4 :
|
||||||
RDMA_NETWORK_IPV6;
|
RDMA_NETWORK_IPV6;
|
||||||
else
|
else
|
||||||
dev_addr->network = RDMA_NETWORK_IB;
|
dev_addr->network = RDMA_NETWORK_IB;
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
static int rdma_set_src_addr_rcu(struct rdma_dev_addr *dev_addr,
|
||||||
|
unsigned int *ndev_flags,
|
||||||
|
const struct sockaddr *dst_in,
|
||||||
|
const struct dst_entry *dst)
|
||||||
|
{
|
||||||
|
struct net_device *ndev = READ_ONCE(dst->dev);
|
||||||
|
|
||||||
|
*ndev_flags = ndev->flags;
|
||||||
|
/* A physical device must be the RDMA device to use */
|
||||||
|
if (ndev->flags & IFF_LOOPBACK) {
|
||||||
|
/*
|
||||||
|
* RDMA (IB/RoCE, iWarp) doesn't run on lo interface or
|
||||||
|
* loopback IP address. So if route is resolved to loopback
|
||||||
|
* interface, translate that to a real ndev based on non
|
||||||
|
* loopback IP address.
|
||||||
|
*/
|
||||||
|
ndev = rdma_find_ndev_for_src_ip_rcu(dev_net(ndev), dst_in);
|
||||||
|
if (!ndev)
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
copy_src_l2_addr(dev_addr, dst_in, dst, ndev);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int addr_resolve(struct sockaddr *src_in,
|
static int addr_resolve(struct sockaddr *src_in,
|
||||||
|
@ -498,6 +524,7 @@ static int addr_resolve(struct sockaddr *src_in,
|
||||||
u32 seq)
|
u32 seq)
|
||||||
{
|
{
|
||||||
struct dst_entry *dst = NULL;
|
struct dst_entry *dst = NULL;
|
||||||
|
unsigned int ndev_flags = 0;
|
||||||
struct rtable *rt = NULL;
|
struct rtable *rt = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -506,22 +533,26 @@ static int addr_resolve(struct sockaddr *src_in,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
if (src_in->sa_family == AF_INET) {
|
if (src_in->sa_family == AF_INET) {
|
||||||
ret = addr4_resolve(src_in, dst_in, addr, &rt);
|
ret = addr4_resolve(src_in, dst_in, addr, &rt);
|
||||||
dst = &rt->dst;
|
dst = &rt->dst;
|
||||||
} else {
|
} else {
|
||||||
ret = addr6_resolve(src_in, dst_in, addr, &dst);
|
ret = addr6_resolve(src_in, dst_in, addr, &dst);
|
||||||
}
|
}
|
||||||
if (ret)
|
if (ret) {
|
||||||
|
rcu_read_unlock();
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
|
ret = rdma_set_src_addr_rcu(addr, &ndev_flags, dst_in, dst);
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
ret = rdma_set_src_addr(dst, dst_in, addr);
|
|
||||||
/*
|
/*
|
||||||
* Resolve neighbor destination address if requested and
|
* Resolve neighbor destination address if requested and
|
||||||
* only if src addr translation didn't fail.
|
* only if src addr translation didn't fail.
|
||||||
*/
|
*/
|
||||||
if (!ret && resolve_neigh)
|
if (!ret && resolve_neigh)
|
||||||
ret = addr_resolve_neigh(dst, dst_in, addr, seq);
|
ret = addr_resolve_neigh(dst, dst_in, addr, ndev_flags, seq);
|
||||||
|
|
||||||
if (src_in->sa_family == AF_INET)
|
if (src_in->sa_family == AF_INET)
|
||||||
ip_rt_put(rt);
|
ip_rt_put(rt);
|
||||||
|
|
Loading…
Reference in New Issue