Ethernet Service API

Using the Ethernet Controller

The Ethernet Service API provides an Ethernet bus abstraction through the IEthernetController interface. An Ethernet controller is created by calling CreateEthernetController given a controller name and network name:

auto* ethernetController = participant->CreateEthernetController("Eth1", "Eth");

Ethernet controllers will only communicate within the same network.

Initialization

The Ethernet controller first has to call Activate() before being able to send frames. Note that Activate() can be called in the CommunicationReadyHandler of a LifecycleService.

Sending Ethernet Frames

An EthernetFrame is sent with SendFrame() and received as an EthernetFrameEvent. The EthernetFrame must be setup according to layer 2 of IEEE 802.3, with

  • a source and destination MAC address (6 octets each),

  • an optional 802.1Q tag (4 octets),

  • the EtherType or length (Ethernet II or IEEE 802.3, 2 octets), and

  • the payload to be transmitted (46 octets or more).

Note

The frame check sequence (32-bit CRC, 4 octets) is omitted. Thus, the minimum length of a frame is 60 octets.

Note

If the frame is shorter than the minimum length of 60 octets, the frame will be padded with zeros to the minimum length.

A valid frame can be setup and sent as follows:

// Prepare an Ethernet frame
const std::array<uint8_t, 6> destinationAddress{ 0xf6, 0x04, 0x68, 0x71, 0xaa, 0xc2 };
const std::array<uint8_t, 6> sourceAddress{ 0xf6, 0x04, 0x68, 0x71, 0xaa, 0xc1 };
const uint16_t etherType{ 0x0800 };

const std::string message("Ensure that the payload is long enough to constitute"
                    " a valid Ethernet frame ----------------------------");
const std::vector<uint8_t> payload{ message.begin(), message.end() };

EthernetFrame frame{};
std::copy(destinationAddress.begin(), destinationAddress.end(), std::back_inserter(frame.raw));
std::copy(sourceAddress.begin(), sourceAddress.end(), std::back_inserter(frame.raw));
auto etherTypeBytes = reinterpret_cast<const uint8_t*>(&etherType);
frame.raw.push_back(etherTypeBytes[1]);  // We assume our platform to be little-endian
frame.raw.push_back(etherTypeBytes[0]);
std::copy(payload.begin(), payload.end(), std::back_inserter(frame.raw));

ethernetController->SendFrame(frame);

Transmission Acknowledgement

To be notified of the success or failure of the transmission, a FrameTransmitHandler can be registered using AddFrameTransmitHandler():

auto frameTransmitHandler = [](IEthernetController*, const EthernetFrameTransmitEvent& frameTransmitEvent)
{
  // Handle frameTransmitEvent
};
ethernetController->AddFrameTransmitHandler(frameTransmitHandler);

An optional second parameter of AddFrameTransmitHandler() allows to specify the status (EthernetTransmitStatus::Transmitted, …) of the EthernetFrameTransmitEvent to be received. By default, each status is enabled.

Note

In a simple simulation, the EthernetTransmitStatus of the EthernetFrameTransmitEvent will always be EthernetTransmitStatus::Transmitted.

Receiving Ethernet Frame Events

An EthernetFrame is received as an EthernetFrameEvent consisting of a transmitId used to identify the acknowledgement of the frame, a timestamp and the actual EthernetFrame.

To receive Ethernet frames, a frame handler must be registered using AddFrameHandler(). The handler is called whenever an Ethernet frame is received:

auto frameHandler = [](IEthernetController*, const EthernetFrameEvent& frameEvent)
{
  // Handle frameEvent
};
ethernetController->AddFrameHandler(frameHandler);

An optional second parameter of AddFrameHandler() allows to specify the direction (Tx, Rx, Tx/Rx) of the Ethernet frames to be received. By default, only frames of Rx direction are handled.

Managing the Event Handlers

Adding a handler will return a HandlerId which can be used to remove the handler via:

Switches

Switches can be used in a detailed simulation. Refer to the documentation of the network simulator for further information.

Receiving State Change Events

To receive state changes of an Ethernet controller, a StateChangeHandler must be registered using AddStateChangeHandler():

auto stateChangedHandler = [](IEthernetController*, const EthernetStateChangeEvent& stateChangeEvent)
{
  // Handle stateChangeEvent;
};
ethernetController->AddStateChangeHandler(stateChangedHandler);

