Esempio n. 1
0
    def unconnected_send(self,
                         path,
                         route_path=None,
                         send_path=None,
                         timeout=None,
                         read_frag=None,
                         write_frag=None):
        if route_path is None:
            # Default to the CPU in chassis (link 0), port 1
            route_path = [{'link': 0, 'port': 1}]
        if send_path is None:
            # Default to the Connection Manager
            send_path = [{'class': 6}, {'instance': 1}]
        assert isinstance(path, list)

        data = cpppo.dotdict()
        data.enip = {}
        data.enip.session_handle = self.session
        data.enip.options = 0
        data.enip.status = 0
        data.enip.sender_context = {}
        data.enip.sender_context.input = bytearray([0x00] * 8)
        data.enip.CIP = {}
        data.enip.CIP.send_data = {}

        sd = data.enip.CIP.send_data
        sd.interface = 0
        sd.timeout = 0
        sd.CPF = {}
        sd.CPF.item = [cpppo.dotdict(), cpppo.dotdict()]
        sd.CPF.item[0].type_id = 0
        sd.CPF.item[1].type_id = 178
        sd.CPF.item[1].unconnected_send = {}

        us = sd.CPF.item[1].unconnected_send
        us.service = 82
        us.status = 0
        us.priority = 5
        us.timeout_ticks = 157
        us.path = {'segment': [cpppo.dotdict(d) for d in send_path]}
        us.route_path = {'segment': [cpppo.dotdict(d) for d in route_path]}
        us.request = {}
        us.request.path = {'segment': [cpppo.dotdict(d) for d in path]}

        if read_frag:
            us.request.read_frag = read_frag
        elif write_frag:
            us.request.write_frag = write_frag
        else:
            raise ValueError("Expected a Read/Write Tag [Fragmented] request")

        us.request.input = bytearray(logix.Logix.produce(us.request))
        sd.input = bytearray(enip.CPF.produce(sd.CPF))
        data.enip.input = bytearray(enip.CIP.produce(data.enip))
        data.input = bytearray(enip.enip_encode(data.enip))

        self.send(data.input, timeout=timeout)
        return data
Esempio n. 2
0
def logix_remote( count, svraddr, kwargs ):
  try:
    time.sleep(.25) # Wait for server to be established
    # Confirm that a known Register encodes as expected
    data			= cpppo.dotdict()
    data.enip			= {}
    data.enip.options		= 0
    data.enip.session_handle	= 0
    data.enip.status		= 0
    data.enip.sender_context	= {}
    data.enip.sender_context.input = bytearray( [0x00] * 8 )
    data.enip.CIP		= {}
    data.enip.CIP.register 	= {}
    data.enip.CIP.register.options 		= 0
    data.enip.CIP.register.protocol_version	= 1

    data.enip.input		= bytearray( enip.CIP.produce( data.enip ))
    data.input			= bytearray( enip.enip_encode( data.enip ))
    log.normal( "Register Request: %r" % data )
    
    assert bytes( data.input ) == rss_004_request

    # Try to Register a real session, followed by commands
    timeout			= 5

    begun			= cpppo.timer()
    cli				= client.client( host=svraddr[0], port=svraddr[1] )
    assert cli.writable( timeout=timeout )
    elapsed			= cpppo.timer() - begun
    log.normal( "Client Connected in  %7.3f/%7.3fs" % ( elapsed, timeout ))

    begun			= cpppo.timer()
    with cli:
        cli.register( timeout=timeout )
        data,elapsed		= client.await_response( cli, timeout=timeout )
    log.normal( "Client Register Rcvd %7.3f/%7.3fs: %r", elapsed, timeout, data )
    assert data is not None and 'enip.CIP.register' in data, "Failed to receive Register response"
    assert data.enip.status == 0, "Register response indicates failure: %s" % data.enip.status

    # Establish the EtherNet/IP "session handle" used by all further requests
    cli.session			= data.enip.session_handle

    start			= cpppo.timer()
    with cli:
        for _ in range( count ):
            begun		= cpppo.timer()
            cli.read( path=[{'symbolic': 'SCADA'}, {'element': 12}],
                      elements=201, offset=2, timeout=timeout )
            data,elapsed	= client.await_response( cli, timeout=timeout )
            log.normal( "Client ReadFrg. Rcvd %7.3f/%7.3fs: %r", elapsed, timeout, data )

    duration			= cpppo.timer() - start
    log.warning( "Client ReadFrg. Average %7.3f TPS (%7.3fs ea)." % ( count / duration, duration / count ))

    log.normal( "Signal shutdown w/ server.control in object %s", id( kwargs['server']['control'] ))
  finally:
    kwargs['server']['control'].done= True # Signal the server to terminate
