class IEC104_IE_RCO(IEC104_IE_QOC): """ RCO - regulating step command EN 60870-5-101:2003, sec. 7.2.6.17 (p. 50) """ RCO_FLAG_STATE_INVALID_0 = 0 RCO_FLAG_STATE_STEP_DOWN = 1 RCO_FLAG_STATE_STEP_UP = 2 RCO_FLAG_STATE_INVALID_3 = 3 RCO_FLAGS = { RCO_FLAG_STATE_INVALID_0: 'invalid (0)', RCO_FLAG_STATE_STEP_DOWN: 'step down', RCO_FLAG_STATE_STEP_UP: 'step up', RCO_FLAG_STATE_INVALID_3: 'invalid (3)', } informantion_element_fields = IEC104_IE_QOC.informantion_element_fields + [ BitEnumField('rcs', 0, 2, RCO_FLAGS) ]
class IEC104_IE_DCO(IEC104_IE_QOC): """ DCO - double command EN 60870-5-101:2003, sec. 7.2.6.16 (p. 50) """ DCS_FLAG_STATE_INVALID_0 = 0 DCS_FLAG_STATE_OFF = 1 DCS_FLAG_STATE_ON = 2 DCS_FLAG_STATE_INVALID_3 = 3 DCS_FLAGS = { DCS_FLAG_STATE_INVALID_0: 'invalid (0)', DCS_FLAG_STATE_OFF: 'off', DCS_FLAG_STATE_ON: 'on', DCS_FLAG_STATE_INVALID_3: 'invalid (3)', } informantion_element_fields = IEC104_IE_QOC.informantion_element_fields + [ BitEnumField('dcs', 0, 2, DCS_FLAGS) ]
class BTLE_DATA(Packet): name = "BTLE data header" fields_desc = [ BitField("RFU", 0, 3), # Unused BitField("MD", 0, 1), BitField("SN", 0, 1), BitField("NESN", 0, 1), BitEnumField("LLID", 0, 2, { 1: "continue", 2: "start", 3: "control" }), ByteField( "len", None), # BLE 4.2 and upwards can use 1 entire byte for length ] def post_build(self, p, pay): if self.len is None: p = p[:-1] + chb(len(pay)) return p + pay def do_dissect_payload(self, s): if s is not None: cls = self.guess_payload_class(s) try: p = cls(s, _internal=1, _underlayer=self) except KeyboardInterrupt: raise except Exception: if conf.debug_dissector: if issubtype(cls, Packet): log_runtime.error("%s dissector failed" % cls.__name__) else: log_runtime.error( "%s.guess_payload_class() returned [%s]" % (self.__class__.__name__, repr(cls))) # noqa: E501 if cls is not None: raise p = conf.raw_layer(s, _internal=1, _underlayer=self) self.add_payload(p)
class RPLDAGMCNSA(DAGMCObj): """ DAG Metric: Node State and Attributes """ name = "Node State and Attributes" fields_desc = [ ByteEnumField("otype", 1, DAGMC_OBJTYPE), BitField("resflags", 0, 5), BitField("P", 0, 1), BitField("C", 0, 1), BitField("O", 0, 1), BitField("R", 0, 1), BitEnumField("A", 0, 3, AGG_RTMETRIC), BitField("prec", 0, 4), ByteField("len", None), # NSA Object Body Format ByteField("res", 0), BitField("flags", 0, 6), BitField("A2", 0, 1), BitField("O2", 0, 1) ]
class QueryConfigInformation(Packet): name = "Query Config Information" fields_desc = [ ShortField("buffer_count", 0), ShortField("firmware", 0), ByteField("sector_count", 0), BitField("aoe", 0, 4), BitEnumField( "ccmd", 0, 4, { 0: "Read config string", 1: "Test config string", 2: "Test config string prefix", 3: "Set config string", 4: "Force set config string" }), FieldLenField("config_length", None, length_of="config"), StrLenField("config", None, length_from=lambda x: x.config_length) ] def extract_padding(self, s): return "", s
class ZigbeeAppDataPayloadStub(Packet): name = "Zigbee Application Layer Data Payload for Inter-PAN Transmission" fields_desc = [ FlagsField("frame_control", 0, 4, ['reserved1', 'security', 'ack_req', 'extended_hdr']), # noqa: E501 BitEnumField("delivery_mode", 0, 2, {0: 'unicast', 2: 'broadcast', 3: 'group'}), # noqa: E501 BitField("frametype", 3, 2), # value 0b11 (3) is a reserved frame type # Group Address present only when delivery mode field has a value of 0b11 (group delivery mode) # noqa: E501 ConditionalField( XLEShortField("group_addr", 0x0), # 16-bit identifier of the group lambda pkt: pkt.getfieldval("delivery_mode") == 0b11 ), # Cluster identifier EnumField("cluster", 0, _zcl_cluster_identifier, fmt="<H"), # unsigned short (little-endian) # noqa: E501 # Profile identifier EnumField("profile", 0, _zcl_profile_identifier, fmt="<H"), # ZigBee Payload ConditionalField( StrField("data", ""), lambda pkt: pkt.frametype == 3 ), ]
class L2TP(Packet): name = "L2TP" fields_desc = [ FlagsField( "hdr", 0, 12, [ 'res00', 'res01', 'res02', 'res03', 'priority', 'offset', # noqa: E501 'res06', 'sequence', 'res08', 'res09', 'length', 'control' ]), # noqa: E501 BitEnumField("version", 2, 4, {2: 'L2TPv2'}), ConditionalField(ShortField("len", None), lambda pkt: pkt.hdr & 'control+length'), ShortField("tunnel_id", 0), ShortField("session_id", 0), ConditionalField(ShortField("ns", 0), lambda pkt: pkt.hdr & 'sequence+control'), ConditionalField(ShortField("nr", 0), lambda pkt: pkt.hdr & 'sequence+control'), ConditionalField( PadField(ShortField("offset", 0), 4, b"\x00"), lambda pkt: not (pkt.hdr & 'control') and pkt.hdr & 'offset') ] def post_build(self, pkt, pay): if self.len is None and self.hdr & 'control+length': tmp_len = len(pkt) + len(pay) pkt = pkt[:2] + struct.pack("!H", tmp_len) + pkt[4:] return pkt + pay
class ZigbeeNWK(Packet): name = "Zigbee Network Layer" fields_desc = [ BitField("discover_route", 0, 2), BitField("proto_version", 2, 4), BitEnumField("frametype", 0, 2, {0: 'data', 1: 'command', 3: 'Inter-PAN'}), FlagsField("flags", 0, 8, ['multicast', 'security', 'source_route', 'extended_dst', 'extended_src', 'reserved1', 'reserved2', 'reserved3']), # noqa: E501 XLEShortField("destination", 0), XLEShortField("source", 0), ByteField("radius", 0), ByteField("seqnum", 1), # ConditionalField(XLongField("ext_dst", 0), lambda pkt:pkt.flags & 8), ConditionalField(dot15d4AddressField("ext_dst", 0, adjust=lambda pkt, x: 8), lambda pkt:pkt.flags & 8), # noqa: E501 ConditionalField(dot15d4AddressField("ext_src", 0, adjust=lambda pkt, x: 8), lambda pkt:pkt.flags & 16), # noqa: E501 ConditionalField(ByteField("relay_count", 1), lambda pkt:pkt.flags & 0x04), # noqa: E501 ConditionalField(ByteField("relay_index", 0), lambda pkt:pkt.flags & 0x04), # noqa: E501 ConditionalField(FieldListField("relays", [], XLEShortField("", 0x0000), count_from=lambda pkt:pkt.relay_count), lambda pkt:pkt.flags & 0x04), # noqa: E501 ] @classmethod def dispatch_hook(cls, _pkt=None, *args, **kargs): if _pkt and len(_pkt) >= 2: frametype = ord(_pkt[:1]) & 3 if frametype == 3: return ZigbeeNWKStub return cls def guess_payload_class(self, payload): if self.flags.security: return ZigbeeSecurityHeader elif self.frametype == 0: return ZigbeeAppDataPayload elif self.frametype == 1: return ZigbeeNWKCommandPayload else: return Packet.guess_payload_class(self, payload)
class LcEMI(Packet): name = "L_cEMI" fields_desc = [ FieldLenField("additional_information_length", 0, fmt="B", length_of="additional_information"), StrLenField("additional_information", None, length_from=lambda pkt: pkt.additional_information_length), # Controlfield 1 (1 byte made of 8*1 bits) BitEnumField("frame_type", 1, 1, { 1: "standard" }), BitField("reserved_1", 0, 1), BitField("repeat_on_error", 1, 1), BitEnumField("broadcast_type", 1, 1, { 1: "domain" }), BitEnumField("priority", 3, 2, { 3: "low" }), BitField("ack_request", 0, 1), BitField("confirmation_error", 0, 1), # Controlfield 2 (1 byte made of 1+3+4 bits) BitEnumField("address_type", 1, 1, { 1: "group" }), BitField("hop_count", 6, 3), BitField("extended_frame_format", 0, 4), KNXAddressField("source_address", None), KNXGroupField("destination_address", "1/2/3"), FieldLenField("npdu_length", 0x01, fmt="B", length_of="data"), # TPCI and APCI (2 byte made of 1+1+4+4+6 bits) BitEnumField("packet_type", 0, 1, { 0: "data" }), BitEnumField("sequence_type", 0, 1, { 0: "unnumbered" }), BitField("reserved_2", 0, 4), BitEnumField("acpi", 2, 4, KNX_ACPI_CODES), BitField("data", 0, 6) ]
class DNSRRNSEC3(_DNSRRdummy): name = "DNS NSEC3 Resource Record" fields_desc = [ DNSStrField("rrname", ""), ShortEnumField("type", 50, dnstypes), ShortEnumField("rclass", 1, dnsclasses), IntField("ttl", 0), ShortField("rdlen", None), ByteField("hashalg", 0), BitEnumField("flags", 0, 8, {1: "Opt-Out"}), ShortField("iterations", 0), FieldLenField("saltlength", 0, fmt="!B", length_of="salt"), StrLenField("salt", "", length_from=lambda x: x.saltlength), FieldLenField("hashlength", 0, fmt="!B", length_of="nexthashedownername"), # noqa: E501 StrLenField("nexthashedownername", "", length_from=lambda x: x.hashlength), # noqa: E501 RRlistField("typebitmaps", "") ]
class RPLDAGMCNodeEnergy(DAGMCObj): """ DAG Metric: Node Energy """ name = "Node Energy" fields_desc = [ ByteEnumField("otype", 2, DAGMC_OBJTYPE), BitField("resflags", 0, 5), BitField("P", 0, 1), BitField("C", 0, 1), BitField("O", 0, 1), BitField("R", 0, 1), BitEnumField("A", 0, 3, AGG_RTMETRIC), BitField("prec", 0, 4), ByteField("len", None), # NE Sub-Object Format BitField("flags", 0, 4), BitField("I", 0, 1), BitField("T", 0, 2), BitField("E", 0, 1), ByteField("E_E", 0) ]
class USB(Packet): """ Represents a USB packet saved by USBPcap. Based on packet format specified at http://desowin.org/usbpcap/captureformat.html. """ name = "USBPcap" fields_desc = [ LEShortField("headerLen", None), LELongField("irpId", None), LEIntField("status", None), LEShortField("function", None), ByteField("info", None), LEShortField("bus", 0), LEShortField("device", 0), BitEnumField("direction", 0, 1, { 0: "OUT", 1: "IN" }), BitField("endpoint", 0, 7), ByteEnumField("transfer", 0, { 0: "isochronous", 1: "interrupt", 2: "control", 3: "bulk" }), LEIntField("bodyLength", 0) ] def detail(self): if hasattr(self.payload, "detail"): return self.sprintf("USB ep=%USB.endpoint% %-3s,USB.direction%") + " | " + self.payload.detail() else: pass
class ZigbeeClusterLibrary(Packet): name = "Zigbee Cluster Library (ZCL) Frame" fields_desc = [ # Frame control (8 bits) BitField("reserved", 0, 3), BitField("disable_default_response", 0, 1), # 0 default response command will be returned # noqa: E501 BitField("direction", 0, 1), # 0 command sent from client to server; 1 command sent from server to client # noqa: E501 BitField("manufacturer_specific", 0, 1), # 0 manufacturer code shall not be included in the ZCL frame # noqa: E501 # Frame Type # 0b00 command acts across the entire profile # 0b01 command is specific to a cluster # 0b10 - 0b11 reserved BitEnumField("zcl_frametype", 0, 2, {0: 'profile-wide', 1: 'cluster-specific', 2: 'reserved2', 3: 'reserved3'}), # noqa: E501 # Manufacturer code (0/16 bits) only present then manufacturer_specific field is set to 1 # noqa: E501 ConditionalField(XLEShortField("manufacturer_code", 0x0), lambda pkt: pkt.getfieldval("manufacturer_specific") == 1 # noqa: E501 ), # Transaction sequence number (8 bits) ByteField("transaction_sequence", 0), # Command identifier (8 bits): the cluster command ByteEnumField("command_identifier", 0, _zcl_command_frames), ] def guess_payload_class(self, payload): # Profile-wide commands if self.zcl_frametype == 0x00 and self.command_identifier == 0x00: return ZCLGeneralReadAttributes elif self.zcl_frametype == 0x00 and self.command_identifier == 0x01: return ZCLGeneralReadAttributesResponse # Cluster-specific commands elif self.zcl_frametype == 0x01 and self.command_identifier == 0x00 and self.direction == 0 and self.underlayer.cluster == 0x0700: # "price" # noqa: E501 return ZCLPriceGetCurrentPrice elif self.zcl_frametype == 0x01 and self.command_identifier == 0x01 and self.direction == 0 and self.underlayer.cluster == 0x0700: # "price" # noqa: E501 return ZCLPriceGetScheduledPrices elif self.zcl_frametype == 0x01 and self.command_identifier == 0x00 and self.direction == 1 and self.underlayer.cluster == 0x0700: # "price" # noqa: E501 return ZCLPricePublishPrice else: return Packet.guess_payload_class(self, payload)
class LLDPDUGenericOrganisationSpecific(LLDPDU): ORG_UNIQUE_CODE_PNO = 0x000ecf ORG_UNIQUE_CODE_IEEE_802_1 = 0x0080c2 ORG_UNIQUE_CODE_IEEE_802_3 = 0x00120f ORG_UNIQUE_CODE_TIA_TR_41_MED = 0x0012bb ORG_UNIQUE_CODE_HYTEC = 0x30b216 ORG_UNIQUE_CODES = { ORG_UNIQUE_CODE_PNO: "PROFIBUS International (PNO)", ORG_UNIQUE_CODE_IEEE_802_1: "IEEE 802.1", ORG_UNIQUE_CODE_IEEE_802_3: "IEEE 802.3", ORG_UNIQUE_CODE_TIA_TR_41_MED: "TIA TR-41 Committee . Media Endpoint Discovery", # noqa: E501 ORG_UNIQUE_CODE_HYTEC: "Hytec Geraetebau GmbH" } fields_desc = [ BitEnumField('_type', 127, 7, LLDPDU.TYPES), BitFieldLenField('_length', None, 9, length_of='data', adjust=lambda pkt, x: len(pkt.data) + 4), # noqa: E501 ThreeBytesEnumField('org_code', 0, ORG_UNIQUE_CODES), ByteField('subtype', 0x00), XStrLenField('data', '', length_from=lambda pkt: pkt._length - 4) ]
class ExpectedSubmodule(Packet): """Description of a submodule in an API of an expected submodule""" name = "Submodule" fields_desc = [ XShortField("SubslotNumber", 0), XIntField("SubmoduleIdentNumber", 0), # Submodule Properties XByteField("SubmoduleProperties_reserved_2", 0), BitField("SubmoduleProperties_reserved_1", 0, 2), BitField("SubmoduleProperties_DiscardIOXS", 0, 1), BitField("SubmoduleProperties_ReduceOutputSubmoduleDataLength", 0, 1), BitField("SubmoduleProperties_ReduceInputSubmoduleDataLength", 0, 1), BitField("SubmoduleProperties_SharedInput", 0, 1), BitEnumField("SubmoduleProperties_Type", 0, 2, ["NO_IO", "INPUT", "OUTPUT", "INPUT_OUTPUT"]), PacketListField( "DataDescription", [], ExpectedSubmoduleDataDescription, count_from=lambda p: 2 if p.SubmoduleProperties_Type == 3 else 1 ), ] def extract_padding(self, s): return None, s # No extra payload
class AlarmCRBlockReq(Block): """Alarm CR block request""" fields_desc = [ BlockHeader, XShortEnumField("AlarmCRType", 1, ALARM_CR_TYPE), ShortField("LT", 0x8892), BitField("AlarmCRProperties_Priority", 0, 1), BitEnumField("AlarmCRProperties_Transport", 0, 1, ALARM_CR_TRANSPORT), BitField("AlarmCRProperties_Reserved1", 0, 22), BitField("AlarmCRProperties_Reserved2", 0, 8), ShortField("RTATimeoutFactor", 0x0001), ShortField("RTARetries", 0x0003), ShortField("LocalAlarmReference", 0x0003), ShortField("MaxAlarmDataLength", 0x00C8), ShortField("AlarmCRTagHeaderHigh", 0xC000), ShortField("AlarmCRTagHeaderLow", 0xA000), ] # default block_type value block_type = 0x0103 def post_build(self, p, pay): # Set the LT based on transport if self.AlarmCRProperties_Transport == 0x1: p = p[:8] + struct.pack("!H", 0x0800) + p[10:] return Block.post_build(self, p, pay) def get_response(self): """Generate the response block of this request. Careful: it only sets the fields which can be set from the request """ res = AlarmCRBlockRes() for field in ["AlarmCRType", "LocalAlarmReference"]: res.setfieldval(field, self.getfieldval(field)) res.block_type = self.block_type + 0x8000 return res
class PIMv2Hdr(Packet): name = "Protocol Independent Multicast Version 2 Header" fields_desc = [ BitField("version", 2, 4), BitEnumField("type", 0, 4, PIM_TYPE), ByteField("reserved", 0), XShortField("chksum", None) ] def post_build(self, p, pay): """ Called implicitly before a packet is sent to compute and place PIM checksum. Parameters: self The instantiation of an PIMv2Hdr class p The PIMv2Hdr message in hex in network byte order pay Additional payload for the PIMv2Hdr message """ p += pay if self.chksum is None: ck = checksum(p) p = p[:2] + struct.pack("!H", ck) + p[4:] return p
class IEC104_IE_AFQ: """ AFQ - acknowledge file or section qualifier EN 60870-5-101:2003, sec. 7.2.6.32 (p. 55) """ ACK_FILE_OR_SEC_FLAG_UNUSED = 0 ACK_FILE_OR_SEC_FLAG_POSITIVE_ACK_FILE_TRANSFER = 1 ACK_FILE_OR_SEC_FLAG_NEGATIVE_ACK_FILE_TRANSFER = 2 ACK_FILE_OR_SEC_FLAG_POSITIVE_ACK_SECTION_TRANSFER = 3 ACK_FILE_OR_SEC_FLAG_NEGATIVE_ACK_SECTION_TRANSFER = 4 ACK_FILE_OR_SEC_FLAGS = { ACK_FILE_OR_SEC_FLAG_UNUSED: 'unused', ACK_FILE_OR_SEC_FLAG_POSITIVE_ACK_FILE_TRANSFER: 'positive acknowledge' ' file transfer', ACK_FILE_OR_SEC_FLAG_NEGATIVE_ACK_FILE_TRANSFER: 'negative acknowledge' ' file transfer', ACK_FILE_OR_SEC_FLAG_POSITIVE_ACK_SECTION_TRANSFER: 'positive ' 'acknowledge ' 'section transfer', ACK_FILE_OR_SEC_FLAG_NEGATIVE_ACK_SECTION_TRANSFER: 'negative ' 'acknowledge ' 'section transfer' } ACK_FILE_OR_SEC_ERR_FLAG_UNUSED = 0 ACK_FILE_OR_SEC_ERR_FLAG_REQ_MEM_AREA_NO_AVAIL = 1 ACK_FILE_OR_SEC_ERR_FLAG_INVALID_CHECKSUM = 2 ACK_FILE_OR_SEC_ERR_FLAG_UNEXPECTED_COMMUNICATION_SERVICE = 3 ACK_FILE_OR_SEC_ERR_FLAG_UNEXPECTED_FILENAME = 4 ACK_FILE_OR_SEC_ERR_FLAG_UNEXPECTED_SECTION_NAME = 5 ACK_FILE_OR_SEC_ERR_FLAGS = { ACK_FILE_OR_SEC_ERR_FLAG_UNUSED: 'unused', ACK_FILE_OR_SEC_ERR_FLAG_REQ_MEM_AREA_NO_AVAIL: 'requested memory ' 'area not available', ACK_FILE_OR_SEC_ERR_FLAG_INVALID_CHECKSUM: 'invalid checksum', ACK_FILE_OR_SEC_ERR_FLAG_UNEXPECTED_COMMUNICATION_SERVICE: 'unexpected' ' communica' 'tion ' 'service', ACK_FILE_OR_SEC_ERR_FLAG_UNEXPECTED_FILENAME: 'unexpected file name', ACK_FILE_OR_SEC_ERR_FLAG_UNEXPECTED_SECTION_NAME: 'unexpected ' 'section name' } GENERATED_ATTRIBUTES = [ ('ACK_FILE_OR_SEC_FLAG_COMPATIBLE_RESERVED', 'compatible reserved', ACK_FILE_OR_SEC_FLAGS, 5, 10), ('ACK_FILE_OR_SEC_FLAG_PRIVATE_RESERVED', 'private reserved', ACK_FILE_OR_SEC_FLAGS, 11, 15), ('ACK_FILE_OR_SEC_ERR_FLAG_COMPATIBLE_RESERVED', 'compatible reserved', ACK_FILE_OR_SEC_ERR_FLAGS, 6, 10), ('ACK_FILE_OR_SEC_ERR_FLAG_PRIVATE_RESERVED', 'private reserved', ACK_FILE_OR_SEC_ERR_FLAGS, 11, 15) ] informantion_element_fields = [ BitEnumField('errors', 0, 4, ACK_FILE_OR_SEC_ERR_FLAGS), BitEnumField('ack_file_or_sec', 0, 4, ACK_FILE_OR_SEC_FLAGS) ]
class IEC104_IE_SCQ: """ SCQ - select and call qualifier EN 60870-5-101:2003, sec. 7.2.6.30 (p. 55) """ SEL_CALL_FLAG_UNUSED = 0 SEL_CALL_FLAG_FILE_SELECT = 1 SEL_CALL_FLAG_FILE_REQUEST = 2 SEL_CALL_FLAG_FILE_ABORT = 3 SEL_CALL_FLAG_FILE_DELETE = 4 SEL_CALL_FLAG_SECTION_SELECTION = 5 SEL_CALL_FLAG_SECTION_REQUEST = 6 SEL_CALL_FLAG_SECTION_ABORT = 7 SEL_CALL_FLAGS = { SEL_CALL_FLAG_UNUSED: 'unused', SEL_CALL_FLAG_FILE_SELECT: 'file select', SEL_CALL_FLAG_FILE_REQUEST: 'file request', SEL_CALL_FLAG_FILE_ABORT: 'file abort', SEL_CALL_FLAG_FILE_DELETE: 'file delete', SEL_CALL_FLAG_SECTION_SELECTION: 'section selection', SEL_CALL_FLAG_SECTION_REQUEST: 'section request', SEL_CALL_FLAG_SECTION_ABORT: 'section abort' } SEL_CALL_ERR_FLAG_UNUSED = 0 SEL_CALL_ERR_FLAG_REQ_MEM_AREA_NO_AVAIL = 1 SEL_CALL_ERR_FLAG_INVALID_CHECKSUM = 2 SEL_CALL_ERR_FLAG_UNEXPECTED_COMMUNICATION_SERVICE = 3 SEL_CALL_ERR_FLAG_UNEXPECTED_FILENAME = 4 SEL_CALL_ERR_FLAG_UNEXPECTED_SECTION_NAME = 5 SEL_CALL_ERR_FLAG_COMPATIBLE_RESERVED_6 = 6 SEL_CALL_ERR_FLAG_COMPATIBLE_RESERVED_7 = 7 SEL_CALL_ERR_FLAG_COMPATIBLE_RESERVED_8 = 8 SEL_CALL_ERR_FLAG_COMPATIBLE_RESERVED_9 = 9 SEL_CALL_ERR_FLAG_COMPATIBLE_RESERVED_10 = 10 SEL_CALL_ERR_FLAG_PRIVATE_RESERVED_11 = 11 SEL_CALL_ERR_FLAG_PRIVATE_RESERVED_12 = 12 SEL_CALL_ERR_FLAG_PRIVATE_RESERVED_13 = 13 SEL_CALL_ERR_FLAG_PRIVATE_RESERVED_14 = 14 SEL_CALL_ERR_FLAG_PRIVATE_RESERVED_15 = 15 SEL_CALL_ERR_FLAGS = { SEL_CALL_ERR_FLAG_UNUSED: 'unused', SEL_CALL_ERR_FLAG_REQ_MEM_AREA_NO_AVAIL: 'requested memory area ' 'not available', SEL_CALL_ERR_FLAG_INVALID_CHECKSUM: 'invalid checksum', SEL_CALL_ERR_FLAG_UNEXPECTED_COMMUNICATION_SERVICE: 'unexpected ' 'communication ' 'service', SEL_CALL_ERR_FLAG_UNEXPECTED_FILENAME: 'unexpected file name', SEL_CALL_ERR_FLAG_UNEXPECTED_SECTION_NAME: 'unexpected section name' } GENERATED_ATTRIBUTES = [('SEL_CALL_FLAG_COMPATIBLE_RESERVED', 'compatible reserved', SEL_CALL_FLAGS, 8, 10), ('SEL_CALL_FLAG_PRIVATE_RESERVED', 'private reserved', SEL_CALL_FLAGS, 11, 15), ('SEL_CALL_ERR_FLAG_COMPATIBLE_RESERVED', 'compatible reserved', SEL_CALL_ERR_FLAGS, 6, 10), ('SEL_CALL_ERR_FLAG_PRIVATE_RESERVED', 'private reserved', SEL_CALL_ERR_FLAGS, 11, 15)] informantion_element_fields = [ BitEnumField('errors', 0, 4, SEL_CALL_ERR_FLAGS), BitEnumField('select_call', 0, 4, SEL_CALL_FLAGS) ]
class Dot11(Packet): name = "802.11" fields_desc = [ BitField("subtype", 0, 4), BitEnumField("type", 0, 2, ["Management", "Control", "Data", "Reserved"]), BitField("proto", 0, 2), FlagsField("FCfield", 0, 8, [ "to-DS", "from-DS", "MF", "retry", "pw-mgt", "MD", "protected", "order" ]), ShortField("ID", 0), MACField("addr1", ETHER_ANY), ConditionalField( MACField("addr2", ETHER_ANY), lambda pkt: (pkt.type != 1 or pkt.subtype in [0x8, 0x9, 0xa, 0xb, 0xe, 0xf]), ), ConditionalField( MACField("addr3", ETHER_ANY), lambda pkt: pkt.type in [0, 2], ), ConditionalField(LEShortField("SC", 0), lambda pkt: pkt.type != 1), ConditionalField( MACField("addr4", ETHER_ANY), lambda pkt: (pkt.type == 2 and pkt.FCfield & 3 == 3), # from-DS+to-DS ) ] def mysummary(self): # Supports both Dot11 and Dot11FCS # return self.sprintf("802.11 %%%s.type%% %%%s.subtype%% %%%s.addr2%% > %%%s.addr1%%" % ((self.__class__.__name__,) * 4)) # noqa: E501 return self.sprintf("802.11 %%%s.type%% %%%s.subtype%%" % ((self.__class__.__name__, ) * 2)) # noqa: E501 def guess_payload_class(self, payload): if self.type == 0x02 and (0x08 <= self.subtype <= 0xF and self.subtype != 0xD): # noqa: E501 return Dot11QoS elif self.FCfield.protected: # When a frame is handled by encryption, the Protected Frame bit # (previously called WEP bit) is set to 1, and the Frame Body # begins with the appropriate cryptographic header. return Dot11Encrypted else: return Packet.guess_payload_class(self, payload) def answers(self, other): if isinstance(other, Dot11): if self.type == 0: # management if self.addr1.lower() != other.addr2.lower( ): # check resp DA w/ req SA # noqa: E501 return 0 if (other.subtype, self.subtype) in [(0, 1), (2, 3), (4, 5)]: return 1 if self.subtype == other.subtype == 11: # auth return self.payload.answers(other.payload) elif self.type == 1: # control return 0 elif self.type == 2: # data return self.payload.answers(other.payload) elif self.type == 3: # reserved return 0 return 0 def unwep(self, key=None, warn=1): if self.FCfield & 0x40 == 0: if warn: warning("No WEP to remove") return if isinstance(self.payload.payload, NoPayload): if key or conf.wepkey: self.payload.decrypt(key) if isinstance(self.payload.payload, NoPayload): if warn: warning("Dot11 can't be decrypted. Check conf.wepkey.") return self.FCfield &= ~0x40 self.payload = self.payload.payload
class DNS(Packet): name = "DNS" fields_desc = [ ConditionalField(ShortField("length", None), lambda p: isinstance(p.underlayer, TCP)), ShortField("id", 0), BitField("qr", 0, 1), BitEnumField("opcode", 0, 4, { 0: "QUERY", 1: "IQUERY", 2: "STATUS" }), BitField("aa", 0, 1), BitField("tc", 0, 1), BitField("rd", 1, 1), BitField("ra", 0, 1), BitField("z", 0, 1), # AD and CD bits are defined in RFC 2535 BitField("ad", 0, 1), # Authentic Data BitField("cd", 0, 1), # Checking Disabled BitEnumField( "rcode", 0, 4, { 0: "ok", 1: "format-error", 2: "server-failure", 3: "name-error", 4: "not-implemented", 5: "refused" }), DNSRRCountField("qdcount", None, "qd"), DNSRRCountField("ancount", None, "an"), DNSRRCountField("nscount", None, "ns"), DNSRRCountField("arcount", None, "ar"), DNSQRField("qd", "qdcount", DNSQR()), DNSRRField("an", "ancount", None), DNSRRField("ns", "nscount", None), DNSRRField("ar", "arcount", None, 0), ] def answers(self, other): return (isinstance(other, DNS) and self.id == other.id and self.qr == 1 and other.qr == 0) def mysummary(self): name = "" if self.qr: type = "Ans" if self.ancount > 0 and isinstance(self.an, DNSRR): name = ' "%s"' % self.an.rdata else: type = "Qry" if self.qdcount > 0 and isinstance(self.qd, DNSQR): name = ' "%s"' % self.qd.qname return 'DNS %s%s ' % (type, name) def post_build(self, pkt, pay): if isinstance(self.underlayer, TCP) and self.length is None: pkt = struct.pack("!H", len(pkt) - 2) + pkt[2:] return pkt + pay def compress(self): """Return the compressed DNS packet (using `dns_compress()`""" return dns_compress(self) def pre_dissect(self, s): """ Check that a valid DNS over TCP message can be decoded """ if isinstance(self.underlayer, TCP): # Compute the length of the DNS packet if len(s) >= 2: dns_len = struct.unpack("!H", s[:2])[0] else: message = "Malformed DNS message: too small!" log_runtime.info(message) raise Scapy_Exception(message) # Check if the length is valid if dns_len < 14 or len(s) < dns_len: message = "Malformed DNS message: invalid length!" log_runtime.info(message) raise Scapy_Exception(message) return s
class LoWPAN_IPHC(Packet): """6LoWPAN IPv6 header compressed packets It follows the implementation of draft-ietf-6lowpan-hc-15. """ # the LOWPAN_IPHC encoding utilizes 13 bits, 5 dispatch type name = "LoWPAN IP Header Compression Packet" _address_modes = ["Unspecified", "1", "16-bits inline", "Compressed"] _state_mode = ["Stateless", "Stateful"] fields_desc = [ # dispatch BitField("_reserved", 0x03, 3), BitField("tf", 0x0, 2), BitEnumField("nh", 0x0, 1, ["Inline", "Compressed"]), BitField("hlim", 0x0, 2), BitEnumField("cid", 0x0, 1, [False, True]), BitEnumField("sac", 0x0, 1, _state_mode), BitEnumField("sam", 0x0, 2, _address_modes), BitEnumField("m", 0x0, 1, [False, True]), BitEnumField("dac", 0x0, 1, _state_mode), BitEnumField("dam", 0x0, 2, _address_modes), ConditionalField(ByteField("_contextIdentifierExtension", 0x0), lambda pkt: pkt.cid == 0x1), # TODO: THIS IS WRONG!!!!! BitVarSizeField("tc_ecn", 0, calculate_length=lambda pkt: tf_last_attempt(pkt)[0] ), # noqa: E501 BitVarSizeField("tc_dscp", 0, calculate_length=lambda pkt: tf_last_attempt(pkt)[1] ), # noqa: E501 BitVarSizeField("_padd", 0, calculate_length=lambda pkt: tf_last_attempt(pkt)[2] ), # noqa: E501 BitVarSizeField("flowlabel", 0, calculate_length=lambda pkt: tf_last_attempt(pkt)[3] ), # noqa: E501 # NH ConditionalField(ByteField("_nhField", 0x0), lambda pkt: not pkt.nh), # HLIM: Hop Limit: if it's 0 ConditionalField(ByteField("_hopLimit", 0x0), lambda pkt: pkt.hlim == 0x0), IP6FieldLenField("sourceAddr", "::", 0, length_of=source_addr_mode2), IP6FieldLenField( "destinyAddr", "::", 0, length_of=destiny_addr_mode), # problem when it's 0 # noqa: E501 # LoWPAN_UDP Header Compression ######################################## # noqa: E501 # TODO: IMPROVE!!!!! ConditionalField( FlagsField( "header_compression", 0, 8, ["A", "B", "C", "D", "E", "C", "PS", "PD"]), # noqa: E501 lambda pkt: pkt.nh), ConditionalField( BitFieldLenField( "udpSourcePort", 0x0, 16, length_of=lambda pkt: nhc_port(pkt)[0]), # noqa: E501 # ShortField("udpSourcePort", 0x0), lambda pkt: pkt.nh and pkt.header_compression & 0x2 == 0x0), ConditionalField( BitFieldLenField( "udpDestinyPort", 0x0, 16, length_of=lambda pkt: nhc_port(pkt)[1]), # noqa: E501 lambda pkt: pkt.nh and pkt.header_compression & 0x1 == 0x0), ConditionalField( XShortField("udpChecksum", 0x0), lambda pkt: pkt.nh and pkt.header_compression & 0x4 == 0x0), ] def post_dissect(self, data): """dissect the IPv6 package compressed into this IPHC packet. The packet payload needs to be decompressed and depending on the arguments, several conversions should be done. """ # uncompress payload packet = IPv6() packet.version = IPHC_DEFAULT_VERSION packet.tc, packet.fl = self._getTrafficClassAndFlowLabel() if not self.nh: packet.nh = self._nhField # HLIM: Hop Limit if self.hlim == 0: packet.hlim = self._hopLimit elif self.hlim == 0x1: packet.hlim = 1 elif self.hlim == 0x2: packet.hlim = 64 else: packet.hlim = 255 # TODO: Payload length can be inferred from lower layers from either the # noqa: E501 # 6LoWPAN Fragmentation header or the IEEE802.15.4 header packet.src = self.decompressSourceAddr(packet) packet.dst = self.decompressDestinyAddr(packet) if self.nh == 1: # The Next Header field is compressed and the next header is # encoded using LOWPAN_NHC udp = UDP() if self.header_compression and \ self.header_compression & 0x4 == 0x0: udp.chksum = self.udpChecksum s, d = nhc_port(self) if s == 16: udp.sport = self.udpSourcePort elif s == 8: udp.sport = 0xF000 + s elif s == 4: udp.sport = 0xF0B0 + s if d == 16: udp.dport = self.udpDestinyPort elif d == 8: udp.dport = 0xF000 + d elif d == 4: udp.dport = 0xF0B0 + d packet.payload = udp / data data = raw(packet) # else self.nh == 0 not necessary elif self._nhField & 0xE0 == 0xE0: # IPv6 Extension Header Decompression # noqa: E501 warning('Unimplemented: IPv6 Extension Header decompression' ) # noqa: E501 packet.payload = conf.raw_layer(data) data = raw(packet) else: packet.payload = conf.raw_layer(data) data = raw(packet) return Packet.post_dissect(self, data) def decompressDestinyAddr(self, packet): try: tmp_ip = inet_pton(socket.AF_INET6, self.destinyAddr) except socket.error: tmp_ip = b"\x00" * 16 if self.m == 0 and self.dac == 0: if self.dam == 0: pass elif self.dam == 1: tmp_ip = LINK_LOCAL_PREFIX[0:8] + tmp_ip[-8:] elif self.dam == 2: tmp_ip = LINK_LOCAL_PREFIX[ 0:8] + b"\x00\x00\x00\xff\xfe\x00" + tmp_ip[ -2:] # noqa: E501 """else: #self.dam == 3 raise Exception('Unimplemented')""" elif self.m == 0 and self.dac == 1: if self.dam == 0: raise Exception('Reserved') elif self.dam == 0x3: underlayer = self.underlayer while underlayer is not None and not isinstance( underlayer, Dot15d4Data): # noqa: E501 underlayer = underlayer.underlayer if type(underlayer) == Dot15d4Data: if underlayer.underlayer.fcf_destaddrmode == 3: tmp_ip = LINK_LOCAL_PREFIX[0:8] + struct.pack( ">Q", underlayer.dest_addr) # noqa: E501 # Turn off the bit 7. tmp_ip = tmp_ip[0:8] + struct.pack( "B", (orb(tmp_ip[8]) ^ 0x2)) + tmp_ip[9:16] # noqa: E501 elif underlayer.underlayer.fcf_destaddrmode == 2: tmp_ip = LINK_LOCAL_PREFIX[0:8] + \ b"\x00\x00\x00\xff\xfe\x00" + \ struct.pack(">Q", underlayer.dest_addr)[6:] else: # Most of the times, it's necessary the IEEE 802.15.4 data to extract this address # noqa: E501 raise Exception( 'Unimplemented: IP Header is contained into IEEE 802.15.4 frame, in this case it\'s not available.' ) # noqa: E501 elif self.dam not in [0x1, 0x2]: warning("Unknown destiny address compression mode !") elif self.m == 1 and self.dac == 0: if self.dam == 0: raise Exception("unimplemented") elif self.dam == 1: tmp = b"\xff" + chb(tmp_ip[16 - destiny_addr_mode(self)]) tmp_ip = tmp + b"\x00" * 9 + tmp_ip[-5:] elif self.dam == 2: tmp = b"\xff" + chb(tmp_ip[16 - destiny_addr_mode(self)]) tmp_ip = tmp + b"\x00" * 11 + tmp_ip[-3:] else: # self.dam == 3: tmp_ip = b"\xff\x02" + b"\x00" * 13 + tmp_ip[-1:] elif self.m == 1 and self.dac == 1: if self.dam == 0x0: raise Exception( "Unimplemented: I didn't understand the 6lowpan specification" ) # noqa: E501 else: # all the others values raise Exception("Reserved value by specification.") self.destinyAddr = inet_ntop(socket.AF_INET6, tmp_ip) return self.destinyAddr def compressSourceAddr(self, ipv6): tmp_ip = inet_pton(socket.AF_INET6, ipv6.src) if self.sac == 0: if self.sam == 0x0: tmp_ip = tmp_ip elif self.sam == 0x1: tmp_ip = tmp_ip[8:16] elif self.sam == 0x2: tmp_ip = tmp_ip[14:16] else: # self.sam == 0x3: pass else: # self.sac == 1 if self.sam == 0x0: tmp_ip = b"\x00" * 16 elif self.sam == 0x1: tmp_ip = tmp_ip[8:16] elif self.sam == 0x2: tmp_ip = tmp_ip[14:16] self.sourceAddr = inet_ntop(socket.AF_INET6, b"\x00" * (16 - len(tmp_ip)) + tmp_ip) # noqa: E501 return self.sourceAddr def compressDestinyAddr(self, ipv6): tmp_ip = inet_pton(socket.AF_INET6, ipv6.dst) if self.m == 0 and self.dac == 0: if self.dam == 0x0: tmp_ip = tmp_ip elif self.dam == 0x1: tmp_ip = b"\x00" * 8 + tmp_ip[8:16] elif self.dam == 0x2: tmp_ip = b"\x00" * 14 + tmp_ip[14:16] elif self.m == 0 and self.dac == 1: if self.dam == 0x1: tmp_ip = b"\x00" * 8 + tmp_ip[8:16] elif self.dam == 0x2: tmp_ip = b"\x00" * 14 + tmp_ip[14:16] elif self.m == 1 and self.dac == 0: if self.dam == 0x1: tmp_ip = b"\x00" * 10 + tmp_ip[1:2] + tmp_ip[11:16] elif self.dam == 0x2: tmp_ip = b"\x00" * 12 + tmp_ip[1:2] + tmp_ip[13:16] elif self.dam == 0x3: tmp_ip = b"\x00" * 15 + tmp_ip[15:16] elif self.m == 1 and self.dac == 1: raise Exception('Unimplemented') self.destinyAddr = inet_ntop(socket.AF_INET6, tmp_ip) def decompressSourceAddr(self, packet): try: tmp_ip = inet_pton(socket.AF_INET6, self.sourceAddr) except socket.error: tmp_ip = b"\x00" * 16 if self.sac == 0: if self.sam == 0x0: pass elif self.sam == 0x1: tmp_ip = LINK_LOCAL_PREFIX[0:8] + tmp_ip[ 16 - source_addr_mode2(self):16] # noqa: E501 elif self.sam == 0x2: tmp = LINK_LOCAL_PREFIX[0:8] + b"\x00\x00\x00\xff\xfe\x00" tmp_ip = tmp + tmp_ip[16 - source_addr_mode2(self):16] elif self.sam == 0x3: # EXTRACT ADDRESS FROM Dot15d4 underlayer = self.underlayer if underlayer is not None: while underlayer is not None and not isinstance( underlayer, Dot15d4Data): # noqa: E501 underlayer = underlayer.underlayer assert type(underlayer) == Dot15d4Data if underlayer.underlayer.fcf_srcaddrmode == 3: tmp_ip = LINK_LOCAL_PREFIX[0:8] + struct.pack( ">Q", underlayer.src_addr) # noqa: E501 # Turn off the bit 7. tmp_ip = tmp_ip[0:8] + struct.pack( "B", (orb(tmp_ip[8]) ^ 0x2)) + tmp_ip[9:16] # noqa: E501 elif underlayer.underlayer.fcf_srcaddrmode == 2: tmp_ip = LINK_LOCAL_PREFIX[0:8] + \ b"\x00\x00\x00\xff\xfe\x00" + \ struct.pack(">Q", underlayer.src_addr)[6:] else: # Most of the times, it's necessary the IEEE 802.15.4 data to extract this address # noqa: E501 raise Exception( 'Unimplemented: IP Header is contained into IEEE 802.15.4 frame, in this case it\'s not available.' ) # noqa: E501 else: warning("Unknown source address compression mode !") else: # self.sac == 1: if self.sam == 0x0: pass elif self.sam == 0x2: # TODO: take context IID tmp = LINK_LOCAL_PREFIX[0:8] + b"\x00\x00\x00\xff\xfe\x00" tmp_ip = tmp + tmp_ip[16 - source_addr_mode2(self):16] elif self.sam == 0x3: tmp_ip = LINK_LOCAL_PREFIX[ 0:8] + b"\x00" * 8 # TODO: CONTEXT ID # noqa: E501 else: raise Exception('Unimplemented') self.sourceAddr = inet_ntop(socket.AF_INET6, tmp_ip) return self.sourceAddr def guess_payload_class(self, payload): if self.underlayer and isinstance( self.underlayer, (LoWPANFragmentationFirst, LoWPANFragmentationSubsequent)): # noqa: E501 return Raw return IPv6 def do_build(self): if not isinstance(self.payload, IPv6): return Packet.do_build(self) ipv6 = self.payload self._reserved = 0x03 # NEW COMPRESSION TECHNIQUE! # a ) Compression Techniques # 1. Set Traffic Class if self.tf == 0x0: self.tc_ecn = ipv6.tc >> 6 self.tc_dscp = ipv6.tc & 0x3F self.flowlabel = ipv6.fl elif self.tf == 0x1: self.tc_ecn = ipv6.tc >> 6 self.flowlabel = ipv6.fl elif self.tf == 0x2: self.tc_ecn = ipv6.tc >> 6 self.tc_dscp = ipv6.tc & 0x3F else: # self.tf == 0x3: pass # no field is set # 2. Next Header if self.nh == 0x0: self.nh = 0 # ipv6.nh elif self.nh == 0x1: self.nh = 0 # disable compression # The Next Header field is compressed and the next header is encoded using LOWPAN_NHC, which is discussed in Section 4.1. # noqa: E501 warning( 'Next header compression is not implemented yet ! Will be ignored' ) # noqa: E501 # 3. HLim if self.hlim == 0x0: self._hopLimit = ipv6.hlim else: # if hlim is 1, 2 or 3, there are nothing to do! pass # 4. Context (which context to use...) if self.cid == 0x0: pass else: # TODO: Context Unimplemented yet in my class self._contextIdentifierExtension = 0 # 5. Compress Source Addr self.compressSourceAddr(ipv6) self.compressDestinyAddr(ipv6) return Packet.do_build(self) def do_build_payload(self): if self.header_compression and\ self.header_compression & 240 == 240: # TODO: UDP header IMPROVE return raw(self.payload)[40 + 16:] else: return raw(self.payload)[40:] def _getTrafficClassAndFlowLabel(self): """Page 6, draft feb 2011 """ if self.tf == 0x0: return (self.tc_ecn << 6) + self.tc_dscp, self.flowlabel elif self.tf == 0x1: return (self.tc_ecn << 6), self.flowlabel elif self.tf == 0x2: return (self.tc_ecn << 6) + self.tc_dscp, 0 else: return 0, 0
class RadioTap(Packet): name = "RadioTap dummy" fields_desc = [ ByteField('version', 0), ByteField('pad', 0), LEShortField('len', None), FlagsField('present', None, -32, _rt_present), # noqa: E501 # Extended presence mask ConditionalField( PacketListField("Ext", [], next_cls_cb=_next_radiotap_extpm), lambda pkt: pkt.present and pkt.present.Ext), # noqa: E501 # RadioTap fields - each starts with a _RadiotapReversePadField # to handle padding # TSFT ConditionalField( _RadiotapReversePadField(LELongField("mac_timestamp", 0)), lambda pkt: pkt.present and pkt.present.TSFT), # Flags ConditionalField( _RadiotapReversePadField(FlagsField("Flags", None, -8, _rt_flags)), lambda pkt: pkt.present and pkt.present.Flags), # Rate ConditionalField(_RadiotapReversePadField(ByteField("Rate", 0)), lambda pkt: pkt.present and pkt.present.Rate), # Channel ConditionalField(_RadiotapReversePadField(LEShortField("Channel", 0)), lambda pkt: pkt.present and pkt.present.Channel), ConditionalField( FlagsField("ChannelFlags", None, -16, _rt_channelflags), lambda pkt: pkt.present and pkt.present.Channel), # dBm_AntSignal ConditionalField( _RadiotapReversePadField(_dbmField("dBm_AntSignal", -256)), lambda pkt: pkt.present and pkt.present.dBm_AntSignal), # dBm_AntNoise ConditionalField( _RadiotapReversePadField(_dbmField("dBm_AntNoise", -256)), lambda pkt: pkt.present and pkt.present.dBm_AntNoise), # Lock_Quality ConditionalField( _RadiotapReversePadField(LEShortField("Lock_Quality", 0), ), lambda pkt: pkt.present and pkt.present.Lock_Quality), # Antenna ConditionalField(_RadiotapReversePadField(ByteField("Antenna", 0)), lambda pkt: pkt.present and pkt.present.Antenna), # RX Flags ConditionalField( _RadiotapReversePadField( FlagsField("RXFlags", None, -16, _rt_rxflags)), lambda pkt: pkt.present and pkt.present.RXFlags), # TX Flags ConditionalField( _RadiotapReversePadField( FlagsField("TXFlags", None, -16, _rt_txflags)), lambda pkt: pkt.present and pkt.present.TXFlags), # ChannelPlus ConditionalField( _RadiotapReversePadField( FlagsField("ChannelFlags2", None, -32, _rt_channelflags2)), lambda pkt: pkt.present and pkt.present.ChannelPlus), ConditionalField(LEShortField("ChannelFrequency", 0), lambda pkt: pkt.present and pkt.present.ChannelPlus), ConditionalField(ByteField("ChannelNumber", 0), lambda pkt: pkt.present and pkt.present.ChannelPlus), # MCS ConditionalField( _RadiotapReversePadField( FlagsField("knownMCS", None, -8, _rt_knownmcs)), lambda pkt: pkt.present and pkt.present.MCS), ConditionalField(BitField("Ness_LSB", 0, 1), lambda pkt: pkt.present and pkt.present.MCS), ConditionalField(BitField("STBC_streams", 0, 2), lambda pkt: pkt.present and pkt.present.MCS), ConditionalField(BitEnumField("FEC_type", 0, 1, { 0: "BCC", 1: "LDPC" }), lambda pkt: pkt.present and pkt.present.MCS), ConditionalField( BitEnumField("HT_format", 0, 1, { 0: "mixed", 1: "greenfield" }), lambda pkt: pkt.present and pkt.present.MCS), ConditionalField( BitEnumField("guard_interval", 0, 1, { 0: "Long_GI", 1: "Short_GI" }), # noqa: E501 lambda pkt: pkt.present and pkt.present.MCS), ConditionalField(BitEnumField("MCS_bandwidth", 0, 2, _rt_bandwidth), lambda pkt: pkt.present and pkt.present.MCS), ConditionalField(ByteField("MCS_index", 0), lambda pkt: pkt.present and pkt.present.MCS), # A_MPDU ConditionalField(_RadiotapReversePadField(LEIntField("A_MPDU_ref", 0)), lambda pkt: pkt.present and pkt.present.A_MPDU), ConditionalField( FlagsField("A_MPDU_flags", None, -32, _rt_a_mpdu_flags), lambda pkt: pkt.present and pkt.present.A_MPDU), # VHT ConditionalField( _RadiotapReversePadField( FlagsField("KnownVHT", None, -16, _rt_knownvht)), lambda pkt: pkt.present and pkt.present.VHT), ConditionalField(FlagsField("PresentVHT", None, -8, _rt_presentvht), lambda pkt: pkt.present and pkt.present.VHT), ConditionalField(ByteEnumField("VHT_bandwidth", 0, _rt_vhtbandwidth), lambda pkt: pkt.present and pkt.present.VHT), ConditionalField(StrFixedLenField("mcs_nss", 0, length=5), lambda pkt: pkt.present and pkt.present.VHT), ConditionalField(ByteField("GroupID", 0), lambda pkt: pkt.present and pkt.present.VHT), ConditionalField(ShortField("PartialAID", 0), lambda pkt: pkt.present and pkt.present.VHT), # timestamp ConditionalField(_RadiotapReversePadField(LELongField("timestamp", 0)), lambda pkt: pkt.present and pkt.present.timestamp), ConditionalField(LEShortField("ts_accuracy", 0), lambda pkt: pkt.present and pkt.present.timestamp), ConditionalField(ByteField("ts_position", 0), lambda pkt: pkt.present and pkt.present.timestamp), ConditionalField(ByteField("ts_flags", 0), lambda pkt: pkt.present and pkt.present.timestamp), # HE - XXX not complete ConditionalField(_RadiotapReversePadField(ShortField("he_data1", 0)), lambda pkt: pkt.present and pkt.present.HE), ConditionalField(ShortField("he_data2", 0), lambda pkt: pkt.present and pkt.present.HE), ConditionalField(ShortField("he_data3", 0), lambda pkt: pkt.present and pkt.present.HE), ConditionalField(ShortField("he_data4", 0), lambda pkt: pkt.present and pkt.present.HE), ConditionalField(ShortField("he_data5", 0), lambda pkt: pkt.present and pkt.present.HE), ConditionalField(ShortField("he_data6", 0), lambda pkt: pkt.present and pkt.present.HE), # HE_MU ConditionalField( _RadiotapReversePadField(LEShortField("hemu_flags1", 0)), lambda pkt: pkt.present and pkt.present.HE_MU), ConditionalField(LEShortField("hemu_flags2", 0), lambda pkt: pkt.present and pkt.present.HE_MU), ConditionalField( FieldListField("RU_channel1", [], ByteField, count_from=lambda x: 4), lambda pkt: pkt.present and pkt.present.HE_MU), ConditionalField( FieldListField("RU_channel2", [], ByteField, count_from=lambda x: 4), lambda pkt: pkt.present and pkt.present.HE_MU), # HE_MU_other_user ConditionalField( _RadiotapReversePadField(LEShortField("hemuou_per_user_1", 0x7fff)), lambda pkt: pkt.present and pkt.present.HE_MU_other_user), ConditionalField( LEShortField("hemuou_per_user_2", 0x003f), lambda pkt: pkt.present and pkt.present.HE_MU_other_user), ConditionalField( ByteField("hemuou_per_user_position", 0), lambda pkt: pkt.present and pkt.present.HE_MU_other_user), ConditionalField( FlagsField("hemuou_per_user_known", 0, -16, _rt_hemuother_per_user_known), lambda pkt: pkt.present and pkt.present.HE_MU_other_user), # L_SIG ConditionalField( _RadiotapReversePadField( FlagsField("lsig_data1", 0, -16, ["rate", "length"])), lambda pkt: pkt.present and pkt.present.L_SIG), ConditionalField(BitField("lsig_length", 0, 12), lambda pkt: pkt.present and pkt.present.L_SIG), ConditionalField(BitField("lsig_rate", 0, 4), lambda pkt: pkt.present and pkt.present.L_SIG), # Remaining StrLenField( 'notdecoded', "", length_from=lambda pkt: max(pkt.len - pkt._tmp_dissect_pos, 0)) ] def guess_payload_class(self, payload): if self.present and self.present.Flags and self.Flags.FCS: return Dot11FCS return Dot11 def post_build(self, p, pay): if self.len is None: p = p[:2] + struct.pack("!H", len(p))[::-1] + p[4:] return p + pay
class MQTTConnect(Packet): name = "MQTT connect" fields_desc = [ FieldLenField("length", None, length_of="protoname"), StrLenField("protoname", "", length_from=lambda pkt: pkt.length), ByteEnumField("protolevel", 5, PROTOCOL_LEVEL), BitEnumField("usernameflag", 0, 1, { 0: 'Disabled', 1: 'Enabled' }), BitEnumField("passwordflag", 0, 1, { 0: 'Disabled', 1: 'Enabled' }), BitEnumField("willretainflag", 0, 1, { 0: 'Disabled', 1: 'Enabled' }), BitEnumField("willQOSflag", 0, 2, QOS_LEVEL), BitEnumField("willflag", 0, 1, { 0: 'Disabled', 1: 'Enabled' }), BitEnumField("cleansess", 0, 1, { 0: 'Disabled', 1: 'Enabled' }), BitEnumField("reserved", 0, 1, { 0: 'Disabled', 1: 'Enabled' }), ShortField("klive", 0), #CONNECT PROPERTIES FieldLenField("proplen", None, fmt='B', length_of="properties"), ConditionalField( PacketListField("properties", [], pkt_cls=MQTTProperty, length_from=lambda pkt: pkt.proplen), lambda pkt: pkt.proplen != 0), FieldLenField("clientIdlen", None, length_of="clientId"), StrLenField("clientId", "", length_from=lambda pkt: pkt.clientIdlen), # Payload with optional fields depending on the flags # WILL PROPERTIES ConditionalField( FieldLenField("willproplen", None, fmt='B', length_of="willproperties"), lambda pkt: pkt.willflag == 1), ConditionalField( PacketListField("willproperties", [], pkt_cls=MQTTWillProperty, length_from=lambda pkt: pkt.willproplen), lambda pkt: pkt.willflag == 1), ConditionalField(FieldLenField("wtoplen", None, length_of="willtopic"), lambda pkt: pkt.willflag == 1), ConditionalField( StrLenField("willtopic", "", length_from=lambda pkt: pkt.wtoplen), lambda pkt: pkt.willflag == 1), ConditionalField(FieldLenField("wmsglen", None, length_of="willmsg"), lambda pkt: pkt.willflag == 1), ConditionalField( StrLenField("willmsg", "", length_from=lambda pkt: pkt.wmsglen), lambda pkt: pkt.willflag == 1), ConditionalField(FieldLenField("userlen", None, length_of="username"), lambda pkt: pkt.usernameflag == 1), ConditionalField( StrLenField("username", "", length_from=lambda pkt: pkt.userlen), lambda pkt: pkt.usernameflag == 1), ConditionalField(FieldLenField("passlen", None, length_of="password"), lambda pkt: pkt.passwordflag == 1), ConditionalField( StrLenField("password", "", length_from=lambda pkt: pkt.passlen), lambda pkt: pkt.passwordflag == 1), ]
class LLDPDUManagementAddress(LLDPDU): """ ieee 802.1ab-2016 - sec. 8.5.9 / p. 32 currently only 0x00..0x1e are used by standards, no way to use anything > 0xff as management address subtype is only one octet wide see https://www.iana.org/assignments/address-family-numbers/address-family-numbers.xhtml # noqa: E501 """ IANA_ADDRESS_FAMILY_NUMBERS = { 0x00: 'other', 0x01: 'IPv4', 0x02: 'IPv6', 0x03: 'NSAP', 0x04: 'HDLC', 0x05: 'BBN', 0x06: '802', 0x07: 'E.163', 0x08: 'E.164', 0x09: 'F.69', 0x0a: 'X.121', 0x0b: 'IPX', 0x0c: 'Appletalk', 0x0d: 'Decnet IV', 0x0e: 'Banyan Vines', 0x0f: 'E.164 with NSAP', 0x10: 'DNS', 0x11: 'Distinguished Name', 0x12: 'AS Number', 0x13: 'XTP over IPv4', 0x14: 'XTP over IPv6', 0x15: 'XTP native mode XTP', 0x16: 'Fiber Channel World-Wide Port Name', 0x17: 'Fiber Channel World-Wide Node Name', 0x18: 'GWID', 0x19: 'AFI for L2VPN', 0x1a: 'MPLS-TP Section Endpoint ID', 0x1b: 'MPLS-TP LSP Endpoint ID', 0x1c: 'MPLS-TP Pseudowire Endpoint ID', 0x1d: 'MT IP Multi-Topology IPv4', 0x1e: 'MT IP Multi-Topology IPv6' } SUBTYPE_MANAGEMENT_ADDRESS_OTHER = 0x00 SUBTYPE_MANAGEMENT_ADDRESS_IPV4 = 0x01 SUBTYPE_MANAGEMENT_ADDRESS_IPV6 = 0x02 SUBTYPE_MANAGEMENT_ADDRESS_NSAP = 0x03 SUBTYPE_MANAGEMENT_ADDRESS_HDLC = 0x04 SUBTYPE_MANAGEMENT_ADDRESS_BBN = 0x05 SUBTYPE_MANAGEMENT_ADDRESS_802 = 0x06 SUBTYPE_MANAGEMENT_ADDRESS_E_163 = 0x07 SUBTYPE_MANAGEMENT_ADDRESS_E_164 = 0x08 SUBTYPE_MANAGEMENT_ADDRESS_F_69 = 0x09 SUBTYPE_MANAGEMENT_ADDRESS_X_121 = 0x0A SUBTYPE_MANAGEMENT_ADDRESS_IPX = 0x0B SUBTYPE_MANAGEMENT_ADDRESS_APPLETALK = 0x0C SUBTYPE_MANAGEMENT_ADDRESS_DECNET_IV = 0x0D SUBTYPE_MANAGEMENT_ADDRESS_BANYAN_VINES = 0x0E SUBTYPE_MANAGEMENT_ADDRESS_E_164_WITH_NSAP = 0x0F SUBTYPE_MANAGEMENT_ADDRESS_DNS = 0x10 SUBTYPE_MANAGEMENT_ADDRESS_DISTINGUISHED_NAME = 0x11 SUBTYPE_MANAGEMENT_ADDRESS_AS_NUMBER = 0x12 SUBTYPE_MANAGEMENT_ADDRESS_XTP_OVER_IPV4 = 0x13 SUBTYPE_MANAGEMENT_ADDRESS_XTP_OVER_IPV6 = 0x14 SUBTYPE_MANAGEMENT_ADDRESS_XTP_NATIVE_MODE_XTP = 0x15 SUBTYPE_MANAGEMENT_ADDRESS_FIBER_CHANNEL_WORLD_WIDE_PORT_NAME = 0x16 SUBTYPE_MANAGEMENT_ADDRESS_FIBER_CHANNEL_WORLD_WIDE_NODE_NAME = 0x17 SUBTYPE_MANAGEMENT_ADDRESS_GWID = 0x18 SUBTYPE_MANAGEMENT_ADDRESS_AFI_FOR_L2VPN = 0x19 SUBTYPE_MANAGEMENT_ADDRESS_MPLS_TP_SECTION_ENDPOINT_ID = 0x1A SUBTYPE_MANAGEMENT_ADDRESS_MPLS_TP_LSP_ENDPOINT_ID = 0x1B SUBTYPE_MANAGEMENT_ADDRESS_MPLS_TP_PSEUDOWIRE_ENDPOINT_ID = 0x1C SUBTYPE_MANAGEMENT_ADDRESS_MT_IP_MULTI_TOPOLOGY_IPV4 = 0x1D SUBTYPE_MANAGEMENT_ADDRESS_MT_IP_MULTI_TOPOLOGY_IPV6 = 0x1E INTERFACE_NUMBERING_SUBTYPES = { 0x01: 'unknown', 0x02: 'ifIndex', 0x03: 'system port number' } SUBTYPE_INTERFACE_NUMBER_UNKNOWN = 0x01 SUBTYPE_INTERFACE_NUMBER_IF_INDEX = 0x02 SUBTYPE_INTERFACE_NUMBER_SYSTEM_PORT_NUMBER = 0x03 ''' Note - calculation of _length field:: _length = 1@_management_address_string_length + 1@management_address_subtype + management_address.len + 1@interface_numbering_subtype + 4@interface_number + 1@_oid_string_length + object_id.len ''' fields_desc = [ BitEnumField('_type', 0x08, 7, LLDPDU.TYPES), BitFieldLenField('_length', None, 9, length_of='management_address', adjust=lambda pkt, x: 8 + len(pkt.management_address) + len(pkt.object_id)), BitFieldLenField('_management_address_string_length', None, 8, length_of='management_address', adjust=lambda pkt, x: len(pkt.management_address) + 1 ), # noqa: E501 ByteEnumField('management_address_subtype', 0x00, IANA_ADDRESS_FAMILY_NUMBERS), XStrLenField('management_address', '', length_from=lambda pkt: 0 if pkt._management_address_string_length is None else pkt. _management_address_string_length - 1), ByteEnumField('interface_numbering_subtype', SUBTYPE_INTERFACE_NUMBER_UNKNOWN, INTERFACE_NUMBERING_SUBTYPES), BitField('interface_number', 0, 32), BitFieldLenField('_oid_string_length', None, 8, length_of='object_id'), XStrLenField('object_id', '', length_from=lambda pkt: pkt._oid_string_length), ] def _check(self): """ run layer specific checks """ if conf.contribs['LLDP'].strict_mode(): management_address_len = len(self.management_address) if management_address_len == 0 or management_address_len > 31: raise LLDPInvalidLengthField( 'management address must be 1..31 characters long - ' 'got string of size {}'.format(management_address_len))
class IE_SelectionMode(IE_Base): # Indicates the origin of the APN in the message name = "Selection Mode" fields_desc = [ByteEnumField("ietype", 15, IEType), BitEnumField("SelectionMode", "MS or APN", 8, Selection_Mode)]
class IEC104_IE_SCD: """ SCD - status and status change detection EN 60870-5-101:2003, sec. 7.2.6.40 (p. 57) """ ST_FLAG_STATE_OFF = 0 ST_FLAG_STATE_ON = 1 ST_FLAGS = {ST_FLAG_STATE_OFF: 'off', ST_FLAG_STATE_ON: 'on'} CD_FLAG_STATE_NOT_CHANGED = 0 CD_FLAG_STATE_CHANGED = 1 CD_FLAGS = { CD_FLAG_STATE_NOT_CHANGED: 'state not changed', CD_FLAG_STATE_CHANGED: 'state changed' } informantion_element_fields = [ BitEnumField('cd_16', 0, 1, CD_FLAGS), BitEnumField('cd_15', 0, 1, CD_FLAGS), BitEnumField('cd_14', 0, 1, CD_FLAGS), BitEnumField('cd_13', 0, 1, CD_FLAGS), BitEnumField('cd_12', 0, 1, CD_FLAGS), BitEnumField('cd_11', 0, 1, CD_FLAGS), BitEnumField('cd_10', 0, 1, CD_FLAGS), BitEnumField('cd_9', 0, 1, CD_FLAGS), BitEnumField('cd_8', 0, 1, CD_FLAGS), BitEnumField('cd_7', 0, 1, CD_FLAGS), BitEnumField('cd_6', 0, 1, CD_FLAGS), BitEnumField('cd_5', 0, 1, CD_FLAGS), BitEnumField('cd_4', 0, 1, CD_FLAGS), BitEnumField('cd_3', 0, 1, CD_FLAGS), BitEnumField('cd_2', 0, 1, CD_FLAGS), BitEnumField('cd_1', 0, 1, CD_FLAGS), BitEnumField('st_16', 0, 1, ST_FLAGS), BitEnumField('st_15', 0, 1, ST_FLAGS), BitEnumField('st_14', 0, 1, ST_FLAGS), BitEnumField('st_13', 0, 1, ST_FLAGS), BitEnumField('st_12', 0, 1, ST_FLAGS), BitEnumField('st_11', 0, 1, ST_FLAGS), BitEnumField('st_10', 0, 1, ST_FLAGS), BitEnumField('st_9', 0, 1, ST_FLAGS), BitEnumField('st_8', 0, 1, ST_FLAGS), BitEnumField('st_7', 0, 1, ST_FLAGS), BitEnumField('st_6', 0, 1, ST_FLAGS), BitEnumField('st_5', 0, 1, ST_FLAGS), BitEnumField('st_4', 0, 1, ST_FLAGS), BitEnumField('st_3', 0, 1, ST_FLAGS), BitEnumField('st_2', 0, 1, ST_FLAGS), BitEnumField('st_1', 0, 1, ST_FLAGS), ]
class LoWPAN_IPHC(Packet): """6LoWPAN IPv6 header compressed packets It follows the implementation of RFC6282 """ __slots__ = ["_ipv6"] # the LOWPAN_IPHC encoding utilizes 13 bits, 5 dispatch type name = "LoWPAN IP Header Compression Packet" _address_modes = ["Unspecified (0)", "1", "16-bits inline (3)", "Compressed (3)"] _state_mode = ["Stateless (0)", "Stateful (1)"] deprecated_fields = { "_nhField": ("nhField", "2.4.4"), "_hopLimit": ("hopLimit", "2.4.4"), "sourceAddr": ("src", "2.4.4"), "destinyAddr": ("dst", "2.4.4"), "udpDestinyPort": ("udpDestPort", "2.4.4"), } fields_desc = [ # Base Format https://tools.ietf.org/html/rfc6282#section-3.1.2 BitField("_reserved", 0x03, 3), BitField("tf", 0x0, 2), BitEnumField("nh", 0x0, 1, ["Inline", "Compressed"]), BitEnumField("hlim", 0x0, 2, {0: "Inline", 1: "Compressed/HL1", 2: "Compressed/HL64", 3: "Compressed/HL255"}), BitEnumField("cid", 0x0, 1, {1: "Present (1)"}), BitEnumField("sac", 0x0, 1, _state_mode), BitEnumField("sam", 0x0, 2, _address_modes), BitEnumField("m", 0x0, 1, {1: "multicast (1)"}), BitEnumField("dac", 0x0, 1, _state_mode), BitEnumField("dam", 0x0, 2, _address_modes), # https://tools.ietf.org/html/rfc6282#section-3.1.2 # Context Identifier Extension ConditionalField( BitField("sci", 0, 4), lambda pkt: pkt.cid == 0x1 ), ConditionalField( BitField("dci", 0, 4), lambda pkt: pkt.cid == 0x1 ), # https://tools.ietf.org/html/rfc6282#section-3.2.1 ConditionalField( BitField("tc_ecn", 0, 2), lambda pkt: pkt.tf in [0, 1, 2] ), ConditionalField( BitField("tc_dscp", 0, 6), lambda pkt: pkt.tf in [0, 2], ), ConditionalField( MultipleTypeField( [(BitField("rsv", 0, 4), lambda pkt: pkt.tf == 0)], BitField("rsv", 0, 2), ), lambda pkt: pkt.tf in [0, 1] ), ConditionalField( BitField("flowlabel", 0, 20), lambda pkt: pkt.tf in [0, 1] ), # Inline fields https://tools.ietf.org/html/rfc6282#section-3.1.1 ConditionalField( ByteEnumField("nhField", 0x0, ipv6nh), lambda pkt: pkt.nh == 0x0 ), ConditionalField( ByteField("hopLimit", 0x0), lambda pkt: pkt.hlim == 0x0 ), # The src and dst fields are filled up or removed in the # pre_dissect and post_build, depending on the other options. IP6FieldLenField("src", "::", length_of=source_addr_size), IP6FieldLenField("dst", "::", length_of=dest_addr_size), # problem when it's 0 # noqa: E501 ] def post_dissect(self, data): """dissect the IPv6 package compressed into this IPHC packet. The packet payload needs to be decompressed and depending on the arguments, several conversions should be done. """ # uncompress payload packet = IPv6() packet.tc, packet.fl = self._getTrafficClassAndFlowLabel() if not self.nh: packet.nh = self.nhField # HLIM: Hop Limit if self.hlim == 0: packet.hlim = self.hopLimit elif self.hlim == 0x1: packet.hlim = 1 elif self.hlim == 0x2: packet.hlim = 64 else: packet.hlim = 255 packet.src = self.decompressSourceAddr(packet) packet.dst = self.decompressDestAddr(packet) pay_cls = self.guess_payload_class(data) if pay_cls == IPv6: packet.add_payload(data) data = raw(packet) elif pay_cls == LoWPAN_NHC: self._ipv6 = packet return Packet.post_dissect(self, data) def decompressDestAddr(self, packet): # https://tools.ietf.org/html/rfc6282#section-3.1.1 try: tmp_ip = inet_pton(socket.AF_INET6, self.dst) except socket.error: tmp_ip = b"\x00" * 16 if self.m == 0 and self.dac == 0: if self.dam == 0: # Address fully carried pass elif self.dam == 1: tmp_ip = LINK_LOCAL_PREFIX[0:8] + tmp_ip[-8:] elif self.dam == 2: tmp_ip = LINK_LOCAL_PREFIX[0:8] + b"\x00\x00\x00\xff\xfe\x00" + tmp_ip[-2:] # noqa: E501 elif self.dam == 3: tmp_ip = _extract_upperaddress(self, source=False) elif self.m == 0 and self.dac == 1: if self.dam == 0: # reserved pass elif self.dam == 0x3: # should use context IID + encapsulating header tmp_ip = _extract_upperaddress(self, source=False) elif self.dam not in [0x1, 0x2]: # https://tools.ietf.org/html/rfc6282#page-9 # Should use context information: unimplemented pass elif self.m == 1 and self.dac == 0: if self.dam == 0: # Address fully carried pass elif self.dam == 1: tmp = b"\xff" + chb(tmp_ip[16 - dest_addr_size(self)]) tmp_ip = tmp + b"\x00" * 9 + tmp_ip[-5:] elif self.dam == 2: tmp = b"\xff" + chb(tmp_ip[16 - dest_addr_size(self)]) tmp_ip = tmp + b"\x00" * 11 + tmp_ip[-3:] else: # self.dam == 3: tmp_ip = b"\xff\x02" + b"\x00" * 13 + tmp_ip[-1:] elif self.m == 1 and self.dac == 1: if self.dam == 0x0: # https://tools.ietf.org/html/rfc6282#page-10 # https://github.com/wireshark/wireshark/blob/f54611d1104d85a425e52c7318c522ed249916b6/epan/dissectors/packet-6lowpan.c#L2149-L2166 # Format: ffXX:XXLL:PPPP:PPPP:PPPP:PPPP:XXXX:XXXX # P and L should be retrieved from context P = b"\x00" * 16 L = b"\x00" X = tmp_ip[-6:] tmp_ip = b"\xff" + X[:2] + L + P[:8] + X[2:6] else: # all the others values: reserved pass self.dst = inet_ntop(socket.AF_INET6, tmp_ip) return self.dst def compressSourceAddr(self, ipv6): # https://tools.ietf.org/html/rfc6282#section-3.1.1 tmp_ip = inet_pton(socket.AF_INET6, ipv6.src) if self.sac == 0: if self.sam == 0x0: pass elif self.sam == 0x1: tmp_ip = tmp_ip[8:16] elif self.sam == 0x2: tmp_ip = tmp_ip[14:16] else: # self.sam == 0x3: pass else: # self.sac == 1 if self.sam == 0x0: tmp_ip = b"\x00" * 16 elif self.sam == 0x1: tmp_ip = tmp_ip[8:16] elif self.sam == 0x2: tmp_ip = tmp_ip[14:16] self.src = inet_ntop(socket.AF_INET6, b"\x00" * (16 - len(tmp_ip)) + tmp_ip) # noqa: E501 return self.src def compressDestAddr(self, ipv6): # https://tools.ietf.org/html/rfc6282#section-3.1.1 tmp_ip = inet_pton(socket.AF_INET6, ipv6.dst) if self.m == 0 and self.dac == 0: if self.dam == 0x0: pass elif self.dam == 0x1: tmp_ip = b"\x00" * 8 + tmp_ip[8:16] elif self.dam == 0x2: tmp_ip = b"\x00" * 14 + tmp_ip[14:16] elif self.m == 0 and self.dac == 1: if self.dam == 0x1: tmp_ip = b"\x00" * 8 + tmp_ip[8:16] elif self.dam == 0x2: tmp_ip = b"\x00" * 14 + tmp_ip[14:16] elif self.m == 1 and self.dac == 0: if self.dam == 0x0: pass if self.dam == 0x1: tmp_ip = b"\x00" * 10 + tmp_ip[1:2] + tmp_ip[11:16] elif self.dam == 0x2: tmp_ip = b"\x00" * 12 + tmp_ip[1:2] + tmp_ip[13:16] elif self.dam == 0x3: tmp_ip = b"\x00" * 15 + tmp_ip[15:16] elif self.m == 1 and self.dac == 1: if self.dam == 0: tmp_ip = b"\x00" * 10 + tmp_ip[1:3] + tmp_ip[12:16] self.dst = inet_ntop(socket.AF_INET6, tmp_ip) def decompressSourceAddr(self, packet): # https://tools.ietf.org/html/rfc6282#section-3.1.1 try: tmp_ip = inet_pton(socket.AF_INET6, self.src) except socket.error: tmp_ip = b"\x00" * 16 if self.sac == 0: if self.sam == 0x0: # Full address is carried in-line pass elif self.sam == 0x1: tmp_ip = LINK_LOCAL_PREFIX[0:8] + tmp_ip[16 - source_addr_size(self):16] # noqa: E501 elif self.sam == 0x2: tmp = LINK_LOCAL_PREFIX[0:8] + b"\x00\x00\x00\xff\xfe\x00" tmp_ip = tmp + tmp_ip[16 - source_addr_size(self):16] elif self.sam == 0x3: # Taken from encapsulating header tmp_ip = _extract_upperaddress(self, source=True) else: # self.sac == 1: if self.sam == 0x0: # Unspecified address :: pass elif self.sam == 0x1: # should use context IID pass elif self.sam == 0x2: # should use context IID tmp = LINK_LOCAL_PREFIX[0:8] + b"\x00\x00\x00\xff\xfe\x00" tmp_ip = tmp + tmp_ip[16 - source_addr_size(self):16] elif self.sam == 0x3: # should use context IID tmp_ip = LINK_LOCAL_PREFIX[0:8] + b"\x00" * 8 self.src = inet_ntop(socket.AF_INET6, tmp_ip) return self.src def guess_payload_class(self, payload): if self.nh: return LoWPAN_NHC u = self.underlayer if u and isinstance(u, (LoWPANFragmentationFirst, LoWPANFragmentationSubsequent)): return Raw return IPv6 def do_build(self): _cur = self if isinstance(_cur.payload, LoWPAN_NHC): _cur = _cur.payload if not isinstance(_cur.payload, IPv6): return Packet.do_build(self) ipv6 = _cur.payload self._reserved = 0x03 # NEW COMPRESSION TECHNIQUE! # a ) Compression Techniques # 1. Set Traffic Class if self.tf == 0x0: self.tc_ecn = ipv6.tc >> 6 self.tc_dscp = ipv6.tc & 0x3F self.flowlabel = ipv6.fl elif self.tf == 0x1: self.tc_ecn = ipv6.tc >> 6 self.flowlabel = ipv6.fl elif self.tf == 0x2: self.tc_ecn = ipv6.tc >> 6 self.tc_dscp = ipv6.tc & 0x3F else: # self.tf == 0x3: pass # no field is set # 2. Next Header if self.nh == 0x0: self.nhField = ipv6.nh elif self.nh == 1: # This will be handled in LoWPAN_NHC pass # 3. HLim if self.hlim == 0x0: self.hopLimit = ipv6.hlim else: # if hlim is 1, 2 or 3, there are nothing to do! pass # 4. Context (which context to use...) if self.cid == 0x0: pass else: # TODO: Context Unimplemented yet pass # 5. Compress Source Addr self.compressSourceAddr(ipv6) self.compressDestAddr(ipv6) return Packet.do_build(self) def do_build_payload(self): # Elide the IPv6 payload if isinstance(self.payload, IPv6): return raw(self.payload.payload) return Packet.do_build_payload(self) def _getTrafficClassAndFlowLabel(self): """Page 6, draft feb 2011 """ if self.tf == 0x0: return (self.tc_ecn << 6) + self.tc_dscp, self.flowlabel elif self.tf == 0x1: return (self.tc_ecn << 6), self.flowlabel elif self.tf == 0x2: return (self.tc_ecn << 6) + self.tc_dscp, 0 else: return 0, 0
class IEC104_IE_CP56TIME2A(IEC104_IE_CommonQualityFlags): """ CP56Time2a - dual time, 7 octets (milliseconds, valid flag, minutes, hours, summer-time-indicator, day of month, weekday, years) well, someone should have talked to them about the idea of the unix timestamp... EN 60870-5-101:2003, sec. 7.2.6.18 (p. 50) time representation format according IEC 60870-5-4:1993, sec. 6.8, p. 23 """ WEEK_DAY_FLAG_UNUSED = 0 WEEK_DAY_FLAG_MONDAY = 1 WEEK_DAY_FLAG_TUESDAY = 2 WEEK_DAY_FLAG_WEDNESDAY = 3 WEEK_DAY_FLAG_THURSDAY = 4 WEEK_DAY_FLAG_FRIDAY = 5 WEEK_DAY_FLAG_SATURDAY = 6 WEEK_DAY_FLAG_SUNDAY = 7 WEEK_DAY_FLAGS = { WEEK_DAY_FLAG_UNUSED: 'unused', WEEK_DAY_FLAG_MONDAY: 'Monday', WEEK_DAY_FLAG_TUESDAY: 'Tuesday', WEEK_DAY_FLAG_WEDNESDAY: 'Wednesday', WEEK_DAY_FLAG_THURSDAY: 'Thursday', WEEK_DAY_FLAG_FRIDAY: 'Friday', WEEK_DAY_FLAG_SATURDAY: 'Saturday', WEEK_DAY_FLAG_SUNDAY: 'Sunday' } GEN_FLAG_REALTIME = 0 GEN_FLAG_SUBSTITUTED_TIME = 1 GEN_FLAGS = { GEN_FLAG_REALTIME: 'real time', GEN_FLAG_SUBSTITUTED_TIME: 'substituted time' } SU_FLAG_NORMAL_TIME = 0 SU_FLAG_SUMMER_TIME = 1 SU_FLAGS = { SU_FLAG_NORMAL_TIME: 'normal time', SU_FLAG_SUMMER_TIME: 'summer time' } informantion_element_fields = [ LEShortField('sec_milli', 0), BitEnumField('iv_time', 0, 1, IEC104_IE_CommonQualityFlags.IV_FLAGS), BitEnumField('gen', 0, 1, GEN_FLAGS), # only valid in monitor direction ToDo: special treatment needed? BitField('minutes', 0, 6), BitEnumField('su', 0, 1, SU_FLAGS), BitField('reserved_2', 0, 2), BitField('hours', 0, 5), BitEnumField('weekday', 0, 3, WEEK_DAY_FLAGS), BitField('day-of-month', 0, 5), BitField('reserved_3', 0, 4), BitField('month', 0, 4), BitField('reserved_4', 0, 1), BitField('year', 0, 7), ]
class LoWPAN_HC1(Packet): name = "LoWPAN_HC1 Compressed IPv6" fields_desc = [ # https://tools.ietf.org/html/rfc4944#section-10.1 ByteField("reserved", 0x42), BitEnumField("sp", 0, 1, ["In-line", "Compressed"]), BitEnumField("si", 0, 1, ["In-line", "Elided"]), BitEnumField("dp", 0, 1, ["In-line", "Compressed"]), BitEnumField("di", 0, 1, ["In-line", "Elided"]), BitEnumField("tc_fl", 0, 1, ["Not compressed", "zero"]), BitEnumField("nh", 0, 2, {0: "not compressed", 1: "UDP", 2: "ICMP", 3: "TCP"}), BitEnumField("hc2", 0, 1, ["No more header compression bits", "HC2 Present"]), # https://tools.ietf.org/html/rfc4944#section-10.2 ConditionalField( MultipleTypeField( [ (PacketField("hc2Field", LoWPAN_HC2_UDP(), LoWPAN_HC2_UDP), lambda pkt: pkt.nh == 1), # TODO: ICMP & TCP not implemented yet for HC1 # (PacketField("hc2Field", LoWPAN_HC2_ICMP(), # LoWPAN_HC2_ICMP), # lambda pkt: pkt.nh == 2), # (PacketField("hc2Field", LoWPAN_HC2_TCP(), # LoWPAN_HC2_TCP), # lambda pkt: pkt.nh == 3), ], StrFixedLenField("hc2Field", b"", 0), ), lambda pkt: pkt.hc2 ), # IPv6 header fields # https://tools.ietf.org/html/rfc4944#section-10.3.1 ByteField("hopLimit", 0x0), IP6FieldLenField("src", "::", lambda pkt: (0 if pkt.sp else 8) + (0 if pkt.si else 8)), IP6FieldLenField("dst", "::", lambda pkt: (0 if pkt.dp else 8) + (0 if pkt.di else 8)), ConditionalField( ByteField("traffic_class", 0), lambda pkt: not pkt.tc_fl ), ConditionalField( BitField("flow_label", 0, 20), lambda pkt: not pkt.tc_fl ), # Other fields # https://tools.ietf.org/html/rfc4944#section-10.3.2 ConditionalField( MultipleTypeField( [(BitScalingField("udpSourcePort", 0, 4, offset=0xF0B0), lambda pkt: getattr(pkt.hc2Field, "sc", 0))], BitField("udpSourcePort", 0, 16) ), lambda pkt: pkt.nh == 1 and pkt.hc2 ), ConditionalField( MultipleTypeField( [(BitScalingField("udpDestPort", 0, 4, offset=0xF0B0), lambda pkt: getattr(pkt.hc2Field, "dc", 0))], BitField("udpDestPort", 0, 16) ), lambda pkt: pkt.nh == 1 and pkt.hc2 ), ConditionalField( BitField("udpLength", 0, 16), lambda pkt: pkt.nh == 1 and pkt.hc2 and not pkt.hc2Field.lc ), ConditionalField( XBitField("udpChecksum", 0, 16), lambda pkt: pkt.nh == 1 and pkt.hc2 ), # Out of spec BitFixedLenField("pad", 0, _get_hc1_pad) ] def post_dissect(self, data): # uncompress payload packet = IPv6() packet.version = IPHC_DEFAULT_VERSION packet.tc = self.traffic_class packet.fl = self.flow_label nh_match = { 1: socket.IPPROTO_UDP, 2: socket.IPPROTO_ICMP, 3: socket.IPPROTO_TCP } if self.nh: packet.nh = nh_match.get(self.nh) packet.hlim = self.hopLimit packet.src = self.decompressSourceAddr() packet.dst = self.decompressDestAddr() if self.hc2 and self.nh == 1: # UDP udp = UDP() udp.sport = self.udpSourcePort udp.dport = self.udpDestPort udp.len = self.udpLength or None udp.chksum = self.udpChecksum udp.add_payload(data) packet.add_payload(udp) else: packet.add_payload(data) data = raw(packet) return Packet.post_dissect(self, data) def decompressSourceAddr(self): if not self.sp and not self.si: # Prefix & Interface return self.src elif not self.si: # Only interface addr = inet_pton(socket.AF_INET6, self.src)[-8:] addr = LINK_LOCAL_PREFIX[:8] + addr else: # Interface not provided addr = _extract_upperaddress(self, source=True) self.src = inet_ntop(socket.AF_INET6, addr) return self.src def decompressDestAddr(self): if not self.dp and not self.di: # Prefix & Interface return self.dst elif not self.di: # Only interface addr = inet_pton(socket.AF_INET6, self.dst)[-8:] addr = LINK_LOCAL_PREFIX[:8] + addr else: # Interface not provided addr = _extract_upperaddress(self, source=False) self.dst = inet_ntop(socket.AF_INET6, addr) return self.dst def do_build(self): if not isinstance(self.payload, IPv6): return Packet.do_build(self) # IPv6 ipv6 = self.payload self.src = ipv6.src self.dst = ipv6.dst self.flow_label = ipv6.fl self.traffic_class = ipv6.tc self.hopLimit = ipv6.hlim if isinstance(ipv6.payload, UDP): self.nh = 1 self.hc2 = 1 udp = ipv6.payload self.udpSourcePort = udp.sport self.udpDestPort = udp.dport if not udp.len or not udp.chksum: udp = UDP(raw(udp)) self.udpLength = udp.len self.udpChecksum = udp.chksum return Packet.do_build(self) def do_build_payload(self): # Elide the IPv6 and UDP payload if isinstance(self.payload, IPv6): if isinstance(self.payload.payload, UDP): return raw(self.payload.payload.payload) return raw(self.payload.payload) return Packet.do_build_payload(self)