Skip to content

Commit

Permalink
IB/core: Validate route when we init ah
Browse files Browse the repository at this point in the history
In order to make sure API users don't try to use SGIDs which don't
conform to the routing table, validate the route before searching
the RoCE GID table.

Signed-off-by: Matan Barak <matanb@mellanox.com>
Signed-off-by: Doug Ledford <dledford@redhat.com>
  • Loading branch information
matanb10 authored and dledford committed Dec 23, 2015
1 parent 6020d7e commit 2002983
Show file tree
Hide file tree
Showing 7 changed files with 270 additions and 80 deletions.
175 changes: 124 additions & 51 deletions drivers/infiniband/core/addr.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ int rdma_copy_addr(struct rdma_dev_addr *dev_addr, struct net_device *dev,
}
EXPORT_SYMBOL(rdma_copy_addr);

int rdma_translate_ip(struct sockaddr *addr, struct rdma_dev_addr *dev_addr,
int rdma_translate_ip(const struct sockaddr *addr,
struct rdma_dev_addr *dev_addr,
u16 *vlan_id)
{
struct net_device *dev;
Expand All @@ -139,7 +140,7 @@ int rdma_translate_ip(struct sockaddr *addr, struct rdma_dev_addr *dev_addr,
switch (addr->sa_family) {
case AF_INET:
dev = ip_dev_find(dev_addr->net,
((struct sockaddr_in *) addr)->sin_addr.s_addr);
((const struct sockaddr_in *)addr)->sin_addr.s_addr);

if (!dev)
return ret;
Expand All @@ -154,7 +155,7 @@ int rdma_translate_ip(struct sockaddr *addr, struct rdma_dev_addr *dev_addr,
rcu_read_lock();
for_each_netdev_rcu(dev_addr->net, dev) {
if (ipv6_chk_addr(dev_addr->net,
&((struct sockaddr_in6 *) addr)->sin6_addr,
&((const struct sockaddr_in6 *)addr)->sin6_addr,
dev, 1)) {
ret = rdma_copy_addr(dev_addr, dev, NULL);
if (vlan_id)
Expand Down Expand Up @@ -198,7 +199,8 @@ static void queue_req(struct addr_req *req)
mutex_unlock(&lock);
}

static int dst_fetch_ha(struct dst_entry *dst, struct rdma_dev_addr *dev_addr, void *daddr)
static int dst_fetch_ha(struct dst_entry *dst, struct rdma_dev_addr *dev_addr,
const void *daddr)
{
struct neighbour *n;
int ret;
Expand All @@ -222,8 +224,9 @@ static int dst_fetch_ha(struct dst_entry *dst, struct rdma_dev_addr *dev_addr, v
}

static int addr4_resolve(struct sockaddr_in *src_in,
struct sockaddr_in *dst_in,
struct rdma_dev_addr *addr)
const struct sockaddr_in *dst_in,
struct rdma_dev_addr *addr,
struct rtable **prt)
{
__be32 src_ip = src_in->sin_addr.s_addr;
__be32 dst_ip = dst_in->sin_addr.s_addr;
Expand All @@ -243,36 +246,23 @@ static int addr4_resolve(struct sockaddr_in *src_in,
src_in->sin_family = AF_INET;
src_in->sin_addr.s_addr = fl4.saddr;

if (rt->dst.dev->flags & IFF_LOOPBACK) {
ret = rdma_translate_ip((struct sockaddr *)dst_in, addr, NULL);
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 (rt->dst.dev->flags & IFF_NOARP) {
ret = rdma_copy_addr(addr, rt->dst.dev, NULL);
goto put;
}

/* If there's a gateway, we're definitely in RoCE v2 (as RoCE v1 isn't
* routable) and we could set the network type accordingly.
*/
if (rt->rt_uses_gateway)
addr->network = RDMA_NETWORK_IPV4;

ret = dst_fetch_ha(&rt->dst, addr, &fl4.daddr);
put:
ip_rt_put(rt);
*prt = rt;
return 0;
out:
return ret;
}

#if IS_ENABLED(CONFIG_IPV6)
static int addr6_resolve(struct sockaddr_in6 *src_in,
struct sockaddr_in6 *dst_in,
struct rdma_dev_addr *addr)
const struct sockaddr_in6 *dst_in,
struct rdma_dev_addr *addr,
struct dst_entry **pdst)
{
struct flowi6 fl6;
struct dst_entry *dst;
Expand All @@ -299,49 +289,109 @@ static int addr6_resolve(struct sockaddr_in6 *src_in,
src_in->sin6_addr = fl6.saddr;
}

if (dst->dev->flags & IFF_LOOPBACK) {
ret = rdma_translate_ip((struct sockaddr *)dst_in, addr, NULL);
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;
}

/* If there's a gateway, we're definitely in RoCE v2 (as RoCE v1 isn't
* routable) and we could set the network type accordingly.
*/
if (rt->rt6i_flags & RTF_GATEWAY)
addr->network = RDMA_NETWORK_IPV6;

ret = dst_fetch_ha(dst, addr, &fl6.daddr);
*pdst = dst;
return 0;
put:
dst_release(dst);
return ret;
}
#else
static int addr6_resolve(struct sockaddr_in6 *src_in,
struct sockaddr_in6 *dst_in,
struct rdma_dev_addr *addr)
const struct sockaddr_in6 *dst_in,
struct rdma_dev_addr *addr,
struct dst_entry **pdst)
{
return -EADDRNOTAVAIL;
}
#endif

static int addr_resolve_neigh(struct dst_entry *dst,
const struct sockaddr *dst_in,
struct rdma_dev_addr *addr)
{
if (dst->dev->flags & IFF_LOOPBACK) {
int ret;

ret = rdma_translate_ip(dst_in, addr, NULL);
if (!ret)
memcpy(addr->dst_dev_addr, addr->src_dev_addr,
MAX_ADDR_LEN);

return ret;
}

/* If the device doesn't do ARP internally */
if (!(dst->dev->flags & IFF_NOARP)) {
const struct sockaddr_in *dst_in4 =
(const struct sockaddr_in *)dst_in;
const struct sockaddr_in6 *dst_in6 =
(const struct sockaddr_in6 *)dst_in;

return dst_fetch_ha(dst, addr,
dst_in->sa_family == AF_INET ?
(const void *)&dst_in4->sin_addr.s_addr :
(const void *)&dst_in6->sin6_addr);
}

return rdma_copy_addr(addr, dst->dev, NULL);
}

static int addr_resolve(struct sockaddr *src_in,
struct sockaddr *dst_in,
struct rdma_dev_addr *addr)
const struct sockaddr *dst_in,
struct rdma_dev_addr *addr,
bool resolve_neigh)
{
struct net_device *ndev;
struct dst_entry *dst;
int ret;

if (src_in->sa_family == AF_INET) {
return addr4_resolve((struct sockaddr_in *) src_in,
(struct sockaddr_in *) dst_in, addr);
} else
return addr6_resolve((struct sockaddr_in6 *) src_in,
(struct sockaddr_in6 *) dst_in, addr);
struct rtable *rt = NULL;
const struct sockaddr_in *dst_in4 =
(const struct sockaddr_in *)dst_in;

ret = addr4_resolve((struct sockaddr_in *)src_in,
dst_in4, addr, &rt);
if (ret)
return ret;

if (resolve_neigh)
ret = addr_resolve_neigh(&rt->dst, dst_in, addr);

ndev = rt->dst.dev;
dev_hold(ndev);

ip_rt_put(rt);
} else {
const struct sockaddr_in6 *dst_in6 =
(const struct sockaddr_in6 *)dst_in;

ret = addr6_resolve((struct sockaddr_in6 *)src_in,
dst_in6, addr,
&dst);
if (ret)
return ret;

if (resolve_neigh)
ret = addr_resolve_neigh(dst, dst_in, addr);

ndev = dst->dev;
dev_hold(ndev);

dst_release(dst);
}

addr->bound_dev_if = ndev->ifindex;
addr->net = dev_net(ndev);
dev_put(ndev);

return ret;
}

static void process_req(struct work_struct *work)
Expand All @@ -357,7 +407,8 @@ static void process_req(struct work_struct *work)
if (req->status == -ENODATA) {
src_in = (struct sockaddr *) &req->src_addr;
dst_in = (struct sockaddr *) &req->dst_addr;
req->status = addr_resolve(src_in, dst_in, req->addr);
req->status = addr_resolve(src_in, dst_in, req->addr,
true);
if (req->status && time_after_eq(jiffies, req->timeout))
req->status = -ETIMEDOUT;
else if (req->status == -ENODATA)
Expand Down Expand Up @@ -417,7 +468,7 @@ int rdma_resolve_ip(struct rdma_addr_client *client,
req->client = client;
atomic_inc(&client->refcount);

req->status = addr_resolve(src_in, dst_in, addr);
req->status = addr_resolve(src_in, dst_in, addr, true);
switch (req->status) {
case 0:
req->timeout = jiffies;
Expand All @@ -439,6 +490,25 @@ int rdma_resolve_ip(struct rdma_addr_client *client,
}
EXPORT_SYMBOL(rdma_resolve_ip);

int rdma_resolve_ip_route(struct sockaddr *src_addr,
const struct sockaddr *dst_addr,
struct rdma_dev_addr *addr)
{
struct sockaddr_storage ssrc_addr = {};
struct sockaddr *src_in = (struct sockaddr *)&ssrc_addr;

if (src_addr->sa_family != dst_addr->sa_family)
return -EINVAL;

if (src_addr)
memcpy(src_in, src_addr, rdma_addr_size(src_addr));
else
src_in->sa_family = dst_addr->sa_family;

return addr_resolve(src_in, dst_addr, addr, false);
}
EXPORT_SYMBOL(rdma_resolve_ip_route);

void rdma_addr_cancel(struct rdma_dev_addr *addr)
{
struct addr_req *req, *temp_req;
Expand Down Expand Up @@ -471,7 +541,7 @@ static void resolve_cb(int status, struct sockaddr *src_addr,
}

int rdma_addr_find_dmac_by_grh(const union ib_gid *sgid, const union ib_gid *dgid,
u8 *dmac, u16 *vlan_id, int if_index)
u8 *dmac, u16 *vlan_id, int *if_index)
{
int ret = 0;
struct rdma_dev_addr dev_addr;
Expand All @@ -489,7 +559,8 @@ int rdma_addr_find_dmac_by_grh(const union ib_gid *sgid, const union ib_gid *dgi
rdma_gid2ip(&dgid_addr._sockaddr, dgid);

memset(&dev_addr, 0, sizeof(dev_addr));
dev_addr.bound_dev_if = if_index;
if (if_index)
dev_addr.bound_dev_if = *if_index;
dev_addr.net = &init_net;

ctx.addr = &dev_addr;
Expand All @@ -505,6 +576,8 @@ int rdma_addr_find_dmac_by_grh(const union ib_gid *sgid, const union ib_gid *dgi
dev = dev_get_by_index(&init_net, dev_addr.bound_dev_if);
if (!dev)
return -ENODEV;
if (if_index)
*if_index = dev_addr.bound_dev_if;
if (vlan_id)
*vlan_id = rdma_vlan_dev_vlan_id(dev);
dev_put(dev);
Expand Down
10 changes: 8 additions & 2 deletions drivers/infiniband/core/cm.c
Original file line number Diff line number Diff line change
Expand Up @@ -1646,8 +1646,11 @@ static int cm_req_handler(struct cm_work *work)
cm_id_priv->av.ah_attr.grh.sgid_index,
&gid, &gid_attr);
if (!ret) {
if (gid_attr.ndev)
if (gid_attr.ndev) {
work->path[0].ifindex = gid_attr.ndev->ifindex;
work->path[0].net = dev_net(gid_attr.ndev);
dev_put(gid_attr.ndev);
}
work->path[0].gid_type = gid_attr.gid_type;
ret = cm_init_av_by_path(&work->path[0], &cm_id_priv->av);
}
Expand All @@ -1656,8 +1659,11 @@ static int cm_req_handler(struct cm_work *work)
work->port->port_num, 0,
&work->path[0].sgid,
&gid_attr);
if (!err && gid_attr.ndev)
if (!err && gid_attr.ndev) {
work->path[0].ifindex = gid_attr.ndev->ifindex;
work->path[0].net = dev_net(gid_attr.ndev);
dev_put(gid_attr.ndev);
}
work->path[0].gid_type = gid_attr.gid_type;
ib_send_cm_rej(cm_id, IB_CM_REJ_INVALID_GID,
&work->path[0].sgid, sizeof work->path[0].sgid,
Expand Down
30 changes: 28 additions & 2 deletions drivers/infiniband/core/cma.c
Original file line number Diff line number Diff line change
Expand Up @@ -454,8 +454,20 @@ static inline int cma_validate_port(struct ib_device *device, u8 port,
if ((dev_type != ARPHRD_INFINIBAND) && rdma_protocol_ib(device, port))
return ret;

if (dev_type == ARPHRD_ETHER)
if (dev_type == ARPHRD_ETHER) {
ndev = dev_get_by_index(&init_net, bound_if_index);
if (ndev && ndev->flags & IFF_LOOPBACK) {
pr_info("detected loopback device\n");
dev_put(ndev);

if (!device->get_netdev)
return -EOPNOTSUPP;

ndev = device->get_netdev(device, port);
if (!ndev)
return -ENODEV;
}
}

ret = ib_find_cached_gid_by_port(device, gid, IB_GID_TYPE_IB, port,
ndev, NULL);
Expand Down Expand Up @@ -2314,8 +2326,22 @@ static int cma_resolve_iboe_route(struct rdma_id_private *id_priv)

if (addr->dev_addr.bound_dev_if) {
ndev = dev_get_by_index(&init_net, addr->dev_addr.bound_dev_if);
if (!ndev)
return -ENODEV;

if (ndev->flags & IFF_LOOPBACK) {
dev_put(ndev);
if (!id_priv->id.device->get_netdev)
return -EOPNOTSUPP;

ndev = id_priv->id.device->get_netdev(id_priv->id.device,
id_priv->id.port_num);
if (!ndev)
return -ENODEV;
}

route->path_rec->net = &init_net;
route->path_rec->ifindex = addr->dev_addr.bound_dev_if;
route->path_rec->ifindex = ndev->ifindex;
route->path_rec->gid_type = id_priv->gid_type;
}
if (!ndev) {
Expand Down
Loading

0 comments on commit 2002983

Please sign in to comment.