Esempio n. 3
0
def logix_remote( count, svraddr, kwargs ):
  try:
    time.sleep(.25) # Wait for server to be established
    # Confirm that a known Register encodes as expected
    data			= cpppo.dotdict()
    data.enip			= {}
    data.enip.options		= 0
    data.enip.session_handle	= 0
    data.enip.status		= 0
    data.enip.sender_context	= {}
    data.enip.sender_context.input = bytearray( [0x00] * 8 )
    data.enip.CIP		= {}
    data.enip.CIP.register 	= {}
    data.enip.CIP.register.options 		= 0
    data.enip.CIP.register.protocol_version	= 1

    data.enip.input		= bytearray( enip.CIP.produce( data.enip ))
    data.input			= bytearray( enip.enip_encode( data.enip ))
    log.normal( "Register Request: %r" % data )
    
    assert bytes( data.input ) == rss_004_request

    # Try to Register a real session, followed by commands
    timeout			= 5

    begun			= cpppo.timer()
    cli				= client.client( host=svraddr[0], port=svraddr[1] )
    assert cli.writable( timeout=timeout )
    elapsed			= cpppo.timer() - begun
    log.normal( "Client Connected in  %7.3f/%7.3fs" % ( elapsed, timeout ))

    begun			= cpppo.timer()
    with cli:
        cli.register( timeout=timeout )
        data,elapsed		= client.await( cli, timeout=timeout )
    log.normal( "Client Register Rcvd %7.3f/%7.3fs: %r", elapsed, timeout, data )
    assert data is not None and 'enip.CIP.register' in data, "Failed to receive Register response"
    assert data.enip.status == 0, "Register response indicates failure: %s" % data.enip.status

    # Establish the EtherNet/IP "session handle" used by all further requests
    cli.session			= data.enip.session_handle

    start			= cpppo.timer()
    with cli:
        for _ in range( count ):
            begun		= cpppo.timer()
            cli.read( path=[{'symbolic': 'SCADA'}, {'element': 12}],
                      elements=201, offset=2, timeout=timeout )
            data,elapsed	= client.await( cli, timeout=timeout )
            log.normal( "Client ReadFrg. Rcvd %7.3f/%7.3fs: %r", elapsed, timeout, data )

    duration			= cpppo.timer() - start
    log.warning( "Client ReadFrg. Average %7.3f TPS (%7.3fs ea)." % ( count / duration, duration / count ))

    log.normal( "Signal shutdown w/ server.control in object %s", id( kwargs['server']['control'] ))
  finally:
    kwargs['server']['control'].done= True # Signal the server to terminate
