scsi: fcoe: Harden CVL handling when we have not logged into the fabric.
authorChad Dupuis <chad.dupuis@cavium.com>
Fri, 30 Sep 2016 09:01:20 +0000 (11:01 +0200)
committerMartin K. Petersen <martin.petersen@oracle.com>
Tue, 8 Nov 2016 22:29:47 +0000 (17:29 -0500)
If we haven't logged into the fabric yet we want to be a little more nuanced
with our CVL handling than what we've been:

- If the FCF has been selected, check the source MAC to make sure the frame is
from the FCF we've selected.
- If a FCF is selected and the CVL is from the FCF but we have not logged in
yet, then reset everything and go back to solicitation.

Signed-off-by: Chad Dupuis <chad.dupuis@cavium.com>
Reviewed-by: Hannes Reinecke <hare@suse.com>
Acked-by: Johannes Thumshirn <jth@kernel.org>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/fcoe/fcoe_ctlr.c

index 9bba58191b3d32da3ad22da4cfc506bafc3ba8ea..05573c32254dac15d3065ef1bbd183addd1e83ec 100644 (file)
@@ -1316,7 +1316,7 @@ drop:
  * The overall length has already been checked.
  */
 static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
-                                    struct fip_header *fh)
+                                    struct sk_buff *skb)
 {
        struct fip_desc *desc;
        struct fip_mac_desc *mp;
@@ -1331,20 +1331,49 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
        int num_vlink_desc;
        int reset_phys_port = 0;
        struct fip_vn_desc **vlink_desc_arr = NULL;
+       struct fip_header *fh = (struct fip_header *)skb->data;
+       struct ethhdr *eh = eth_hdr(skb);
 
        LIBFCOE_FIP_DBG(fip, "Clear Virtual Link received\n");
 
-       if (!fcf || !lport->port_id) {
+       if (!fcf) {
                /*
                 * We are yet to select best FCF, but we got CVL in the
                 * meantime. reset the ctlr and let it rediscover the FCF
                 */
+               LIBFCOE_FIP_DBG(fip, "Resetting fcoe_ctlr as FCF has not been "
+                   "selected yet\n");
                mutex_lock(&fip->ctlr_mutex);
                fcoe_ctlr_reset(fip);
                mutex_unlock(&fip->ctlr_mutex);
                return;
        }
 
+       /*
+        * If we've selected an FCF check that the CVL is from there to avoid
+        * processing CVLs from an unexpected source.  If it is from an
+        * unexpected source drop it on the floor.
+        */
+       if (!ether_addr_equal(eh->h_source, fcf->fcf_mac)) {
+               LIBFCOE_FIP_DBG(fip, "Dropping CVL due to source address "
+                   "mismatch with FCF src=%pM\n", eh->h_source);
+               return;
+       }
+
+       /*
+        * If we haven't logged into the fabric but receive a CVL we should
+        * reset everything and go back to solicitation.
+        */
+       if (!lport->port_id) {
+               LIBFCOE_FIP_DBG(fip, "lport not logged in, resoliciting\n");
+               mutex_lock(&fip->ctlr_mutex);
+               fcoe_ctlr_reset(fip);
+               mutex_unlock(&fip->ctlr_mutex);
+               fc_lport_reset(fip->lp);
+               fcoe_ctlr_solicit(fip, NULL);
+               return;
+       }
+
        /*
         * mask of required descriptors.  Validating each one clears its bit.
         */
@@ -1576,7 +1605,7 @@ static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb)
        if (op == FIP_OP_DISC && sub == FIP_SC_ADV)
                fcoe_ctlr_recv_adv(fip, skb);
        else if (op == FIP_OP_CTRL && sub == FIP_SC_CLR_VLINK)
-               fcoe_ctlr_recv_clr_vlink(fip, fiph);
+               fcoe_ctlr_recv_clr_vlink(fip, skb);
        kfree_skb(skb);
        return 0;
 drop: