/** * fcoe_fip_recv - handle a received FIP frame. * @skb: the receive skb * @dev: associated &net_device * @ptype: the &packet_type structure which was used to register this handler. * @orig_dev: original receive &net_device, in case @dev is a bond. * * Returns: 0 for success */ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype, struct net_device *orig_dev) { struct fcoe_softc *fc; fc = container_of(ptype, struct fcoe_softc, fip_packet_type); fcoe_ctlr_recv(&fc->ctlr, skb); return 0; }
/** * fnic_import_rq_eth_pkt() - handle received FCoE or FIP frame. * @fnic: fnic instance. * @skb: Ethernet Frame. */ static inline int fnic_import_rq_eth_pkt(struct fnic *fnic, struct sk_buff *skb) { struct fc_frame *fp; struct ethhdr *eh; struct fcoe_hdr *fcoe_hdr; struct fcoe_crc_eof *ft; /* * Undo VLAN encapsulation if present. */ eh = (struct ethhdr *)skb->data; if (eh->h_proto == htons(ETH_P_8021Q)) { memmove((u8 *)eh + VLAN_HLEN, eh, ETH_ALEN * 2); eh = (struct ethhdr *)skb_pull(skb, VLAN_HLEN); skb_reset_mac_header(skb); } if (eh->h_proto == htons(ETH_P_FIP)) { skb_pull(skb, sizeof(*eh)); fcoe_ctlr_recv(&fnic->ctlr, skb); return 1; /* let caller know packet was used */ } if (eh->h_proto != htons(ETH_P_FCOE)) goto drop; skb_set_network_header(skb, sizeof(*eh)); skb_pull(skb, sizeof(*eh)); fcoe_hdr = (struct fcoe_hdr *)skb->data; if (FC_FCOE_DECAPS_VER(fcoe_hdr) != FC_FCOE_VER) goto drop; fp = (struct fc_frame *)skb; fc_frame_init(fp); fr_sof(fp) = fcoe_hdr->fcoe_sof; skb_pull(skb, sizeof(struct fcoe_hdr)); skb_reset_transport_header(skb); ft = (struct fcoe_crc_eof *)(skb->data + skb->len - sizeof(*ft)); fr_eof(fp) = ft->fcoe_eof; skb_trim(skb, skb->len - sizeof(*ft)); return 0; drop: dev_kfree_skb_irq(skb); return -1; }
/* Process incoming FIP frames. */ void qedf_fip_recv(struct qedf_ctx *qedf, struct sk_buff *skb) { struct ethhdr *eth_hdr; struct fip_header *fiph; struct fip_desc *desc; struct fip_mac_desc *mp; struct fip_wwn_desc *wp; struct fip_vn_desc *vp; size_t rlen, dlen; uint32_t cvl_port_id; __u8 cvl_mac[ETH_ALEN]; u16 op; u8 sub; eth_hdr = (struct ethhdr *)skb_mac_header(skb); fiph = (struct fip_header *) ((void *)skb->data + 2 * ETH_ALEN + 2); op = ntohs(fiph->fip_op); sub = fiph->fip_subcode; QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, "FIP frame received: " "skb=%p fiph=%p source=%pM op=%x sub=%x", skb, fiph, eth_hdr->h_source, op, sub); if (qedf_dump_frames) print_hex_dump(KERN_WARNING, "fip ", DUMP_PREFIX_OFFSET, 16, 1, skb->data, skb->len, false); /* Handle FIP VLAN resp in the driver */ if (op == FIP_OP_VLAN && sub == FIP_SC_VL_NOTE) { qedf_fcoe_process_vlan_resp(qedf, skb); qedf->vlan_hw_insert = 0; kfree_skb(skb); } else if (op == FIP_OP_CTRL && sub == FIP_SC_CLR_VLINK) { QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "Clear virtual " "link received.\n"); /* Check that an FCF has been selected by fcoe */ if (qedf->ctlr.sel_fcf == NULL) { QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "Dropping CVL since FCF has not been selected " "yet."); return; } cvl_port_id = 0; memset(cvl_mac, 0, ETH_ALEN); /* * We need to loop through the CVL descriptors to determine * if we want to reset the fcoe link */ rlen = ntohs(fiph->fip_dl_len) * FIP_BPW; desc = (struct fip_desc *)(fiph + 1); while (rlen >= sizeof(*desc)) { dlen = desc->fip_dlen * FIP_BPW; switch (desc->fip_dtype) { case FIP_DT_MAC: mp = (struct fip_mac_desc *)desc; QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, "fd_mac=%pM\n", mp->fd_mac); ether_addr_copy(cvl_mac, mp->fd_mac); break; case FIP_DT_NAME: wp = (struct fip_wwn_desc *)desc; QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, "fc_wwpn=%016llx.\n", get_unaligned_be64(&wp->fd_wwn)); break; case FIP_DT_VN_ID: vp = (struct fip_vn_desc *)desc; QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, "fd_fc_id=%x.\n", ntoh24(vp->fd_fc_id)); cvl_port_id = ntoh24(vp->fd_fc_id); break; default: /* Ignore anything else */ break; } desc = (struct fip_desc *)((char *)desc + dlen); rlen -= dlen; } QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, "cvl_port_id=%06x cvl_mac=%pM.\n", cvl_port_id, cvl_mac); if (cvl_port_id == qedf->lport->port_id && ether_addr_equal(cvl_mac, qedf->ctlr.sel_fcf->fcf_mac)) { fcoe_ctlr_link_down(&qedf->ctlr); qedf_wait_for_upload(qedf); fcoe_ctlr_link_up(&qedf->ctlr); } kfree_skb(skb); } else { /* Everything else is handled by libfcoe */ __skb_pull(skb, ETH_HLEN); fcoe_ctlr_recv(&qedf->ctlr, skb); } }