Esempio n. 4
0
    def unconnected_send( self, path, route_path=None, send_path=None, timeout=None,
                          read_frag=None, write_frag=None ):
        if route_path is None:
            # Default to the CPU in chassis (link 0), port 1
            route_path		= [{'link': 0, 'port': 1}]
        if send_path is None:
            # Default to the Connection Manager
            send_path		= [{'class': 6}, {'instance': 1}]
        assert isinstance( path, list )

        data			= cpppo.dotdict()
        data.enip		= {}
        data.enip.session_handle= self.session
        data.enip.options	= 0
        data.enip.status	= 0
        data.enip.sender_context= {}
        data.enip.sender_context.input = bytearray( [0x00] * 8 )
        data.enip.CIP		= {}
        data.enip.CIP.send_data = {}

        sd			= data.enip.CIP.send_data
        sd.interface		= 0
        sd.timeout		= 0
        sd.CPF			= {}
        sd.CPF.item		= [ cpppo.dotdict(), cpppo.dotdict() ]
        sd.CPF.item[0].type_id	= 0
        sd.CPF.item[1].type_id	= 178
        sd.CPF.item[1].unconnected_send = {}

        us			= sd.CPF.item[1].unconnected_send
        us.service		= 82
        us.status		= 0
        us.priority		= 5
        us.timeout_ticks	= 157
        us.path			= { 'segment': [ cpppo.dotdict( d ) for d in send_path ]}
        us.route_path		= { 'segment': [ cpppo.dotdict( d ) for d in route_path ]}
        us.request		= {}
        us.request.path		= { 'segment': [ cpppo.dotdict( d ) for d in path ]}
    
        if read_frag:
            us.request.read_frag= read_frag
        elif write_frag:
            us.request.write_frag= write_frag
        else:
            raise ValueError( "Expected a Read/Write Tag [Fragmented] request" )

        us.request.input	= bytearray( logix.Logix.produce( us.request ))
        sd.input		= bytearray( enip.CPF.produce( sd.CPF ))
        data.enip.input		= bytearray( enip.CIP.produce( data.enip ))
        data.input		= bytearray( enip.enip_encode( data.enip ))

        self.send( data.input, timeout=timeout )
        return data
Esempio n. 5
0
    def unconnected_send(self, request, route_path=None, send_path=None, timeout=None):
        if route_path is None:
            # Default to the CPU in chassis (link 0), port 1
            route_path = [{"link": 0, "port": 1}]
        if send_path is None:
            # Default to the Connection Manager
            send_path = [{"class": 6}, {"instance": 1}]
        assert isinstance(request, dict)

        data = cpppo.dotdict()
        data.enip = {}
        data.enip.session_handle = self.session
        data.enip.options = 0
        data.enip.status = 0
        data.enip.sender_context = {}
        data.enip.sender_context.input = bytearray([0x00] * 8)
        data.enip.CIP = {}
        data.enip.CIP.send_data = {}

        sd = data.enip.CIP.send_data
        sd.interface = 0
        sd.timeout = 0
        sd.CPF = {}
        sd.CPF.item = [cpppo.dotdict(), cpppo.dotdict()]
        sd.CPF.item[0].type_id = 0
        sd.CPF.item[1].type_id = 178
        sd.CPF.item[1].unconnected_send = {}

        us = sd.CPF.item[1].unconnected_send
        us.service = 82
        us.status = 0
        us.priority = 5
        us.timeout_ticks = 157
        us.path = {"segment": [cpppo.dotdict(d) for d in send_path]}
        us.route_path = {"segment": [cpppo.dotdict(d) for d in route_path]}

        us.request = request

        log.detail("Client Unconnected Send: %s", enip.enip_format(data))

        us.request.input = bytearray(logix.Logix.produce(us.request))
        sd.input = bytearray(enip.CPF.produce(sd.CPF))
        data.enip.input = bytearray(enip.CIP.produce(data.enip))
        data.input = bytearray(enip.enip_encode(data.enip))

        self.send(data.input, timeout=timeout)
        return data
Esempio n. 6
0
    def register(self, timeout=None):
        data = cpppo.dotdict()
        data.enip = {}
        data.enip.session_handle = 0
        data.enip.options = 0
        data.enip.status = 0
        data.enip.sender_context = {}
        data.enip.sender_context.input = bytearray([0x00] * 8)
        data.enip.CIP = {}
        data.enip.CIP.register = {}
        data.enip.CIP.register.options = 0
        data.enip.CIP.register.protocol_version = 1

        data.enip.input = bytearray(enip.CIP.produce(data.enip))
        data.input = bytearray(enip.enip_encode(data.enip))

        self.send(data.input, timeout=timeout)
        return data
