]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
IB/{cm,ipoib}: Filter traffic using ACL
authorYuval Shaia <yuval.shaia@oracle.com>
Mon, 4 Apr 2016 12:39:49 +0000 (15:39 +0300)
committerChuck Anderson <chuck.anderson@oracle.com>
Tue, 12 Jul 2016 19:46:33 +0000 (12:46 -0700)
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 <yuval.shaia@oracle.com>
Reviewed-by: Santosh Shilimkar <santosh.shilimkar@oracle.com>
drivers/infiniband/core/cm.c
drivers/infiniband/ulp/ipoib/ipoib.h
drivers/infiniband/ulp/ipoib/ipoib_ib.c
include/rdma/ib_verbs.h

index 63d7a7af32a78e260d8ad0b5d99837dcd1b6c4a2..912fa921b6aa83d15fb44165c05567bf72d2887f 100644 (file)
@@ -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;
 
index 1c254e6e15868c2c78d7b8ff7a5ab782a77c4e07..c1b49451424b1357f190199f6d41ef46712359f4 100644 (file)
@@ -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 {
index 1da39f666107f301746e450df2b5a82f75bb2415..033197a8d4bff350d06a514ba69d2294a798d36a 100644 (file)
@@ -40,6 +40,7 @@
 
 #include <linux/ip.h>
 #include <linux/tcp.h>
+#include <net/arp.h>
 
 #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 "
index 302d5e9b672684366107c730fbb72f44e945b5da..4f939728280ea4a9d36d26b357bf75153594655f 100644 (file)
@@ -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) {