From: Yuval Shaia Date: Mon, 4 Apr 2016 12:39:49 +0000 (+0300) Subject: IB/{cm,ipoib}: Filter traffic using ACL X-Git-Tag: v4.1.12-92~108^2~15 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=1668e23daecb673acc7ea910dda63c0104190b79;p=users%2Fjedix%2Flinux-maple.git IB/{cm,ipoib}: Filter traffic using ACL Implement two packet filtering points, one at ib_ipoib driver when processing ARP packets and second in ib_cm when processing connection requests. Orabug: 23222944 Signed-off-by: Yuval Shaia Reviewed-by: Santosh Shilimkar --- diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c index 63d7a7af32a78..912fa921b6aa8 100644 --- a/drivers/infiniband/core/cm.c +++ b/drivers/infiniband/core/cm.c @@ -1792,12 +1792,87 @@ static void cm_process_routed_req(struct cm_req_msg *req_msg, struct ib_wc *wc) } } +static char const cm_drop_reason_desc[4][32] = { + "N/A", + "No GRH", + "grh.guid != mad.guid", + "acl drop" +}; + +enum cm_drop_reason { + CM_DROP_ACCEPT, + CM_DROP_NO_GRH, + CM_DROP_INV_MAD_GUID, + CM_DROP_ACL +}; + +static enum cm_drop_reason cm_acl_filter(struct cm_work *work) +{ + struct cm_req_msg *req_msg; + struct ib_grh *grh; + struct ib_cm_dpp dpp; + struct ib_cm_acl *acl; + u64 subnet_prefix = 0, guid = 0; + struct ib_wc *wc; + enum cm_drop_reason drop = CM_DROP_ACCEPT; + + req_msg = (struct cm_req_msg *)work->mad_recv_wc->recv_buf.mad; + grh = (struct ib_grh *)work->mad_recv_wc->recv_buf.grh; + wc = (struct ib_wc *)work->mad_recv_wc->wc; + + ib_cm_dpp_init(&dpp, work->port->cm_dev->ib_device, + work->port->port_num, be16_to_cpu(req_msg->pkey)); + ib_cm_dpp_dbg("CM Request from interface", &dpp); + acl = ib_cm_dpp_acl_lookup(&dpp); + if (acl && acl->enabled) { + if (!(wc->wc_flags & IB_WC_GRH)) + drop = CM_DROP_NO_GRH; + else if (grh->sgid.global.interface_id != + req_msg->primary_local_gid.global.interface_id) + drop = CM_DROP_INV_MAD_GUID; + else { + subnet_prefix = be64_to_cpu(req_msg-> + primary_local_gid.global. + subnet_prefix); + guid = be64_to_cpu(req_msg->primary_local_gid.global. + interface_id); + if (!ib_cm_acl_lookup(acl, subnet_prefix, guid)) + drop = CM_DROP_ACL; + } + + if (drop) { + pr_debug("Blocked CM request, reason=%s\n", + cm_drop_reason_desc[drop]); + pr_debug("\tgrh.sgid=(0x%llx,0x%llx), grh.dgid=(0x%llx,0x%llx)\n", + be64_to_cpu(grh->sgid.global.subnet_prefix), + be64_to_cpu(grh->sgid.global.interface_id), + be64_to_cpu(grh->dgid.global.subnet_prefix), + be64_to_cpu(grh->dgid.global.interface_id)); + pr_debug("\tlocal=(0x%llx,0x%llx), remote=(0x%llx,0x%llx)\n", + be64_to_cpu(req_msg->primary_local_gid.global. + subnet_prefix), + be64_to_cpu(req_msg->primary_local_gid.global. + interface_id), + be64_to_cpu(req_msg->primary_remote_gid.global. + subnet_prefix), + be64_to_cpu(req_msg->primary_remote_gid.global. + interface_id)); + } + } + + return drop; +} + static int cm_req_handler(struct cm_work *work) { struct ib_cm_id *cm_id; struct cm_id_private *cm_id_priv, *listen_cm_id_priv; struct cm_req_msg *req_msg; - int ret; + int ret = 0; + + ret = cm_acl_filter(work); + if (ret) + return -EINVAL; req_msg = (struct cm_req_msg *)work->mad_recv_wc->recv_buf.mad; diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h index 1c254e6e15868..c1b49451424b1 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib.h +++ b/drivers/infiniband/ulp/ipoib/ipoib.h @@ -416,6 +416,9 @@ struct ipoib_dev_priv { /* Device specific; obtained from query_device */ unsigned max_sge; struct ib_cm_acl acl; + int arp_blocked; + int arp_accepted; + int ud_blocked; }; struct ipoib_ah { diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c index 1da39f666107f..033197a8d4bff 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c @@ -40,6 +40,7 @@ #include #include +#include #include "ipoib.h" @@ -173,13 +174,50 @@ static int ipoib_ib_post_receives(struct net_device *dev) return 0; } +static inline int ipoib_is_valid_arp_address(struct net_device *dev, + struct sk_buff *skb, + __be64 interface_id) +{ + struct ipoib_dev_priv *priv = netdev_priv(dev); + union ib_gid *sgid; + + sgid = (union ib_gid *)(skb->data + sizeof(struct arphdr) + 4); + + if (sgid->global.interface_id != interface_id) { + ipoib_dbg(priv, "Invalid ARP: sguid=0x%llx, arp.sguid=0x%llx\n", + be64_to_cpu(interface_id), + be64_to_cpu(sgid->global.interface_id)); + return 0; + } + + return 1; +} + +static char const ipoib_drop_reason_desc[5][32] = { + "N/A", + "No GRH", + "grh.guid != arp.guid", + "acl", + "non CM packet" +}; + +enum ipoib_drop_reason { + IPOIB_DROP_ACCEPT, + IPOIB_DROP_NO_GRH, + IPOIB_DROP_INV_ARP_GUID, + IPOIB_DROP_ACL, + IPOIB_DROP_NON_CM_PACKRT +}; + static void ipoib_ib_handle_rx_wc(struct net_device *dev, struct ib_wc *wc) { struct ipoib_dev_priv *priv = netdev_priv(dev); unsigned int wr_id = wc->wr_id & ~IPOIB_OP_RECV; struct sk_buff *skb; u64 mapping[IPOIB_UD_RX_SG]; - union ib_gid *dgid; + union ib_gid *dgid, *sgid; + enum ipoib_drop_reason drop = IPOIB_DROP_ACCEPT; + u64 subnet_prefix = 0, guid = 0; ipoib_dbg_data(priv, "recv completion: id %d, status: %d\n", wr_id, wc->status); @@ -190,7 +228,7 @@ static void ipoib_ib_handle_rx_wc(struct net_device *dev, struct ib_wc *wc) return; } - skb = priv->rx_ring[wr_id].skb; + skb = priv->rx_ring[wr_id].skb; if (unlikely(wc->status != IB_WC_SUCCESS)) { if (wc->status != IB_WC_WR_FLUSH_ERR) @@ -231,6 +269,7 @@ static void ipoib_ib_handle_rx_wc(struct net_device *dev, struct ib_wc *wc) /* First byte of dgid signals multicast when 0xff */ dgid = &((struct ib_grh *)skb->data)->dgid; + sgid = &((struct ib_grh *)skb->data)->sgid; if (!(wc->wc_flags & IB_WC_GRH) || dgid->raw[0] != 0xff) skb->pkt_type = PACKET_HOST; @@ -250,6 +289,31 @@ static void ipoib_ib_handle_rx_wc(struct net_device *dev, struct ib_wc *wc) ++dev->stats.rx_packets; dev->stats.rx_bytes += skb->len; + if (unlikely(be16_to_cpu(skb->protocol) == ETH_P_ARP)) { + if (priv->acl.enabled) { + subnet_prefix = be64_to_cpu(sgid->global.subnet_prefix); + guid = be64_to_cpu(sgid->global.interface_id); + if (!(wc->wc_flags & IB_WC_GRH)) + drop = IPOIB_DROP_NO_GRH; + else if (!ipoib_is_valid_arp_address(dev, skb, + sgid->global.interface_id)) + drop = IPOIB_DROP_INV_ARP_GUID; + else if (!ib_cm_acl_lookup(&priv->acl, subnet_prefix, + guid)) + drop = IPOIB_DROP_ACL; + if (drop) { + goto drop; + priv->arp_blocked++; + } + } + priv->arp_accepted++; + } else + if (priv->acl.enabled) { + priv->ud_blocked++; + drop = IPOIB_DROP_NON_CM_PACKRT; + goto drop; + } + skb->dev = dev; if ((dev->features & NETIF_F_RXCSUM) && likely(wc->wc_flags & IB_WC_IP_CSUM_OK)) @@ -257,6 +321,15 @@ static void ipoib_ib_handle_rx_wc(struct net_device *dev, struct ib_wc *wc) napi_gro_receive(&priv->napi, skb); + goto repost; + +drop: + ipoib_dbg(priv, "Blocking packet (%s) from 0x%llx 0x%llx\n", + ipoib_drop_reason_desc[drop], subnet_prefix, guid); + dev_kfree_skb_any(skb); + ++dev->stats.rx_dropped; + goto repost; + repost: if (unlikely(ipoib_ib_post_receive(dev, wr_id))) ipoib_warn(priv, "ipoib_ib_post_receive failed " diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 302d5e9b67268..4f939728280ea 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -225,6 +225,12 @@ enum ib_mtu { IB_MTU_4096 = 5 }; +static inline int ib_gid_cmp(union ib_gid *gid1, union ib_gid *gid2) +{ + return (gid1->global.subnet_prefix == gid2->global.subnet_prefix) && + (gid1->global.interface_id == gid2->global.interface_id); +} + static inline int ib_mtu_enum_to_int(enum ib_mtu mtu) { switch (mtu) {