Esempio n. 7
0
    def register(self, timeout=None):
        data = cpppo.dotdict()
        data.enip = {}
        data.enip.session_handle = 0
        data.enip.options = 0
        data.enip.status = 0
        data.enip.sender_context = {}
        data.enip.sender_context.input = bytearray([0x00] * 8)
        data.enip.CIP = {}
        data.enip.CIP.register = {}
        data.enip.CIP.register.options = 0
        data.enip.CIP.register.protocol_version = 1

        data.enip.input = bytearray(enip.CIP.produce(data.enip))
        data.input = bytearray(enip.enip_encode(data.enip))

        self.send(data.input, timeout=timeout)
        return data
Esempio n. 8
0
def logix_remote( count, svraddr, kwargs ):
    time.sleep(.25)
    data			= cpppo.dotdict()
    data.enip			= {}
    data.enip.options		= 0
    data.enip.session_handle	= 0
    data.enip.status		= 0
    data.enip.sender_context	= {}
    data.enip.sender_context.input = bytearray( [0x00] * 8 )
    	#array.array( cpppo.type_bytes_array_symbol, "\x00" * 8 )
    data.enip.CIP		= {}
    data.enip.CIP.register 	= {}
    data.enip.CIP.register.options 		= 0
    data.enip.CIP.register.protocol_version	= 1

    data.enip.input		= bytearray( enip.CIP.produce( data.enip ))
    data.input			= bytearray( enip.enip_encode( data.enip ))
    log.normal( "Register Request: %r" % data )
    
    assert bytes( data.input ) == rss_004_request


    timeout			= 5

    begun			= cpppo.timer()
    cli				= client.client( host=svraddr[0], port=svraddr[1] )
    assert cli.writable( timeout=timeout )
    elapsed			= cpppo.timer() - begun
    log.normal( "Client Connected in  %7.3f/%7.3fs" % ( elapsed, timeout ))

    begun			= cpppo.timer()
    request			= cli.register( timeout=timeout )
    elapsed			= cpppo.timer() - begun
    log.normal( "Client Register Sent %7.3f/%7.3fs: %r" % ( elapsed, timeout, request ))
    for data in cli:
        elapsed			= cpppo.timer() - begun
        log.detail( "Client Register Resp %7.3f/%7.3fs: %r" % ( elapsed, timeout, data ))
        if data is None:
            if elapsed <= timeout:
                cli.readable( timeout=timeout - elapsed )
                continue
        break
    elapsed			= cpppo.timer() - begun
    log.normal( "Client Register Rcvd %7.3f/%7.3fs: %r" % ( elapsed, timeout, data ))
    assert data is not None and 'enip.CIP.register' in data, "Failed to receive Register response"
    assert data.enip.status == 0, "Register response indicates failure: %s" % data.enip.status

    cli.session			= data.enip.session_handle


    start			= cpppo.timer()
    for _ in range( count ):
        begun			= cpppo.timer()
        request			= cli.read( path=[{'symbolic': 'SCADA'}, {'element': 12}],
                                                elements=1, offset=0, timeout=timeout )
        elapsed			= cpppo.timer() - begun
        log.normal( "Client ReadFrg. Sent %7.3f/%7.3fs: %r" % ( elapsed, timeout, request ))
        for data in cli:
            elapsed		= cpppo.timer() - begun
            log.detail( "Client ReadFrg. Resp %7.3f/%7.3fs: %r" % ( elapsed, timeout, data ))
            if data is None:
                if elapsed <= timeout:
                    cli.readable( timeout=timeout - elapsed )
                    continue
            break
        elapsed			= cpppo.timer() - begun
        log.normal( "Client ReadFrg. Rcvd %7.3f/%7.3fs: %r" % ( elapsed, timeout, data ))

    duration			= cpppo.timer() - start
    log.warning( "Client ReadFrg. Average %7.3f TPS (%7.3fs ea)." % ( count / duration, duration / count ))

    kwargs['server'].control.done= True
