Skip to content

Commit c31d4b2

Browse files
paravmellanoxjgunthorpe
authored andcommitted
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>
1 parent 307edde commit c31d4b2

File tree

1 file changed

+46
-15
lines changed

1 file changed

+46
-15
lines changed

drivers/infiniband/core/addr.c

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -450,23 +450,26 @@ static int addr6_resolve(struct sockaddr *src_sock,
450450
static int addr_resolve_neigh(const struct dst_entry *dst,
451451
const struct sockaddr *dst_in,
452452
struct rdma_dev_addr *addr,
453+
unsigned int ndev_flags,
453454
u32 seq)
454455
{
455-
if (dst->dev->flags & IFF_LOOPBACK) {
456+
int ret = 0;
457+
458+
if (ndev_flags & IFF_LOOPBACK) {
456459
memcpy(addr->dst_dev_addr, addr->src_dev_addr, MAX_ADDR_LEN);
457-
return 0;
460+
} else {
461+
if (!(ndev_flags & IFF_NOARP)) {
462+
/* If the device doesn't do ARP internally */
463+
ret = fetch_ha(dst, addr, dst_in, seq);
464+
}
458465
}
459-
460-
/* If the device doesn't do ARP internally */
461-
if (!(dst->dev->flags & IFF_NOARP))
462-
return fetch_ha(dst, addr, dst_in, seq);
463-
464-
return 0;
466+
return ret;
465467
}
466468

467-
static int rdma_set_src_addr(const struct dst_entry *dst,
469+
static void copy_src_l2_addr(struct rdma_dev_addr *dev_addr,
468470
const struct sockaddr *dst_in,
469-
struct rdma_dev_addr *dev_addr)
471+
const struct dst_entry *dst,
472+
const struct net_device *ndev)
470473
{
471474
int ret = 0;
472475

@@ -481,14 +484,37 @@ static int rdma_set_src_addr(const struct dst_entry *dst,
481484
* network type accordingly.
482485
*/
483486
if (has_gateway(dst, dst_in->sa_family) &&
484-
dst->dev->type != ARPHRD_INFINIBAND)
487+
ndev->type != ARPHRD_INFINIBAND)
485488
dev_addr->network = dst_in->sa_family == AF_INET ?
486489
RDMA_NETWORK_IPV4 :
487490
RDMA_NETWORK_IPV6;
488491
else
489492
dev_addr->network = RDMA_NETWORK_IB;
493+
}
490494

491-
return ret;
495+
static int rdma_set_src_addr_rcu(struct rdma_dev_addr *dev_addr,
496+
unsigned int *ndev_flags,
497+
const struct sockaddr *dst_in,
498+
const struct dst_entry *dst)
499+
{
500+
struct net_device *ndev = READ_ONCE(dst->dev);
501+
502+
*ndev_flags = ndev->flags;
503+
/* A physical device must be the RDMA device to use */
504+
if (ndev->flags & IFF_LOOPBACK) {
505+
/*
506+
* RDMA (IB/RoCE, iWarp) doesn't run on lo interface or
507+
* loopback IP address. So if route is resolved to loopback
508+
* interface, translate that to a real ndev based on non
509+
* loopback IP address.
510+
*/
511+
ndev = rdma_find_ndev_for_src_ip_rcu(dev_net(ndev), dst_in);
512+
if (!ndev)
513+
return -ENODEV;
514+
}
515+
516+
copy_src_l2_addr(dev_addr, dst_in, dst, ndev);
517+
return 0;
492518
}
493519

494520
static int addr_resolve(struct sockaddr *src_in,
@@ -498,6 +524,7 @@ static int addr_resolve(struct sockaddr *src_in,
498524
u32 seq)
499525
{
500526
struct dst_entry *dst = NULL;
527+
unsigned int ndev_flags = 0;
501528
struct rtable *rt = NULL;
502529
int ret;
503530

@@ -506,22 +533,26 @@ static int addr_resolve(struct sockaddr *src_in,
506533
return -EINVAL;
507534
}
508535

536+
rcu_read_lock();
509537
if (src_in->sa_family == AF_INET) {
510538
ret = addr4_resolve(src_in, dst_in, addr, &rt);
511539
dst = &rt->dst;
512540
} else {
513541
ret = addr6_resolve(src_in, dst_in, addr, &dst);
514542
}
515-
if (ret)
543+
if (ret) {
544+
rcu_read_unlock();
516545
return ret;
546+
}
547+
ret = rdma_set_src_addr_rcu(addr, &ndev_flags, dst_in, dst);
548+
rcu_read_unlock();
517549

518-
ret = rdma_set_src_addr(dst, dst_in, addr);
519550
/*
520551
* Resolve neighbor destination address if requested and
521552
* only if src addr translation didn't fail.
522553
*/
523554
if (!ret && resolve_neigh)
524-
ret = addr_resolve_neigh(dst, dst_in, addr, seq);
555+
ret = addr_resolve_neigh(dst, dst_in, addr, ndev_flags, seq);
525556

526557
if (src_in->sa_family == AF_INET)
527558
ip_rt_put(rt);

0 commit comments

Comments
 (0)