""" Fibre Channel Data decoder for Chapter 10 data types Supports Fibre Channel Format 0 (0x79) """ import struct from typing import Dict, Any, Optional from .base import DataTypeDecoder, DecodedPayload class FibreChannelDecoder(DataTypeDecoder): """Decoder for Fibre Channel Data type (0x79)""" def __init__(self): super().__init__() self.data_type_base = 0x79 self.data_type_name = "Fibre Channel Data" self.supported_formats = [0x79] def can_decode(self, data_type: int) -> bool: return data_type == 0x79 def get_data_type_name(self, data_type: int) -> str: return "Fibre Channel Data Format 0" def decode(self, payload: bytes, ch10_header: Dict[str, Any]) -> Optional[DecodedPayload]: """Decode Fibre Channel payload""" decoded_data = {} errors = [] # Parse IPH iph = self._parse_intra_packet_header(payload) if iph: decoded_data.update(iph) data_start = iph['data_start'] else: data_start = 0 errors.append("Failed to parse intra-packet header") # Parse FC frame header if data_start + 24 <= len(payload): # FC frame header (simplified) fc_header = self._safe_unpack('> 24) & 0xFF, 'fc_d_id': fc_header[2] & 0xFFFFFF, 'fc_cs_ctl': (fc_header[3] >> 24) & 0xFF, 'fc_s_id': fc_header[3] & 0xFFFFFF, 'fc_type': (fc_header[4] >> 24) & 0xFF, 'fc_f_ctl': fc_header[4] & 0xFFFFFF, 'fc_seq_id': (fc_header[5] >> 24) & 0xFF, 'fc_df_ctl': (fc_header[5] >> 16) & 0xFF, 'fc_seq_cnt': fc_header[5] & 0xFFFF }) # Decode R_CTL field r_ctl = decoded_data['fc_r_ctl'] decoded_data['fc_r_ctl_description'] = self._decode_r_ctl(r_ctl) # Decode Type field fc_type = decoded_data['fc_type'] decoded_data['fc_type_description'] = self._decode_fc_type(fc_type) # Extract payload fc_payload_start = data_start + 24 frame_length = decoded_data['fc_frame_length'] if fc_payload_start + frame_length <= len(payload): fc_payload = payload[fc_payload_start:fc_payload_start + frame_length] decoded_data['fc_payload_length'] = len(fc_payload) decoded_data['fc_payload_preview'] = fc_payload[:64].hex() # Analyze payload based on type if fc_type == 0x08: # SCSI FCP scsi_data = self._parse_scsi_fcp(fc_payload) decoded_data.update(scsi_data) else: errors.append("FC payload extends beyond packet") else: errors.append("Failed to parse FC header") return DecodedPayload( data_type=0x79, data_type_name="Fibre Channel Data Format 0", format_version=0, decoded_data=decoded_data, raw_payload=payload, errors=errors, metadata={'decoder': 'FibreChannelDecoder'} ) def _decode_r_ctl(self, r_ctl: int) -> str: """Decode R_CTL field""" r_ctl_types = { 0x00: "Device Data", 0x01: "Extended Link Data", 0x02: "FC-4 Link Data", 0x03: "Video Data", 0x20: "Basic Link Data", 0x21: "ACK_1", 0x22: "ACK_0", 0x23: "P_RJT", 0x24: "F_RJT", 0x25: "P_BSY", 0x26: "F_BSY" } return r_ctl_types.get(r_ctl, f"Unknown (0x{r_ctl:02x})") def _decode_fc_type(self, fc_type: int) -> str: """Decode FC Type field""" fc_types = { 0x00: "Basic Link Service", 0x01: "Extended Link Service", 0x04: "IP over FC", 0x05: "ATM over FC", 0x08: "SCSI FCP", 0x09: "SCSI GPP", 0x0A: "IPI-3 Master", 0x0B: "IPI-3 Slave", 0x0C: "IPI-3 Peer" } return fc_types.get(fc_type, f"Unknown (0x{fc_type:02x})") def _parse_scsi_fcp(self, payload: bytes) -> Dict[str, Any]: """Parse SCSI FCP payload""" scsi_data = {} if len(payload) >= 32: # FCP_CMND structure lun = payload[0:8] task_codes = payload[8] task_mgmt = payload[9] add_cdb_len = payload[10] rddata = bool(payload[11] & 0x02) wrdata = bool(payload[11] & 0x01) scsi_data.update({ 'scsi_lun': lun.hex(), 'scsi_task_codes': task_codes, 'scsi_task_mgmt': task_mgmt, 'scsi_rddata': rddata, 'scsi_wrdata': wrdata }) # CDB starts at offset 12 if len(payload) >= 16: cdb = payload[12:16] scsi_data['scsi_cdb'] = cdb.hex() if cdb[0] in [0x12, 0x00, 0x28, 0x2A]: # Common SCSI commands scsi_data['scsi_command'] = self._decode_scsi_command(cdb[0]) return scsi_data def _decode_scsi_command(self, opcode: int) -> str: """Decode SCSI command opcode""" commands = { 0x00: "TEST UNIT READY", 0x12: "INQUIRY", 0x28: "READ(10)", 0x2A: "WRITE(10)", 0x35: "SYNCHRONIZE CACHE", 0x3C: "READ BUFFER" } return commands.get(opcode, f"Unknown (0x{opcode:02x})")