Esempio n. 9
0
def logix_remote(count, svraddr, kwargs):
    time.sleep(.25)
    data = cpppo.dotdict()
    data.enip = {}
    data.enip.options = 0
    data.enip.session_handle = 0
    data.enip.status = 0
    data.enip.sender_context = {}
    data.enip.sender_context.input = bytearray([0x00] * 8)
    #array.array( cpppo.type_bytes_array_symbol, "\x00" * 8 )
    data.enip.CIP = {}
    data.enip.CIP.register = {}
    data.enip.CIP.register.options = 0
    data.enip.CIP.register.protocol_version = 1

    data.enip.input = bytearray(enip.CIP.produce(data.enip))
    data.input = bytearray(enip.enip_encode(data.enip))
    log.normal("Register Request: %r" % data)

    assert bytes(data.input) == rss_004_request

    timeout = 5

    begun = cpppo.timer()
    cli = client.client(host=svraddr[0], port=svraddr[1])
    assert cli.writable(timeout=timeout)
    elapsed = cpppo.timer() - begun
    log.normal("Client Connected in  %7.3f/%7.3fs" % (elapsed, timeout))

    begun = cpppo.timer()
    request = cli.register(timeout=timeout)
    elapsed = cpppo.timer() - begun
    log.normal("Client Register Sent %7.3f/%7.3fs: %r" %
               (elapsed, timeout, request))
    for data in cli:
        elapsed = cpppo.timer() - begun
        log.detail("Client Register Resp %7.3f/%7.3fs: %r" %
                   (elapsed, timeout, data))
        if data is None:
            if elapsed <= timeout:
                cli.readable(timeout=timeout - elapsed)
                continue
        break
    elapsed = cpppo.timer() - begun
    log.normal("Client Register Rcvd %7.3f/%7.3fs: %r" %
               (elapsed, timeout, data))
    assert data is not None and 'enip.CIP.register' in data, "Failed to receive Register response"
    assert data.enip.status == 0, "Register response indicates failure: %s" % data.enip.status

    cli.session = data.enip.session_handle

    start = cpppo.timer()
    for _ in range(count):
        begun = cpppo.timer()
        request = cli.read(path=[{
            'symbolic': 'SCADA'
        }, {
            'element': 12
        }],
                           elements=1,
                           offset=0,
                           timeout=timeout)
        elapsed = cpppo.timer() - begun
        log.normal("Client ReadFrg. Sent %7.3f/%7.3fs: %r" %
                   (elapsed, timeout, request))
        for data in cli:
            elapsed = cpppo.timer() - begun
            log.detail("Client ReadFrg. Resp %7.3f/%7.3fs: %r" %
                       (elapsed, timeout, data))
            if data is None:
                if elapsed <= timeout:
                    cli.readable(timeout=timeout - elapsed)
                    continue
            break
        elapsed = cpppo.timer() - begun
        log.normal("Client ReadFrg. Rcvd %7.3f/%7.3fs: %r" %
                   (elapsed, timeout, data))

    duration = cpppo.timer() - start
    log.warning("Client ReadFrg. Average %7.3f TPS (%7.3fs ea)." %
                (count / duration, duration / count))

    kwargs['server'].control.done = True
