From a8db2aefc860cdfec0b76ae767f5c91fe7ec042f Mon Sep 17 00:00:00 2001
From: garciay <yann.garcia@fscom.fr>
Date: Tue, 22 Oct 2024 07:01:35 +0200
Subject: [PATCH] Add offline/online SCTP support

---
 ccsrc/Framework/include/params.hh             |   4 +
 ccsrc/Framework/src/params.cc                 |   4 +
 ccsrc/Protocols/IP/ip_offline_layer.cc        |  73 +++
 ccsrc/Protocols/IP/ip_offline_layer.hh        |  48 ++
 .../Protocols/IP/ip_offline_layer_factory.hh  |  42 ++
 ccsrc/Protocols/IP/module.mk                  |   3 +
 ccsrc/Protocols/Pcap/pcap_offline_layer.cc    | 463 +++++++++---------
 ccsrc/Protocols/Sctp/module.mk                |   2 +
 ccsrc/Protocols/Sctp/sctp_layer.cc            | 285 +++++++++++
 ccsrc/Protocols/Sctp/sctp_layer.hh            | 108 ++++
 ccsrc/Protocols/Sctp/sctp_layer_factory.hh    |  41 ++
 ccsrc/Protocols/Sctp/sctp_offline_layer.cc    | 118 +++++
 ccsrc/Protocols/Sctp/sctp_offline_layer.hh    |  76 +++
 .../Sctp/sctp_offline_layer_factory.hh        |  42 ++
 14 files changed, 1082 insertions(+), 227 deletions(-)
 create mode 100644 ccsrc/Protocols/IP/ip_offline_layer.cc
 create mode 100644 ccsrc/Protocols/IP/ip_offline_layer.hh
 create mode 100644 ccsrc/Protocols/IP/ip_offline_layer_factory.hh
 create mode 100644 ccsrc/Protocols/IP/module.mk
 create mode 100644 ccsrc/Protocols/Sctp/module.mk
 create mode 100644 ccsrc/Protocols/Sctp/sctp_layer.cc
 create mode 100644 ccsrc/Protocols/Sctp/sctp_layer.hh
 create mode 100644 ccsrc/Protocols/Sctp/sctp_layer_factory.hh
 create mode 100644 ccsrc/Protocols/Sctp/sctp_offline_layer.cc
 create mode 100644 ccsrc/Protocols/Sctp/sctp_offline_layer.hh
 create mode 100644 ccsrc/Protocols/Sctp/sctp_offline_layer_factory.hh

diff --git a/ccsrc/Framework/include/params.hh b/ccsrc/Framework/include/params.hh
index bc683cf..9dc4b61 100644
--- a/ccsrc/Framework/include/params.hh
+++ b/ccsrc/Framework/include/params.hh
@@ -35,6 +35,10 @@ public: //! \publicsection
 
   static const std::string& nic;          //! Network Interface Card parameter name
 
+  static const std::string& ip_src;      //! Source IP address parameter name
+  static const std::string& ip_dst;      //! Destination IP address parameter name
+  static const std::string& ip_proto;    //! IP proto parameter name
+
   static const std::string& server;       //! Remote server address (e.g. www.etsi.org)
   static const std::string& port;         //! Remote server port. Default: 80
   static const std::string& use_ssl;      //! Set to 1 to use SSL to communicate with the HTTP server. Default: false
diff --git a/ccsrc/Framework/src/params.cc b/ccsrc/Framework/src/params.cc
index a183714..bc4ebb8 100644
--- a/ccsrc/Framework/src/params.cc
+++ b/ccsrc/Framework/src/params.cc
@@ -28,6 +28,10 @@ const std::string& params::filter       = std::string("filter");
 
 const std::string& params::nic          = std::string("nic");
 
+const std::string& params::ip_src      = std::string("ip_src");
+const std::string& params::ip_dst      = std::string("ip_dst");
+const std::string& params::ip_proto    = std::string("ip_proto");
+
 const std::string& params::server       = std::string("server");
 const std::string& params::port         = std::string("port");
 const std::string& params::use_ssl      = std::string("use_ssl");
