class CIP_ReqGetAttributeList(scapy_all.Packet): """The list of requested attributes in a CIP Get_Attribute_List request""" fields_desc = [ utils.LEShortLenField("count", None, count_of="attrs"), scapy_all.FieldListField("attrs", [], scapy_all.LEShortField("", 0), count_from=lambda pkt: pkt.count), ]
class CIP_ReqConnectionManager(scapy_all.Packet): fields_desc = [ scapy_all.BitField("reserved", 0, 3), scapy_all.BitField("priority", 0, 1), scapy_all.BitField("ticktime", 5, 4), scapy_all.ByteField("timeout_ticks", 157), utils.LEShortLenField("message_size", None, length_of="message"), scapy_all.PacketLenField("message", None, CIP, length_from=lambda pkt: pkt.message_size), scapy_all.StrLenField("message_padding", None, length_from=lambda pkt: pkt.message_size % 2), scapy_all.ByteField("route_path_size", 1), # TODO: size in words scapy_all.ByteField("reserved2", 0), scapy_all.ByteField("route_path_size_port", 1), scapy_all.ByteField("route_path_size_addr", 0), ] def post_build(self, p, pay): # Autofill the padding if len(p) % 2: p = p[:-4] + b"\0" + p[-4:] return p + pay
class ENIP_ListServices(scapy_all.Packet): name = "ENIP_ListServices" fields_desc = [ utils.LEShortLenField("count", 1, count_of="TargetItems"), scapy_all.PacketListField("TargetItems", [], ENIP_ListServices_TargetItem, count_from=lambda p: p.count), ]
class ENIP_SendUnitData(scapy_all.Packet): """Data in ENIP header specific to the specified command""" name = "ENIP_SendUnitData" fields_desc = [ scapy_all.LEIntField("interface_handle", 0), scapy_all.LEShortField("timeout", 0), utils.LEShortLenField("count", None, count_of="items"), scapy_all.PacketListField("items", [], ENIP_SendUnitData_Item, count_from=lambda p: p.count), ]
class ENIP_UDP(scapy_all.Packet): """Ethernet/IP packet over UDP""" name = "ENIP_UDP" fields_desc = [ utils.LEShortLenField("count", None, count_of="items"), scapy_all.PacketListField("items", [], enip_cpf.CPF_Item, count_from=lambda p: p.count), ] def extract_padding(self, p): return "", p
class ENIP_ListIdentity(scapy_all.Packet): name = "ENIP_ListIdentity" fields_desc = [ utils.LEShortLenField("count", 1, count_of="IdentityItems"), scapy_all.PacketListField("IdentityItems", [], ENIP_ListIdentity_TargetItem, count_from=lambda p: p.count), ] def extract_padding(self, p): # print self.__class__.__name__ + ": P=" + str(p) return "", p
class ENIP_CPF(scapy_all.Packet): name = "ENIP_CPF" fields_desc = [ utils.LEShortLenField("count", 2, count_of="items"), scapy_all.PacketListField( "items", [CPF_AddressDataItem('', 0, 0), CPF_AddressDataItem('', 0, 0)], CPF_AddressDataItem, count_from=lambda p: p.count), ] def extract_padding(self, p): return '', p
class ENIP_CPF(scapy_all.Packet): name = "ENIP_CPF" fields_desc = [ utils.LEShortLenField("count", 2, count_of="items"), # Changed implementation to reflect use of CIP_Item above scapy_all.PacketListField("items", [], CPF_Item, count_from=lambda p: p.count), # Due to potential 'optional' packet components at end in protocol scapy_all.ConditionalField( scapy_all.PacketListField("optional_items", None, CPF_Item, count_from=lambda p: p.count - 2), lambda p: p.count > 2), ] def extract_padding(self, p): return '', p
class CIP_MultipleServicePacket(scapy_all.Packet): """Multiple_Service_Packet request or response""" name = "CIP_MultipleServicePacket" fields_desc = [ utils.LEShortLenField("count", None, count_of="packets"), scapy_all.FieldListField("offsets", [], scapy_all.LEShortField("", 0), count_from=lambda pkt: pkt.count), # Assume the offsets are increasing, and no padding. FIXME: remove this assumption _CIPMSPPacketList("packets", [], CIP) ] def do_build(self): """Build the packet by concatenating packets and building the offsets list""" # Build the sub packets subpkts = [str(pkt) for pkt in self.packets] # Build the offset lists current_offset = 2 + 2 * len(subpkts) offsets = [] for p in subpkts: offsets.append(struct.pack("<H", current_offset)) current_offset += len(p) return struct.pack("<H", len(subpkts)) + "".join(offsets) + "".join(subpkts)