|
@@ -1619,6 +1619,56 @@ static void fcoe_percpu_flush_done(struct sk_buff *skb)
|
|
|
complete(&fcoe_flush_completion);
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * fcoe_filter_frames() - filter out bad fcoe frames, i.e. bad CRC
|
|
|
+ * @lport: The local port the frame was received on
|
|
|
+ * @fp: The received frame
|
|
|
+ *
|
|
|
+ * Return: 0 on passing filtering checks
|
|
|
+ */
|
|
|
+static inline int fcoe_filter_frames(struct fc_lport *lport,
|
|
|
+ struct fc_frame *fp)
|
|
|
+{
|
|
|
+ struct fcoe_interface *fcoe;
|
|
|
+ struct fc_frame_header *fh;
|
|
|
+ struct sk_buff *skb = (struct sk_buff *)fp;
|
|
|
+ struct fcoe_dev_stats *stats;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We only check CRC if no offload is available and if it is
|
|
|
+ * it's solicited data, in which case, the FCP layer would
|
|
|
+ * check it during the copy.
|
|
|
+ */
|
|
|
+ if (lport->crc_offload && skb->ip_summed == CHECKSUM_UNNECESSARY)
|
|
|
+ fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
|
|
|
+ else
|
|
|
+ fr_flags(fp) |= FCPHF_CRC_UNCHECKED;
|
|
|
+
|
|
|
+ fh = (struct fc_frame_header *) skb_transport_header(skb);
|
|
|
+ fh = fc_frame_header_get(fp);
|
|
|
+ if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA && fh->fh_type == FC_TYPE_FCP)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ fcoe = ((struct fcoe_port *)lport_priv(lport))->fcoe;
|
|
|
+ if (is_fip_mode(&fcoe->ctlr) && fc_frame_payload_op(fp) == ELS_LOGO &&
|
|
|
+ ntoh24(fh->fh_s_id) == FC_FID_FLOGI) {
|
|
|
+ FCOE_DBG("fcoe: dropping FCoE lport LOGO in fip mode\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!fr_flags(fp) & FCPHF_CRC_UNCHECKED ||
|
|
|
+ le32_to_cpu(fr_crc(fp)) == ~crc32(~0, skb->data, skb->len)) {
|
|
|
+ fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ stats = per_cpu_ptr(lport->dev_stats, get_cpu());
|
|
|
+ stats->InvalidCRCCount++;
|
|
|
+ if (stats->InvalidCRCCount < 5)
|
|
|
+ printk(KERN_WARNING "fcoe: dropping frame with CRC error\n");
|
|
|
+ return -EINVAL;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* fcoe_recv_frame() - process a single received frame
|
|
|
* @skb: frame to process
|
|
@@ -1629,7 +1679,6 @@ static void fcoe_recv_frame(struct sk_buff *skb)
|
|
|
struct fc_lport *lport;
|
|
|
struct fcoe_rcv_info *fr;
|
|
|
struct fcoe_dev_stats *stats;
|
|
|
- struct fc_frame_header *fh;
|
|
|
struct fcoe_crc_eof crc_eof;
|
|
|
struct fc_frame *fp;
|
|
|
struct fcoe_port *port;
|
|
@@ -1660,7 +1709,6 @@ static void fcoe_recv_frame(struct sk_buff *skb)
|
|
|
* was done in fcoe_rcv already.
|
|
|
*/
|
|
|
hp = (struct fcoe_hdr *) skb_network_header(skb);
|
|
|
- fh = (struct fc_frame_header *) skb_transport_header(skb);
|
|
|
|
|
|
stats = per_cpu_ptr(lport->dev_stats, get_cpu());
|
|
|
if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) {
|
|
@@ -1693,35 +1741,11 @@ static void fcoe_recv_frame(struct sk_buff *skb)
|
|
|
if (pskb_trim(skb, fr_len))
|
|
|
goto drop;
|
|
|
|
|
|
- /*
|
|
|
- * We only check CRC if no offload is available and if it is
|
|
|
- * it's solicited data, in which case, the FCP layer would
|
|
|
- * check it during the copy.
|
|
|
- */
|
|
|
- if (lport->crc_offload &&
|
|
|
- skb->ip_summed == CHECKSUM_UNNECESSARY)
|
|
|
- fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
|
|
|
- else
|
|
|
- fr_flags(fp) |= FCPHF_CRC_UNCHECKED;
|
|
|
-
|
|
|
- fh = fc_frame_header_get(fp);
|
|
|
- if ((fh->fh_r_ctl != FC_RCTL_DD_SOL_DATA ||
|
|
|
- fh->fh_type != FC_TYPE_FCP) &&
|
|
|
- (fr_flags(fp) & FCPHF_CRC_UNCHECKED)) {
|
|
|
- if (le32_to_cpu(fr_crc(fp)) !=
|
|
|
- ~crc32(~0, skb->data, fr_len)) {
|
|
|
- if (stats->InvalidCRCCount < 5)
|
|
|
- printk(KERN_WARNING "fcoe: dropping "
|
|
|
- "frame with CRC error\n");
|
|
|
- stats->InvalidCRCCount++;
|
|
|
- goto drop;
|
|
|
- }
|
|
|
- fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
|
|
|
+ if (!fcoe_filter_frames(lport, fp)) {
|
|
|
+ put_cpu();
|
|
|
+ fc_exch_recv(lport, fp);
|
|
|
+ return;
|
|
|
}
|
|
|
- put_cpu();
|
|
|
- fc_exch_recv(lport, fp);
|
|
|
- return;
|
|
|
-
|
|
|
drop:
|
|
|
stats->ErrorFrames++;
|
|
|
put_cpu();
|