DEV Community

Phil Yeh
Phil Yeh

Posted on

The Architecture of Implicit Messaging: Implementing Raw CIP I/O in Python

The Challenge of Class 1 I/O
Ethernet/IP (EIP) is based on the Common Industrial Protocol (CIP), which defines two primary messaging types:

Explicit Messaging (TCP 44818): Request/Response—used for configuration and diagnostics.

Implicit Messaging (UDP 2222): Cyclic I/O—used for high-speed, repetitive data exchange (Class 1 Connections).

The architectural challenge lies in managing resource contention and time determinism when setting up these complex connections using raw sockets, rather than relying on commercial drivers.

🏗️ The 4-Step Connection Sequence
A robust implementation requires careful management of the TCP setup and the subsequent UDP I/O lifecycle. Our architecture follows these four steps within a cyclic process:

  1. Register Session (TCP)
    The process begins with an Explicit Message to the target device's Encapsulation Layer (TCP 44818). This step establishes a Session Handle, which identifies the connection for subsequent requests.

  2. Forward Open (TCP)
    This is the most critical step. The client sends a Forward Open command containing all parameters necessary for the Class 1 I/O connection, including:

Requested Packet Interval (RPI).

Connection Path (Assembly Instance IDs for the I/O data).

Connection Timeout parameters. The device returns the O2T (Originator to Target) and T2O (Target to Origin) Connection IDs.

  1. Cyclic I/O Exchange (UDP)
    Once the connection is established via TCP, the system shifts to high-speed UDP 2222. The client sends periodic data using the established O2T connection ID, and the device responds with the T2O data. This ensures minimal latency for cyclic data updates.

  2. Forward Close & Teardown (TCP)
    To prevent the target device from eventually timing out and reporting a fault, the client must explicitly send a Forward Close command (TCP). This gracefully releases the resources allocated by the device before the socket is closed.

💻 Architecture for Deterministic Testing
To reliably test this intricate sequence, our study kit employs a dual-application structure:

Raw Socket Client: Implements the full TCP and UDP state machine, managing the cyclic Open/Close sequence.

Mock PLC Server: A separate Python application running on localhost that listens on TCP 44818 and UDP 2222. The mock server is essential for deterministic testing, as it guarantees correct, instantaneous responses during the handshake, allowing developers to isolate logic errors from physical layer noise.

Python I/O Loop Structure
The raw socket implementation requires precise packet assembly. Below is the conceptual structure used to manage the cyclic UDP exchange:

Python

# Conceptual Structure for I/O Loop

while running:
    # 1. Open Session (TCP) & Forward Open (TCP) is performed here...

    # 2. UDP Exchange Phase:
    try:
        # Construct and send O2T packet using raw bytes
        udp_socket.sendto(o2t_packet, (target_ip, 2222))

        # Receive T2O packet (Target to Origin)
        received_data, addr = udp_socket.recvfrom(1024)

        # Log and parse the raw data...

    except socket.timeout:
        log("Warning: UDP I/O Timeout occurred.")

    # 3. Forward Close (TCP) & TCP Disconnect is performed here...

    time.sleep(RPI_WAIT_TIME)
Enter fullscreen mode Exit fullscreen mode

🔒 Conclusion
Understanding the raw socket implementation of CIP is critical for developers working in industrial cybersecurity, custom SCADA integration, or embedded systems where external libraries are too large or unavailable.

We have documented the complete architecture and technical insights for this project on GitHub. If you are interested in acquiring the full, ready-to-deploy Python source code for this framework and diving into the raw packet structure for your own custom industrial solutions:

View the complete project and detailed architecture: Link

By Phil Yeh | Senior Automation Engineer

Top comments (0)