Esempio n. 10
0
def test_CIP_HART(repeat=1):
    """HART protocol enip CIP messages
    """
    enip.lookup_reset()  # Flush out any existing CIP Objects for a fresh start

    ENIP = enip.enip_machine(context='enip')
    CIP = enip.CIP()
    # We'll use a HART Message Router, to handle its expanded porfolio of commands
    MR = HART(instance_id=1)

    for pkt, tst in client.recycle(CIP_HART_tests, times=repeat):
        # Parse just the CIP portion following the EtherNet/IP encapsulation header
        data = cpppo.dotdict()
        source = cpppo.chainable(pkt)
        with ENIP as machine:
            for i, (m, s) in enumerate(machine.run(source=source, data=data)):
                log.detail("%s #%3d -> %10.10s; next byte %3d: %-10.10r: %r",
                           machine.name_centered(), i, s, source.sent,
                           source.peek(), data)
        # In a real protocol implementation, an empty header (EOF with no input at all) is
        # acceptable; it indicates a session closed by the client.
        if not data:
            log.normal("EtherNet/IP Request: Empty (session terminated): %s",
                       enip.enip_format(data))
            continue

        if log.isEnabledFor(logging.NORMAL):
            log.normal("EtherNet/IP Request: %s", enip.enip_format(data))

        # Parse the encapsulated .input
        with CIP as machine:
            for i, (m, s) in enumerate(
                    machine.run(path='enip',
                                source=cpppo.peekable(
                                    data.enip.get('input', b'')),
                                data=data)):
                log.detail("%s #%3d -> %10.10s; next byte %3d: %-10.10r: %r",
                           machine.name_centered(), i, s, source.sent,
                           source.peek(), data)

        if log.isEnabledFor(logging.NORMAL):
            log.normal("EtherNet/IP CIP Request: %s", enip.enip_format(data))

        # Assume the request in the CIP's CPF items are HART requests.
        # Now, parse the encapsulated message(s).  We'll assume it is destined for a HART Object.
        if 'enip.CIP.send_data' in data:
            for item in data.enip.CIP.send_data.CPF.item:
                if 'unconnected_send.request' in item:
                    # An Unconnected Send that contained an encapsulated request (ie. not just a Get
                    # Attribute All)
                    with MR.parser as machine:
                        if log.isEnabledFor(logging.NORMAL):
                            log.normal(
                                "Parsing %3d bytes using %s.parser, from %s",
                                len(item.unconnected_send.request.input), MR,
                                enip.enip_format(item))
                        # Parse the unconnected_send.request.input octets, putting parsed items into the
                        # same request context
                        for i, (m, s) in enumerate(
                                machine.run(
                                    source=cpppo.peekable(
                                        item.unconnected_send.request.input),
                                    data=item.unconnected_send.request)):
                            log.detail(
                                "%s #%3d -> %10.10s; next byte %3d: %-10.10r: %r",
                                machine.name_centered(), i, s, source.sent,
                                source.peek(), data)
                    # Post-processing of some parsed items is only performed after lock released!
                    if log.isEnabledFor(logging.NORMAL):
                        log.normal(
                            "Parsed  %3d bytes using %s.parser, into %s",
                            len(item.unconnected_send.request.input), MR,
                            enip.enip_format(data))
        try:
            for k, v in tst.items():
                assert data[k] == v, ("data[%r] == %r\n"
                                      "expected:   %r" % (k, data[k], v))
        except:
            log.warning("%r not in data, or != %r: %s", k, v,
                        enip.enip_format(data))
            raise

        # Ensure that we can get the original EtherNet/IP CIP back
        for k in list(data.keys()):
            if k.endswith('input') and 'sender_context' not in k:
                log.detail("del data[%r]", k)
                del data[k]
        try:
            # First reconstruct any SendRRData CPF items, containing encapsulated requests/responses
            if 'enip.CIP.send_data' in data:
                cpf = data.enip.CIP.send_data
                for item in cpf.CPF.item:
                    if 'unconnected_send' in item:
                        item.unconnected_send.request.input = bytearray(
                            MR.produce(item.unconnected_send.request))
                        log.normal("Produce HART message from: %r",
                                   item.unconnected_send.request)

            # Next, reconstruct the CIP Register, ListIdentity, ListServices, or SendRRData.  The CIP.produce must
            # be provided the EtherNet/IP header, because it contains data (such as .command)
            # relevant to interpreting the .CIP... contents.
            data.enip.input = bytearray(enip.CIP.produce(data.enip))
            # And finally the EtherNet/IP encapsulation itself
            data.input = bytearray(enip.enip_encode(data.enip))
            log.detail("EtherNet/IP CIP Request produced payload: %r",
                       bytes(data.input))
            assert data.input == pkt, "original:\n" + hexdump(
                pkt) + "\nproduced:\n" + hexdump(data.input)
        except Exception as exc:
            log.warning(
                "Exception %s; Invalid packet produced from EtherNet/IP CIP data: %s",
                exc, enip.enip_format(data))
            raise