Acknowledgements

When sending frames, the EthernetTransmitStatus of the EthernetFrameTransmitEvent received in the FrameTransmitHandler will be one of the following values:

API and Data Type Reference

Ethernet Controller API

class IEthernetController

Abstract Ethernet Controller API to be used by vECUs.

Public Types

using CallbackT = std::function<void(IEthernetController *controller, const MsgT &msg)>

Generic Ethernet callback method.

using FrameHandler = CallbackT<EthernetFrameEvent>

Callback type to indicate that an EthernetFrameEvent has been received. Cf. AddFrameHandler(FrameHandler,DirectionMask);

using FrameTransmitHandler = CallbackT<EthernetFrameTransmitEvent>

Callback type to indicate that an EthernetFrameTransmitEvent has been received. Cf. AddFrameTransmitHandler(FrameTransmitHandler,EthernetTransmitStatusMask);

using StateChangeHandler = CallbackT<EthernetStateChangeEvent>

Callback type to indicate that the EthernetState has changed. Cf. AddStateChangeHandler(StateChangeHandler);

using BitrateChangeHandler = CallbackT<EthernetBitrateChangeEvent>

Callback type to indicate that the link bit rate has changed. Cf. AddBitrateChangeHandler(BitrateChangeHandler);

Public Functions

virtual ~IEthernetController() = default
virtual void Activate() = 0

Activates the Ethernet controller.

Upon activation of the controller, the controller attempts to establish a link. Messages can only be sent once the link has been successfully established, cf. AddStateChangeHandler() and AddBitrateChangeHandler().

virtual void Deactivate() = 0

Deactivate the Ethernet controller.

Deactivate the controller and shut down the link. The controller will no longer receive messages, and it cannot send messages anymore.

virtual auto AddFrameHandler(FrameHandler handler, DirectionMask directionMask = static_cast<DirectionMask>(TransmitDirection::RX)) -> HandlerId = 0

Register a callback for Ethernet message reception.

The handler is called when the controller receives a new Ethernet message.

Returns

Returns a SilKit::Util::HandlerId that can be used to remove the callback.

virtual void RemoveFrameHandler(HandlerId handlerId) = 0

Remove a FrameHandler by SilKit::Util::HandlerId on this controller.

Parameters

handlerId – Identifier of the callback to be removed. Obtained upon adding to respective handler.

virtual auto AddFrameTransmitHandler(FrameTransmitHandler handler, EthernetTransmitStatusMask transmitStatusMask = SilKit_EthernetTransmitStatus_DefaultMask) -> HandlerId = 0

Register a callback for Ethernet transmit acknowledgments.

The handler is called when a previously sent message was successfully transmitted or when the transmission has failed. The original message is identified by the transmitId.

NB: Full support in a detailed simulation. In a simple simulation, all messages are immediately positively acknowledged.

Returns

Returns a SilKit::Util::HandlerId that can be used to remove the callback.

virtual void RemoveFrameTransmitHandler(HandlerId handlerId) = 0

Remove a FrameTransmitHandler by SilKit::Util::HandlerId on this controller.

Parameters

handlerId – Identifier of the callback to be removed. Obtained upon adding to respective handler.

virtual auto AddStateChangeHandler(StateChangeHandler handler) -> HandlerId = 0

Register a callback for changes of the controller state.

The handler is called when the state of the controller changes. E.g., a call to Activate() causes the controller to change from state SilKit::Services::Ethernet::EthernetState::Inactive to SilKit::Services::Ethernet::EthernetState::LinkDown. Later, when the link has been established, the state changes again from SilKit::Services::Ethernet::EthernetState::LinkDown to SilKit::Services::Ethernet::EthernetState::LinkUp. Similarly, the status changes back to SilKit::Services::Ethernet::EthernetState::Inactive upon a call to Deactivate().

Returns

Returns a SilKit::Util::HandlerId that can be used to remove the callback.

virtual void RemoveStateChangeHandler(HandlerId handlerId) = 0

Remove a StateChangeHandler by SilKit::Util::HandlerId on this controller.

Parameters

handlerId – Identifier of the callback to be removed. Obtained upon adding to respective handler.

