From e95a33b73fc3f8cdbd4edde7126a59b594db0e34 Mon Sep 17 00:00:00 2001 From: Ka-Cheong Poon Date: Tue, 24 Oct 2017 07:59:30 -0700 Subject: [PATCH] {IB/{core,ipoib},net/rds}: IPv6 support for ACL MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The IB ACL components are extended to support IPv6 address. Some of the ACL ioctls use a 32 bit integer to represent an IP address. To support IPv6, struct in6_addr needs to be used. To ensure backward compatibility, a new ioctl will be introduced for each of those ioctls that take a 32 bit integer as address. The original ioctls are kept and can still be used. The new ioctls can take IPv4 mapped IPv6 address so new apps only need to use the new ioctls. The IPOIBACLNGET and IPOIBACLNADD commands re-use the same ipoib_ioctl_req_data, except that the ips field should actually be a pointer to a list of struct in6_addr. Here we assume that the pointer size to an u32 and in6_addr are the same. Orabug: 25410192 Signed-off-by: Ka-Cheong Poon Reviewed-by: Yuval Shaia Reviewed-by: HÃ¥kon Bugge --- drivers/infiniband/core/cm.c | 13 +++-- drivers/infiniband/ulp/ipoib/ipoib.h | 27 +++++---- drivers/infiniband/ulp/ipoib/ipoib_ioctl.c | 66 +++++++++++++++++++--- include/rdma/ib_cm.h | 10 ++-- net/rds/ib_cm.c | 35 ++++-------- 5 files changed, 98 insertions(+), 53 deletions(-) diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c index 46c7659bf69a9..e8bb4a92cbd60 100644 --- a/drivers/infiniband/core/cm.c +++ b/drivers/infiniband/core/cm.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include @@ -767,8 +768,8 @@ void ib_cm_acl_init(struct ib_cm_acl *acl) } EXPORT_SYMBOL(ib_cm_acl_init); -int ib_cm_acl_insert(struct ib_cm_acl *acl, u64 subnet_prefix, u64 guid, u32 ip, - const char *uuid) +int ib_cm_acl_insert(struct ib_cm_acl *acl, u64 subnet_prefix, u64 guid, + struct in6_addr *ip, const char *uuid) { struct ib_cm_acl_elem *elem; struct rb_node **new, *parent = NULL; @@ -805,7 +806,7 @@ int ib_cm_acl_insert(struct ib_cm_acl *acl, u64 subnet_prefix, u64 guid, u32 ip, goto err_nomem; elem->guid = guid; elem->subnet_prefix = subnet_prefix; - elem->ip = ip; + elem->ip = *ip; memcpy(elem->uuid, uuid, UUID_SZ); elem->ref_count = 1; rb_link_node(&elem->node, parent, new); @@ -868,7 +869,8 @@ struct ib_cm_acl_elem *ib_cm_acl_lookup(struct ib_cm_acl *acl, EXPORT_SYMBOL(ib_cm_acl_lookup); struct ib_cm_acl_elem *ib_cm_acl_lookup_uuid_ip(struct ib_cm_acl *acl, - char *uuid, u32 ip) + char *uuid, + const struct in6_addr *ip) { struct ib_cm_acl_elem *elem, *ret = NULL; struct rb_node *node; @@ -878,7 +880,8 @@ struct ib_cm_acl_elem *ib_cm_acl_lookup_uuid_ip(struct ib_cm_acl *acl, node = rb_first(&acl->allowed_list); while (node) { elem = container_of(node, struct ib_cm_acl_elem, node); - if ((ip == elem->ip) && (!memcmp(uuid, elem->uuid, UUID_SZ))) { + if (ipv6_addr_equal(ip, &elem->ip) && + (!memcmp(uuid, elem->uuid, UUID_SZ))) { ret = elem; goto out; } diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h index bb977b906f5fb..24fbd6341825b 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib.h +++ b/drivers/infiniband/ulp/ipoib/ipoib.h @@ -133,17 +133,22 @@ enum { #define IPOIB_QPN_MASK ((__force u32) cpu_to_be32(0xFFFFFF)) /* AC ioctl commands */ -#define IPOIBACIOCTLSTART (SIOCDEVPRIVATE) -#define IPOIBSTATUSGET (IPOIBACIOCTLSTART + 0) -#define IPOIBSTATUSSET (IPOIBACIOCTLSTART + 1) -#define IPOIBACLINSTSZ (IPOIBACIOCTLSTART + 2) -#define IPOIBACLINSTGET (IPOIBACIOCTLSTART + 3) -#define IPOIBACLINSTADD (IPOIBACIOCTLSTART + 4) -#define IPOIBACLINSTDEL (IPOIBACIOCTLSTART + 5) -#define IPOIBACLSZ (IPOIBACIOCTLSTART + 6) -#define IPOIBACLGET (IPOIBACIOCTLSTART + 7) -#define IPOIBACLADD (IPOIBACIOCTLSTART + 8) -#define IPOIBACLDEL (IPOIBACIOCTLSTART + 9) +#define IPOIBACLIOCTLSTART (SIOCDEVPRIVATE) +#define IPOIBSTATUSGET (IPOIBACLIOCTLSTART + 0) +#define IPOIBSTATUSSET (IPOIBACLIOCTLSTART + 1) +#define IPOIBACLINSTSZ (IPOIBACLIOCTLSTART + 2) +#define IPOIBACLINSTGET (IPOIBACLIOCTLSTART + 3) +#define IPOIBACLINSTADD (IPOIBACLIOCTLSTART + 4) +#define IPOIBACLINSTDEL (IPOIBACLIOCTLSTART + 5) +#define IPOIBACLSZ (IPOIBACLIOCTLSTART + 6) +#define IPOIBACLGET (IPOIBACLIOCTLSTART + 7) +#define IPOIBACLADD (IPOIBACLIOCTLSTART + 8) +#define IPOIBACLDEL (IPOIBACLIOCTLSTART + 9) +/* Commands which can handle IPv6 address. */ +#define IPOIBACLNGET (IPOIBACLIOCTLSTART + 10) +#define IPOIBACLNADD (IPOIBACLIOCTLSTART + 11) +/* Value of IPOIBACIOCTLEND should be updated when new commands are added. */ +#define IPOIBACLIOCTLEND (IPOIBACLIOCTLSTART + 11) /* structs */ diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ioctl.c b/drivers/infiniband/ulp/ipoib/ipoib_ioctl.c index 1b08d2d6ad764..9e417b1fb358f 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_ioctl.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_ioctl.c @@ -30,6 +30,7 @@ #include #include +#include #include "ipoib.h" @@ -42,18 +43,19 @@ int ipoib_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) struct ib_cm_acl_elem *list; ssize_t list_count, i; u64 guid, subnet_prefix; - u32 ip; + struct in6_addr addr6; char uuid[UUID_SZ]; struct ib_cm_acl *acl; char *buf; - if (cmd < IPOIBSTATUSGET || cmd > IPOIBACLDEL) { + if (cmd < IPOIBACLIOCTLSTART || cmd > IPOIBACLIOCTLEND) { ipoib_dbg(priv, "invalid ioctl opcode 0x%x\n", cmd); return -EOPNOTSUPP; } rc = copy_from_user(&req_data, rq->req_data, sizeof(struct ipoib_ioctl_req_data)); + if (rc != 0) { ipoib_warn(priv, "ioctl fail to copy request data\n"); return -EINVAL; @@ -104,6 +106,7 @@ int ipoib_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) req_data.sz = list_count; break; case IPOIBACLGET: + case IPOIBACLNGET: if (!strcmp(req_data.instance_name, DRIVER_ACL_NAME)) acl = &priv->acl; else @@ -113,8 +116,15 @@ int ipoib_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) return -EINVAL; ib_cm_acl_scan(acl, &list, &list_count); + for (i = req_data.from_idx; (i < list_count) && (i < req_data.sz) ; i++) { + /* Need to skip entries with IPv6 address for old + * IPOIBACLGET command. + */ + if (cmd == IPOIBACLGET && + !ipv6_addr_v4mapped(&list[i].ip)) + continue; rc = copy_to_user(&req_data.subnet_prefixes[i - req_data.from_idx], &(list[i].subnet_prefix), @@ -122,9 +132,24 @@ int ipoib_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) rc |= copy_to_user(&req_data.guids[i - req_data.from_idx], &(list[i].guid), sizeof(u64)); - rc |= copy_to_user(&req_data.ips[i - - req_data.from_idx], &(list[i].ip), - sizeof(u32)); + if (cmd == IPOIBACLGET) + rc |= copy_to_user(&req_data.ips[i - + req_data.from_idx], + &(list[i].ip.s6_addr32[3]), + sizeof(u32)); + else + /* The IPOIBACLNGET command re-uses the same + * ipoib_ioctl_req_data, except that the + * ips should actually be a pointer to a list + * of struct in6_addr. Here we assume that + * the pointer size to an u32 and in6_addr are + * the same. + */ + rc |= copy_to_user((struct in6_addr *) + &req_data.ips[i - + req_data.from_idx], + &list[i].ip, + sizeof(list[i].ip)); rc |= copy_to_user((req_data.uuids + i * UUID_SZ), list[i].uuid, UUID_SZ); if (rc) { @@ -139,18 +164,41 @@ int ipoib_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) req_data.sz = i - req_data.from_idx; break; case IPOIBACLADD: + case IPOIBACLNADD: acl = ipoib_get_instance_acl(req_data.instance_name, dev); if (!acl) return -EINVAL; + if (cmd == IPOIBACLADD) { + /* IPOIBACLADD command contains a list of IPv4 + * addresses. Need to convert them to IPv4 mapped + * IPv6 addresses. Pre-initialize the IPv6 address + * to be an IPv4 mapped address. + */ + addr6.s6_addr32[0] = 0; + addr6.s6_addr32[1] = 0; + addr6.s6_addr32[2] = htonl(0xFFFF); + } + for (i = 0; i < req_data.sz; i++) { rc = copy_from_user(&subnet_prefix, &req_data.subnet_prefixes[i], sizeof(u64)); rc |= copy_from_user(&guid, &req_data.guids[i], sizeof(u64)); - rc |= copy_from_user(&ip, &req_data.ips[i], - sizeof(u32)); + if (cmd == IPOIBACLADD) + rc |= copy_from_user(&addr6.s6_addr32[3], + &req_data.ips[i], + sizeof(u32)); + else + /* The IPOIBACLNADD command re-uses the same + * ipoib_ioctl_req_data, except that the + * ips should actually be a pointer to a list + * of struct in6_addr. + */ + rc |= copy_from_user(&addr6, + &req_data.ips[i], + sizeof(addr6)); rc |= copy_from_user(&uuid, (req_data.uuids + i * UUID_SZ), UUID_SZ); @@ -160,10 +208,10 @@ int ipoib_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) i); return -EINVAL; } - rc = ib_cm_acl_insert(acl, subnet_prefix, guid, ip, + rc = ib_cm_acl_insert(acl, subnet_prefix, guid, &addr6, uuid); rc |= ib_cm_acl_insert(&priv->acl, subnet_prefix, guid, - ip, uuid); + &addr6, uuid); if (rc) { ipoib_warn(priv, "ioctl fail to insert index %ld to ACL\n", diff --git a/include/rdma/ib_cm.h b/include/rdma/ib_cm.h index 7c06afdd01bfb..bc620bd98b62f 100644 --- a/include/rdma/ib_cm.h +++ b/include/rdma/ib_cm.h @@ -36,6 +36,7 @@ #define IB_CM_H #include +#include #include #include @@ -320,7 +321,7 @@ struct ib_cm_acl_elem { struct rb_node node; u64 guid; u64 subnet_prefix; - u32 ip; + struct in6_addr ip; char uuid[UUID_SZ]; int ref_count; }; @@ -333,12 +334,13 @@ struct ib_cm_acl { }; void ib_cm_acl_init(struct ib_cm_acl *acl); -int ib_cm_acl_insert(struct ib_cm_acl *acl, u64 subnet_prefix, u64 guid, u32 ip, - const char *uuid); +int ib_cm_acl_insert(struct ib_cm_acl *acl, u64 subnet_prefix, u64 guid, + struct in6_addr *ip, const char *uuid); struct ib_cm_acl_elem *ib_cm_acl_lookup(struct ib_cm_acl *acl, u64 subnet_prefix, u64 guid); struct ib_cm_acl_elem *ib_cm_acl_lookup_uuid_ip(struct ib_cm_acl *acl, - char *uuid, u32 ip); + char *uuid, + const struct in6_addr *ip); int ib_cm_acl_delete(struct ib_cm_acl *acl, u64 subnet_prefix, u64 guid); void ib_cm_acl_scan(struct ib_cm_acl *acl, struct ib_cm_acl_elem **list, ssize_t *list_count); diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c index 2445f161cbfab..00244bf5e37c3 100644 --- a/net/rds/ib_cm.c +++ b/net/rds/ib_cm.c @@ -202,14 +202,14 @@ static inline void rds_ib_init_ic_frag(struct rds_ib_connection *ic) * 1 - acl is not enabled * -1 - acl match failed */ -static int rds_ib_match_acl(struct rdma_cm_id *cm_id, __be32 saddr) +static int rds_ib_match_acl(struct rdma_cm_id *cm_id, + const struct in6_addr *saddr) { struct ib_cm_acl *acl = 0; struct ib_cm_acl_elem *acl_elem = 0; __be64 fguid = cm_id->route.path_rec->dgid.global.interface_id; __be64 fsubnet = cm_id->route.path_rec->dgid.global.subnet_prefix; struct ib_cm_dpp dpp; - u32 addr; ib_cm_dpp_init(&dpp, cm_id->device, cm_id->port_num, ntohs(cm_id->route.path_rec->pkey)); @@ -227,14 +227,10 @@ static int rds_ib_match_acl(struct rdma_cm_id *cm_id, __be32 saddr) goto out; } - addr = be32_to_cpu(saddr); - if (!addr) - goto out; - - acl_elem = ib_cm_acl_lookup_uuid_ip(acl, acl_elem->uuid, addr); + acl_elem = ib_cm_acl_lookup_uuid_ip(acl, acl_elem->uuid, saddr); if (!acl_elem) { - pr_err_ratelimited("RDS/IB: IP %pI4 ib_cm_acl_lookup_uuid_ip() failed\n", - &saddr); + pr_err_ratelimited("RDS/IB: IP %pI6c ib_cm_acl_lookup_uuid_ip() failed\n", + saddr); goto out; } @@ -1028,16 +1024,11 @@ int rds_ib_cm_handle_connect(struct rdma_cm_id *cm_id, (unsigned long long)be64_to_cpu(fguid), dp_cmn->ricpc_tos); - /* IPoIB ACL only supports IPv4. Let all IPv6 traffic pass. */ - if (ipv6_addr_v4mapped(saddr6)) { - acl_ret = rds_ib_match_acl(cm_id, saddr6->s6_addr32[3]); - if (acl_ret < 0) { - err = RDS_ACL_FAILURE; - rdsdebug("RDS: IB: passive: rds_ib_match_acl failed\n"); - goto out; - } - } else { - acl_ret = 0; + acl_ret = rds_ib_match_acl(cm_id, saddr6); + if (acl_ret < 0) { + err = RDS_ACL_FAILURE; + rdsdebug("RDS: IB: passive: rds_ib_match_acl failed\n"); + goto out; } /* RDS/IB is not currently netns aware, thus init_net */ @@ -1203,11 +1194,7 @@ int rds_ib_cm_initiate_connect(struct rdma_cm_id *cm_id, bool isv6) u16 frag; int ret; - /* IPoIB ACL only supports IPv4. Let all IPv6 traffic pass. */ - if (ipv6_addr_v4mapped(&conn->c_faddr)) - ret = rds_ib_match_acl(ic->i_cm_id, conn->c_faddr.s6_addr32[3]); - else - ret = 0; + ret = rds_ib_match_acl(ic->i_cm_id, &conn->c_faddr); if (ret < 0) { pr_err("RDS: IB: active conn=%p, <%pI6c,%pI6c,%d> destroyed due ACL violation\n", conn, &conn->c_laddr, &conn->c_faddr, -- 2.50.1