pretty good
This commit is contained in:
166
analyzer/protocols/decoders/fibre_channel.py
Normal file
166
analyzer/protocols/decoders/fibre_channel.py
Normal file
@@ -0,0 +1,166 @@
|
||||
"""
|
||||
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('<IIIIII', payload, data_start)
|
||||
if fc_header:
|
||||
decoded_data.update({
|
||||
'fc_timestamp': fc_header[0],
|
||||
'fc_frame_length': fc_header[1],
|
||||
'fc_r_ctl': (fc_header[2] >> 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})")
|
||||
Reference in New Issue
Block a user