class SCION(scapy.Packet): name = 'SCION' fields_desc = [ # Common header scapy.BitField('version', 0, 4), scapy.BitEnumField('dst_type', "ipv4", 6, SCION_ADDR_TYPE), scapy.BitEnumField('src_type', "ipv4", 6, SCION_ADDR_TYPE), scapy.BitField('total_len', None, 16), scapy.BitField('hdr_len', None, 8), scapy.BitField('curr_inf', None, 8), scapy.BitField('curr_hf', None, 8), scapy.BitEnumField('next_hdr', None, 8, scapy.IP_PROTOS), scapy.PacketField('addr', None, SCIONAddr), scapy.PacketListField('path', None, PathSegment, count_from=lambda _: 3), scapy.PacketField('ext', None, SecurityExtension) ] # this is terrible, but apparently that's how scapy works :-/ def post_build(self, pkt, pay): # compute lengths if self.total_len == None: self.total_len = len(pkt) + len(pay) pkt = pkt[:2] + struct.pack('!H', self.total_len) + pkt[4:] if self.hdr_len == None: self.hdr_len = len(pkt) - self.ext.hdr_len if self.hdr_len % 8 != 0: raise ValueError( "SCION packet header length not multiple of 8 bytes!") self.hdr_len = int(self.hdr_len / 8) #must divide by 8 pkt = pkt[:4] + struct.pack('B', self.hdr_len) + pkt[5:] return pkt + pay
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 CIP_ReqForwardOpen(scapy_all.Packet): """Forward Open request""" name = "CIP_ReqForwardOpen" fields_desc = [ scapy_all.BitField("priority", 0, 4), scapy_all.BitField("tick_time", 0, 4), scapy_all.ByteField("timeout_ticks", 249), scapy_all.LEIntField("OT_network_connection_id", 0x80000031), scapy_all.LEIntField("TO_network_connection_id", 0x80fe0030), scapy_all.LEShortField("connection_serial_number", 0x1337), scapy_all.LEShortField("vendor_id", 0x004d), scapy_all.LEIntField("originator_serial_number", 0xdeadbeef), scapy_all.ByteField("connection_timeout_multiplier", 0), scapy_all.X3BytesField("reserved", 0), scapy_all.LEIntField("OT_rpi", 0x007a1200), # 8000 ms For ToCNC and ToCell #scapy_all.LEIntField("OT_rpi", 0x00000000), # for j scapy_all.LEShortField("OT_connection_param", 0x4802), #for ToCNC and ToCell #scapy_all.LEShortField("OT_connection_param", 0x0400), #for j #scapy_all.PacketField('OT_connection_param', CIP_ConnectionParam(), CIP_ConnectionParam), scapy_all.LEIntField("TO_rpi", 0x007a1200), #ToCNC and ToCell #scapy_all.LEIntField("TO_rpi", 0x00000000), #for j scapy_all.LEShortField("TO_connection_param", 0x281a), #for ToCNC and ToCell #scapy_all.LEShortField("TO_connection_param", 0x0400), #for j #scapy_all.PacketField('TO_connection_param', CIP_ConnectionParam(), CIP_ConnectionParam), scapy_all.XByteField("transport_type", 0x81), # direction server, application object, class 3 scapy_all.ByteField("path_wordsize", None), CIP_PathField("path", None, length_from=lambda p: 2 * p.path_wordsize), ]
class CIP_ConnectionParam(scapy_all.Packet): """CIP Connection parameters""" name = "CIP_ConnectionParam" fields_desc = [ scapy_all.BitEnumField("owner", 0, 1, {0: "exclusive", 1: "multiple"}), scapy_all.BitEnumField("connection_type", 2, 2, {0: "null", 1: "multicast", 2: "point-to-point", 3: "reserved"}), scapy_all.BitField("reserved", 0, 1), scapy_all.BitEnumField("priority", 0, 2, {0: "low", 1: "high", 2: "scheduled", 3: "urgent"}), scapy_all.BitEnumField("connection_size_type", 0, 1, {0: "fixed", 1: "variable"}), scapy_all.BitField("connection_size", 500, 9), ] def pre_dissect(self, s): b = struct.unpack('<H', s[:2])[0] return struct.pack('>H', int(b)) + s[2:] def do_build(self): # p = '' # Corrected issue of malformed packet construction when attempting to # communicate with PLC - MED p = super(CIP_ConnectionParam, self).do_build() return p[::-1] # we have to flip the output def extract_padding(self, s): return '', s
class CPU(scapy.Packet): name = 'CpuPacket' fields_desc = [ scapy.BitField('ipv4_pools_version', 3, p4settings['TABLE_VERSIONS_SIZE']), scapy.BitField('flow_hash', 3, 6), ]
class SWAT_P1_RIO_DO(scapy_all.Packet): name = "SWAT_P1_RIO_DO" fields_desc = [ scapy_all.LEShortField('counter', 0), scapy_all.ByteField('number', 0), scapy_all.StrFixedLenField('reserved', 0, length=5), scapy_all.BitField('spare', 0, 4), scapy_all.BitEnumField('valve_close', 0, 1, { 0: 'disable', 1: 'enable' }), scapy_all.BitEnumField('valve_open', 0, 1, { 0: 'disable', 1: 'enable' }), scapy_all.BitEnumField('pump2_start', 0, 1, { 0: 'disable', 1: 'enable' }), scapy_all.BitEnumField('pump1_start', 0, 1, { 0: 'disable', 1: 'enable' }), scapy_all.FieldListField( "spare2", [], scapy_all.ByteField('', 0), length_from=lambda p: p.underlayer.length - 9), ]
class CIP_PortSegment(scapy_all.Packet): name="CIP_PortSegment" PORT_IDENT={ 0: "Reserved", 1: "Back-Plane", 15: "Extended", } fields_desc = [ scapy_all.BitField("extended_link_address_size", 0, 1), scapy_all.BitEnumField("port_identifier", 0, 4, PORT_IDENT), scapy_all.ByteField("link_address_size", 0), scapy_all.ConditionalField( scapy_all.LEIntField("extended_port_identifier", 0), lambda p: p.port_identifier == 0xf # If the Port Identifier is 15, then a 16-bit field, called the Extended Port Identifier, shall be the next part of the Port Segment ), scapy_all.ConditionalField( scapy_all.FieldListField("Link_Address", [], scapy_all.XByteField("",0), length_from=lambda p: p.link_address_size ), lambda p: p.extended_link_address_size == 0x1 # If the Port Identifier is 15, then a 16-bit field, called the Extended Port Identifier, shall be the next part of the Port Segment ) ] def extract_padding(self, p): print self.__class__.__name__ + ": P=" + str(p) return "", p
class PathSegment(scapy.Packet): name = 'SCION Path segment' fields_desc = [ scapy.BitField('flags', 0x0, 8), UnixTimeField('timestamp', None), ISDField('isd', None), scapy.FieldLenField('nhops', None, count_of='hops', fmt='B'), scapy.PacketListField('hops', None, HopField, count_from=lambda p: p.nhops), ] def extract_padding(self, p): return "", p def post_build(self, pkt, pay): # compute MACs on HFs # this is not a thing that should be done by the client normally :D # => only useful for testing # TODO This is *not* verified against a "real" SCION packet yet! # Somebody should do something! def calculate_mac(current, prev): if prev != None: prev_data = prev[HopField.RANGE_SKIP_FLAGS:HopField.RANGE_END] else: prev_data = b'\0' * (HopField.RANGE_END - HopField.RANGE_SKIP_FLAGS) data = ( struct.pack('!I', self.timestamp) + struct.pack( 'B', current[HopField.FLAGS] & HopField.IMMUTABLE_FLAGS) + current[HopField.RANGE_SKIP_FLAGS:HopField.RANGE_BEFORE_MAC] + prev_data) # print('prev_data: ', len(prev_data), prev_data.encode('hex')) # print('data: ', len(data), data.encode('hex')) assert len(data) == 128 // 8 c = cmac.CMAC(algorithms.AES(HF_MAC_KEY), backend=default_backend()) c.update(data) return c.finalize() for i in range(len(self.hops)): if not self.hops[i].mac: curr_beg = 8 + 8 * i curr_end = 8 + 8 * (i + 1) prev_beg = curr_beg - 8 mac_beg = curr_end - 3 curr = pkt[curr_beg:curr_end] prev = pkt[prev_beg:curr_beg] if i > 0 else None mac = calculate_mac(curr, prev) # print('DEBUG: updating MAC: {} -> {}'.format(struct.pack('!I', self.hops[i].mac).encode('hex'), mac.encode('hex'))) # print('DEBUG: updating MAC -> {}'.format(mac.encode('hex'))) mac_bytes = mac[:3] # take the most significant bits pkt = pkt[:mac_beg] + mac_bytes + pkt[curr_end:] return pkt + pay
class HopField(scapy.Packet): IMMUTABLE_FLAGS = 0x0 # TODO FLAGS = 0 RANGE_SKIP_FLAGS = 1 RANGE_END = 8 RANGE_BEFORE_MAC = 5 name = 'SCION Hop field' fields_desc = [ scapy.XBitField('flags', 0x0, 8), scapy.ByteField('expiry', 63), scapy.BitField('ingress_if', None, 12), scapy.BitField('egress_if', None, 12), scapy.XBitField('mac', None, 3 * 8), # TODO ] def extract_padding(self, p): return "", p
class CIP_ReqForwardOpen(scapy_all.Packet): """Forward Open request""" name = "CIP_ReqForwardOpen" SEGMENT_TYPE = { 0x00: "Port Segment", 0x01: "Logical Segment", 0x02: "Network Segment", 0x03: "Symbolic Segment", 0x04: "Data Segment", 0x05: "Data Type (constructed)", 0x06: "Data Type (elementary)", 0x07: "Reserved for future use", } fields_desc = [ # Updated a few field descriptions to adjust how they are displayed # Altered fields begin with utils. rather than scapy_all. - MED scapy_all.BitField("priority", 0, 4), scapy_all.BitField("tick_time", 0, 4), scapy_all.ByteField("timeout_ticks", 249), utils.XLEIntField("OT_network_connection_id", 0x80000031), utils.XLEIntField("TO_network_connection_id", 0x80fe0030), scapy_all.LEShortField("connection_serial_number", 0x1337), utils.XLEShortField("vendor_id", 0x004d), utils.XLEIntField("originator_serial_number", 0xdeadbeef), scapy_all.ByteField("connection_timeout_multiplier", 0), scapy_all.X3BytesField("reserved", 0), utils.XLEIntField("OT_rpi", 0x007a1200), # 8000 ms scapy_all.PacketField('OT_connection_param', CIP_ConnectionParam(), CIP_ConnectionParam), utils.XLEIntField("TO_rpi", 0x007a1200), scapy_all.PacketField('TO_connection_param', CIP_ConnectionParam(), CIP_ConnectionParam), scapy_all.XByteField("transport_type", 0xa3), # direction server, application object, class 3 # Changed name - MED scapy_all.ByteField("Connection_Path_Size", None), #The number of 16 bit words in the Connection_Path field. # scapy_all.PacketListField("path_segment_items", [], CIP_Path1, length_from=lambda p: 2 * p.Connection_Path_Size), # Modified Implementation - MED scapy_all.PacketListField("path_segment_items", [], CIP_PathPadded, length_from=lambda p: 6), # CIP_PathField("path", None, length_from=lambda p: 2 * p.path_wordsize), ]
class CIP_ConnectionParam(scapy_all.Packet): """CIP Connection parameters""" name = "CIP_ConnectionParam" fields_desc = [ scapy_all.BitEnumField("owner", 0, 1, { 0: "exclusive", 1: "multiple" }), scapy_all.BitEnumField("connection_type", 2, 2, { 0: "null", 1: "multicast", 2: "point-to-point", 3: "reserved" }), scapy_all.BitField("reserved", 0, 1), scapy_all.BitEnumField("priority", 0, 2, { 0: "low", 1: "high", 2: "scheduled", 3: "urgent" }), scapy_all.BitEnumField("connection_size_type", 0, 1, { 0: "fixed", 1: "variable" }), scapy_all.BitField("connection_size", 500, 9), ] def pre_dissect(self, s): b = struct.unpack('<H', s[:2])[0] return struct.pack('>H', int(b)) + s[2:] def do_build(self): p = '' return p def extract_padding(self, s): return '', s
class SWAT_P1_RIO_DI(scapy_all.Packet): name = 'SWAT_P1_RIO_DI' fields_desc = [ scapy_all.LEShortField('counter', 0), scapy_all.LEIntField('padding', 0), scapy_all.BitEnumField('pump2_fault', 0, 1, { 0: 'disable', 1: 'enable' }), scapy_all.BitEnumField('pump2_run', 0, 1, { 0: 'disable', 1: 'enable' }), scapy_all.BitEnumField('pump2_auto', 0, 1, { 0: 'disable', 1: 'enable' }), scapy_all.BitEnumField('pump1_fault', 0, 1, { 0: 'disable', 1: 'enable' }), scapy_all.BitEnumField('pump1_run', 0, 1, { 0: 'disable', 1: 'enable' }), scapy_all.BitEnumField('pump1_auto', 0, 1, { 0: 'disable', 1: 'enable' }), scapy_all.BitEnumField('rio_wireless', 0, 1, { 0: 'disable', 1: 'enable' }), scapy_all.BitEnumField('plc_wireless', 0, 1, { 0: 'disable', 1: 'enable' }), scapy_all.BitField('spare', 0, 6), scapy_all.BitEnumField('valve_close', 0, 1, { 0: 'disable', 1: 'enable' }), scapy_all.BitEnumField('valve_open', 0, 1, { 0: 'disable', 1: 'enable' }), scapy_all.LEShortField('spare2', 0), ]
class RdmaROME_RECEIVERstate(scapy.Packet): name = "RdmaROME_RECEIVERstate" fields_desc = [ scapy.BitField("state", 0, 3), scapy.BitField("rsvd0", 0, 5), scapy.BitField("minTimestampDiff", 0, 32), scapy.BitField("linkDataRate", 0, 2), scapy.BitField("recoverRate", 0, 27), scapy.BitField("minRate", 0, 27), scapy.BitField("weight", 0, 4), scapy.BitField("rxDMA_tick", 0, 3), scapy.BitField("wait", 0, 1), scapy.BitField("avgDelay", 0, 20), scapy.BitField("preAvgDelay", 0, 20), scapy.BitField("cycleMinDelay", 0, 20), scapy.BitField("cycleMaxDelay", 0, 20), scapy.BitField("totalBytesRx", 0, 32), scapy.BitField("rxBurstBytes", 0, 16), scapy.BitField("byte_update", 0, 16), scapy.BitField("th_byte", 0, 32), scapy.BitField("cur_timestamp", 0, 10), scapy.BitField("thput", 0, 27), scapy.BitField("MD_amount", 0, 27), scapy.BitField("last_cycle", 0, 32), scapy.BitField("last_thput", 0, 32), scapy.BitField("last_epoch", 0, 32), scapy.BitField("last_update", 0, 32), scapy.BitField("txDMA_tick", 0, 3), scapy.BitField("fspeed_cnt", 0, 10), scapy.BitField("currentRate", 0, 27), ]
class RdmaROME_SENDERstate(scapy.Packet): name = "RdmaROME_SENDERstate" fields_desc = [ scapy.BitField("localClkResolution", 0, 8), scapy.BitField("remoteClkRightShift", 0, 8), scapy.BitField("clkScaleFactor", 0, 31), scapy.BitField("txMinRate", 0, 17), scapy.BitField("remoteClkStartEpoch", 0, 32), scapy.BitField("stalocalClkStartEpochte", 0, 48), scapy.BitField("totalBytesSent", 0, 32), scapy.BitField("totalBytesAcked", 0, 32), scapy.BitField("window", 0, 32), scapy.BitField("currentRate", 0, 27), scapy.BitField("log_sq_size", 0, 5), scapy.BitField("numCnpPktsRx", 0, 32), scapy.BitField("last_sched_timestamp", 0, 48), scapy.BitField("delta_ticks_last_sched", 0, 16), scapy.BitField("cur_avail_tokens", 0, 48), scapy.BitField("token_bucket_size", 0, 48), scapy.BitField("sq_cindex", 0, 16), scapy.BitField("cur_timestamp", 0, 32), ]
class RdmaDCQCNstate(scapy.Packet): name = "RdmaDCQCNstate" fields_desc = [ scapy.BitField("last_cnp_timestamp", 0, 48), scapy.IntField("byte_counter_thr", 0), scapy.ByteField("rsvd1", 0), scapy.BitField("sq_msg_psn", 0, 24), scapy.IntField("rate_enforced", 0), scapy.IntField("target_rate", 0), scapy.BitField("alpha_value", 0, 16), scapy.IntField("cur_byte_counter", 0), scapy.BitField("byte_counter_exp_cnt", 0, 16), scapy.BitField("timer_exp_cnt", 0, 16), scapy.BitField("num_alpha_exp_cnt", 0, 16), scapy.ByteField("num_cnp_rcvd", 0), scapy.ByteField("num_cnp_processed", 0), scapy.BitField("max_rate_reached", 0, 1), scapy.BitField("log_sq_size", 0, 5), scapy.BitField("resp_rl_failure", 0, 1), scapy.BitField("rsvd0", 0, 1), scapy.BitField("last_sched_timestamp", 0, 48), scapy.BitField("delta_tokens_last_sched", 0, 16), scapy.BitField("cur_avail_tokens", 0, 48), scapy.BitField("token_bucket_size", 0, 48), scapy.BitField("sq_cindex", 0, 16), scapy.ByteField("num_sched_drop", 0), scapy.BitField("cur_timestamp", 0, 32), ]