diff --git a/ccsrc/Protocols/IP/ip_offline_layer.cc b/ccsrc/Protocols/IP/ip_offline_layer.cc
new file mode 100644
index 0000000..3070210
--- /dev/null
+++ b/ccsrc/Protocols/IP/ip_offline_layer.cc
@@ -0,0 +1,73 @@
+#include "ip_offline_layer_factory.hh"
+
+#include "loggers.hh"
+
+ip_offline_layer::ip_offline_layer(const std::string&  p_type, const std::string&  p_param) : layer(p_type), _params() {
+  loggers::get_instance().log(">>> ip_offline_layer::ip_offline_layer: '%s', %s", to_string().c_str(), p_param.c_str());
+  // Setup parameters
+  params::convert(_params, p_param);
+}
+
+void ip_offline_layer::send_data(OCTETSTRING& p_data, params& p_params) {
+  loggers::get_instance().log_msg(">>> ip_offline_layer::send_data: ", p_data);
+
+  //OCTETSTRING ip;
+  // FIXME FSCOM: To be done
+  loggers::get_instance().error("ip_offline_layer::send_data: Not implemented. On;ly for offline mode");
+  //ip    += p_data;
+  //send_to_all_layers(ip, static_cast<params&>(p_params));
+}
+
+void ip_offline_layer::receive_data(OCTETSTRING& p_data, params& p_params) {
+  loggers::get_instance().log_msg(">>> ip_offline_layer::receive_data: ", p_data);
+
+  // Version
+  uint8_t v = static_cast<const uint8_t>(*p_data);
+  OCTETSTRING version = int2oct(1, v >> 4);
+  loggers::get_instance().log_msg("ip_offline_layer::receive_data: verion: ", version);
+  // Length in bytes
+  uint8_t length = (v & 0x0f) * 4;
+  loggers::get_instance().log("ip_offline_layer::receive_data: length: %d", length);
+  // DSF
+  OCTETSTRING dsf = OCTETSTRING(1, 1 + static_cast<const uint8_t *>(p_data));
+  loggers::get_instance().log_msg("ip_offline_layer::receive_data: dsf: ", dsf);
+  // Total Length
+  OCTETSTRING total_length = OCTETSTRING(2, 2 + static_cast<const uint8_t*>(p_data));
+  loggers::get_instance().log_msg("ip_offline_layer::receive_data: total_length: ", total_length);
+  // Identification
+  OCTETSTRING id = OCTETSTRING(2, 4 + static_cast<const uint8_t*>(p_data));
+  loggers::get_instance().log_msg("ip_offline_layer::receive_data: id: ", id);
+  // Flags 
+  OCTETSTRING flags = OCTETSTRING(2, 6 + static_cast<const uint8_t*>(p_data));
+  loggers::get_instance().log_msg("ip_offline_layer::receive_data: id: ", flags);
+  // TTL 
+  OCTETSTRING ttl = OCTETSTRING(1, 8 + static_cast<const uint8_t*>(p_data));
+  loggers::get_instance().log_msg("ip_offline_layer::receive_data: ttl: ", ttl);
+  // Protocol 
+  OCTETSTRING protocol = OCTETSTRING(1, 9 + static_cast<const uint8_t*>(p_data));
+  loggers::get_instance().log_msg("ip_offline_layer::receive_data: protocol: ", protocol);
+  // checksum 
+  OCTETSTRING checksum = OCTETSTRING(2, 10 + static_cast<const uint8_t*>(p_data));
+  loggers::get_instance().log_msg("ip_offline_layer::receive_data: checksum: ", checksum);
+  // src 
+  OCTETSTRING src = OCTETSTRING(4, 12 + static_cast<const uint8_t*>(p_data));
+  loggers::get_instance().log_msg("ip_offline_layer::receive_data: src: ", src);
+  // dst 
+  OCTETSTRING dst = OCTETSTRING(4, 16 + static_cast<const uint8_t*>(p_data));
+  loggers::get_instance().log_msg("ip_offline_layer::receive_data: dst: ", dst);
+
+  OCTETSTRING data = OCTETSTRING(p_data.lengthof() - length, length + static_cast<const uint8_t *>(p_data));
+  loggers::get_instance().log_msg("ip_offline_layer::receive_data: payload for upper layer:", data);
+
+  // Update params
+  CHARSTRING s = oct2str(dst);
+  p_params.insert(std::pair<std::string, std::string>(params::ip_dst, std::string(static_cast<const char *>(s))));
+  s = oct2str(src);
+  p_params.insert(std::pair<std::string, std::string>(params::ip_src, std::string(static_cast<const char *>(s))));
+  s = oct2str(protocol);
+  p_params.insert(std::pair<std::string, std::string>(params::ip_proto, std::string(static_cast<const char *>(s))));
+
+  receive_to_all_layers(data, static_cast<params&>(p_params));
+}
+
+ip_offline_layer_factory ip_offline_layer_factory::_f;
diff --git a/ccsrc/Protocols/IP/ip_offline_layer.hh b/ccsrc/Protocols/IP/ip_offline_layer.hh
new file mode 100644
index 0000000..3b31a80
--- /dev/null
+++ b/ccsrc/Protocols/IP/ip_offline_layer.hh
@@ -0,0 +1,48 @@
+/*!
+ * \file      ip_offline_layer.hh
+ * \brief     Header file for ITS IP protocol layer definition.
+ * \author    ETSI TTF T041
+ * \copyright ETSI Copyright Notification
+ *            No part may be reproduced except as authorized by written permission.
+ *            The copyright and the foregoing restriction extend to reproduction in all media.
+ *            All rights reserved.
+ * \version   0.1
+ */
+#pragma once
+
+#include "params.hh"
+#include "t_layer.hh"
+
+class ip_offline_layer : public layer {
+  params _params; //! Layer parameters
+
+public: //! \publicsection
+  /*!
+   * \brief Specialised constructor
+   *        Create a new instance of the ip_offline_layer class
+   * \param[in] p_type \todo
+   * \param[in] p_param \todo
+   */
+  ip_offline_layer(const std::string& p_type, const std::string& p_param);
+  /*!
+   * \brief Default destructor
+   */
+  virtual ~ip_offline_layer(){};
+
+  /*!
+   * \virtual
+   * \fn void send_data(OCTETSTRING& data, params& p_params);
+   * \brief Send bytes formated data to the lower layers
+   * \param[in] p_data The data to be sent
+   * \param[in] p_params Some parameters to overwrite default value of the lower layers parameters
+   */
+  virtual void send_data(OCTETSTRING& p_data, params& p_params);
+  /*!
+   * \virtual
+   * \fn void receive_data(OCTETSTRING& data, params& p_params);
+   * \brief Receive bytes formated data from the lower layers
+   * \param[in] p_data The bytes formated data received
+   * \param[in] p_params Some lower layers parameters values when data was received
+   */
+  virtual void receive_data(OCTETSTRING& p_data, params& p_params);
+}; // End of class ip_offline_layer
diff --git a/ccsrc/Protocols/IP/ip_offline_layer_factory.hh b/ccsrc/Protocols/IP/ip_offline_layer_factory.hh
new file mode 100644
index 0000000..9a40841
--- /dev/null
+++ b/ccsrc/Protocols/IP/ip_offline_layer_factory.hh
@@ -0,0 +1,42 @@
+/*!
+ * \file      ip_offline_layer_factory.hh
+ * \brief     Header file for ITS Ethernet protocol layer factory.
+ * \author    ETSI TTF T041
+ * \copyright ETSI Copyright Notification
+ *            No part may be reproduced except as authorized by written permission.
+ *            The copyright and the foregoing restriction extend to reproduction in all media.
+ *            All rights reserved.
+ * \version   0.1
+ */
+#pragma once
+
+#include "layer_stack_builder.hh"
+
+#include "ip_offline_layer.hh"
+
+/*!
+ * \class ip_offline_layer_factory
+ * \brief  This class provides a factory class to create an ip_offline_layer class instance
+ */
+class ip_offline_layer_factory : public layer_factory {
+  static ip_offline_layer_factory _f; //! Reference to the unique instance of this class
+public:                             //! \publicsection
+  /*!
+   * \brief Default constructor
+   *        Create a new instance of the ip_offline_layer_factory class
+   * \remark The ETH layer identifier is ETH
+   */
+  ip_offline_layer_factory() {
+    // register factory
+    layer_stack_builder::register_layer_factory("IP_OFFLINE", this);
+  };
+  /*!
+   * \fn layer* create_layer(const std::string&  type, const std::string&  param);
+   * \brief  Create the layers stack based on the provided layers stack description
+   * \param[in] p_type The provided layers stack description
+   * \param[in] p_params Optional parameters
+   * \return 0 on success, -1 otherwise
+   * \inline
+   */
+  inline virtual layer *create_layer(const std::string& p_type, const std::string& p_param) { return new ip_offline_layer(p_type, p_param); };
+}; // End of class ip_offline_layer_factory
diff --git a/ccsrc/Protocols/IP/module.mk b/ccsrc/Protocols/IP/module.mk
new file mode 100644
index 0000000..d53608b
--- /dev/null
+++ b/ccsrc/Protocols/IP/module.mk
@@ -0,0 +1,3 @@
+sources := ip_offline_layer.cc 
+includes := .
+
diff --git a/ccsrc/Protocols/Pcap/pcap_offline_layer.cc b/ccsrc/Protocols/Pcap/pcap_offline_layer.cc
index e400f53..2530f7d 100644
--- a/ccsrc/Protocols/Pcap/pcap_offline_layer.cc
+++ b/ccsrc/Protocols/Pcap/pcap_offline_layer.cc
@@ -1,227 +1,236 @@
-#if defined(__CYGWIN__)
-#define _GNU_SOURCE
-#endif
-#include <chrono>
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <Port.hh>
-
-#include "pcap_offline_layer_factory.hh"
-
-#include "loggers.hh"
-
-#include <pcap.h>
-
-#ifdef __CYGWIN__
-typedef struct {
-  bpf_int32 tv_sec;  /* seconds */
-  bpf_int32 tv_usec; /* microseconds */
-} pcap_o_timeval;
-
-typedef struct pcap_o_pkthdr {
-  pcap_o_timeval ts;     /* time stamp */
-  bpf_u_int32    caplen; /* length of portion present */
-  bpf_u_int32    len;    /* length this packet (off wire) */
-} pcap_o_pkthdr;
-#else
-typedef struct pcap_pkthdr pcap_o_pkthdr;
-typedef struct timeval     pcap_o_timeval;
-#endif
-
-pcap_offline_layer::pcap_offline_layer(const std::string& p_type, const std::string& param)
-  : layer(p_type), PORT(p_type.c_str()), _params(), _device(NULL), _running(FALSE), _time_key("pcap_offline_layer::Handle_Fd_Event_Readable") {
-  loggers::get_instance().log(">>> pcap_offline_layer::pcap_offline_layer: '%s', %s", p_type.c_str(), param.c_str());
-  params::convert(_params, param);
-
-  _o_params.insert(std::pair<std::string, std::string>(std::string("timestamp"), std::string()));
-
-  char                   error_buffer[PCAP_ERRBUF_SIZE];
-  params::const_iterator it;
-
-  it        = _params.find(std::string("realtime"));
-  _realtime = ((it != _params.end()) && !it->second.empty());
-
-  it    = _params.find(std::string("loop"));
-  _loop = ((it != _params.end()) && !it->second.empty());
-
-  it = _params.find(std::string("file"));
-  if ((it != _params.end()) && !it->second.empty()) {
-    const std::string& file = it->second;
-    _device                 = pcap_open_offline(file.c_str(), error_buffer);
-    if (_device) {
-
-      // Add user defined filter
-      it = _params.find(params::filter);
-      if ((it != _params.end()) && !it->second.empty()) {
-        const std::string& filter = it->second;
-        // Log final PCAP filter
-        loggers::get_instance().user("pcap_offline_layer::pcap_offline_layer: Filter: '%s'", filter.c_str());
-        struct bpf_program f = {0};
-        if (pcap_compile(_device, &f, filter.c_str(), 1, PCAP_NETMASK_UNKNOWN) != 0) {
-          loggers::get_instance().error("pcap_offline_layer::pcap_offline_layer: Failed to compile PCAP filter");
-        } else {
-          if (pcap_setfilter(_device, &f) != 0) {
-            loggers::get_instance().error("pcap_offline_layer::pcap_offline_layer: Failed to set PCAP filter");
-          }
-        }
-        pcap_freecode(&f);
-      }
-
-      // create pipe and run thread
-      if (pipe2(_fd, O_NONBLOCK) == -1) {
-        loggers::get_instance().error("pcap_offline_layer::pcap_offline_layer: Failed to create a pipe: '%s'", ::strerror(errno));
-      }
-      // Pass the pipe handler to the polling procedure
-      loggers::get_instance().log("pcap_offline_layer::pcap_offline_layer: Call handler with descriptor %d", _fd[0]);
-      Handler_Add_Fd_Read(_fd[0]);
-      // Create the offline reader thread
-      _thread = new std::thread(&pcap_offline_layer::run, (void *)this);
-      if (_thread == NULL) {
-        loggers::get_instance().error("pcap_offline_layer::pcap_offline_layer: Failed to start offline thread");
-      }
-      while (_running == FALSE) {
-        std::this_thread::sleep_for(std::chrono::milliseconds(500));
-      }
-      // Thread was started
-      loggers::get_instance().log("<<< pcap_offline_layer::pcap_offline_layer");
-    }
-  }
-} // End of ctor
-
-pcap_offline_layer::~pcap_offline_layer() {
-  loggers::get_instance().log(">>> pcap_offline_layer::~pcap_offline_layer");
-
-  if (_device != NULL) {
-    if (_thread != NULL) {
-      _running = FALSE;
-      // Wait for the working thread to terminate
-      _thread->join();
-      loggers::get_instance().log("pcap_offline_layer::~pcap_offline_layer: Thread were stops");
-      // Cleanup
-      delete _thread;
-      close(_fd[0]);
-      close(_fd[1]);
-    }
-    pcap_close(_device);
-  }
-} // End of dtor
-
-void *pcap_offline_layer::run(void *p_this) {
-  pcap_offline_layer &p = *static_cast<pcap_offline_layer *>(p_this);
-  return p.thread();
-}
-
-static long timeval_diff(const pcap_o_timeval &x, const pcap_o_timeval &y) {
-  pcap_o_timeval z = y;
-  /* Perform the carry for the later subtraction by updating y. */
-  if (x.tv_usec < y.tv_usec) {
-    int nsec = (y.tv_usec - x.tv_usec) / 1000000 + 1;
-    z.tv_usec -= 1000000 * nsec;
-    z.tv_sec += nsec;
-  }
-  if (x.tv_usec - z.tv_usec > 1000000) {
-    int nsec = (x.tv_usec - z.tv_usec) / 1000000;
-    z.tv_usec += 1000000 * nsec;
-    z.tv_sec -= nsec;
-  }
-
-  return (x.tv_sec - z.tv_sec) * 1000 + ((x.tv_usec - z.tv_usec) / 1000);
-}
-
-void *pcap_offline_layer::thread() {
-  pcap_o_pkthdr *pkt_header;
-  pcap_o_pkthdr  lh;
-  const u_char * pkt_data;
-  uint8_t  pkt_count = 0;
-
-  //  loggers::get_instance().log(">>> pcap_offline_layer::run");
-
-  memset(&lh, 0, sizeof(lh));
-
-  _running = TRUE;
-
-  int                    delay = 1000;
-  params::const_iterator it;
-  it = _params.find(std::string("delay"));
-  if (it != _params.cend()) {
-    delay = std::stoi(it->second);
-  }
-
-  // wait a bit before sending first packet
-  std::this_thread::sleep_for(std::chrono::milliseconds(delay));
-
-  while (_running) { // Loop while _running flag is up
-    // get next frame
-    int result = pcap_next_ex(_device, (struct pcap_pkthdr **)&pkt_header, &pkt_data);
-    if (result == 2) {
-      if (_loop) {
-
-      } else {
-        _running = FALSE;
-        return NULL;
-      }
-    }
-    if (_realtime) {
-      // wait for next packet timestamp
-      if (lh.ts.tv_sec | lh.ts.tv_usec) {
-        long diff = timeval_diff(pkt_header->ts, lh.ts);
-        if (diff > 0) {
-          loggers::get_instance().log("<<< pcap_offline_layer::run: Wait %d msec", diff);
-          std::this_thread::sleep_for(std::chrono::milliseconds(diff));
-          loggers::get_instance().log("<<< pcap_offline_layer::run: Wait done");
-        }
-      }
-    }
-    while (_running && !_resume.try_lock()) {
-      std::this_thread::sleep_for(std::chrono::milliseconds(100));
-    }
-    lh = *pkt_header;
-#if 0
-    {
-      char buf[128];
-      std::time_t t = pkt_header->ts.tv_sec;
-      std::tm * pt = std::localtime( &t );
-      t = std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", pt);
-      std::sprintf(buf+t, ".%06ld", pkt_header->ts.tv_usec);
-      _o_params["timestamp"] = std::string(buf);
-    }
-#else
-    _o_params["timestamp"] = std::to_string(pkt_header->ts.tv_usec);
-#endif
-    _o_data = OCTETSTRING(pkt_header->len, pkt_data);
-    write(_fd[1], &pkt_count, 1);
-    pkt_count++;
-  }
-
-  //  loggers::get_instance().log("<<< pcap_offline_layer::run");
-  return NULL;
-}
-
-void pcap_offline_layer::send_data(OCTETSTRING &data, params &params) {
-  loggers::get_instance().log("pcap_offline_layer::send_data: Offline mode, operation was skipped");
-}
-
-void pcap_offline_layer::receive_data(OCTETSTRING &data, params &params) {
-  loggers::get_instance().log(">>> pcap_offline_layer::receive_data: Received %d bytes", data.lengthof());
-  loggers::get_instance().log_to_hexa("Packet dump", data);
-
-  // Pass the packet to the upper layers
-  receive_to_all_layers(data, params);
-}
-
-void pcap_offline_layer::Handle_Fd_Event_Readable(int fd) {
-  // loggers::get_instance().log(">>> pcap_offline_layer::Handle_Fd_Event_Readable: %d", fd);
-  char  c[2];
-  float duration;
-  // Process the packet at this layer
-  loggers::get_instance().set_start_time(_time_key);
-  this->receive_data(_o_data, _o_params);
-  loggers::get_instance().set_stop_time(_time_key, duration);
-  read(_fd[0], &c, 1);
-  _resume.unlock();
-}
-
-pcap_offline_layer_factory pcap_offline_layer_factory::_f;
+#if defined(__CYGWIN__)
+#define _GNU_SOURCE
+#endif
+#include <chrono>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <Port.hh>
+
+#include "pcap_offline_layer_factory.hh"
+
+#include "loggers.hh"
+
+#include <pcap.h>
+
+#ifdef __CYGWIN__
+typedef struct {
+  bpf_int32 tv_sec;  /* seconds */
+  bpf_int32 tv_usec; /* microseconds */
+} pcap_o_timeval;
+
+typedef struct pcap_o_pkthdr {
+  pcap_o_timeval ts;     /* time stamp */
+  bpf_u_int32    caplen; /* length of portion present */
+  bpf_u_int32    len;    /* length this packet (off wire) */
+} pcap_o_pkthdr;
+#else
+typedef struct pcap_pkthdr pcap_o_pkthdr;
+typedef struct timeval     pcap_o_timeval;
+#endif
+
+pcap_offline_layer::pcap_offline_layer(const std::string& p_type, const std::string& param)
+  : layer(p_type), PORT(p_type.c_str()), _params(), _device(NULL), _running(FALSE), _time_key("pcap_offline_layer::Handle_Fd_Event_Readable") {
+  loggers::get_instance().log(">>> pcap_offline_layer::pcap_offline_layer: '%s', %s", p_type.c_str(), param.c_str());
+  params::convert(_params, param);
+
+  _o_params.insert(std::pair<std::string, std::string>(std::string("timestamp"), std::string()));
+
+  char                   error_buffer[PCAP_ERRBUF_SIZE];
+  params::const_iterator it;
+
+  it        = _params.find(std::string("realtime"));
+  _realtime = ((it != _params.end()) && !it->second.empty());
+
+  it    = _params.find(std::string("loop"));
+  _loop = ((it != _params.end()) && !it->second.empty());
+
+  it = _params.find(std::string("file"));
+  if ((it != _params.end()) && !it->second.empty()) {
+    const std::string& file = it->second;
+    _device                 = pcap_open_offline(file.c_str(), error_buffer);
+    if (_device) {
+
+      // Add user defined filter
+      it = _params.find(params::filter);
+      if ((it != _params.end()) && !it->second.empty()) {
+        const std::string& filter = it->second;
+        // Log final PCAP filter
+        loggers::get_instance().user("pcap_offline_layer::pcap_offline_layer: Filter: '%s'", filter.c_str());
+        struct bpf_program f = {0};
+        if (pcap_compile(_device, &f, filter.c_str(), 1, PCAP_NETMASK_UNKNOWN) != 0) {
+          loggers::get_instance().error("pcap_offline_layer::pcap_offline_layer: Failed to compile PCAP filter");
+        } else {
+          if (pcap_setfilter(_device, &f) != 0) {
+            loggers::get_instance().error("pcap_offline_layer::pcap_offline_layer: Failed to set PCAP filter");
+          }
+        }
+        pcap_freecode(&f);
+      }
+
+      // create pipe and run thread
+      if (pipe2(_fd, O_NONBLOCK) == -1) {
+        loggers::get_instance().error("pcap_offline_layer::pcap_offline_layer: Failed to create a pipe: '%s'", ::strerror(errno));
+      }
+      // Pass the pipe handler to the polling procedure
+      loggers::get_instance().log("pcap_offline_layer::pcap_offline_layer: Call handler with descriptor %d", _fd[0]);
+      Handler_Add_Fd_Read(_fd[0]);
+      // Create the offline reader thread
+      _thread = new std::thread(&pcap_offline_layer::run, (void *)this);
+      if (_thread == NULL) {
+        loggers::get_instance().error("pcap_offline_layer::pcap_offline_layer: Failed to start offline thread");
+      }
+      while (_running == FALSE) {
+        std::this_thread::sleep_for(std::chrono::milliseconds(500));
+      }
+      // Thread was started
+      loggers::get_instance().log("<<< pcap_offline_layer::pcap_offline_layer");
+    } else {
+      loggers::get_instance().error("pcap_offline_layer::pcap_offline_layer: Failed to create device: '%s'", error_buffer);
+    }
+  }
+} // End of ctor
+
+pcap_offline_layer::~pcap_offline_layer() {
+  loggers::get_instance().log(">>> pcap_offline_layer::~pcap_offline_layer");
+
+  if (_device != NULL) {
+    if (_thread != NULL) {
+      _running = FALSE;
+      // Wait for the working thread to terminate
+      _thread->join();
+      loggers::get_instance().log("pcap_offline_layer::~pcap_offline_layer: Thread were stops");
+      // Cleanup
+      delete _thread;
+      close(_fd[0]);
+      close(_fd[1]);
+    }
+    pcap_close(_device);
+  }
+} // End of dtor
+
+void *pcap_offline_layer::run(void *p_this) {
+  pcap_offline_layer &p = *static_cast<pcap_offline_layer *>(p_this);
+  return p.thread();
+}
+
+static long timeval_diff(const pcap_o_timeval &x, const pcap_o_timeval &y) {
+  pcap_o_timeval z = y;
+  /* Perform the carry for the later subtraction by updating y. */
+  if (x.tv_usec < y.tv_usec) {
+    int nsec = (y.tv_usec - x.tv_usec) / 1000000 + 1;
+    z.tv_usec -= 1000000 * nsec;
+    z.tv_sec += nsec;
+  }
+  if (x.tv_usec - z.tv_usec > 1000000) {
+    int nsec = (x.tv_usec - z.tv_usec) / 1000000;
+    z.tv_usec += 1000000 * nsec;
+    z.tv_sec -= nsec;
+  }
+
+  return (x.tv_sec - z.tv_sec) * 1000 + ((x.tv_usec - z.tv_usec) / 1000);
+}
+
+void *pcap_offline_layer::thread() {
+  pcap_o_pkthdr *pkt_header;
+  pcap_o_pkthdr  lh;
+  const u_char * pkt_data;
+  uint8_t  pkt_count = 0;
+
+  //  loggers::get_instance().log(">>> pcap_offline_layer::run");
+
+  memset(&lh, 0, sizeof(lh));
+
+  _running = TRUE;
+
+  int                    delay = 1000;
+  params::const_iterator it;
+  it = _params.find(std::string("delay"));
+  if (it != _params.cend()) {
+    delay = std::stoi(it->second);
+  }
+
+  // wait a bit before sending first packet
+  std::this_thread::sleep_for(std::chrono::milliseconds(delay));
+
+  while (_running) { // Loop while _running flag is up
+    // get next frame
+    int result = pcap_next_ex(_device, (struct pcap_pkthdr **)&pkt_header, &pkt_data);
+    if (result == 2) {
+      if (_loop) {
+
+      } else {
+        _running = FALSE;
+        return NULL;
+      }
+    }
+    if (_realtime) {
+      // wait for next packet timestamp
+      if (lh.ts.tv_sec | lh.ts.tv_usec) {
+        long diff = timeval_diff(pkt_header->ts, lh.ts);
+        if (diff > 0) {
+          loggers::get_instance().log("<<< pcap_offline_layer::run: Wait %d msec", diff);
+          std::this_thread::sleep_for(std::chrono::milliseconds(diff));
+          loggers::get_instance().log("<<< pcap_offline_layer::run: Wait done");
+        }
+      }
+    }
+    while (_running && !_resume.try_lock()) {
+      std::this_thread::sleep_for(std::chrono::milliseconds(100));
+    }
+    lh = *pkt_header;
+#if 0
+    {
+      char buf[128];
+      std::time_t t = pkt_header->ts.tv_sec;
+      std::tm * pt = std::localtime( &t );
+      t = std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", pt);
+      std::sprintf(buf+t, ".%06ld", pkt_header->ts.tv_usec);
+      _o_params["timestamp"] = std::string(buf);
+    }
+#else
+    _o_params["timestamp"] = std::to_string(pkt_header->ts.tv_usec);
+#endif
+    _o_data = OCTETSTRING(pkt_header->len, pkt_data);
+    write(_fd[1], &pkt_count, 1);
+    pkt_count++;
+  }
+
+  //  loggers::get_instance().log("<<< pcap_offline_layer::run");
+  return NULL;
+}
+
+void pcap_offline_layer::send_data(OCTETSTRING &data, params &params) {
+  loggers::get_instance().log("pcap_offline_layer::send_data: Offline mode, operation was skipped");
+}
+
+void pcap_offline_layer::receive_data(OCTETSTRING &data, params &params) {
+  loggers::get_instance().log(">>> pcap_offline_layer::receive_data: Received %d bytes", data.lengthof());
+
+  const uint8_t *p = static_cast<const uint8_t*>(data);
+  if ((*p == 0x00) && ((*(p + 1) == 0x00) || (*(p + 1) == 0x04))) { // Linux PCAP header to be skipped
+    // It's up to the user to know the protocol (IPv4/IPv6) and the source MAC address
+    loggers::get_instance().log_to_hexa("Skipping Linux PCAP header.", data);
+    data = OCTETSTRING(data.lengthof() - 16, 16 + static_cast<const uint8_t*>(data));
+  }
+
+  // Pass the packet to the upper layers
+  loggers::get_instance().log_to_hexa("Packet dump", data);
+  receive_to_all_layers(data, params);
+}
+
+void pcap_offline_layer::Handle_Fd_Event_Readable(int fd) {
+  // loggers::get_instance().log(">>> pcap_offline_layer::Handle_Fd_Event_Readable: %d", fd);
+  char  c[2];
+  float duration;
+  // Process the packet at this layer
+  loggers::get_instance().set_start_time(_time_key);
+  this->receive_data(_o_data, _o_params);
+  loggers::get_instance().set_stop_time(_time_key, duration);
+  read(_fd[0], &c, 1);
+  _resume.unlock();
+}
+
+pcap_offline_layer_factory pcap_offline_layer_factory::_f;
diff --git a/ccsrc/Protocols/Sctp/module.mk b/ccsrc/Protocols/Sctp/module.mk
new file mode 100644
index 0000000..a711a9f
--- /dev/null
+++ b/ccsrc/Protocols/Sctp/module.mk
@@ -0,0 +1,2 @@
+sources := sctp_layer.cc sctp_offline_layer.cc
+includes := .
diff --git a/ccsrc/Protocols/Sctp/sctp_layer.cc b/ccsrc/Protocols/Sctp/sctp_layer.cc
new file mode 100644
index 0000000..3b53bd3
--- /dev/null
+++ b/ccsrc/Protocols/Sctp/sctp_layer.cc
@@ -0,0 +1,285 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <chrono>
+
+#include "sctp_layer_factory.hh"
+
+#include "loggers.hh"
+
+sctp_layer::sctp_layer(const std::string&  p_type, const std::string&  param) : layer(p_type), SSL_Socket(), PORT(p_type.c_str()), _params(), _client_id{-1}, _time_key("sctp_layer::Handle_Fd_Event_Readable"), _reconnect_on_send{false} {
+  loggers::get_instance().log(">>> sctp_layer::sctp_layer (1): '%s', %s", to_string().c_str(), param.c_str());
+  // Setup parameters
+  params::convert(_params, param);
+  _params.log();
+
+  init();                                                                                                                                                   }
+
+sctp_layer::sctp_layer(const std::string&  p_type, const params & param) : layer(p_type), SSL_Socket(), PORT(p_type.c_str()), _params(), _client_id{-1}, _time_key("sctp_layer::Handle_Fd_Event_Readable"), _reconnect_on_send{false} {
+  loggers::get_instance().log(">>> sctp_layer::sctp_layer (2): '%s'", to_string().c_str());
+  // Setup parameters
+  _params = param;
+
+  init();
+}
+
+void sctp_layer::init() {
+  loggers::get_instance().log(">>> sctp_layer::init");
+
+  set_ip_proto(IPPROTO_SCTP);
+
+  set_socket_debugging(false);
+  params::const_iterator it = _params.find(params::debug);
+  if (it == _params.cend()) {
+    _params.insert(std::pair<std::string, std::string>(std::string("debug"), "0"));
+  } else if (it->second.compare("1") == 0) {
+    set_socket_debugging(true);
+  }
+  bool server_mode = false;
+  it = _params.find(params::server_mode);
+  if (it != _params.cend()) {
+    server_mode = (1 == std::stoi(it->second));
+  } else {
+    _params.insert(std::pair<std::string, std::string>(std::string("server_mode"), "0"));
+  }
+  it = _params.find(params::server);
+  if (it == _params.cend()) {
+    _params.insert(std::pair<std::string, std::string>(std::string("server"), "127.0.0.1")); // TODO Try using params::server instead of std::string("server")
+  }
+  if (!parameter_set(remote_address_name(), _params[params::server].c_str())) {
+    loggers::get_instance().warning("sctp_layer::set_parameter: Unprocessed parameter: 'remote_address_name()'");
+  }
+  bool ssl_mode = false;
+  it = _params.find(params::use_ssl);
+  if (it == _params.cend()) {
+    _params.insert(std::pair<std::string, std::string>(std::string("use_ssl"), "0"));
+  } else if (it->second.compare("1") == 0) {
+    _params.insert(std::pair<std::string, std::string>(std::string("use_ssl"), "1"));
+    ssl_mode = true;
+  }
+  set_ssl_use_ssl(ssl_mode);
+  it = _params.find(params::port);
+  if (it == _params.cend()) {
+    if (_params[params::use_ssl].compare("0") == 0) { // Use standard SCTP port
+      _params.insert(std::pair<std::string, std::string>(std::string("port"), "13"));
+    } else { // Use standard HTTPS port
+      _params.insert(std::pair<std::string, std::string>(std::string("port"), "22"));
+    }
+  }
+  if (!parameter_set(remote_port_name(), _params[params::port].c_str())) {
+    loggers::get_instance().warning("sctp_layer::set_parameter: Unprocessed parameter: '%s'", params::port.c_str());
+  }
+  it = _params.find(params::local_port);
+  if (it == _params.cend()) {
+    if (_params[params::use_ssl].compare("0") == 0) { // Use standard SCTP port
+      _params.insert(std::pair<std::string, std::string>(std::string("local_port"), "13"));
+    } else { // Use standard HTTPS local_port
+      _params.insert(std::pair<std::string, std::string>(std::string("local_port"), "22"));
+    }
+  }
+  if (!parameter_set(local_port_name(), _params[params::local_port].c_str())) {
+    loggers::get_instance().warning("sctp_layer::set_parameter: Unprocessed parameter: '%s'", params::local_port.c_str());
+  }
+
+  it = _params.find(params::local_server);
+  if (it == _params.cend()) {
+    _params.insert(std::pair<std::string, std::string>(std::string("local_server"), "0.0.0.0"));
+  }
+  if (!parameter_set(local_address_name(), _params[params::local_server].c_str())) {
+    loggers::get_instance().warning("sctp_layer::set_parameter: Unprocessed parameter: '%s'", params::local_server.c_str());
+  }
+
+  parameter_set(use_connection_ASPs_name(), (!server_mode) ? "yes" : "no");
+  loggers::get_instance().warning("sctp_layer::set_parameter: Limit to one simultanneous accepted connection (server_backlog == 1)");
+  parameter_set(server_backlog_name(), "1"); // Limit to one simultanneous accepted connection 
+  loggers::get_instance().log("sctp_layer::init: server_mode=%x", server_mode);
+  set_server_mode(server_mode);
+  if (server_mode) { // Apply default values or specified ones
+    parameter_set(local_port_name(), _params[params::local_port].c_str());
+    parameter_set(local_address_name(), _params[params::local_server].c_str());
+  }
+  if (ssl_mode) { // Add certificate bundle
+    // Check mutual authentication param
+    _params.insert(std::pair<std::string, std::string>(std::string("mutual_tls"), "0"));
+    parameter_set(ssl_verifycertificate_name(), "no");
+    it = _params.find(params::mutual_auth);
+    if (it != _params.cend()) {
+      if (_params[params::mutual_auth].compare("1") == 0) { // Use mutual authentication
+        parameter_set(ssl_verifycertificate_name(), "yes");
+        _params.insert(std::pair<std::string, std::string>(std::string("mutual_tls"), "1"));
+      }
+    }
+    // Set trusted CA file
+    it = _params.find(params::trusted_ca_list);
+    if (it != _params.cend()) {
+      parameter_set(ssl_trustedCAlist_file_name(), it->second.c_str());
+    } else {
+      // Use Let's Encrypt to generate your certificates
+      // https://manpages.ubuntu.com/manpages/impish/en/man1/certbot.1.html
+      loggers::get_instance().error("sctp_layer::set_parameter: Trusted CA list is required for TLS");
+    }
+    // Set additional certificates
+    it = _params.find(params::privkey);
+    if (it != _params.cend()) {
+      parameter_set(ssl_private_key_file_name(), it->second.c_str());
+    } else {
+      // Use Let's Encrypt to generate your certificates
+      // https://manpages.ubuntu.com/manpages/impish/en/man1/certbot.1.html
+      loggers::get_instance().error("sctp_layer::set_parameter: Certificate private key is required for TLS");
+    }
+    it = _params.find(params::certificate);
+    if (it != _params.cend()) {
+      parameter_set(ssl_certificate_file_name(), it->second.c_str());
+    } else {
+      // Use Let's Encrypt to generate your certificates
+      // https://manpages.ubuntu.com/manpages/impish/en/man1/certbot.1.html
+      loggers::get_instance().error("sctp_layer::set_parameter: Certificate is required for TLS");
+    }
+  }
+  set_ttcn_buffer_usercontrol(false);
+  set_handle_half_close(true);
+
+  map_user();
+
+  parameter_set(client_TCP_reconnect_name(), "yes");
+
+  if (server_mode == 0) {
+    loggers::get_instance().log("sctp_layer::init: Establish connection: '%s'/%s", _params[params::server].c_str(), _params[params::port].c_str());
+	  open_client_connection(_params[params::server].c_str(), _params[params::port].c_str(), NULL, NULL);
+  }
+}
+
+sctp_layer::~sctp_layer() {
+  loggers::get_instance().log(">>> sctp_layer::~sctp_layer: %d", _client_id);
+  if (_client_id != -1) {
+    remove_client(_client_id);
+  }
+
+  unmap_user();
+}
+
+void sctp_layer::Handle_Fd_Event(int fd, boolean is_readable, boolean is_writable, boolean is_error)
+{
+  loggers::get_instance().log(">>> sctp_layer::Handle_Fd_Event: %d - _client_id: %d", fd, _client_id);
+  Handle_Socket_Event(fd, is_readable, is_writable, is_error);
+  log_debug("<<< sctp_layer::Handle_Fd_Event");
+}
+
+void sctp_layer::Handle_Timeout(double time_since_last_call)
+{
+  loggers::get_instance().log(">>> sctp_layer::Handle_Timeout: %f", time_since_last_call);
+  Handle_Timeout_Event(time_since_last_call);
+  loggers::get_instance().log("<<< sctp_layer::Handle_Timeout");
+}
+
+void sctp_layer::send_data(OCTETSTRING& data, params& params) {
+  loggers::get_instance().log_msg(">>> sctp_layer::send_data: ", data);
+
+  loggers::get_instance().log("sctp_layer::send_data: SSL mode: %x", get_ssl_use_ssl());
+  loggers::get_instance().log("sctp_layer::send_data: server_mode: '%s'", _params[params::server_mode].c_str());
+  loggers::get_instance().log("sctp_layer::send_data: peer_list_get_nr_of_peers: %d", peer_list_get_nr_of_peers());
+
+  if ((_params[params::server_mode].compare("0") == 0) && (peer_list_get_nr_of_peers() == 0)) {
+    // Reconnect (e.g. HTTP connection lost
+    loggers::get_instance().log("sctp_layer::send_data: Re-establish connection: '%s'/%s", _params[params::server].c_str(), _params[params::port].c_str());
+	  open_client_connection(_params[params::server].c_str(), _params[params::port].c_str(), NULL, NULL);
+  }
+  send_outgoing(static_cast<const uint8_t*>(data), data.lengthof(), _client_id);
+}
+
+void sctp_layer::receive_data(OCTETSTRING& data, params& params) {
+  loggers::get_instance().log_msg(">>> sctp_layer::receive_data: ", data);
+
+  receive_to_all_layers(data, params);
+}
+
+void sctp_layer::message_incoming(const uint8_t* message_buffer, int length, int client_id) {
+  loggers::get_instance().log(">>> sctp_layer::message_incoming");
+  loggers::get_instance().log_to_hexa("sctp_layer::message_incoming: ", message_buffer, length);
+
+  float duration;
+  loggers::get_instance().set_start_time(_time_key);
+  OCTETSTRING data(length, message_buffer);
+  params params;
+  params.insert(std::pair<std::string, std::string>(
+    std::string("timestamp"),
+    std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count())));
+  this->receive_data(data, params); // TODO Check execution time for decoding operation
+  params::const_iterator it = params.find(std::string("Buffurizing"));
+  if (it != params.end()) {
+    loggers::get_instance().log("sctp_layer::message_incoming: Buffurizing requested");
+  }
+  loggers::get_instance().set_stop_time(_time_key, duration);
+}
+
+void sctp_layer::client_connection_opened(int p_client_id)
+{
+  loggers::get_instance().log(">>> sctp_layer::client_connection_opened - _client_id: %d: %d", p_client_id, _client_id);
+}
+
+bool sctp_layer::add_user_data(int p_client_id)
+{
+  loggers::get_instance().log(">>> sctp_layer::add_user_data: %d - _client_id: %d", p_client_id, _client_id);
+  _client_id = p_client_id;
+  if (_params[params::use_ssl].compare("0") == 0) {
+    loggers::get_instance().log("sctp_layer::add_user_data: Non secured mode");
+    return Abstract_Socket::add_user_data(p_client_id);
+  }
+  loggers::get_instance().log("sctp_layer::add_user_data: SSL mode");
+  return SSL_Socket::add_user_data(p_client_id);
+}
+
+int sctp_layer::send_message_on_fd(int p_client_id, const uint8_t * message_buffer, int length_of_message)
+{
+  loggers::get_instance().log(">>> sctp_layer::send_message_on_fd: %d", p_client_id);
+
+  if(get_user_data(p_client_id)) {
+    loggers::get_instance().log("sctp_layer::send_message_on_fd: SSL mode");
+    return SSL_Socket::send_message_on_fd(p_client_id, message_buffer, length_of_message);
+  }
+
+  loggers::get_instance().log("sctp_layer::send_message_on_fd: Non secured mode");
+  return Abstract_Socket::send_message_on_fd(p_client_id, message_buffer, length_of_message);
+}
+
+int sctp_layer::send_message_on_nonblocking_fd(int p_client_id, const uint8_t * message_buffer, int length_of_message)
+{
+	loggers::get_instance().log(">>> sctp_layer::send_message_on_nonblocking_fd: %d", p_client_id);
+
+  if(get_user_data(p_client_id)) {
+    loggers::get_instance().log("sctp_layer::send_message_on_nonblocking_fd: SSL mode");
+    return SSL_Socket::send_message_on_nonblocking_fd(p_client_id, message_buffer, length_of_message);
+  }
+
+  loggers::get_instance().log("sctp_layer::send_message_on_nonblocking_fd: Non secured mode");
+  return Abstract_Socket::send_message_on_nonblocking_fd(p_client_id, message_buffer, length_of_message);
+}
+
+int sctp_layer::receive_message_on_fd(int p_client_id)
+{
+	loggers::get_instance().log(">>> sctp_layer::receive_message_on_fd: %d", p_client_id);
+
+  if(get_user_data(p_client_id)) {
+    // INFO: it is assumed that only SSL_Socket assigns user data to each peer
+    loggers::get_instance().log("sctp_layer::receive_message_on_fd: SSL mode");
+    return SSL_Socket::receive_message_on_fd(p_client_id);
+  }
+
+  loggers::get_instance().log("sctp_layer::receive_message_on_fd: Non secured mode");
+  if (_params[std::string("sctp_fragmented")].compare("1") == 0) {
+    sleep(4); // FIXME When HTTP paquet is fragmented into several TCP packets, a timer is required. This is a Q&D solution
+  }
+  return Abstract_Socket::receive_message_on_fd(p_client_id);
+}
+
+void sctp_layer::peer_disconnected(int p_client_id)
+{
+	loggers::get_instance().log(">>> sctp_layer::peer_disconnected: %d", p_client_id);
+
+  Abstract_Socket::peer_disconnected(p_client_id);
+	_client_id = -1;
+}
+
+sctp_layer_factory sctp_layer_factory::_f;
+
diff --git a/ccsrc/Protocols/Sctp/sctp_layer.hh b/ccsrc/Protocols/Sctp/sctp_layer.hh
new file mode 100644
index 0000000..8b91550
--- /dev/null
+++ b/ccsrc/Protocols/Sctp/sctp_layer.hh
@@ -0,0 +1,108 @@
+/*!
+ * \file      sctp_layer.hh
+ * \brief     Header file for ITS TCP socket based protocol port layer.
+ * \author    ETSI TTF T041
+ * \copyright ETSI Copyright Notification
+ *            No part may be reproduced except as authorized by written permission.
+ *            The copyright and the foregoing restriction extend to reproduction in all media.
+ *            All rights reserved.
+ * \version   0.1
+ */
+#pragma once
+
+#include "layer.hh"
+
+using namespace std; // Required for isnan()
+#include "Abstract_Socket.hh"
+
+class PORT; //! Forward declaration of TITAN class
+
+/*!
+ * \class sctp_layer
+ * \brief  This class provides description of ITS TCP port protocol layer
+ */
+class sctp_layer : public layer, public SSL_Socket, public PORT {
+  params      _params;            //! Layer parameters
+  int         _client_id;         //! Connection identifier
+  std::string _time_key;          //! \todo
+  bool        _reconnect_on_send; //! Set to true if connection shall be done when sending data. Otherwise, connection is established by the \see constructor
+
+public: //! \publicsection
+  //sctp_layer(): PORT("TCP") {};
+  /*!
+   * \brief Specialised constructor
+   *        Create a new instance of the sctp_layer class
+   * \param[in] p_type \todo
+   * \param[in] p_param \todo
+   */
+  sctp_layer(const std::string& p_type, const std::string& p_param);
+  /*!
+   * \brief Specialised constructor
+   *        Create a new instance of the sctp_layer class
+   * \param[in] p_type \todo
+   * \param[in] p_param \todo
+   */
+  sctp_layer(const std::string& p_type, const params &p_param);
+  /*!
+   * \brief Default destructor
+   * \remark If \see _reconnect_on_send is set to false, the disconnection is done by the destructor
+   */
+  virtual ~sctp_layer();
+
+  /*!
+   * \virtual
+   * \fn void send_data(OCTETSTRING& data, params& params);
+   * \brief Send bytes formated data to the lower layers
+   * \param[in] p_data The data to be sent
+   * \param[in] p_params Some parameters to overwrite default value of the lower layers parameters
+   * \virtual
+   */
+  virtual void send_data(OCTETSTRING &data, params &params);
+  /*!
+   * \virtual
+   * \fn void receive_data(OCTETSTRING& data, params& params);
+   * \brief Receive bytes formated data from the lower layers
+   * \param[in] p_data The bytes formated data received
+   * \param[in] p_params Some lower layers parameters values when data was received
+   */
+  virtual void receive_data(OCTETSTRING &data, params &info);
+
+  /*!
+   * \virtual
+   * \fn void message_incoming(const unsigned char* message_buffer, int length, int client_id = -1);
+   * \brief Receive bytes formated data from the lower layers
+   * \param[in] p_buffer The bytes formated data received
+   * \param[in] p_length The number of bytes received
+   * \param[in] p_client_id The connection identifier.Default: -1
+   */
+  virtual void message_incoming(const unsigned char *p_buffer, int p_length, int p_client_id = -1);
+
+protected: //! \protectedsection
+  void init();
+
+  void Add_Fd_Read_Handler(int fd) { Handler_Add_Fd_Read(fd); };
+  void Add_Fd_Write_Handler(int fd) { Handler_Add_Fd_Write(fd); };
+  void Remove_Fd_Read_Handler(int fd) { Handler_Remove_Fd_Read(fd); };
+  void Remove_Fd_Write_Handler(int fd) { Handler_Remove_Fd_Write(fd); };
+  void Remove_Fd_All_Handlers(int fd) { Handler_Remove_Fd(fd); };
+  void Handler_Uninstall() { Uninstall_Handler(); }
+  void Timer_Set_Handler(double call_interval, boolean is_timeout = TRUE, boolean call_anyway = TRUE, boolean is_periodic = TRUE) {
+    Handler_Set_Timer(call_interval, is_timeout, call_anyway, is_periodic);
+  };
+
+  const char *remote_address_name() { return params::server.c_str(); };
+  const char *remote_port_name() { return params::port.c_str(); };
+  const char *socket_debugging_name() { return params::debug.c_str(); };
+  const char *ssl_use_ssl_name() { return params::use_ssl.c_str(); };
+
+  void client_connection_opened(int p_client_id);
+  bool add_user_data(int p_client_id);
+  int  send_message_on_fd(int p_client_id, const unsigned char *message_buffer, int length_of_message);
+  int  send_message_on_nonblocking_fd(int client_id, const unsigned char *message_buffer, int length_of_message);
+  int  receive_message_on_fd(int p_client_id);
+  void peer_disconnected(int p_client_id);
+
+private: //! \privatesection
+  void Handle_Fd_Event(int fd, boolean is_readable, boolean is_writable, boolean is_error);
+  void Handle_Timeout(double time_since_last_call);
+}; // End of class sctp_layer
diff --git a/ccsrc/Protocols/Sctp/sctp_layer_factory.hh b/ccsrc/Protocols/Sctp/sctp_layer_factory.hh
new file mode 100644
index 0000000..59fa0f6
--- /dev/null
+++ b/ccsrc/Protocols/Sctp/sctp_layer_factory.hh
@@ -0,0 +1,41 @@
+/*!
+ * \file      sctp_layer_factory.hh
+ * \brief     Header file for ITS TCP socket based protocol layer factory.
+ * \author    ETSI TTF T010
+ * \copyright ETSI Copyright Notification
+ *            No part may be reproduced except as authorized by written permission.
+ *            The copyright and the foregoing restriction extend to reproduction in all media.
+ *            All rights reserved.
+ * \version   0.1
+ */
+#pragma once
+
+#include "layer_stack_builder.hh"
+
+#include "sctp_layer.hh"
+
+/*!
+ * \class sctp_layer_factory
+ * \brief  This class provides a factory class to create an sctp_layer class instance
+ */
+class sctp_layer_factory : public layer_factory {
+  static sctp_layer_factory _f; //! Reference to the unique instance of this class
+public:                        //! \publicsection
+  /*!
+   * \brief Default constructor
+   *        Create a new instance of the sctp_layer class
+   * \remark The TCP socket based layer identifier is TCP
+   */
+  sctp_layer_factory() {
+    // Register factory
+    layer_stack_builder::register_layer_factory("SCTP", this);
+  };
+  /*!
+   * \fn layer* create_layer(const std::string&  type, const std::string&  param);
+   * \brief  Create the layers stack based on the provided layers stack description
+   * \param[in] p_type The provided layers stack description
+   * \param[in] p_params Optional parameters
+   * \return 0 on success, -1 otherwise
+   */
+  inline virtual layer *create_layer(const std::string& p_type, const std::string& p_param) { return new sctp_layer(p_type, p_param); };
+}; // End of class sctp_layer_factory
diff --git a/ccsrc/Protocols/Sctp/sctp_offline_layer.cc b/ccsrc/Protocols/Sctp/sctp_offline_layer.cc
new file mode 100644
index 0000000..c6dbccb
--- /dev/null
+++ b/ccsrc/Protocols/Sctp/sctp_offline_layer.cc
@@ -0,0 +1,118 @@
+#include <chrono>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "sctp_offline_layer_factory.hh"
+
+#include "loggers.hh"
+
+sctp_offline_layer::sctp_offline_layer(const std::string& p_type, const std::string& param)
+  : layer(p_type), _params(), _time_key("sctp_offline_layer::Handle_Fd_Event_Readable") {
+  loggers::get_instance().log(">>> sctp_offline_layer::sctp_offline_layer: '%s', %s", p_type.c_str(), param.c_str());
+  params::convert(_params, param);
+
+  _o_params.insert(std::pair<std::string, std::string>(std::string("timestamp"), std::string()));
+
+} // End of ctor
+
+sctp_offline_layer::~sctp_offline_layer() {
+  loggers::get_instance().log(">>> sctp_offline_layer::~sctp_offline_layer");
+} // End of dtor
+
+
+void sctp_offline_layer::send_data(OCTETSTRING &p_data, params &p_params) {
+  loggers::get_instance().log_msg(">>> sctp_offline_layer::send_data: ", p_data);
+
+  //OCTETSTRING sctp;
+  // FIXME FSCOM: To be done
+  loggers::get_instance().error("sctp_offline_layer::send_data: Not implemented. On;ly for offline mode");
+  //sctp    += p_data;
+  //send_to_all_layers(sctp, static_cast<params&>(p_params));
+}
+
+void sctp_offline_layer::receive_data(OCTETSTRING &p_data, params &p_params) {
+  loggers::get_instance().log(">>> sctp_offline_layer::receive_data: Received %d bytes", p_data.lengthof());
+  loggers::get_instance().log_to_hexa("Packet dump", p_data);
+
+  // Source port
+  OCTETSTRING src_port = OCTETSTRING(2, static_cast<const uint8_t *>(p_data));
+  loggers::get_instance().log_msg("sctp_offline_layer::receive_data: src_port: ", src_port);
+  // Dst port
+  OCTETSTRING dst_port = OCTETSTRING(2, 2 + static_cast<const uint8_t *>(p_data));
+  loggers::get_instance().log_msg("sctp_offline_layer::receive_data: dst_port: ", dst_port);
+  // Verification tag
+  OCTETSTRING vtag = OCTETSTRING(4, 4 + static_cast<const uint8_t *>(p_data));
+  loggers::get_instance().log_msg("sctp_offline_layer::receive_data: vtag: ", vtag);
+  // checksum 
+  OCTETSTRING checksum = OCTETSTRING(4, 8 + static_cast<const uint8_t*>(p_data));
+  loggers::get_instance().log_msg("sctp_offline_layer::receive_data: checksum: ", checksum);
+  // Chunk
+  OCTETSTRING chunk = OCTETSTRING(p_data.lengthof() - 12, 12 + static_cast<const uint8_t*>(p_data));
+  loggers::get_instance().log_msg("sctp_offline_layer::receive_data: chunk: ", chunk);
+  
+  OCTETSTRING payload(0, nullptr);
+  process_chunk(chunk, p_params, payload);
+  if (payload.lengthof() == 0) {
+    loggers::get_instance().log("sctp_offline_layer::receive_data: SCTP chunk, skip it");
+    return;
+  }
+
+  // Update params
+  // CHARSTRING s = oct2str(dst);
+  // p_params.insert(std::pair<std::string, std::string>(params::ip_dst, std::string(static_cast<const char *>(s))));
+  // s = oct2str(src);
+  // p_params.insert(std::pair<std::string, std::string>(params::ip_src, std::string(static_cast<const char *>(s))));
+  // s = oct2str(protocol);
+  // p_params.insert(std::pair<std::string, std::string>(params::ip_proto, std::string(static_cast<const char *>(s))));
+  // Pass the packet to the upper layers
+  receive_to_all_layers(payload, p_params);
+}
+
+void sctp_offline_layer::process_chunk(const OCTETSTRING& p_chunk, params &p_params, OCTETSTRING& p_payload) {
+  loggers::get_instance().log_msg(">>> sctp_offline_layer::process_chunk: ", p_chunk);
+
+  const uint8_t* p = static_cast<const uint8_t*>(p_chunk); // Chunk type
+  switch (static_cast<const uint8_t>(*p)) {
+    case 0x00: { // DATA chunk
+        p += 2; // Skip Chunk type + Chunk flag
+        // Chunk length
+        uint16_t l = (*p << 8 | *(p + 1)) & 0xffff;
+        loggers::get_instance().log("sctp_offline_layer::receive_data: l: %d", l);
+        p += 2;
+
+        p += 4; // Skip Transmission sequence number
+
+        // Stream identifier
+        OCTETSTRING stream_id = OCTETSTRING(2, p);
+        loggers::get_instance().log_msg("sctp_offline_layer::receive_data: stream_id: ", stream_id);
+        p += 2;
+        // Stream sequence number
+        OCTETSTRING stream_num = OCTETSTRING(2, p);
+        loggers::get_instance().log_msg("sctp_offline_layer::receive_data: stream_num: ", stream_num);
+        p += 2;
+        // Protocol
+        uint32_t protocol = (*p << 24 | *(p + 1) << 16 | *(p + 2) << 8 | *(p + 3)) & 0xffffffff;
+        loggers::get_instance().log("sctp_offline_layer::receive_data: protocol: %d", protocol);
+        p += 4;
+        loggers::get_instance().log("sctp_offline_layer::receive_data: pointer offset: %d", (uint32_t)(p - static_cast<const uint8_t*>(p_chunk)));
+        p_payload = OCTETSTRING(p_chunk.lengthof() - (uint32_t)(p - static_cast<const uint8_t*>(p_chunk)), p);
+      }
+      break;
+    case 0x01: // INIT chunk
+      break;
+    case 0x02: // INIT_ACK chunk
+      break;
+    case 0x0a: // COOKIE_ECHO chunk
+      break;
+    case 0x0b: // COOKIE_ACK chunk
+      break;
+    default: //
+      loggers::get_instance().warning("sctp_offline_layer::send_data: Unprocessed chunk: 0x%02x", *p);
+  }
+
+  loggers::get_instance().log_msg("<<< sctp_offline_layer::process_chunk: p_payload: ", p_payload);
+}
+
+sctp_offline_layer_factory sctp_offline_layer_factory::_f;
diff --git a/ccsrc/Protocols/Sctp/sctp_offline_layer.hh b/ccsrc/Protocols/Sctp/sctp_offline_layer.hh
new file mode 100644
index 0000000..c78204b
--- /dev/null
+++ b/ccsrc/Protocols/Sctp/sctp_offline_layer.hh
@@ -0,0 +1,76 @@
+/*!
+ * \file      sctp_offline_layer.hh
+ * \brief     Header file for offline Sctp layer.
+ * \author    ETSI TTF T041
+ * \copyright ETSI Copyright Notification
+ *            No part may be reproduced except as authorized by written permission.
+ *            The copyright and the foregoing restriction extend to reproduction in all media.
+ *            All rights reserved.
+ * \version   0.1
+ */
+#pragma once
+
+#include "params.hh"
+#include "t_layer.hh"
+
+#include <Octetstring.hh>
+
+/*!
+ * \class sctp_offline_layer
+ * \brief  This class provides description of offline Sctp layer protocol layer
+ */
+class sctp_offline_layer : public layer {
+  params       _params; //! Layer parameters
+
+  params      _o_params;
+  OCTETSTRING _o_data;
+
+  std::string _time_key;
+
+  static void *run(void *p_this);
+
+public:
+  void *thread(void);
+
+public: //! \publicsection
+  /*!
+   * \brief Specialised constructor
+   *        Create a new instance of the sctp_offline_layer class
+   * \param[in] p_type \todo
+   * \param[in] p_param \todo
+   */
+  sctp_offline_layer(const std::string& p_type, const std::string& param);
+  /*!
+   * \brief Default destructor
+   */
+  virtual ~sctp_offline_layer();
+
+  /*!
+   * \virtual
+   * \fn void send_data(OCTETSTRING& data, params& params);
+   * \brief Send bytes formated data to the lower layers
+   * \param[in] p_data The data to be sent
+   * \param[in] p_params Some parameters to overwrite default value of the lower layers parameters
+   */
+  virtual void send_data(OCTETSTRING &p_data, params &p_params);
+  /*!
+   * \virtual
+   * \fn void receive_data(OCTETSTRING& data, params& params);
+   * \brief Receive bytes formated data from the lower layers
+   * \param[in] p_data The bytes formated data received
+   * \param[in] p_params Some lower layers parameters values when data was received
+   */
+  virtual void receive_data(OCTETSTRING &p_data, params &p_params);
+
+private: //! \privatesection
+  /*!
+   * \fn void process_chunk(const OCTETSTRING& p_chunk, OCTETSTRING& p_payload);
+   * \brief Process SCTP chunk and extrat the payload if the chunck is of DATA type
+   * \param[in] p_data The bytes formated data received
+   * \param[inout] p_params Some lower layers parameters values when data was received
+   * \param[out] p_payload The payload if the chunck is of DATA type, unchanged otherwise
+   */
+  void process_chunk(const OCTETSTRING& p_chunk, params &p_params, OCTETSTRING& p_payload);
+
+  void Handle_Fd_Event_Readable(int fd);
+};
diff --git a/ccsrc/Protocols/Sctp/sctp_offline_layer_factory.hh b/ccsrc/Protocols/Sctp/sctp_offline_layer_factory.hh
new file mode 100644
index 0000000..dda7674
--- /dev/null
+++ b/ccsrc/Protocols/Sctp/sctp_offline_layer_factory.hh
@@ -0,0 +1,42 @@
+/*!
+ * \file      sctp_offline_layer_factory.hh
+ * \brief     Header file for Sctp layer factory.
+ * \author    ETSI TTF T041
+ * \copyright ETSI Copyright Notification
+ *            No part may be reproduced except as authorized by written permission.
+ *            The copyright and the foregoing restriction extend to reproduction in all media.
+ *            All rights reserved.
+ * \version   0.1
+ */
+#pragma once
+
+#include "layer_stack_builder.hh"
+
+#include "sctp_offline_layer.hh"
+
+/*!
+ * \class sctp_offline_layer_factory
+ * \brief  This class provides a factory class to create an sctp_offline_layer class instance
+ */
+class sctp_offline_layer_factory : public layer_factory {
+  static sctp_offline_layer_factory _f; //! Reference to the unique instance of this class
+public:                                 //! \publicsection
+  /*!
+   * \brief Default constructor
+   *        Create a new instance of the udp_layer_factory class
+   * \remark The SCTP layer identifier is SCTP
+   */
+  sctp_offline_layer_factory() {
+    // register factory
+    layer_stack_builder::register_layer_factory("SCTP_FILE", this);
+  };
+  /*!
+   * \fn layer* create_layer(const std::string&  type, const std::string&  param);
+   * \brief  Create the layers stack based on the provided layers stack description
+   * \param[in] p_type The provided layers stack description
+   * \param[in] p_params Optional parameters
+   * \return 0 on success, -1 otherwise
+   * \inline
+   */
+  inline virtual layer *create_layer(const std::string& p_type, const std::string& p_param) { return new sctp_offline_layer(p_type, p_param); };
+}; // End of class sctp_offline_layer_factory
-- 
GitLab