virtual auto AddBitrateChangeHandler(BitrateChangeHandler handler) -> HandlerId = 0

Register a callback for changes of the link bit rate.

The handler is called when the bit rate of the connected link changes. This is typically the case when a link was successfully established, or the controller was deactivated.

Returns

Returns a SilKit::Util::HandlerId that can be used to remove the callback.

virtual void RemoveBitrateChangeHandler(HandlerId handlerId) = 0

Remove a BitrateChangeHandler by SilKit::Util::HandlerId on this controller.

Parameters

handlerId – Identifier of the callback to be removed. Obtained upon adding to respective handler.

virtual void SendFrame(EthernetFrame msg, void *userContext = nullptr) = 0

Send an Ethernet frame with the time provider’s current time.

If the size of the Ethernet frame is smaller than the minimum size of 60 bytes (excludes the Frame Check Sequence), it will be padded with zeros.

NB: precise timestamps are always generated by the NetworkSimulator.

Parameters
  • msg – The Ethernet frame to send.

  • userContext – Optional user provided pointer that is reobtained in the FrameTransmitHandler.

Data Structures

struct EthernetFrame

An Ethernet frame (layer 2)

Public Members

Util::Span<const uint8_t> raw

The Ethernet raw frame without the frame check sequence.

struct EthernetFrameEvent

An Ethernet frame including the raw frame, Transmit ID and timestamp.

Public Members

std::chrono::nanoseconds timestamp

Reception time.

EthernetFrame frame

The Ethernet frame.

TransmitDirection direction

Receive/Transmit direction.

void *userContext

Optional pointer provided by user when sending the frame.

struct EthernetFrameTransmitEvent

Publishes status of the simulated Ethernet controller.

Public Members

std::chrono::nanoseconds timestamp

Timestamp of the Ethernet acknowledge.

EthernetTransmitStatus status

Status of the EthernetTransmitRequest.

void *userContext

Optional pointer provided by user when sending the frame.

struct EthernetStateChangeEvent

A state change event of the Ethernet controller.

Public Members

std::chrono::nanoseconds timestamp

Timestamp of the state change.

EthernetState state

State of the Ethernet controller.

struct EthernetBitrateChangeEvent

A bitrate change event of the Ethernet controller.

Public Members

std::chrono::nanoseconds timestamp

Timestamp of the state change.

EthernetBitrate bitrate

Bit rate in kBit/sec of the link when in state LinkUp, otherwise zero.

Enumerations and Typedefs

using SilKit::Services::Ethernet::EthernetBitrate = uint32_t

Bitrate in kBit/sec.

enum SilKit::Services::Ethernet::EthernetTransmitStatus

Acknowledgment status for an EthernetTransmitRequest.

Values:

enumerator Transmitted

The message was successfully transmitted on the Ethernet link.

enumerator ControllerInactive

The transmit request was rejected, because the Ethernet controller is not active.

enumerator LinkDown

The transmit request was rejected, because the Ethernet link is down.

enumerator Dropped

The transmit request was dropped, because the transmit queue is full.

enumerator InvalidFrameFormat

The given raw Ethernet frame is ill formated (e.g. frame length is too small or too large, wrong order of VLAN tags).

enum SilKit::Services::Ethernet::EthernetState

State of the Ethernet controller.

Values:

enumerator Inactive

The Ethernet controller is switched off (default after reset).

enumerator LinkDown

The Ethernet controller is active, but a link to another Ethernet controller in not yet established.

enumerator LinkUp

The Ethernet controller is active and the link to another Ethernet controller is established.

Usage Examples

This section contains complete examples that show the usage and the interaction of two Ethernet controllers. Although the Ethernet controllers would typically belong to different participants and reside in different processes, their interaction is shown sequentially to demonstrate cause and effect.

Assumptions:

  • Variables ethernetReceiver, ethernetSender are of type IEthernetController.

  • All Ethernet controllers are connected to the same switch.

Simple Ethernet Sender / Receiver Example

This example shows a successful data transfer from one Ethernet controller to another.

