Skip to content

Frame recovery Prolonged or not possible in some circumstances #3

@pkilgore2

Description

@pkilgore2

This issue describes what may or may not be a defect in S7Client.RecvIsoPacket() (Sharp7.cs:2091). In this, we first read the first four bytes so that we can extract the TPKT header. When a PDU is rejected in this phase, the stream is not consumed, allowing for an attempt to consume the beginning of the next frame.

Unfortunately, if the PDU does not have a size divisible by 4, the stream position becomes misaligned with what we actually expect; in the best case, the PDU does align and the next packet will supply a valid TPKT header. In somewhat worse cases, a valid TPKT header is never again encountered on the stream. The below test at the end demostrates this (added at the end cause it's still kind of long).

According to RFC 1006, the version field effectively has a constant value of 3. I am not sure what ISO has to say about the matter or if it prescribes a certain method of performing framing, but if we are guaranteed to have 0x03 as the first byte of the TPKT header, is it possible to address this by discarding all subsequent bytes until one reads 0x03, then by reading the TPKT header?

using Sharp7;
using System.Net;
using System.Net.Sockets;

public class Driver
{
    /* Connection */
    private static byte[] M_CONNECT = {
        0x30, 0x00, 0x00, 0x16,
        0x11, 0xD0, 0x00, 0x01, 0x44, 0x31, 0x00, 0xC0,
        0x01, 0x0A, 0xC1, 0x02, 0x01, 0x00, 0xC2, 0x02,
        0x01, 0x02

    };

    /* PDU Negotiation */

    private static byte[] M_NEGOTIATE = {
        0x03, 0x00, 0x00, 0x1B,
        0x02, 0xF0, 0x80, 0x32, 0x03, 0x00, 0x00, 0x04,
        0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0xF0,
        0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0xE0,
    };

    /* A PDU with a single bit error */
    private static byte[] M_INVALID =
    {
        /* TPKT Header 1, will be rejected because of an invalid bit */
        0x03, 0x0, 0x10, 0x9,
        0xAA, 0xBB, 0xCC, 0xDD, 0xEE
    };

    /* A PDU where the packet size is constrained (actual payload is probably invalid). */
    private static readonly byte[] M_VALID = {
        0x03, 0x0, 0x00, 0x9,
        0xAA, 0xBB, 0xCC, 0xDD, 0xEE
    };

    private static bool s_listening = false;

    private static void M_Server()
    {
        using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) {
            socket.Blocking = true;
            socket.NoDelay = true;
            socket.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 102));
            socket.Listen();
            s_listening = true;
            
            byte[] buf = new byte[4096];

            /* First connection is the ping, */
            var remote = socket.Accept();
            remote.Receive(buf);
            remote.Close();

            /* Second connection is the actual connection */
            remote = socket.Accept();

            /* Simulate the connect message */
            long read;
            read = remote.Receive(buf);
            long sent = remote.Send(M_CONNECT);

            /* Simulate PDU Negotatiation */
            read = remote.Receive(buf);
            sent = remote.Send(M_NEGOTIATE);

            /* Now send an invalid response */
            remote.Receive(buf);
            remote.Send(M_INVALID);

            /* Now send validly framed messages */
            for (int i = 0; i < 16; i++) {
                remote.Receive(buf);
                remote.Send(M_VALID);
            }

            /* We're done here, shut down both ends */
            s_listening = false;
            while (!s_listening)
                Thread.Yield();

            remote.Close();
            socket.Close();
        }
    }

    public static void Main(string[] args)
    {
        Console.WriteLine("Starting server thread...");
        Thread server = new Thread(M_Server);
        server.Start();
        
        /* Wait for the server to come up. */
        while(!s_listening) {
            Thread.Yield();
        }

        /* Now connect to the server */
        S7Client client = new S7Client();
        client.ConnectTo("127.0.0.1", 0, 2);

        byte[] buf = new byte[4096];
        client.DBRead(0, 0, 12, buf);
        for (int i = 0; i < 16; i++) {
            client.DBRead(0, 0, 12, buf);
        }

        client.Disconnect();
        s_listening = true;

        server.Join();
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions