//
//  Licensed to the Apache Software Foundation (ASF) under one or more
//  contributor license agreements.  See the NOTICE file distributed with
//  this work for additional information regarding copyright ownership.
//  The ASF licenses this file to You under the Apache License, Version 2.0
//  (the "License"); you may not use this file except in compliance with
//  the License.  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//
:imagesdir: ../../images/

== DeltaV Industrial Ethernet Communication

The DeltaV protocol is used in the Emerson DeltaV system.
In contrast to most other systems, this is a combination of PLC and Control System.
As Emerson seems to insist on the devices not being PLCs, but controllers, well call them this way in this document.
Same with the Control System, these seem to be called OS (Operator System).

=== Disclaimer

As we had absolutely no information on the details of the protocol, we started by taking an existing DeltaV training system and replaced the network switch with a hub and used this to take network captures of the entire network traffic of the DeltaV network.

These network captures and some knowledge of the normal system operation were all we had.

All information we got, we got by inspecting these captures, building hypothesis and implementing tooling to verify these assumptions.

Definitely we will not have interpreted everything correct and we'll probably do a lot of updating of this page.

Due to this uncertainty we are only implementing a `promiscuous mode` driver, which will be able to read ongoing traffic without the ability to actively interact with the system.

Read link:reverse-engineering.html[here] for a document on how we proceeded with reverse engineering this protocol.

=== The Wrapper Protocol

All communication seems to be done using the `UDP` protocol on port `18507`.

When working with the dumps it seems that this version of the DeltaV protocol was implemented by wrapping another protocol within a thin wrapper protocol.
We have seen this for almost all other industrial controller types.
We are hereby assuming that the inner protocol is the version of the DeltaV protocol, which is used for serial communication.

The general structure of such a wrapper packet seems to look like this:

[packetdiag,deltav-wrapper-packet,svg]
....
{
    colwidth = 32

    * Header\n(Constant = 0xFACE) [len = 16]
    * Payload Length [len = 16]
    * Type [len = 16]
    * Message Id [len = 16]
    * Sender Id [len = 16]
    * Timestamp [len = 24]
    * 0x80/0x00 [len = 8]
    * 0x04 [len = 8]
    * 0x00 [len = 8]
    * Payload [len = 64]
    * Checksum [len = 16]
}
....

After the fixed header of `0xFACE` comes a short value providing the `Payload Length`.

After the payload probably comes the message type.
Here we have observed the internal structure of packets with the same type to be very similar, so we're quite sure this is a type.

The `Message Id` seems to be a counter existing for every communication between two partners, which is incremented for every packet sent to the other partner.

We have observed every unit to have a unique `Sender Id`, however a controller has been seen to have a differing id when communicating with a different operator system.
What we did notice however, was that controllers usually had numerical ids while the OSes had hexadecimal letters.
So an id of `0x0002` would probably imply a controller and an id of `0x000d` would imply an operator system.
But we could be wrong with this assumption and this just being a result of convention.

The 3 bytes after the id seem to relate to a timestamp.
Inspecting the packets and correlating this to the timestamps in our packet captures, lead us to the assumption that this timestamp uses the first 14 bits for encoding seconds and the last 10 bits for encoding the sub-second fraction.
While a 3 byte timestamp is only able to measure a 10 minute time-slot and is probably not usable for absolute time measuring, it might be useful to measure transfer times.
For this a 4 byte value would have been a waste of network capacity and a 2 byte value wouldn't be able to measure more than 60 seconds, which might be too short when taking into account packet resending and network timeouts.
So for now we are assuming this format is used.

The following 3 bytes seem to have a constant value of `0x800400` for most packets, except the ones with a type of `0x03` and `0x04`.
For these two types the value seems to be `0x004400`. Here we currently can't make an assumption on the meaning of these fields.
However as the `0x003` and `0x004` seem to be the first packets used for establishing the connection between the OS and the controller, these could somehow relate to the connection state.

After these mysterious 3 bytes comes the actual payload of the packet.
The details of these will be discussed later in this document.

At the end comes a short value that seems to be a completely different value for every packet.
As the message id and timestamp are different for every message, we are assuming this is a simple checksum.
However we currently have no idea on how this is calculated and we haven't invested any time in finding this out.

UDP being connectionless the DeltaV network protocol seems to require acknowledging.
This is done by sending a packet back to the originator, but with a length of `0x0000` and the same `type` and mesasge-id` as the packet that is acknowledged.

=== High Level View of the Protocol

In this section we'll describe the general structure of how the communication looks like - which message types are sent when and in which sequence.

==== Connecting an OS to a Controller

[seqdiag,deltav-connect]
....
{
    Controller; OS;

    edge_length = 400;
    span_height = 18;
    default_fontsize = 12;
    activation = none;

    OS ->  Controller [label = "Type 0x0003 (Connection Request)"];
    OS <-  Controller [label = "Type 0x0004 (Connection Response)"];

    OS ->  Controller [label = "Type 0x0002 Subtype 0x0501 (Version Information)"];
    OS <-- Controller [label = "Ack"];

    OS <- Controller [label = "Type 0x0002 Subtype 0x0502 (Version Information)"];
    OS --> Controller [label = "Ack"];
}
....

==== Regular sync

It seems that every 15 seconds two packets are exchanged.

The type of these is always `0x0006`.

One thing that is quite obvious, is that these are the only messages, for which the message id doesn't seem to be incremented.
So these messages have the exact same id as the following request being sent out.

We are assuming that these are sort of heartbeat messages and additionally sync the message ids so the remote could see if it lost messages in the last 15 seconds.

But that is just an assumption.

[seqdiag,deltav-sync]
....
{
    Controller; OS;

    edge_length = 400;
    span_height = 18;
    default_fontsize = 12;
    activation = none;

    OS -> Controller [label = "Type 0x0006 (Sync)"];
    OS <-- Controller [label = "Type 0x0006 (Sync)"];
}
....

==== Data changes in the Controller

In general it seems as if all sub-types regarding normal data changes start with `0x04`.

If the value of a subscribed value changes in the controller, a message type `0x0002` with sub-type `0x0403` is sent.

[seqdiag,deltav-data]
....
{
    Controller; OS;

    edge_length = 400;
    span_height = 18;
    default_fontsize = 12;
    activation = none;

    OS <- Controller [label = "Type 0x0002 Subtype 0x0403 (...)"];
    OS --> Controller [label = "Ack"];
}
....

==== Alarms in the Controller

In general it seems as if all sub-types regarding events and alarms start with `0x03`.

[seqdiag,deltav-alarm]
....
{
    Controller; OS;

    edge_length = 400;
    span_height = 18;
    default_fontsize = 12;
    activation = none;

    OS <- Controller [label = "Type 0x0002 Subtype 0x030? (...)"];
    OS --> Controller [label = "Ack"];
}
....