/* Copyright (c) 2022 Vector Informatik GmbH

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
// ------------------------------------------------------------
// Receiver Setup
// Register FrameHandler to receive Ethernet messages.
auto receiver_FrameHandler =
    [](IEthernetController*, const EthernetFrameEvent&) {};
ethernetReceiver->AddFrameHandler(receiver_FrameHandler);

ethernetReceiver->Activate();


// ------------------------------------------------------------
// Sender Setup
// Register FrameTransmitHandler to receive acknowledges of transmissions.
auto sender_FrameTransmitHandler =
    [](IEthernetController*, const EthernetFrameTransmitEvent&) {};
ethernetSender->AddFrameTransmitHandler(sender_FrameTransmitHandler);

ethernetSender->Activate();


// ------------------------------------------------------------
// Send an Ethernet message
const std::array<uint8_t, 6> destinationAddress{0xf6, 0x04, 0x68, 0x71, 0xaa, 0xc2};
const std::array<uint8_t, 6> sourceAddress{0xf6, 0x04, 0x68, 0x71, 0xaa, 0xc1};
const std::array<uint8_t, 4> vlanTag{0x81, 0x00, 0x00, 0x00};  // optional
const std::array<uint8_t, 2> etherType{0x08, 0x00};
const std::array<uint8_t, 4> frameCheckSequence{0x00, 0x00, 0x00, 0x00};  // optional

const std::string message{"Ensure that the payload is at least 46 bytes to constitute "
                    "a valid Ethernet frame ------------------------------"};
const std::vector<uint8_t> payload{message.begin(), message.end()};

const std::array<uint8_t, 2> frameCheckSequence{0x00, 0x00};  // optional for 

EthernetFrame frame{};
std::copy(destinationAddress.begin(), destinationAddress.end(), std::back_inserter(frame));
std::copy(sourceAddress.begin(), sourceAddress.end(), std::back_inserter(frame));
std::copy(vlanTag.begin(), vlanTag.end(), std::back_inserter(frame));
std::copy(etherType.begin(), etherType.end(), std::back_inserter(frame));
std::copy(payload.begin(), payload.end(), std::back_inserter(frame));
std::copy(frameCheckSequence.begin(), frameCheckSequence.end(), std::back_inserter(frame));

// The returned transmitId can be used to check if the ethernetFrameTransmitEvent
// that should be triggered after a successful reception has the same transmitId.
auto transmitId = ethernetSender->SendFrame(frame);


// ------------------------------------------------------------
// The following callbacks will be triggered:
//  - TX confirmation for the sender.
sender_FrameTransmitHandler(ethernetSender, ethernetFrameTransmitEvent);
// with:
//  - ethernetFrameTransmitEvent.transmitId == 1
//  - ethernetFrameTransmitEvent.sourceMac == {0xF6, 0x04, 0x68, 0x71, 0xAA, 0xC1}
//  - ethernetFrameTransmitEvent.timestamp == <Timestamp of EthernetFrame>
//  - ethernetFrameTransmitEvent.status == EthernetTransmitStatus::Transmitted 
// Note: In a detailed simulation, the status can also be EthernetTransmitStatus::LinkDown.

//  - RX Ethernet message for the receiver.
receiver_FrameHandler(ethernetReceiver, ethernetFrameEvent);

State Transition Example

This example shows the possible state transitions for an Ethernet controller.

/* Copyright (c) 2022 Vector Informatik GmbH

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
// ------------------------------------------------------------
// Controller Setup
// The initial state for ethernetSender is EthernetState::Inactive.
// Register StateChangeHandler to receive EthernetState changes
auto sender_StateChangedHandler =
    [](IEthernetController*, const EthernetStateChangeEvent& stateChangeEvent) {};
ethernetSender->AddStateChangeHandler(sender_StateChangedHandler);


// ------------------------------------------------------------
// Transition from EthernetState::Inactive to EthernetState::LinkDown.
ethernetSender->Activate();
// The StateChangeHandler callback will be triggered and call the registered handler:
sender_StateChangedHandler(ethernetSender, stateChangeEvent);
// with state == EthernetState::LinkDown

// ------------------------------------------------------------
// Transition from EthernetState::LinkDown to EthernetState::LinkUp.
// After some time, as soon as the link to the switch is successfully established,
// the StateChangeHandler callback will be triggered again and call the registered handler:
sender_StateChangedHandler(ethernetSender, stateChangeEvent);
// with state == EthernetState::LinkUp


// ------------------------------------------------------------
// Transition from EthernetState::LinkUp to EthernetState::Inactive.
ethernetSender->Deactivate();
// The StateChangeHandler callback will be triggered and call the registered handler:
sender_StateChangedHandler(ethernetSender, stateChangeEvent);
// with state == EthernetState::Inactive

Erroneous Transmissions (Detailed Simulation only)

This example shows different possible erroneous Ethernet transmissions.

/* Copyright (c) 2022 Vector Informatik GmbH

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
// ------------------------------------------------------------
// Receiver Setup
ethernetReceiver->Activate();

// ------------------------------------------------------------
// Sender Setup
// Register MessageAckHandler to receive acknowledges of transmissions
auto sender_FrameTransmitHandler =
    [](IEthernetController*, const EthernetFrameTransmitEvent&) {};
ethernetSender->AddFrameTransmitHandler(sender_FrameTransmitHandler);

// ------------------------------------------------------------
// Erroneous Transmission: EthernetTransmitStatus::ControllerInactive
const std::array<uint8_t, 6> sourceAddress{0xf6, 0x04, 0x68, 0x71, 0xaa, 0xc2};
const std::array<uint8_t, 6> destinationAddress{0xf6, 0x04, 0x68, 0x71, 0xaa, 0xc1};
const std::array<uint8_t, 2> etherType{0x08, 0x00};

const std::string message{"Ensure that the payload is at least 46 bytes to constitute "
                    "a valid Ethernet frame ------------------------------"};
const std::vector<uint8_t> payload{ message.begin(), message.end() };

EthernetFrame frame;
std::copy(destinationAddress.begin(), destinationAddress.end(), std::back_inserter(frame));
std::copy(sourceAddress.begin(), sourceAddress.end(), std::back_inserter(frame));
std::copy(etherType.begin(), etherType.end(), std::back_inserter(frame));
std::copy(payload.begin(), payload.end(), std::back_inserter(frame));

ethernetSender->SendFrame(frame);

// The FrameTransmitHandler callback will be triggered and call the registered handler:
sender_FrameTransmitHandler(ethernetSender, frameTransmitEvent);
// with frameTransmitEvent.status == EthernetTransmitStatus::ControllerInactive

// ------------------------------------------------------------
// Erroneous Transmission: EthernetTransmitStatus::LinkDown
ethernetSender->Activate();
ethernetSender->SendFrame(ethernetFrame);

// As long as the Ethernet link is not successfully established,
// the MessageAckHandler callback will be triggered and call the registered handler:
sender_FrameTransmitHandler(ethernetSender, frameTransmitEvent);
// with frameTransmitEvent.status == EthernetTransmitStatus::LinkDown

// ------------------------------------------------------------
// Erroneous Transmission: EthernetTransmitStatus::Dropped
// Assumption: Ethernet link is already successfully established.
for (auto i = 0; i < 50; i++)
{
    ethernetSender->SendFrame(ethernetFrame);
}

// Sending 50 messages directly one after the other will call the registered sender_MessageAckHandler
// positively with some EthernetTransmitStatus::Transmitted until the transmit queue overflows
// and the Ethernet messages are acknowledged with status EthernetTransmitStatus::Dropped.

// ------------------------------------------------------------
// Erroneous Transmission: EthernetTransmitStatus::InvalidFrameFormat
const std::string longMessage(4000, 'a'); // much longer than the maximum allowed Ethernet frame size of 1534 bytes
const std::vector<uint8_t> longPayload{longMessage.begin(), longMessage.end()};

EthernetFrame invalidFrame;
std::copy(destinationAddress.begin(), destinationAddress.end(), std::back_inserter(invalidFrame));
std::copy(sourceAddress.begin(), sourceAddress.end(), std::back_inserter(invalidFrame));
std::copy(etherType.begin(), etherType.end(), std::back_inserter(invalidFrame));
std::copy(longPayload.begin(), longPayload.end(), std::back_inserter(invalidFrame));

ethernetSender->SendFrame(invalidEthernetFrame);

// The MessageAckHandler callback will be triggered and call the registered handler:
sender_FrameTransmitHandler(ethernetSender, frameTransmitEvent);
// with frameTransmitEvent.status == EthernetTransmitStatus::InvalidFrameFormat,
// as the Ethernet frame size is too long.