From a269b4ee220ea95c4aff910eecd1edaf7e7aca1b Mon Sep 17 00:00:00 2001 From: Peter Newman Date: Tue, 25 Aug 2026 00:42:40 +0100 Subject: [PATCH 01/12] Initial Sig-Net receive code N.B. This doesn't (yet) check any of the following security features: Session ID Sequence Number HMAC etc... Use only for basic, no security testing of data! --- common/protocol/Ola.proto | 3 + configure.ac | 14 + olad/DynamicPluginLoader.cpp | 9 + plugins/Makefile.mk | 1 + plugins/signet/Makefile.mk | 37 ++ plugins/signet/README.md | 17 + plugins/signet/SigNetDevice.cpp | 100 +++++ plugins/signet/SigNetDevice.h | 78 ++++ plugins/signet/SigNetEnums.h | 60 +++ plugins/signet/SigNetNode.cpp | 629 ++++++++++++++++++++++++++++++++ plugins/signet/SigNetNode.h | 179 +++++++++ plugins/signet/SigNetPlugin.cpp | 126 +++++++ plugins/signet/SigNetPlugin.h | 62 ++++ plugins/signet/SigNetPort.cpp | 121 ++++++ plugins/signet/SigNetPort.h | 138 +++++++ plugins/signet/SigNetTIDs.h | 57 +++ 16 files changed, 1631 insertions(+) create mode 100644 plugins/signet/Makefile.mk create mode 100644 plugins/signet/README.md create mode 100644 plugins/signet/SigNetDevice.cpp create mode 100644 plugins/signet/SigNetDevice.h create mode 100644 plugins/signet/SigNetEnums.h create mode 100644 plugins/signet/SigNetNode.cpp create mode 100644 plugins/signet/SigNetNode.h create mode 100644 plugins/signet/SigNetPlugin.cpp create mode 100644 plugins/signet/SigNetPlugin.h create mode 100644 plugins/signet/SigNetPort.cpp create mode 100644 plugins/signet/SigNetPort.h create mode 100644 plugins/signet/SigNetTIDs.h diff --git a/common/protocol/Ola.proto b/common/protocol/Ola.proto index 0e0bb1459e..fa3413735d 100644 --- a/common/protocol/Ola.proto +++ b/common/protocol/Ola.proto @@ -68,6 +68,9 @@ enum PluginIds { OLA_PLUGIN_GPIO = 22; OLA_PLUGIN_SPIDMX = 23; OLA_PLUGIN_NANOLEAF = 24; + OLA_PLUGIN_E133 = 25; + OLA_PLUGIN_LLRP = 26; + OLA_PLUGIN_SIGNET = 27; /* * To obtain a new plugin ID, open a ticket at diff --git a/configure.ac b/configure.ac index 446fd9a988..d55bc47229 100644 --- a/configure.ac +++ b/configure.ac @@ -760,6 +760,19 @@ AS_IF([test "x$enable_osc" != xno], AS_IF([test "x$have_liblo" = xyes], [AC_DEFINE([HAVE_LIBLO], [1], [define if liblo is installed])]) +# COAP Support. +# look for libcoap if the user asked us to +AC_ARG_ENABLE( + [signet], + [AS_HELP_STRING([--disable-signet], + [Disable the Sig-Net plugin even if libcoap exists])]) + +have_libcoap="no" +AS_IF([test "x$enable_signet" != xno], + [PKG_CHECK_MODULES(libcoap, [libcoap-1 >= 4.1.2], [have_libcoap="yes"], [true])]) +AS_IF([test "x$have_libcoap" = xyes], + [AC_DEFINE([HAVE_LIBCOAP], [1], [define if libcoap is installed])]) + # Python API AC_ARG_ENABLE( [python-libs], @@ -920,6 +933,7 @@ PLUGIN_SUPPORT(pathport, USE_PATHPORT) PLUGIN_SUPPORT(renard, USE_RENARD) PLUGIN_SUPPORT(sandnet, USE_SANDNET) PLUGIN_SUPPORT(shownet, USE_SHOWNET) +PLUGIN_SUPPORT(signet, USE_SIGNET, [$have_libcoap]) PLUGIN_SUPPORT(spi, USE_SPI, [$have_spi]) PLUGIN_SUPPORT(spidmx, USE_SPIDMX, [$have_spi]) PLUGIN_SUPPORT(stageprofi, USE_STAGEPROFI) diff --git a/olad/DynamicPluginLoader.cpp b/olad/DynamicPluginLoader.cpp index d5b11e60ec..f2997ebacc 100644 --- a/olad/DynamicPluginLoader.cpp +++ b/olad/DynamicPluginLoader.cpp @@ -91,6 +91,10 @@ #include "plugins/shownet/ShowNetPlugin.h" #endif // USE_SHOWNET +#ifdef USE_SIGNET +#include "plugins/signet/SigNetPlugin.h" +#endif // USE_SIGNET + #ifdef USE_SPI #include "plugins/spi/SPIPlugin.h" #endif // USE_SPI @@ -216,6 +220,11 @@ void DynamicPluginLoader::PopulatePlugins() { new ola::plugin::shownet::ShowNetPlugin(m_plugin_adaptor)); #endif // USE_SHOWNET +#ifdef USE_SIGNET + m_plugins.push_back( + new ola::plugin::signet::SigNetPlugin(m_plugin_adaptor)); +#endif // USE_SIGNET + #ifdef USE_SPI m_plugins.push_back( new ola::plugin::spi::SPIPlugin(m_plugin_adaptor)); diff --git a/plugins/Makefile.mk b/plugins/Makefile.mk index b298128a1e..b9ed1ee4f7 100644 --- a/plugins/Makefile.mk +++ b/plugins/Makefile.mk @@ -14,6 +14,7 @@ include plugins/pathport/Makefile.mk include plugins/renard/Makefile.mk include plugins/sandnet/Makefile.mk include plugins/shownet/Makefile.mk +include plugins/signet/Makefile.mk include plugins/spi/Makefile.mk include plugins/spidmx/Makefile.mk include plugins/stageprofi/Makefile.mk diff --git a/plugins/signet/Makefile.mk b/plugins/signet/Makefile.mk new file mode 100644 index 0000000000..f7a34d5e42 --- /dev/null +++ b/plugins/signet/Makefile.mk @@ -0,0 +1,37 @@ +# LIBRARIES +################################################## +if USE_SIGNET +# This is a library which isn't coupled to olad +noinst_LTLIBRARIES += plugins/signet/libolasignetnode.la +plugins_signet_libolasignetnode_la_SOURCES = \ + plugins/signet/SigNetEnums.h \ + plugins/signet/SigNetNode.cpp \ + plugins/signet/SigNetNode.h \ + plugins/signet/SigNetTIDs.h +plugins_signet_libolasignetnode_la_CXXFLAGS = $(COMMON_CXXFLAGS) $(libcoap_CFLAGS) +plugins_signet_libolasignetnode_la_LIBADD = $(libcoap_LIBS) + +lib_LTLIBRARIES += plugins/signet/libolasignet.la + +# Plugin description is generated from README.md +built_sources += plugins/signet/SigNetPluginDescription.h +nodist_plugins_signet_libolasignet_la_SOURCES = \ + plugins/signet/SigNetPluginDescription.h +plugins/signet/SigNetPluginDescription.h: plugins/signet/README.md plugins/signet/Makefile.mk plugins/convert_README_to_header.sh + sh $(top_srcdir)/plugins/convert_README_to_header.sh $(top_srcdir)/plugins/signet $(top_builddir)/plugins/signet/SigNetPluginDescription.h + +plugins_signet_libolasignet_la_SOURCES = \ + plugins/signet/SigNetDevice.cpp \ + plugins/signet/SigNetDevice.h \ + plugins/signet/SigNetPlugin.cpp \ + plugins/signet/SigNetPlugin.h \ + plugins/signet/SigNetPort.cpp \ + plugins/signet/SigNetPort.h +plugins_signet_libolasignet_la_CXXFLAGS = $(COMMON_CXXFLAGS) $(libcoap_CFLAGS) +plugins_signet_libolasignet_la_LIBADD = \ + olad/plugin_api/libolaserverplugininterface.la \ + plugins/signet/libolasignetnode.la +endif + +EXTRA_DIST += \ + plugins/signet/README.md diff --git a/plugins/signet/README.md b/plugins/signet/README.md new file mode 100644 index 0000000000..1aa7112eea --- /dev/null +++ b/plugins/signet/README.md @@ -0,0 +1,17 @@ +Sig-Net Plugin +=============================== + +This plugin allows OLA to send and receive +[Sig-Net](https://sig-net.net/) messages. + + +## Config file: `ola-signet.conf` + +`input_ports = ` +The number of input ports to create. + +`output_ports = ` +The number of output ports to create. + +`udp_listen_port = ` +The UDP Port to listen on for Sig-Net messages. diff --git a/plugins/signet/SigNetDevice.cpp b/plugins/signet/SigNetDevice.cpp new file mode 100644 index 0000000000..814dc60a1d --- /dev/null +++ b/plugins/signet/SigNetDevice.cpp @@ -0,0 +1,100 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * SigNetDevice.cpp + * The SigNet Device. + * Copyright (C) 2026 Peter Newman + */ + +#include +#include + +#include "ola/Logging.h" +#include "ola/io/SelectServerInterface.h" +#include "plugins/signet/SigNetDevice.h" +#include "plugins/signet/SigNetPort.h" + +namespace ola { +namespace plugin { +namespace signet { + +using std::string; +using std::vector; + +const char SigNetDevice::DEVICE_NAME[] = "SigNet Device"; + +/** + * Constructor for the SigNetDevice + * @param owner the plugin which created this device + * @param plugin_adaptor a pointer to a PluginAdaptor object + * @param options the options to use for the device + */ +SigNetDevice::SigNetDevice(AbstractPlugin *owner, + PluginAdaptor *plugin_adaptor, + const SigNetDeviceOptions &options) + : Device(owner, DEVICE_NAME), + m_plugin_adaptor(plugin_adaptor), + m_options(options) { + SigNetNode::SigNetNodeOptions node_options; + // allocate a new SigNetNode but delay the call to Init() until later + m_signet_node.reset(new SigNetNode(plugin_adaptor, + plugin_adaptor->GetExportMap(), + node_options)); +} + +/* + * Start this device. + * @returns true if the device started successfully, false otherwise. + */ +bool SigNetDevice::StartHook() { + bool ok = true; + if (!m_signet_node->Init()) { + OLA_FATAL << "SigNet Node didn't init!"; + return false; + } + + for (unsigned int i = 0; i < m_options.input_ports; i++) { + SigNetInputPort *input_port = new SigNetInputPort( + this, i, m_plugin_adaptor, m_signet_node.get()); + if (!AddPort(input_port)) { + delete input_port; + ok = false; + } + m_input_ports.push_back(input_port); + } + + for (unsigned int i = 0; i < m_options.output_ports; i++) { + SigNetOutputPort *output_port = new SigNetOutputPort( + this, i, m_signet_node.get()); + if (!AddPort(output_port)) { + delete output_port; + ok = false; + } + m_output_ports.push_back(output_port); + } + + return ok; +} + +SigNetInputPort *SigNetDevice::GetSigNetInputPort(unsigned int port_id) { + return (port_id < m_input_ports.size()) ? m_input_ports[port_id] : NULL; +} + +SigNetOutputPort *SigNetDevice::GetSigNetOutputPort(unsigned int port_id) { + return (port_id < m_output_ports.size()) ? m_output_ports[port_id] : NULL; +} +} // namespace signet +} // namespace plugin +} // namespace ola diff --git a/plugins/signet/SigNetDevice.h b/plugins/signet/SigNetDevice.h new file mode 100644 index 0000000000..03f4ffc221 --- /dev/null +++ b/plugins/signet/SigNetDevice.h @@ -0,0 +1,78 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * SigNetDevice.h + * The SigNet Device. + * Copyright (C) 2026 Peter Newman + */ + +#ifndef PLUGINS_SIGNET_SIGNETDEVICE_H_ +#define PLUGINS_SIGNET_SIGNETDEVICE_H_ + +#include +#include +#include +#include "ola/io/SelectServerInterface.h" +#include "olad/Device.h" +#include "plugins/signet/SigNetNode.h" + +namespace ola { + +class AbstractPlugin; + +namespace plugin { +namespace signet { + +class SigNetInputPort; +class SigNetOutputPort; + +class SigNetDevice: public Device { + public: + struct SigNetDeviceOptions { + public: + SigNetDeviceOptions() + : input_ports(0), + output_ports(0) { + } + unsigned int input_ports; + unsigned int output_ports; + }; + + SigNetDevice(AbstractPlugin *owner, + PluginAdaptor *plugin_adaptor, + const SigNetDeviceOptions &options); + + std::string DeviceId() const { return "1"; } + + protected: + PluginAdaptor *m_plugin_adaptor; + std::auto_ptr m_signet_node; + + bool StartHook(); + + static const char DEVICE_NAME[]; + + private: + const SigNetDeviceOptions m_options; + std::vector m_input_ports; + std::vector m_output_ports; + + SigNetInputPort *GetSigNetInputPort(unsigned int port_id); + SigNetOutputPort *GetSigNetOutputPort(unsigned int port_id); +}; +} // namespace signet +} // namespace plugin +} // namespace ola +#endif // PLUGINS_SIGNET_SIGNETDEVICE_H_ diff --git a/plugins/signet/SigNetEnums.h b/plugins/signet/SigNetEnums.h new file mode 100644 index 0000000000..b408a052e9 --- /dev/null +++ b/plugins/signet/SigNetEnums.h @@ -0,0 +1,60 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * SigNetEnums.h + * General enums used in Sig-Net packets + * Copyright (C) 2026 Peter Newman + */ + +#ifndef PLUGINS_SIGNET_SIGNETENUMS_H_ +#define PLUGINS_SIGNET_SIGNETENUMS_H_ +/** + * @addtogroup signet + * @{ + * @file SigNetTIDs.h + * @brief Sig-Net TID values. + * @} + */ + +#include + +namespace ola { +namespace plugin { +namespace signet { + +/** + * @addtogroup signet + * @{ + */ + +/** + * @brief A set of values representing Sig-Net COAP Options. + * @note See section 8.3 of Sig-Net for more information. + */ +typedef enum { + SIGNET_SECURITY_MODE = 2076, /**< Sig-Net-Security-Mode */ + SIGNET_SENDER_ID = 2108, /**< Sig-Net-Sender-ID */ + SIGNET_MFG_CODE = 2140, /**< Sig-Net-Mfg-Code */ + SIGNET_SESSION_ID = 2172, /**< Sig-Net-Session-ID */ + SIGNET_SEQ_NUM = 2204, /**< Sig-Net-Seq-Num */ + SIGNET_AUTH = 2236, /**< */ +} signet_coap_options; +/** + * @} + */ +} // namespace signet +} // namespace plugin +} // namespace ola +#endif // PLUGINS_SIGNET_SIGNETENUMS_H_ diff --git a/plugins/signet/SigNetNode.cpp b/plugins/signet/SigNetNode.cpp new file mode 100644 index 0000000000..280c79f892 --- /dev/null +++ b/plugins/signet/SigNetNode.cpp @@ -0,0 +1,629 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * SigNetNode.cpp + * A self contained object for sending and receiving SigNet messages. + * Copyright (C) 2026 Peter Newman + */ + +#ifdef _WIN32 +#include +#endif // _WIN32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "plugins/signet/SigNetEnums.h" +#include "plugins/signet/SigNetNode.h" +#include "plugins/signet/SigNetTIDs.h" + +namespace ola { +namespace plugin { +namespace signet { + +#ifdef _WIN32 +class UnmanagedSocketDescriptor : public ola::io::UnmanagedFileDescriptor { + public: + explicit UnmanagedSocketDescriptor(int fd) : + ola::io::UnmanagedFileDescriptor(fd) { + m_handle.m_type = ola::io::SOCKET_DESCRIPTOR; + // Set socket to nonblocking to enable WSAEventSelect + u_long mode = 1; + ioctlsocket(fd, FIONBIO, &mode); + } + private: + DISALLOW_COPY_AND_ASSIGN(UnmanagedSocketDescriptor); +}; +#endif // _WIN32 + +using ola::io::SelectServerInterface; +using ola::network::HostToNetwork; +using ola::network::IPV4Address; +using ola::network::IPV4SocketAddress; +using ola::network::NetworkToHost; +using ola::rdm::UID; +using ola::strings::IntToString; +using ola::utils::JoinUInt8; +using std::auto_ptr; +using std::make_pair; +using std::max; +using std::min; +using std::string; +using std::vector; + +const char SigNetNode::SIGNET_PORT_VARIABLE[] = "signet-listen-port"; +const vector SigNetNode::SIGNET_LEVEL_URI({ + "sig-net", "v1", "local", "level"}); + +// TODO(Peter): Convert this into a SocketAddress? +coap_context_t* SigNetNode::GetCoapContext(const IPV4Address ip, + const uint16_t port) { + coap_context_t *ctx = NULL; + struct addrinfo hints; + struct addrinfo *result, *rp; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; // Allow IPv4 + hints.ai_socktype = SOCK_DGRAM; // Coap is UDP based + hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; + + int status = getaddrinfo(ip.ToString().c_str(), IntToString(port).c_str(), + &hints, &result); + if ( status != 0 ) { + OLA_WARN << "Failed to getaddrinfo: " << gai_strerror(status); + return NULL; + } + + // Loop through the results until we've found a suitable address + for (rp = result; rp != NULL; rp = rp->ai_next) { + coap_address_t addr; + + if (rp->ai_addrlen <= sizeof(addr.addr)) { + coap_address_init(&addr); + addr.size = rp->ai_addrlen; + memcpy(&addr.addr, rp->ai_addr, rp->ai_addrlen); + + ctx = coap_new_context(&addr); + if (ctx) { + break; + } + } + } + + if (!ctx) { + OLA_FATAL << "Failed to find a context for interface " << ip.ToString(); + } + + freeaddrinfo(result); + return ctx; +} + +void SigNetUniverseHandler(OLA_UNUSED coap_context_t *ctx, + struct coap_resource_t *resource, + OLA_UNUSED const coap_endpoint_t *local_interface, + OLA_UNUSED coap_address_t *peer, + coap_pdu_t *request, + OLA_UNUSED str *token, + OLA_UNUSED coap_pdu_t *response) { + string uri = string(reinterpret_cast(resource->uri.s), + resource->uri.length); + OLA_INFO << "POST Called! " << uri; + + SigNetNode *node = NULL; + + string *rname = new string("ola-sig-net-node-ptr"); + + coap_attr_t *attr; + attr = coap_find_attr(resource, + (unsigned char *)rname->c_str(), + rname->length()); + + if (attr) { + OLA_DEBUG << "Found our attr"; + + string val = string(reinterpret_cast(attr->value.s), + attr->value.length); + OLA_INFO << "Attr val: " << val; + + node = reinterpret_cast(attr->value.s); + if (node) { + OLA_DEBUG << "Still listening on " << node->ListeningPort(); + } + } else { + OLA_DEBUG << "Didn't find our attr"; + } + + coap_opt_iterator_t opt_iter; + coap_opt_t *option; + + string uri_part; + unsigned int uri_part_i = 0; + uint16_t universe = 0; + int signet_security_mode = -1; + int signet_mfg_code = -1; + int signet_session_id = -1; + int signet_seq_num = -1; + UID signet_sender_id_tuid(0, 0); + uint16_t signet_sender_id_endpoint = 0; + std::ostringstream format_data_str; + + coap_option_iterator_init((coap_pdu_t *)request, &opt_iter, COAP_OPT_ALL); + + while ((option = coap_option_next(&opt_iter))) { + switch (opt_iter.type) { + case COAP_OPTION_URI_PATH: + uri_part = string(reinterpret_cast(coap_opt_value(option)), + coap_opt_length(option)); + if (uri_part_i < SigNetNode::SIGNET_LEVEL_URI.size()) { + OLA_INFO << "Got COAP URI part " << uri_part << " expected " + << SigNetNode::SIGNET_LEVEL_URI[uri_part_i]; + } else { + if (uri_part_i == SigNetNode::SIGNET_LEVEL_URI.size()) { + OLA_INFO << "Got COAP URI part " << uri_part; + if (ola::StringToInt(uri_part, &universe, true)) { + OLA_DEBUG << "Got universe from COAP URI part: " << universe; + } else { + universe = 0; + OLA_WARN << "Failed to parse universe from URI part"; + } + } else { + OLA_WARN << "Got too many extra URI parts"; + } + } + + uri_part_i++; + break; + case SIGNET_SECURITY_MODE: + signet_security_mode = static_cast(coap_decode_var_bytes( + coap_opt_value(option), coap_opt_length(option))); + OLA_INFO << "Got Sig-Net-Security-Mode " << signet_security_mode; + break; + case SIGNET_SENDER_ID: + OLA_INFO << "Got Sig-Net-Sender-ID"; + signet_sender_id_tuid = UID(coap_opt_value(option)); + // TODO(Peter): Check length of UID/TUID before use! + OLA_INFO << "Got Sig-Net-Sender-ID (TUID) " + << signet_sender_id_tuid.ToString(); + signet_sender_id_endpoint = static_cast(coap_decode_var_bytes( + coap_opt_value(option) + UID::LENGTH, + sizeof(signet_sender_id_endpoint))); + OLA_INFO << "Got Sig-Net-Sender-ID (Endpoint) " + << signet_sender_id_endpoint; + break; + case SIGNET_MFG_CODE: + signet_mfg_code = static_cast(coap_decode_var_bytes( + coap_opt_value(option), coap_opt_length(option))); + OLA_INFO << "Got Sig-Net-Mfg-Code " << signet_mfg_code; + break; + case SIGNET_SESSION_ID: + signet_session_id = static_cast(coap_decode_var_bytes( + coap_opt_value(option), coap_opt_length(option))); + OLA_INFO << "Got Sig-Net-Session-ID " << signet_session_id; + break; + case SIGNET_SEQ_NUM: + signet_seq_num = static_cast(coap_decode_var_bytes( + coap_opt_value(option), coap_opt_length(option))); + OLA_INFO << "Got Sig-Net-Seq-Num " << signet_seq_num; + break; + case SIGNET_AUTH: + format_data_str.str(""); + FormatData(&format_data_str, + coap_opt_value(option), + coap_opt_length(option)); + OLA_INFO << "Got Sig-Net-Auth\n" << format_data_str.str(); + break; + default: + OLA_WARN << "Unknown COAP option " << opt_iter.type; + } + } + + size_t data_len; + unsigned char *data; + + if (coap_get_data(request, &data_len, &data)) { + OLA_DEBUG << "Raw Length: " << data_len; + if (data_len >= 4) { + uint16_t tid_data_type = JoinUInt8(data[0], data[1]); + uint16_t tid_data_len = JoinUInt8(data[2], data[3]); + data = data + 4; + data_len = data_len - 4; + OLA_DEBUG << "Got TID " << tid_data_type << " and length " + << tid_data_len; + if (tid_data_len < data_len) { + OLA_WARN << "Not enough data, " << data_len + << " remaining, but TLV claims " << tid_data_len; + } else { + if (tid_data_type == TID_LEVEL) { + OLA_INFO << "DMX Length: " << tid_data_len; + DmxBuffer buffer = DmxBuffer(data, tid_data_len); + OLA_INFO << "DMX: " << buffer.ToString(); + + if (node) { + unsigned int size = min(static_cast(DMX_UNIVERSE_SIZE), + static_cast(tid_data_len)); + node->SetUniverse(universe, static_cast(data), size); + } else { + OLA_WARN << "No node to run SetUniverse on"; + } + } else { + OLA_INFO << "Unrecognised/unhandled TID " << tid_data_type; + } + } + } + } +} + +/* + * The Error handler for the SigNet server. + */ +void SigNetErrorHandler(int error_code, const char *msg, const char *stack) { + string msg_str, stack_str; + if (msg) { + msg_str.assign(msg); + } + if (stack) { + stack_str.assign(stack); + } + OLA_WARN << "SigNet Error. Code " << error_code << ", " << msg_str << ", " + << stack_str; +} + + +/** + * Called when liblo receives data. + * @param signet_address the SigNet Address this data was sent to + * @param types the SigNet data type for the data + * @param argv the data itself + * @param argc the number of data blocks + * @param user_data a pointer to the SigNetNode object. + */ +/*int SigNetDataHandler(const char *signet_address, const char *types, lo_arg **argv, + int argc, lo_message, void *user_data) { + OLA_DEBUG << "Got SigNet message for " << signet_address << ", types are " << types; + + SigNetNode *node = reinterpret_cast(user_data); + const string type(types); + uint16_t slot; + + unsigned int size = min(static_cast(DMX_UNIVERSE_SIZE), + lo_blob_datasize(blob)); + node->SetUniverse( + signet_address, static_cast(lo_blob_dataptr(blob)), size); + return 0; + OLA_WARN << "Unknown SigNet message type " << type; + return 0; +}*/ + + +/* + * Calculate the IP that corresponds to a universe. + * @param universe the universe id + * @param addr where to store the address + * @return true if this is a valid Sig-Net universe, false otherwise + */ +bool SigNetNode::UniverseIP(uint16_t universe, IPV4Address *addr) { + uint8_t universe_high = 0; + uint8_t universe_low = 0; + universe_low = ((universe-1) % 109) + 1; + *addr = IPV4Address( + HostToNetwork(ola::utils::JoinUInt8(239, + 254, + universe_high, + universe_low))); + if (universe && (universe != 0xFFFF)) { + return true; + } + + OLA_WARN << "Universe " << universe << " isn't a valid Sig-Net universe"; + return false; +} + + +bool SigNetNode::UniverseURI(uint16_t universe, string *uri) { + uri->assign("sig-net/v1/local/level/" + ola::strings::IntToString(universe)); + + if (universe && (universe != 0xFFFF)) { + return true; + } + + OLA_WARN << "Universe " << universe << " isn't a valid Sig-Net universe"; + return false; +} + + +/** + * Create a new SigNetNode. + * @param ss the SelectServer to use + * @param export_map a pointer to an ExportMap (may be NULL) + * @param options the SigNetNodeOptions + */ +SigNetNode::SigNetNode(SelectServerInterface *ss, + ExportMap *export_map, + const SigNetNodeOptions &options) + : m_ss(ss), + m_listen_port(options.listen_port) { + if (export_map) { + // export the SigNet listening port if we have an export map + ola::IntegerVariable *signet_port_var = + export_map->GetIntegerVar(SIGNET_PORT_VARIABLE); + signet_port_var->Set(options.listen_port); + } +} + + +/** + * Cleanup + */ +SigNetNode::~SigNetNode() { + Stop(); +} + + +/* + * Setup the Node + */ +bool SigNetNode::Init() { + OLA_DEBUG << "Listening on " << ListeningPort(); + + auto_ptr picker( + ola::network::InterfacePicker::NewPicker()); + // TODO(Peter): Use our preferred interface... + if (!picker->ChooseInterface(&m_interface, "")) { + OLA_INFO << "Failed to find an interface"; + return false; + } + + if (!m_socket.Init()) { + return false; + } + + if (!m_socket.Bind(IPV4SocketAddress(IPV4Address::WildCard(), + m_listen_port))) { + return false; + } + + if (!m_socket.EnableBroadcast()) { + return false; + } + +// m_socket.SetMulticastInterface(m_interface.ip_address); + +// m_socket.SetOnData(NewCallback(&m_incoming_udp_transport, +// &IncomingUDPTransport::Receive)); + + coap_set_log_level(LOG_DEBUG); + + m_coap_context = SigNetNode::GetCoapContext(IPV4Address::WildCard(), + m_listen_port); + if (!m_coap_context) { + OLA_FATAL << "No COAP context!"; + return false; + } + + // Get the socket descriptor that libcoap is using, create a + // UnmanagedFileDescriptor, assign a callback and register with the + // SelectServer. + int fd = m_coap_context->sockfd; +#ifdef _WIN32 + m_descriptor.reset(new UnmanagedSocketDescriptor(fd)); +#else + m_descriptor.reset(new ola::io::UnmanagedFileDescriptor(fd)); +#endif // _WIN32 + m_descriptor->SetOnData(NewCallback(this, &SigNetNode::DescriptorReady)); + m_ss->AddReadDescriptor(m_descriptor.get()); + + return true; +} + + +/** + * Stop this node. This removes all registrations. + */ +void SigNetNode::Stop() { + // TODO(Peter): Clean up m_coap_context + + // Clean up the m_output_map map + OutputGroupMap::iterator group_iter = m_output_map.begin(); + for (; group_iter != m_output_map.end(); ++group_iter) { + delete group_iter->second; + } + m_output_map.clear(); + + // Delete all the RX callbacks. + STLDeleteValues(&m_input_map); + + if (m_descriptor.get()) { + // if there was an UnmanagedFileDescriptor, de-register from the + // SelectServer and delete it. + m_ss->RemoveReadDescriptor(m_descriptor.get()); + m_descriptor.reset(); + } + + // TODO(Peter): Free m_coap_context +} + + +bool SigNetNode::SetHandler(uint16_t universe, + OLA_UNUSED DmxBuffer *buffer, + OLA_UNUSED uint8_t *priority, + DMXCallback *callback) { + IPV4Address addr; + if (!UniverseIP(universe, &addr)) { + OLA_WARN << "Unable to determine multicast group for universe " + << universe; + return false; + } + + OLA_DEBUG << "Joining via handler " << addr; + if (!m_socket.JoinMulticast(m_interface.ip_address, addr)) { + OLA_WARN << "Failed to join multicast group " << addr; + return false; + } + + coap_resource_t *r; + + string *uri1 = new string(); + UniverseURI(universe, uri1); + +// OLA_DEBUG << "Universe URI: " << *uri1; + + r = coap_resource_init((unsigned char *)uri1->c_str(), uri1->length(), + COAP_RESOURCE_FLAGS_NOTIFY_NON); + + string *rname = new string("ola-sig-net-node-ptr"); + + string val = string(reinterpret_cast(this), 4); + OLA_INFO << "Orig Attr val: " << val; + + coap_add_attr(r, + (unsigned char *)rname->c_str(), + rname->length(), + (unsigned char *)this, + 4, + 0); + + coap_register_handler(r, COAP_REQUEST_POST, SigNetUniverseHandler); + + coap_add_resource(m_coap_context, r); + + if (callback) { + // register + SigNetInputGroup *universe_data = STLFindOrNull(m_input_map, universe); + if (universe_data) { + OLA_WARN << "Attempt to register a second callback for " << universe; + // delete the callback, since we have ownership of it + delete callback; + return false; + } else { + // This is a new registration, insert into the CallbackMap. + m_input_map.insert( + make_pair(universe, new SigNetInputGroup(callback))); + } + } else { + // deregister + STLRemoveAndDelete(&m_input_map, universe); + } + + return true; +} + +bool SigNetNode::RemoveHandler(uint16_t universe) { + IPV4Address addr; + if (!UniverseIP(universe, &addr)) { + OLA_WARN << "Unable to determine multicast group for universe " << + universe; + return false; + } + + OLA_DEBUG << "Leaving via handler" << addr; + if (!m_socket.LeaveMulticast(m_interface.ip_address, addr)) { + OLA_WARN << "Failed to leave multicast group " << addr; + return false; + } + + return true; +} + + +/** + * Send the DMX data to all targets registered for this group. + * @param group the group to send the data to + * @param dmx_data the DmxBuffer to send + * @returns true if successfully sent, false if any error occurred. + */ +bool SigNetNode::SendData(unsigned int group, + OLA_UNUSED const ola::DmxBuffer &dmx_data) { + SigNetOutputGroup *output_group = STLFindOrNull(m_output_map, group); + if (!output_group) { + OLA_WARN << "failed to find " << group; + // group doesn't exist, just return + return false; + } + + // TODO(Peter): Fixme! + return true; +} + + +/** + * Called by SigNetDataHandler when there is new data. + * @param signet_address the SigNet address this data arrived on + * @param data the DmxBuffer containing the data. + * @param size the number of slots. + */ +void SigNetNode::SetUniverse(const uint16_t universe, const uint8_t *data, + unsigned int size) { + SigNetInputGroup *universe_data = STLFindOrNull(m_input_map, universe); + if (!universe_data) + return; + + universe_data->dmx.Set(data, size); + if (universe_data->callback.get()) { + universe_data->callback->Run(universe_data->dmx); + } +} + + +/** + * Return the listening port. Will be 0 if the node isn't setup. + */ +uint16_t SigNetNode::ListeningPort() const { + return m_listen_port; +} + + +/** + * Called when the SigNet FD is readable. + */ +void SigNetNode::DescriptorReady() { + // Call into libcoap to read the received data + coap_read(m_coap_context); +} + + +/** + * Send an lo_message to each target in a set. + * @param message the lo_message to send. + * @param targets the list of targets to send the message to. + */ +/*bool SigNetNode::SendMessageToTargets(lo_message message, + const SigNetTargetVector &targets) { + bool ok = true; + SigNetTargetVector::const_iterator target_iter = targets.begin(); + for (; target_iter != targets.end(); ++target_iter) { + int ret = lo_send_message_from( + (*target_iter)->liblo_address, + m_signet_server, + (*target_iter)->signet_address.c_str(), + message); + ok &= (ret > 0); + } + return ok; +}*/ +} // namespace signet +} // namespace plugin +} // namespace ola diff --git a/plugins/signet/SigNetNode.h b/plugins/signet/SigNetNode.h new file mode 100644 index 0000000000..745df47efa --- /dev/null +++ b/plugins/signet/SigNetNode.h @@ -0,0 +1,179 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * SigNetNode.h + * A Sig-Net orientated, C++ wrapper around libcoap. + * Copyright (C) 2026 Peter Newman + */ + +#ifndef PLUGINS_SIGNET_SIGNETNODE_H_ +#define PLUGINS_SIGNET_SIGNETNODE_H_ + +// Required for libcoap +#define WITH_POSIX +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ola { +namespace plugin { +namespace signet { + +/** + * The SigNetNode object handles sending and receiving DMX data using Sig-Net. + * + * Sending: + * For sending + * + * Receiving: + * To receive DMX data, register a Callback for a specific SigNet Address. For + * example: + * + * SigNetNode node(SigNetNode::SigNetNodeOptions(), ...); + * node.Init(); + * + * node.RegisterAddress("/dmx/1", NewCallback(...)); + * // run the SelectServer + * + * // once it's time to stop, de-register this address + * node.RegisterAddress("/dmx/1", NULL); + */ +class SigNetNode { + public: + // The options for the SigNetNode object. + struct SigNetNodeOptions { + uint16_t listen_port; // UDP port to listen on + + SigNetNodeOptions() : listen_port(DEFAULT_SIGNET_PORT) {} + }; + + // The callback run when we receive new DMX data. + typedef Callback1 DMXCallback; + + static const std::vector SIGNET_LEVEL_URI; + + static bool UniverseIP(uint16_t universe, + class ola::network::IPV4Address *addr); + + static bool UniverseURI(uint16_t universe, std::string *uri); + + SigNetNode(ola::io::SelectServerInterface *ss, + ola::ExportMap *export_map, + const SigNetNodeOptions &options); + ~SigNetNode(); + + bool Init(); + void Stop(); + + /** + * @brief Set the Callback to be run when we receive data for this universe. + * @param universe the universe to register the handler for + * @param buffer the DmxBuffer to copy the data to. + * @param priority the priority to set. + * @param handler the Callback to call when there is data for this universe. + * Ownership is transferred. + */ + bool SetHandler(uint16_t universe, ola::DmxBuffer *buffer, + uint8_t *priority, + DMXCallback *callback); + + /** + * @brief Remove the handler for a particular universe. + * @param universe the universe handler to remove + * @return true if removed, false if it didn't exist + */ + bool RemoveHandler(uint16_t universe); + + // Sending methods + bool SendData(unsigned int group, const ola::DmxBuffer &data); + + // Called by the libcoap handlers. + void SetUniverse(uint16_t universe, const uint8_t *data, + unsigned int size); + + // The port SigNet is listening on. + uint16_t ListeningPort() const; + + /** + * @brief Return the Interface this node is using. + */ + const ola::network::Interface &GetInterface() const { return m_interface; } + + /** + * @brief Return the UDP socket this node is using. + */ + ola::network::UDPSocket* GetSocket() { return &m_socket; } + + private: + struct SigNetOutputGroup { +// SigNetTargetVector targets; + DmxBuffer dmx; // holds the last values. + }; + + struct SigNetInputGroup { + explicit SigNetInputGroup(DMXCallback *callback) : callback(callback) {} + + DmxBuffer dmx; + std::auto_ptr callback; + }; + + typedef std::map OutputGroupMap; + + typedef std::map InputUniverseMap; + + ola::io::SelectServerInterface *m_ss; + const uint16_t m_listen_port; + ola::network::Interface m_interface; + ola::network::UDPSocket m_socket; + std::auto_ptr m_descriptor; + coap_context_t *m_coap_context; + OutputGroupMap m_output_map; + InputUniverseMap m_input_map; + + void DescriptorReady(); +// bool SendMessageToTargets(lo_message message, +// const SigNetTargetVector &targets); + +/*void SigNetUniverseHandler(coap_context_t *ctx, + struct coap_resource_t *resource, + const coap_endpoint_t *local_interface, + coap_address_t *peer, + coap_pdu_t *request, + str *token, + coap_pdu_t *response);*/ + + static coap_context_t* GetCoapContext(const ola::network::IPV4Address ip, + const uint16_t port); + + static const uint16_t DEFAULT_SIGNET_PORT = 5683; + static const char SIGNET_PORT_VARIABLE[]; +}; +} // namespace signet +} // namespace plugin +} // namespace ola +#endif // PLUGINS_SIGNET_SIGNETNODE_H_ + diff --git a/plugins/signet/SigNetPlugin.cpp b/plugins/signet/SigNetPlugin.cpp new file mode 100644 index 0000000000..cfb784eea2 --- /dev/null +++ b/plugins/signet/SigNetPlugin.cpp @@ -0,0 +1,126 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * SigNetPlugin.cpp + * The SigNet plugin for ola. This creates a single SigNet device. + * Copyright (C) 2026 Peter Newman + */ + +#define __STDC_LIMIT_MACROS // for UINT8_MAX & friends +#include +#include +#include +#include +#include +#include +#include + +#include "ola/StringUtils.h" +#include "olad/PluginAdaptor.h" +#include "olad/Preferences.h" +#include "plugins/signet/SigNetDevice.h" +#include "plugins/signet/SigNetPlugin.h" +#include "plugins/signet/SigNetPluginDescription.h" + +namespace ola { +namespace plugin { +namespace signet { + +using ola::network::IPV4SocketAddress; +using std::set; +using std::string; +using std::vector; + +const char SigNetPlugin::INPUT_PORT_COUNT_KEY[] = "input_ports"; +const char SigNetPlugin::OUTPUT_PORT_COUNT_KEY[] = "output_ports"; +const char SigNetPlugin::PLUGIN_NAME[] = "SigNet"; +const char SigNetPlugin::PLUGIN_PREFIX[] = "signet"; + +/* + * Start the plugin. + */ +bool SigNetPlugin::StartHook() { + SigNetDevice::SigNetDeviceOptions options; + + if (!StringToInt(m_preferences->GetValue(INPUT_PORT_COUNT_KEY), + &options.input_ports)) { + OLA_WARN << "Invalid value for input_ports"; + } + + if (!StringToInt(m_preferences->GetValue(OUTPUT_PORT_COUNT_KEY), + &options.output_ports)) { + OLA_WARN << "Invalid value for output_ports"; + } + + // Finally create the new SigNetDevice, start it and register the device. + std::auto_ptr device( + new SigNetDevice(this, m_plugin_adaptor, options)); + if (!device->Start()) { + OLA_FATAL << "SigNet device didn't start!"; + return false; + } + m_device = device.release(); + m_plugin_adaptor->RegisterDevice(m_device); + return true; +} + + +/* + * Stop the plugin + * @return true on success, false on failure + */ +bool SigNetPlugin::StopHook() { + if (m_device) { + m_plugin_adaptor->UnregisterDevice(m_device); + bool ret = m_device->Stop(); + delete m_device; + return ret; + } + return true; +} + + +string SigNetPlugin::Description() const { + return plugin_description; +} + + +/** + * Set the default preferences for the SigNet plugin. + */ +bool SigNetPlugin::SetDefaultPreferences() { + if (!m_preferences) { + return false; + } + + bool save = false; + + save |= m_preferences->SetDefaultValue(INPUT_PORT_COUNT_KEY, + UIntValidator(0, 32), + DEFAULT_PORT_COUNT); + + save |= m_preferences->SetDefaultValue(OUTPUT_PORT_COUNT_KEY, + UIntValidator(0, 32), + DEFAULT_PORT_COUNT); + + if (save) { + m_preferences->Save(); + } + + return true; +} +} // namespace signet +} // namespace plugin +} // namespace ola diff --git a/plugins/signet/SigNetPlugin.h b/plugins/signet/SigNetPlugin.h new file mode 100644 index 0000000000..716657f080 --- /dev/null +++ b/plugins/signet/SigNetPlugin.h @@ -0,0 +1,62 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * SigNetPlugin.h + * Interface for the SigNet plugin. + * Copyright (C) 2026 Peter Newman + */ + +#ifndef PLUGINS_SIGNET_SIGNETPLUGIN_H_ +#define PLUGINS_SIGNET_SIGNETPLUGIN_H_ + +#include +#include "olad/Plugin.h" +#include "ola/plugin_id.h" +#include "plugins/signet/SigNetDevice.h" + +namespace ola { +namespace plugin { +namespace signet { + +class SigNetDevice; + +class SigNetPlugin: public ola::Plugin { + public: + explicit SigNetPlugin(ola::PluginAdaptor *plugin_adaptor): + ola::Plugin(plugin_adaptor), + m_device(NULL) {} + + std::string Name() const { return PLUGIN_NAME; } + std::string Description() const; + ola_plugin_id Id() const { return OLA_PLUGIN_SIGNET; } + std::string PluginPrefix() const { return PLUGIN_PREFIX; } + + private: + bool StartHook(); + bool StopHook(); + bool SetDefaultPreferences(); + + SigNetDevice *m_device; + static const uint8_t DEFAULT_PORT_COUNT = 5; + + static const char INPUT_PORT_COUNT_KEY[]; + static const char OUTPUT_PORT_COUNT_KEY[]; + static const char PLUGIN_NAME[]; + static const char PLUGIN_PREFIX[]; +}; +} // namespace signet +} // namespace plugin +} // namespace ola +#endif // PLUGINS_SIGNET_SIGNETPLUGIN_H_ diff --git a/plugins/signet/SigNetPort.cpp b/plugins/signet/SigNetPort.cpp new file mode 100644 index 0000000000..b97453617a --- /dev/null +++ b/plugins/signet/SigNetPort.cpp @@ -0,0 +1,121 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * SigNetPort.cpp + * The SigNetInputPort and SigNetOutputPort classes. + * Copyright (C) 2026 Peter Newman + */ + +#include +#include +#include +#include "ola/Logging.h" +#include "olad/Port.h" +#include "plugins/signet/SigNetDevice.h" +#include "plugins/signet/SigNetNode.h" +#include "plugins/signet/SigNetPort.h" + +namespace ola { +namespace plugin { +namespace signet { + +using std::ostringstream; +using std::string; +using std::vector; + +bool SigNetPortHelper::PreSetUniverse(Universe *old_universe, + Universe *new_universe) { + if (new_universe && (new_universe->UniverseId() == 0 || + new_universe->UniverseId() > MAX_SIGNET_UNIVERSE)) { + OLA_WARN << "Universe id " << new_universe->UniverseId() << " is 0 or > " + << MAX_SIGNET_UNIVERSE; + return false; + } + (void) old_universe; + return true; +} + + +string SigNetPortHelper::Description(Universe *universe) const { + std::ostringstream str; + if (universe) + str << "Sig-Net Universe " << universe->UniverseId(); + return str.str(); +} + + +SigNetInputPort::SigNetInputPort( + SigNetDevice *parent, + unsigned int port_id, + PluginAdaptor *plugin_adaptor, + SigNetNode *node) + : BasicInputPort(parent, port_id, plugin_adaptor), + m_node(node), + m_priority(ola::dmx::SOURCE_PRIORITY_DEFAULT) { + SetPriorityMode(PRIORITY_MODE_INHERIT); +} + + +/* + * Set the universe for an input port. + */ +void SigNetInputPort::PostSetUniverse(Universe *old_universe, + Universe *new_universe) { + if (old_universe) { + m_node->RemoveHandler(old_universe->UniverseId()); + } + + if (new_universe) { + // TODO(Peter): What callback do we actually need here? + m_node->SetHandler( + new_universe->UniverseId(), + &m_buffer, + &m_priority, + NewCallback(this, &SigNetInputPort::NewDMXData)); + } +} + +void SigNetInputPort::NewDMXData(const DmxBuffer &data) { + m_buffer = data; // store the data + DmxChanged(); // signal that our data has changed +} + + +SigNetOutputPort::SigNetOutputPort( + SigNetDevice *device, + unsigned int port_id, + SigNetNode *node) + : BasicOutputPort(device, port_id), + m_node(node) { +} + +/* + * Set the universe for an output port. + */ +void SigNetOutputPort::PostSetUniverse(Universe *old_universe, + Universe *new_universe) { + if (old_universe) { +// m_node->TerminateStream(old_universe->UniverseId(), m_last_priority); + } + if (new_universe) { +// m_node->StartStream(new_universe->UniverseId()); + } +} + +SigNetOutputPort::~SigNetOutputPort() { +} +} // namespace signet +} // namespace plugin +} // namespace ola diff --git a/plugins/signet/SigNetPort.h b/plugins/signet/SigNetPort.h new file mode 100644 index 0000000000..7ebc869d86 --- /dev/null +++ b/plugins/signet/SigNetPort.h @@ -0,0 +1,138 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * SigNetPort.h + * The SigNetInputPort and SigNetOutputPort classes. + * Copyright (C) 2026 Peter Newman + */ + +#ifndef PLUGINS_SIGNET_SIGNETPORT_H_ +#define PLUGINS_SIGNET_SIGNETPORT_H_ + +#include +#include +#include "olad/Port.h" +#include "plugins/signet/SigNetDevice.h" +#include "plugins/signet/SigNetNode.h" + +namespace ola { +namespace plugin { +namespace signet { + +class SigNetPortHelper { + public: + bool PreSetUniverse(Universe *old_universe, Universe *new_universe); + std::string Description(Universe *universe) const; + private: + static const unsigned int MAX_SIGNET_UNIVERSE = 63999; +}; + + +/** + * @brief The Input Port class, for receiving DMX via Sig-Net. + */ +class SigNetInputPort: public BasicInputPort { + public: + /** + * Create an SigNetInputPort. + * @param parent the parent device + * @param port_id the id for this port + * @param plugin_adaptor a PluginAdaptor object, used by the base class. + * @param node the SigNetNode object to use + */ + SigNetInputPort(SigNetDevice *parent, + unsigned int port_id, + PluginAdaptor *plugin_adaptor, + SigNetNode *node); + + /** + * Just return our DmxBuffer. + */ + const DmxBuffer &ReadDMX() const { return m_buffer; } + + /** + * Called during the patch process, just before the Universe of this port + * changes. + */ + bool PreSetUniverse(Universe *old_universe, Universe *new_universe) { + return m_helper.PreSetUniverse(old_universe, new_universe); + } + void PostSetUniverse(Universe *old_universe, Universe *new_universe); + std::string Description() const { + return m_helper.Description(GetUniverse()); + } + + bool SupportsPriorities() const { return true; } + uint8_t InheritedPriority() const { return m_priority; } + + private: + SigNetNode *m_node; + DmxBuffer m_buffer; + SigNetPortHelper m_helper; + uint8_t m_priority; + + /** + * This is called when we receive new DMX values via Sig-Net. + */ + void NewDMXData(const DmxBuffer &data); +}; + + +/** + * The Output Port class, for sending DMX via Sig-Net. + */ +class SigNetOutputPort: public BasicOutputPort { + public: + /** + * @brief Create an SigNetOutputPort. + * @param device the parent device + * @param port_id the id for this port + * @param node the SigNetNode object to use + */ + SigNetOutputPort(SigNetDevice *device, + unsigned int port_id, + SigNetNode *node); + ~SigNetOutputPort(); + + /** + * Called during the patch process, just before the Universe of this port + * changes. + */ + bool PreSetUniverse(Universe *old_universe, Universe *new_universe) { + return m_helper.PreSetUniverse(old_universe, new_universe); + } + void PostSetUniverse(Universe *old_universe, Universe *new_universe); + std::string Description() const { + return m_helper.Description(GetUniverse()); + } + + /** + * @brief Send this DMX buffer using Sig-Net. The second argument (priority) is not + * used. + * @param buffer the DMX data + */ + bool WriteDMX(const DmxBuffer &buffer, uint8_t) { + // TODO(Peter): Handle priority + return m_node->SendData(this->PortId(), buffer); + } + + private: + SigNetNode *m_node; + SigNetPortHelper m_helper; +}; +} // namespace signet +} // namespace plugin +} // namespace ola +#endif // PLUGINS_SIGNET_SIGNETPORT_H_ diff --git a/plugins/signet/SigNetTIDs.h b/plugins/signet/SigNetTIDs.h new file mode 100644 index 0000000000..597adf0f3b --- /dev/null +++ b/plugins/signet/SigNetTIDs.h @@ -0,0 +1,57 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * SigNetTIDs.h + * TIDs used in Sig-Net packets + * Copyright (C) 2026 Peter Newman + */ + +#ifndef PLUGINS_SIGNET_SIGNETTIDS_H_ +#define PLUGINS_SIGNET_SIGNETTIDS_H_ + +/** + * @addtogroup signet + * @{ + * @file SigNetTIDs.h + * @brief Sig-Net TID values. + * @} + */ + +#include + +namespace ola { +namespace plugin { +namespace signet { + +/** + * @addtogroup signet + * @{ + */ + +// Section 17, TID Dictionary +/** + * @brief Sig-Net TIDs used at the root level. + */ +enum RootTID { + TID_LEVEL = 0x0101, /**< TID Level */ + TID_PRIORITY = 0x0102, /**< TID Priority */ +}; +/** + * @} + */ +} // namespace signet +} // namespace plugin +} // namespace ola +#endif // PLUGINS_SIGNET_SIGNETTIDS_H_ From 332d424cdc14f9467f591edfb8fcff39a0473484 Mon Sep 17 00:00:00 2001 From: Peter Newman Date: Fri, 28 Aug 2026 01:00:11 +0100 Subject: [PATCH 02/12] Fix licence header --- plugins/signet/SigNetEnums.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/plugins/signet/SigNetEnums.h b/plugins/signet/SigNetEnums.h index b408a052e9..d754651401 100644 --- a/plugins/signet/SigNetEnums.h +++ b/plugins/signet/SigNetEnums.h @@ -1,17 +1,17 @@ /* - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This library is distributed in the hope that it will be useful, + * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * SigNetEnums.h * General enums used in Sig-Net packets From 69d0e3548a9edb68e7f57b3d7b24648f25a000b6 Mon Sep 17 00:00:00 2001 From: Peter Newman Date: Fri, 28 Aug 2026 01:27:40 +0100 Subject: [PATCH 03/12] First attempt at simple HMAC SHA256 function --- common/Makefile.mk | 1 + common/crypto/HMAC.cpp | 45 ++++++++++++++++++++++ common/crypto/HMACTest.cpp | 68 ++++++++++++++++++++++++++++++++++ common/crypto/Makefile.mk | 27 ++++++++++++++ configure.ac | 7 ++++ include/ola/Makefile.mk | 1 + include/ola/crypto/HMAC.h | 52 ++++++++++++++++++++++++++ include/ola/crypto/Makefile.mk | 3 ++ 8 files changed, 204 insertions(+) create mode 100644 common/crypto/HMAC.cpp create mode 100644 common/crypto/HMACTest.cpp create mode 100644 common/crypto/Makefile.mk create mode 100644 include/ola/crypto/HMAC.h create mode 100644 include/ola/crypto/Makefile.mk diff --git a/common/Makefile.mk b/common/Makefile.mk index 5aeb32c395..a57053b92d 100644 --- a/common/Makefile.mk +++ b/common/Makefile.mk @@ -13,6 +13,7 @@ common_libolacommon_la_LIBADD += -lWs2_32 -lIphlpapi endif include common/base/Makefile.mk +include common/crypto/Makefile.mk include common/dmx/Makefile.mk include common/export_map/Makefile.mk include common/file/Makefile.mk diff --git a/common/crypto/HMAC.cpp b/common/crypto/HMAC.cpp new file mode 100644 index 0000000000..d3f25c6f41 --- /dev/null +++ b/common/crypto/HMAC.cpp @@ -0,0 +1,45 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * HMAC.cpp + * HMAC Crypto functions. + * Copyright (C) 2026 Peter Newman + */ + +#include "ola/crypto/HMAC.h" + +#include +#include +#include "ola/Logging.h" + +namespace ola { +namespace crypto { + +bool HMACSHA256(const uint8_t *key, + const unsigned int key_length, + const uint8_t *data, + const unsigned int data_length, + uint8_t *hmac, + unsigned int *hmac_length) { + HMAC_CTX ctx; + HMAC_Init(&ctx, key, key_length, EVP_sha256()); + HMAC_Update(&ctx, data, data_length); + HMAC_Final(&ctx, hmac, hmac_length); + HMAC_cleanup(&ctx); + + return true; +} +} // namespace crypto +} // namespace ola diff --git a/common/crypto/HMACTest.cpp b/common/crypto/HMACTest.cpp new file mode 100644 index 0000000000..f31f0c4744 --- /dev/null +++ b/common/crypto/HMACTest.cpp @@ -0,0 +1,68 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * HMACTest.cpp + * Unittest for HMAC Crypto functions. + * Copyright (C) 2026 Peter Newman + */ + +#include + +#include "ola/crypto/HMAC.h" +#include "ola/testing/TestUtils.h" + +using ola::crypto::HMACSHA256; + +class HMACTest: public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(HMACTest); + CPPUNIT_TEST(testHMACSHA256); + CPPUNIT_TEST_SUITE_END(); + + public: + void testHMACSHA256(); +}; + + +CPPUNIT_TEST_SUITE_REGISTRATION(HMACTest); + +/* + * Test the HMACSHA256 function + */ +void HMACTest::testHMACSHA256() { + // RFC 4231 - Test Case 1 + // uint8_t hmac[EVP_MAX_MD_SIZE]; + uint8_t hmac[100]; + unsigned int len = 0; + + const uint8_t key[] = { + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b + }; + + const uint8_t data[] = { 'H', 'i', ' ', 'T', 'h', 'e', 'r', 'e' }; + + uint8_t expected_hmac[32] = { + 0xB0, 0x34, 0x4C, 0x61, 0xD8, 0xDB, 0x38, 0x53, + 0x5C, 0xA8, 0xAF, 0xCE, 0xAF, 0x0B, 0xF1, 0x2B, + 0x88, 0x1D, 0xC2, 0x00, 0xC9, 0x83, 0x3D, 0xA7, + 0x26, 0xE9, 0x37, 0x6C, 0x2E, 0x32, 0xCF, 0xF7 + }; + + OLA_ASSERT_TRUE(HMACSHA256(key, sizeof(key), data, sizeof(data), hmac, &len)); + OLA_ASSERT_DATA_EQUALS(expected_hmac, sizeof(expected_hmac), + hmac, len); +} diff --git a/common/crypto/Makefile.mk b/common/crypto/Makefile.mk new file mode 100644 index 0000000000..052bd76607 --- /dev/null +++ b/common/crypto/Makefile.mk @@ -0,0 +1,27 @@ +# LIBRARIES +################################################## +if HAVE_LIBCRYPTO +noinst_LTLIBRARIES += common/crypto/libolacrypto.la +common_crypto_libolacrypto_la_SOURCES = \ + common/crypto/HMAC.cpp +common_crypto_libolacrypto_la_LIBADD = $(libcrypto_LIBS) + +# TODO(Peter): Fixme for Windows +# if USING_WIN32 +# Work around limitations with Windows library linking +# common_crypto_libolacrypto_la_LIBADD = common/libolacommon.la +# endif + +# TESTS +################################################ +test_programs += common/crypto/HMACTester + +COMMON_CRYPTO_TEST_LDADD = $(COMMON_TESTING_LIBS) \ + common/crypto/libolacrypto.la + +common_crypto_HMACTester_SOURCES = \ + common/crypto/HMACTest.cpp +common_crypto_HMACTester_CXXFLAGS = $(COMMON_TESTING_FLAGS) +common_crypto_HMACTester_LDADD = $(COMMON_CRYPTO_TEST_LDADD) +endif + diff --git a/configure.ac b/configure.ac index d55bc47229..4935e7a563 100644 --- a/configure.ac +++ b/configure.ac @@ -773,6 +773,13 @@ AS_IF([test "x$enable_signet" != xno], AS_IF([test "x$have_libcoap" = xyes], [AC_DEFINE([HAVE_LIBCOAP], [1], [define if libcoap is installed])]) +have_libcrypto="no" +AS_IF([test "x$enable_signet" != xno], + [PKG_CHECK_MODULES(libcrypto, [libcrypto >= 1.0.2g], [have_libcrypto="yes"], [true])]) +AM_CONDITIONAL([HAVE_LIBCRYPTO], [test "x$have_libcrypto" = xyes]) +AS_IF([test "x$have_libcrypto" = xyes], + [AC_DEFINE([HAVE_LIBCRYPTO], [1], [define if libcrypto is installed])]) + # Python API AC_ARG_ENABLE( [python-libs], diff --git a/include/ola/Makefile.mk b/include/ola/Makefile.mk index 0f544b15cd..e7d0cd7801 100644 --- a/include/ola/Makefile.mk +++ b/include/ola/Makefile.mk @@ -1,6 +1,7 @@ include include/ola/acn/Makefile.mk include include/ola/base/Makefile.mk include include/ola/client/Makefile.mk +include include/ola/crypto/Makefile.mk include include/ola/dmx/Makefile.mk include include/ola/e133/Makefile.mk include include/ola/file/Makefile.mk diff --git a/include/ola/crypto/HMAC.h b/include/ola/crypto/HMAC.h new file mode 100644 index 0000000000..b19c736bfe --- /dev/null +++ b/include/ola/crypto/HMAC.h @@ -0,0 +1,52 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * HMAC.h + * HMAC Crypto functions. + * Copyright (C) 2026 Peter Newman + */ + +/** + * @file ola/crypto/HMAC.h + * @brief HMAC crypto functions + */ + +#ifndef INCLUDE_OLA_CRYPTO_HMAC_H_ +#define INCLUDE_OLA_CRYPTO_HMAC_H_ + +#include + +namespace ola { +namespace crypto { + +/** + * @brief Generate the SHA256 HMAC of some data. + * @param[in] key pointer to the key + * @param[in] key_length length of the key + * @param[in] data pointer to the data to generate the HMAC of + * @param[in] data_length length of the data to generate the HMAC of + * @param[out] hmac pointer to where the HMAC will be stored + * @param[out] hmac_length pointer to where the length of the generated HMAC + * will be stored + */ +bool HMACSHA256(const uint8_t *key, + const unsigned int key_length, + const uint8_t *data, + const unsigned int data_length, + uint8_t *hmac, + unsigned int *hmac_length); +} // namespace crypto +} // namespace ola +#endif // INCLUDE_OLA_CRYPTO_HMAC_H_ diff --git a/include/ola/crypto/Makefile.mk b/include/ola/crypto/Makefile.mk new file mode 100644 index 0000000000..50d670c36a --- /dev/null +++ b/include/ola/crypto/Makefile.mk @@ -0,0 +1,3 @@ +olacryptoincludedir = $(pkgincludedir)/crypto/ +olacryptoinclude_HEADERS = \ + include/ola/crypto/HMAC.h From 3e60c15756b9df21b4a67cff16a18f9dc4a06882 Mon Sep 17 00:00:00 2001 From: Peter Newman Date: Fri, 28 Aug 2026 14:23:17 +0100 Subject: [PATCH 04/12] Refactor HMAC function to use simplified API As a test for now (cherry picked from commit ca839776ed84c6ce9d7328c2519fec77a14eaa6e) --- common/crypto/HMAC.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/common/crypto/HMAC.cpp b/common/crypto/HMAC.cpp index d3f25c6f41..cb848ef373 100644 --- a/common/crypto/HMAC.cpp +++ b/common/crypto/HMAC.cpp @@ -33,11 +33,12 @@ bool HMACSHA256(const uint8_t *key, const unsigned int data_length, uint8_t *hmac, unsigned int *hmac_length) { - HMAC_CTX ctx; - HMAC_Init(&ctx, key, key_length, EVP_sha256()); + /*HMAC_CTX ctx; + HMAC_Init(&ctx, key, key_length, ); HMAC_Update(&ctx, data, data_length); HMAC_Final(&ctx, hmac, hmac_length); - HMAC_cleanup(&ctx); + HMAC_cleanup(&ctx);*/ + HMAC(EVP_sha256(), key, key_length, data, data_length, hmac, hmac_length); return true; } From 41b0975433f60700f53d372ee864404378d9b04f Mon Sep 17 00:00:00 2001 From: Peter Newman Date: Sun, 30 Aug 2026 15:23:18 +0100 Subject: [PATCH 05/12] Add functions to generate the HMAC for SigNet --- olad/Makefile.mk | 4 ++ plugins/signet/SigNetNode.cpp | 130 +++++++++++++++++++++++++++++++--- plugins/signet/SigNetNode.h | 30 ++++++-- 3 files changed, 151 insertions(+), 13 deletions(-) diff --git a/olad/Makefile.mk b/olad/Makefile.mk index 527bfc26c2..7e1badca05 100644 --- a/olad/Makefile.mk +++ b/olad/Makefile.mk @@ -42,6 +42,10 @@ ola_server_sources += olad/HttpServerActions.cpp \ ola_server_additional_libs += common/http/libolahttp.la endif +if HAVE_LIBCRYPTO +ola_server_additional_libs += common/crypto/libolacrypto.la +endif + # lib olaserver lib_LTLIBRARIES += olad/libolaserver.la diff --git a/plugins/signet/SigNetNode.cpp b/plugins/signet/SigNetNode.cpp index 280c79f892..4fb6ee78d4 100644 --- a/plugins/signet/SigNetNode.cpp +++ b/plugins/signet/SigNetNode.cpp @@ -23,9 +23,13 @@ #endif // _WIN32 #include +#include #include #include #include +#include +#include +#include #include #include #include @@ -79,6 +83,13 @@ const char SigNetNode::SIGNET_PORT_VARIABLE[] = "signet-listen-port"; const vector SigNetNode::SIGNET_LEVEL_URI({ "sig-net", "v1", "local", "level"}); +const uint8_t SigNetNode::key[] = { + 0x78, 0x98, 0x1f, 0xe0, 0x25, 0x76, 0xb2, 0xe9, 0xe4, 0x7d, + 0x91, 0x68, 0x53, 0xd5, 0x96, 0x7f, 0x34, 0xf8, 0xae, 0x8a, + 0xaa, 0xe4, 0x6d, 0xb0, 0x49, 0x5b, 0x17, 0x8a, 0x75, 0x62, + 0x0e, 0x89 +}; + // TODO(Peter): Convert this into a SocketAddress? coap_context_t* SigNetNode::GetCoapContext(const IPV4Address ip, const uint16_t port) { @@ -151,7 +162,7 @@ void SigNetUniverseHandler(OLA_UNUSED coap_context_t *ctx, node = reinterpret_cast(attr->value.s); if (node) { - OLA_DEBUG << "Still listening on " << node->ListeningPort(); + OLA_DEBUG << "Still listening on " << node->ListeningPort(); } } else { OLA_DEBUG << "Didn't find our attr"; @@ -163,10 +174,10 @@ void SigNetUniverseHandler(OLA_UNUSED coap_context_t *ctx, string uri_part; unsigned int uri_part_i = 0; uint16_t universe = 0; - int signet_security_mode = -1; - int signet_mfg_code = -1; - int signet_session_id = -1; - int signet_seq_num = -1; + uint8_t signet_security_mode = 0; + uint16_t signet_mfg_code = 0; + uint32_t signet_session_id = 0; + uint32_t signet_seq_num = 0; UID signet_sender_id_tuid(0, 0); uint16_t signet_sender_id_endpoint = 0; std::ostringstream format_data_str; @@ -246,6 +257,24 @@ void SigNetUniverseHandler(OLA_UNUSED coap_context_t *ctx, if (coap_get_data(request, &data_len, &data)) { OLA_DEBUG << "Raw Length: " << data_len; + + unsigned int len; + unsigned char out[100]; + + node->GenerateHMAC(resource->uri.s, resource->uri.length, + signet_security_mode, signet_sender_id_tuid, + signet_sender_id_endpoint, signet_mfg_code, + signet_session_id, signet_seq_num, + data, data_len, + SigNetNode::key, sizeof(SigNetNode::key), + out, &len); + + std::ostringstream format_be_hmac_str; + ola::strings::FormatData(&format_be_hmac_str, out, len); + OLA_INFO << "BE HMAC\n" << format_be_hmac_str.str(); + + // TODO(Peter): Actually check the generated HMAC matches! + if (data_len >= 4) { uint16_t tid_data_type = JoinUInt8(data[0], data[1]); uint16_t tid_data_len = JoinUInt8(data[2], data[3]); @@ -319,6 +348,65 @@ void SigNetErrorHandler(int error_code, const char *msg, const char *stack) { }*/ +bool SigNetNode::PopulateHMACData( + const uint8_t *uri, const unsigned int uri_length, + const uint8_t security_mode, + const ola::rdm::UID sender_id_tuid, const uint16_t sender_id_endpoint, + const uint16_t mfg_code, + const uint32_t session_id, const uint32_t seq_num, + const uint8_t *payload, const unsigned int payload_length, + uint8_t *hmac_data, unsigned int *hmac_data_length) { + ola::io::IOQueue output_queue; + ola::io::BigEndianOutputStream output_stream(&output_queue); + output_queue.Clear(); + output_stream << (uint8_t)'/'; + output_stream.Write(uri, uri_length); + output_stream << security_mode + << sender_id_tuid.ManufacturerId() + << sender_id_tuid.DeviceId() + << sender_id_endpoint + << mfg_code + << session_id + << seq_num; + output_stream.Write(payload, payload_length); + + OLA_DEBUG << "BE size " << output_queue.Size(); + +// std::ostringstream format_be_data_dump_str; +// output_queue.Dump(&format_be_data_dump_str); +// OLA_INFO << "BE Data Dump\n" << format_be_data_dump_str.str(); + + *hmac_data_length = output_queue.Size(); + output_queue.Peek(hmac_data, *hmac_data_length); + + return true; +} + + +bool SigNetNode::GenerateHMAC( + const uint8_t *uri, const unsigned int uri_length, + const uint8_t security_mode, + const ola::rdm::UID sender_id_tuid, const uint16_t sender_id_endpoint, + const uint16_t mfg_code, + const uint32_t session_id, const uint32_t seq_num, + const uint8_t *payload, const unsigned int payload_length, + const uint8_t *key, const unsigned int key_length, + uint8_t *hmac, unsigned int *hmac_length) { + uint8_t out[1500]; + unsigned int len = 0; + PopulateHMACData(uri, uri_length, + security_mode, sender_id_tuid, + sender_id_endpoint, mfg_code, + session_id, seq_num, + payload, payload_length, + out, &len); + + return ola::crypto::HMACSHA256(key, key_length, + out, len, + hmac, hmac_length); +} + + /* * Calculate the IP that corresponds to a universe. * @param universe the universe id @@ -436,6 +524,11 @@ bool SigNetNode::Init() { m_descriptor->SetOnData(NewCallback(this, &SigNetNode::DescriptorReady)); m_ss->AddReadDescriptor(m_descriptor.get()); + // Test send data to uni 2 + DmxBuffer buffer; + buffer.SetFromString("50,100,150,200,250"); + SendData(2, buffer); + return true; } @@ -467,7 +560,7 @@ void SigNetNode::Stop() { } -bool SigNetNode::SetHandler(uint16_t universe, +bool SigNetNode::SetHandler(const uint16_t universe, OLA_UNUSED DmxBuffer *buffer, OLA_UNUSED uint8_t *priority, DMXCallback *callback) { @@ -555,11 +648,30 @@ bool SigNetNode::RemoveHandler(uint16_t universe) { * @param dmx_data the DmxBuffer to send * @returns true if successfully sent, false if any error occurred. */ -bool SigNetNode::SendData(unsigned int group, +bool SigNetNode::SendData(const uint16_t universe, OLA_UNUSED const ola::DmxBuffer &dmx_data) { - SigNetOutputGroup *output_group = STLFindOrNull(m_output_map, group); + // coap_startup(); + + Clock clock; + TimeStamp timestamp; + clock.CurrentMonotonicTime(×tamp); + +/* GenerateHMAC(resource->uri.s, resource->uri.length, + signet_security_mode, signet_sender_id_tuid, + signet_sender_id_endpoint, signet_mfg_code, + 1, timestamp.Seconds(), + data, data_len, + SigNetNode::key, sizeof(SigNetNode::key), + out, &len); + + std::ostringstream format_be_hmac_str; + ola::strings::FormatData(&format_be_hmac_str, out, len); + OLA_INFO << "BE HMAC\n" << format_be_hmac_str.str();*/ + + + SigNetOutputGroup *output_group = STLFindOrNull(m_output_map, universe); if (!output_group) { - OLA_WARN << "failed to find " << group; + OLA_WARN << "failed to find universe " << universe; // group doesn't exist, just return return false; } diff --git a/plugins/signet/SigNetNode.h b/plugins/signet/SigNetNode.h index 745df47efa..a2a26aedac 100644 --- a/plugins/signet/SigNetNode.h +++ b/plugins/signet/SigNetNode.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -76,6 +77,27 @@ class SigNetNode { static const std::vector SIGNET_LEVEL_URI; + static const uint8_t key[]; + + static bool PopulateHMACData( + const uint8_t *uri, const unsigned int uri_length, + const uint8_t security_mode, + const ola::rdm::UID sender_id_tuid, const uint16_t sender_id_endpoint, + const uint16_t mfg_code, + const uint32_t session_id, const uint32_t seq_num, + const uint8_t *payload, const unsigned int payload_length, + uint8_t *hmac_data, unsigned int *hmac_dat_length); + + static bool GenerateHMAC( + const uint8_t *uri, const unsigned int uri_length, + const uint8_t security_mode, + const ola::rdm::UID sender_id_tuid, const uint16_t sender_id_endpoint, + const uint16_t mfg_code, + const uint32_t session_id, const uint32_t seq_num, + const uint8_t *payload, const unsigned int payload_length, + const uint8_t *key, const unsigned int key_length, + uint8_t *hmac, unsigned int *hmac_length); + static bool UniverseIP(uint16_t universe, class ola::network::IPV4Address *addr); @@ -97,7 +119,7 @@ class SigNetNode { * @param handler the Callback to call when there is data for this universe. * Ownership is transferred. */ - bool SetHandler(uint16_t universe, ola::DmxBuffer *buffer, + bool SetHandler(const uint16_t universe, ola::DmxBuffer *buffer, uint8_t *priority, DMXCallback *callback); @@ -109,11 +131,11 @@ class SigNetNode { bool RemoveHandler(uint16_t universe); // Sending methods - bool SendData(unsigned int group, const ola::DmxBuffer &data); + bool SendData(const uint16_t universe, const ola::DmxBuffer &data); // Called by the libcoap handlers. - void SetUniverse(uint16_t universe, const uint8_t *data, - unsigned int size); + void SetUniverse(const uint16_t universe, const uint8_t *data, + const unsigned int size); // The port SigNet is listening on. uint16_t ListeningPort() const; From 5d509416fe1dbd79e14fefb144a1d6550102de03 Mon Sep 17 00:00:00 2001 From: Peter Newman Date: Mon, 31 Aug 2026 01:25:22 +0100 Subject: [PATCH 06/12] Add the initial ability to send Sig-Net messages too --- plugins/signet/SigNetDevice.cpp | 1 + plugins/signet/SigNetNode.cpp | 197 ++++++++++++++++++++++++-------- plugins/signet/SigNetNode.h | 16 ++- plugins/signet/SigNetPort.h | 5 +- 4 files changed, 163 insertions(+), 56 deletions(-) diff --git a/plugins/signet/SigNetDevice.cpp b/plugins/signet/SigNetDevice.cpp index 814dc60a1d..fbc5643fd1 100644 --- a/plugins/signet/SigNetDevice.cpp +++ b/plugins/signet/SigNetDevice.cpp @@ -51,6 +51,7 @@ SigNetDevice::SigNetDevice(AbstractPlugin *owner, // allocate a new SigNetNode but delay the call to Init() until later m_signet_node.reset(new SigNetNode(plugin_adaptor, plugin_adaptor->GetExportMap(), + plugin_adaptor->DefaultUID(), node_options)); } diff --git a/plugins/signet/SigNetNode.cpp b/plugins/signet/SigNetNode.cpp index 4fb6ee78d4..351dd3a408 100644 --- a/plugins/signet/SigNetNode.cpp +++ b/plugins/signet/SigNetNode.cpp @@ -23,7 +23,6 @@ #endif // _WIN32 #include -#include #include #include #include @@ -72,6 +71,8 @@ using ola::network::NetworkToHost; using ola::rdm::UID; using ola::strings::IntToString; using ola::utils::JoinUInt8; +using ola::utils::SplitUInt16; +using ola::SequenceNumber; using std::auto_ptr; using std::make_pair; using std::max; @@ -450,9 +451,11 @@ bool SigNetNode::UniverseURI(uint16_t universe, string *uri) { * @param options the SigNetNodeOptions */ SigNetNode::SigNetNode(SelectServerInterface *ss, - ExportMap *export_map, - const SigNetNodeOptions &options) + ExportMap *export_map, + const UID &uid, + const SigNetNodeOptions &options) : m_ss(ss), + m_uid(uid), m_listen_port(options.listen_port) { if (export_map) { // export the SigNet listening port if we have an export map @@ -524,11 +527,6 @@ bool SigNetNode::Init() { m_descriptor->SetOnData(NewCallback(this, &SigNetNode::DescriptorReady)); m_ss->AddReadDescriptor(m_descriptor.get()); - // Test send data to uni 2 - DmxBuffer buffer; - buffer.SetFromString("50,100,150,200,250"); - SendData(2, buffer); - return true; } @@ -648,36 +646,33 @@ bool SigNetNode::RemoveHandler(uint16_t universe) { * @param dmx_data the DmxBuffer to send * @returns true if successfully sent, false if any error occurred. */ -bool SigNetNode::SendData(const uint16_t universe, - OLA_UNUSED const ola::DmxBuffer &dmx_data) { - // coap_startup(); +bool SigNetNode::SendDMX(const uint16_t universe, + const ola::DmxBuffer &dmx_data) { + string *uri = new string(); + UniverseURI(universe, uri); - Clock clock; - TimeStamp timestamp; - clock.CurrentMonotonicTime(×tamp); + IPV4Address addr; + if (!UniverseIP(universe, &addr)) { + OLA_WARN << "Unable to determine multicast group for universe " + << universe; + return false; + } -/* GenerateHMAC(resource->uri.s, resource->uri.length, - signet_security_mode, signet_sender_id_tuid, - signet_sender_id_endpoint, signet_mfg_code, - 1, timestamp.Seconds(), - data, data_len, - SigNetNode::key, sizeof(SigNetNode::key), - out, &len); + uint16_t payload_length = 0; + uint8_t payload[(512+4)]; - std::ostringstream format_be_hmac_str; - ola::strings::FormatData(&format_be_hmac_str, out, len); - OLA_INFO << "BE HMAC\n" << format_be_hmac_str.str();*/ + unsigned int dmx_data_length = dmx_data.Size(); + OLA_DEBUG << "Got DMX size: " << dmx_data_length + << " got payload size " << sizeof(payload); + dmx_data.Get(&payload[4], &dmx_data_length); + payload_length = 4 + dmx_data_length; + SplitUInt16(HostToNetwork((uint16_t)TID_LEVEL), &payload[0], &payload[1]); + SplitUInt16(dmx_data_length, &payload[2], &payload[3]); - SigNetOutputGroup *output_group = STLFindOrNull(m_output_map, universe); - if (!output_group) { - OLA_WARN << "failed to find universe " << universe; - // group doesn't exist, just return - return false; - } + OLA_DEBUG << "Sending CoAP data to universe " << universe; - // TODO(Peter): Fixme! - return true; + return SendCoapMessage(*uri, addr, payload, payload_length); } @@ -717,25 +712,127 @@ void SigNetNode::DescriptorReady() { } -/** - * Send an lo_message to each target in a set. - * @param message the lo_message to send. - * @param targets the list of targets to send the message to. - */ -/*bool SigNetNode::SendMessageToTargets(lo_message message, - const SigNetTargetVector &targets) { - bool ok = true; - SigNetTargetVector::const_iterator target_iter = targets.begin(); - for (; target_iter != targets.end(); ++target_iter) { - int ret = lo_send_message_from( - (*target_iter)->liblo_address, - m_signet_server, - (*target_iter)->signet_address.c_str(), - message); - ok &= (ret > 0); +bool SigNetNode::SendCoapMessage(const std::string uri, + const ola::network::IPV4Address dest, + const uint8_t *payload, + const unsigned int payload_length) { + uint8_t signet_security_mode = 0; + UID signet_sender_id_tuid = m_uid; + uint16_t signet_sender_id_endpoint = 1; + uint16_t signet_mfg_code = 0; + uint32_t signet_session_id = 1; + uint32_t signet_seq_num = m_seq_num.Next(); + + uint8_t signet_security_mode_be = HostToNetwork(signet_security_mode); + uint8_t signet_sender_id_be[UID::LENGTH + sizeof(signet_sender_id_endpoint)]; + signet_sender_id_tuid.Pack(&signet_sender_id_be[0], + sizeof(signet_sender_id_be)); + SplitUInt16(signet_sender_id_endpoint, + &signet_sender_id_be[UID::LENGTH], + &signet_sender_id_be[UID::LENGTH + 1]); + + uint16_t signet_mfg_code_be = HostToNetwork(signet_mfg_code); + uint32_t signet_session_id_be = HostToNetwork(signet_session_id); + uint32_t signet_seq_num_be = HostToNetwork(signet_seq_num); + + uint8_t hmac[100]; + unsigned int hmac_length; + + GenerateHMAC(reinterpret_cast(uri.c_str()), uri.length(), + signet_security_mode, signet_sender_id_tuid, + signet_sender_id_endpoint, signet_mfg_code, + signet_session_id, signet_seq_num, + payload, payload_length, + SigNetNode::key, sizeof(SigNetNode::key), + hmac, &hmac_length); + + std::ostringstream format_be_hmac_str; + ola::strings::FormatData(&format_be_hmac_str, hmac, hmac_length); + OLA_INFO << "BE HMAC\n" << format_be_hmac_str.str(); + + coap_address_t dst; + coap_pdu_t *pdu; + + if (!(pdu = coap_pdu_init(COAP_MESSAGE_NON, COAP_REQUEST_POST, + coap_new_message_id(m_coap_context), + COAP_MAX_PDU_SIZE))) { + return false; } - return ok; -}*/ + + if (LOG_DEBUG <= coap_get_log_level()) { + debug("sending CoAP request:\n"); + coap_show_pdu(pdu); + } + + struct addrinfo hints; + struct addrinfo *result, *rp; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; // Allow IPv4 + hints.ai_socktype = SOCK_DGRAM; // Coap is UDP based + + int status = getaddrinfo(dest.ToString().c_str(), + IntToString(m_listen_port).c_str(), + &hints, &result); + if ( status != 0 ) { + OLA_WARN << "Failed to getaddrinfo: " << gai_strerror(status); + return false; + } + + // Loop through the results until we've found a suitable address + for (rp = result; rp != NULL; rp = rp->ai_next) { + coap_address_t addr; + + if (rp->ai_addrlen <= sizeof(dst.addr)) { + coap_address_init(&addr); + dst.size = rp->ai_addrlen; + memcpy(&dst.addr, rp->ai_addr, rp->ai_addrlen); + break; + } + } + + dst.addr.sin.sin_port = HostToNetwork(m_listen_port); + +#define BUFSIZE 40 + unsigned char _buf[BUFSIZE]; + unsigned char *buf = _buf; + size_t buflen; + int res; + + if (uri.length()) { + buflen = BUFSIZE; + res = coap_split_path((unsigned char *)uri.c_str(), uri.length(), + buf, &buflen); + + while (res--) { + coap_add_option(pdu, COAP_OPTION_URI_PATH, + coap_opt_length(buf), + coap_opt_value(buf)); + + buf += coap_opt_size(buf); + } + } + + coap_add_option(pdu, SIGNET_SECURITY_MODE, sizeof(signet_security_mode_be), + reinterpret_cast(&signet_security_mode_be)); + coap_add_option(pdu, SIGNET_SENDER_ID, sizeof(signet_sender_id_be), + reinterpret_cast(&signet_sender_id_be)); + coap_add_option(pdu, SIGNET_MFG_CODE, sizeof(signet_mfg_code_be), + reinterpret_cast(&signet_mfg_code_be)); + coap_add_option(pdu, SIGNET_SESSION_ID, sizeof(signet_session_id_be), + reinterpret_cast(&signet_session_id_be)); + coap_add_option(pdu, SIGNET_SEQ_NUM, sizeof(signet_seq_num_be), + reinterpret_cast(&signet_seq_num_be)); + coap_add_option(pdu, SIGNET_AUTH, hmac_length, hmac); + + coap_add_data(pdu, payload_length, payload); + + coap_send(m_coap_context, m_coap_context->endpoint, &dst, pdu); + + coap_delete_pdu(pdu); + + return true; +} } // namespace signet } // namespace plugin } // namespace ola diff --git a/plugins/signet/SigNetNode.h b/plugins/signet/SigNetNode.h index a2a26aedac..f32ec7168f 100644 --- a/plugins/signet/SigNetNode.h +++ b/plugins/signet/SigNetNode.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -104,8 +105,9 @@ class SigNetNode { static bool UniverseURI(uint16_t universe, std::string *uri); SigNetNode(ola::io::SelectServerInterface *ss, - ola::ExportMap *export_map, - const SigNetNodeOptions &options); + ola::ExportMap *export_map, + const ola::rdm::UID &uid, + const SigNetNodeOptions &options); ~SigNetNode(); bool Init(); @@ -131,7 +133,7 @@ class SigNetNode { bool RemoveHandler(uint16_t universe); // Sending methods - bool SendData(const uint16_t universe, const ola::DmxBuffer &data); + bool SendDMX(const uint16_t universe, const ola::DmxBuffer &data); // Called by the libcoap handlers. void SetUniverse(const uint16_t universe, const uint8_t *data, @@ -168,17 +170,21 @@ class SigNetNode { typedef std::map InputUniverseMap; ola::io::SelectServerInterface *m_ss; + ola::rdm::UID m_uid; const uint16_t m_listen_port; ola::network::Interface m_interface; ola::network::UDPSocket m_socket; + ola::SequenceNumber m_seq_num; std::auto_ptr m_descriptor; coap_context_t *m_coap_context; OutputGroupMap m_output_map; InputUniverseMap m_input_map; void DescriptorReady(); -// bool SendMessageToTargets(lo_message message, -// const SigNetTargetVector &targets); + bool SendCoapMessage(const std::string uri, + const ola::network::IPV4Address dest, + const uint8_t *payload, + const unsigned int payload_length); /*void SigNetUniverseHandler(coap_context_t *ctx, struct coap_resource_t *resource, diff --git a/plugins/signet/SigNetPort.h b/plugins/signet/SigNetPort.h index 7ebc869d86..bd049126e5 100644 --- a/plugins/signet/SigNetPort.h +++ b/plugins/signet/SigNetPort.h @@ -125,7 +125,10 @@ class SigNetOutputPort: public BasicOutputPort { */ bool WriteDMX(const DmxBuffer &buffer, uint8_t) { // TODO(Peter): Handle priority - return m_node->SendData(this->PortId(), buffer); + if (GetUniverse()) { + return m_node->SendDMX(GetUniverse()->UniverseId(), buffer); + } + return true; } private: From 438632bed18ba3de106b24f4a1eebafdab138088 Mon Sep 17 00:00:00 2001 From: Peter Newman Date: Mon, 31 Aug 2026 01:37:48 +0100 Subject: [PATCH 07/12] Improve enums for Sig-Net security mode --- plugins/signet/SigNetEnums.h | 12 +++++++++++- plugins/signet/SigNetNode.cpp | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/plugins/signet/SigNetEnums.h b/plugins/signet/SigNetEnums.h index d754651401..d9cfbec014 100644 --- a/plugins/signet/SigNetEnums.h +++ b/plugins/signet/SigNetEnums.h @@ -49,8 +49,18 @@ typedef enum { SIGNET_MFG_CODE = 2140, /**< Sig-Net-Mfg-Code */ SIGNET_SESSION_ID = 2172, /**< Sig-Net-Session-ID */ SIGNET_SEQ_NUM = 2204, /**< Sig-Net-Seq-Num */ - SIGNET_AUTH = 2236, /**< */ + SIGNET_AUTH = 2236, /**< Sig-Net-Auth */ } signet_coap_options; + +/** + * @brief A set of values representing Sig-Net-Security-Mode choices. + * @note See section 8.3 of Sig-Net for more information. + */ +typedef enum { + SIGNET_SECURITY_MODE_SHA256 = 0x00, /**< Plaintext with, HMAC-SHA256 */ + SIGNET_SECURITY_MODE_OPEN = 0x01, /**< Open, unauthenticated packets */ + SIGNET_SECURITY_MODE_OFFBOARDED = 0xFF, /**< Offboarded device */ +} signet_security_mode; /** * @} */ diff --git a/plugins/signet/SigNetNode.cpp b/plugins/signet/SigNetNode.cpp index 351dd3a408..deef494fee 100644 --- a/plugins/signet/SigNetNode.cpp +++ b/plugins/signet/SigNetNode.cpp @@ -716,7 +716,7 @@ bool SigNetNode::SendCoapMessage(const std::string uri, const ola::network::IPV4Address dest, const uint8_t *payload, const unsigned int payload_length) { - uint8_t signet_security_mode = 0; + uint8_t signet_security_mode = SIGNET_SECURITY_MODE_SHA256; UID signet_sender_id_tuid = m_uid; uint16_t signet_sender_id_endpoint = 1; uint16_t signet_mfg_code = 0; From 2be7dacee0cfd6da71713af63a6c88b684ec23aa Mon Sep 17 00:00:00 2001 From: Peter Newman Date: Mon, 31 Aug 2026 02:39:25 +0100 Subject: [PATCH 08/12] Fix a typo --- plugins/signet/SigNetEnums.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/signet/SigNetEnums.h b/plugins/signet/SigNetEnums.h index d9cfbec014..666b0a9304 100644 --- a/plugins/signet/SigNetEnums.h +++ b/plugins/signet/SigNetEnums.h @@ -23,8 +23,8 @@ /** * @addtogroup signet * @{ - * @file SigNetTIDs.h - * @brief Sig-Net TID values. + * @file SigNetEnumss.h + * @brief Sig-Net enum values. * @} */ From c990edf2b48c4a1cca48d9996d354058670792ff Mon Sep 17 00:00:00 2001 From: Peter Newman Date: Mon, 31 Aug 2026 19:46:41 +0100 Subject: [PATCH 09/12] Switch to libcoap2 and get mostly working again --- configure.ac | 4 +- plugins/signet/SigNetNode.cpp | 138 +++++++++++++++++++++++----------- plugins/signet/SigNetNode.h | 4 +- 3 files changed, 100 insertions(+), 46 deletions(-) diff --git a/configure.ac b/configure.ac index 4935e7a563..b635818846 100644 --- a/configure.ac +++ b/configure.ac @@ -768,8 +768,10 @@ AC_ARG_ENABLE( [Disable the Sig-Net plugin even if libcoap exists])]) have_libcoap="no" +#AS_IF([test "x$enable_signet" != xno], +# [PKG_CHECK_MODULES(libcoap, [libcoap-1 >= 4.1.2], [have_libcoap="yes"], [true])]) AS_IF([test "x$enable_signet" != xno], - [PKG_CHECK_MODULES(libcoap, [libcoap-1 >= 4.1.2], [have_libcoap="yes"], [true])]) + [PKG_CHECK_MODULES(libcoap, [libcoap-2 >= 4.2.1], [have_libcoap="yes"], [true])]) AS_IF([test "x$have_libcoap" = xyes], [AC_DEFINE([HAVE_LIBCOAP], [1], [define if libcoap is installed])]) diff --git a/plugins/signet/SigNetNode.cpp b/plugins/signet/SigNetNode.cpp index deef494fee..05d8907804 100644 --- a/plugins/signet/SigNetNode.cpp +++ b/plugins/signet/SigNetNode.cpp @@ -134,40 +134,52 @@ coap_context_t* SigNetNode::GetCoapContext(const IPV4Address ip, return ctx; } -void SigNetUniverseHandler(OLA_UNUSED coap_context_t *ctx, - struct coap_resource_t *resource, - OLA_UNUSED const coap_endpoint_t *local_interface, - OLA_UNUSED coap_address_t *peer, - coap_pdu_t *request, - OLA_UNUSED str *token, - OLA_UNUSED coap_pdu_t *response) { - string uri = string(reinterpret_cast(resource->uri.s), - resource->uri.length); +static void SigNetUniverseHandler(OLA_UNUSED coap_context_t *ctx, + coap_resource_t *resource, +// OLA_UNUSED const coap_endpoint_t *local_interface, + OLA_UNUSED const coap_session_t *session, +// OLA_UNUSED coap_address_t *peer, + coap_pdu_t *request, +// OLA_UNUSED str *token, + OLA_UNUSED coap_binary_t *token, + OLA_UNUSED coap_string_t *query_string, + OLA_UNUSED coap_pdu_t *response) { +// string uri = string(reinterpret_cast(resource->uri.s), +// resource->uri.length); + coap_str_const_t *uri_str = coap_resource_get_uri_path(resource); + string uri = string(reinterpret_cast(uri_str->s), + uri_str->length); OLA_INFO << "POST Called! " << uri; SigNetNode *node = NULL; - string *rname = new string("ola-sig-net-node-ptr"); +// string *rname = new string("ola-sig-net-node-ptr"); - coap_attr_t *attr; - attr = coap_find_attr(resource, - (unsigned char *)rname->c_str(), - rname->length()); +// coap_attr_t *attr; +// attr = coap_find_attr(resource, +// (unsigned char *)rname->c_str(), +// rname->length()); +// coap_str_const_t *rname_str; +// rname_str = coap_new_str_const((unsigned char *)rname->c_str(), rname->length()); +// attr = coap_find_attr(resource, rname_str); - if (attr) { - OLA_DEBUG << "Found our attr"; +// if (attr) { +// OLA_DEBUG << "Found our attr"; - string val = string(reinterpret_cast(attr->value.s), - attr->value.length); - OLA_INFO << "Attr val: " << val; +// string val = string(reinterpret_cast(attr->value.s), +// attr->value.length); +// string val = string(reinterpret_cast(attr->value->s), +// attr->value->length); +// OLA_INFO << "Attr val: " << val; - node = reinterpret_cast(attr->value.s); +// node = reinterpret_cast(attr->value.s); + node = reinterpret_cast(coap_resource_get_userdata(resource)); if (node) { OLA_DEBUG << "Still listening on " << node->ListeningPort(); } - } else { - OLA_DEBUG << "Didn't find our attr"; - } +// } else { +// OLA_DEBUG << "Didn't find our attr"; +// } coap_opt_iterator_t opt_iter; coap_opt_t *option; @@ -188,7 +200,9 @@ void SigNetUniverseHandler(OLA_UNUSED coap_context_t *ctx, while ((option = coap_option_next(&opt_iter))) { switch (opt_iter.type) { case COAP_OPTION_URI_PATH: - uri_part = string(reinterpret_cast(coap_opt_value(option)), +// uri_part = string(reinterpret_cast(coap_opt_value(option)), +// coap_opt_length(option)); + uri_part = string(reinterpret_cast(coap_opt_value(option)), coap_opt_length(option)); if (uri_part_i < SigNetNode::SIGNET_LEVEL_URI.size()) { OLA_INFO << "Got COAP URI part " << uri_part << " expected " @@ -262,7 +276,8 @@ void SigNetUniverseHandler(OLA_UNUSED coap_context_t *ctx, unsigned int len; unsigned char out[100]; - node->GenerateHMAC(resource->uri.s, resource->uri.length, + coap_str_const_t *uri_str = coap_resource_get_uri_path(resource); + node->GenerateHMAC(uri_str->s, uri_str->length, signet_security_mode, signet_sender_id_tuid, signet_sender_id_endpoint, signet_mfg_code, signet_session_id, signet_seq_num, @@ -506,6 +521,8 @@ bool SigNetNode::Init() { // m_socket.SetOnData(NewCallback(&m_incoming_udp_transport, // &IncomingUDPTransport::Receive)); + coap_startup(); + coap_set_log_level(LOG_DEBUG); m_coap_context = SigNetNode::GetCoapContext(IPV4Address::WildCard(), @@ -518,7 +535,11 @@ bool SigNetNode::Init() { // Get the socket descriptor that libcoap is using, create a // UnmanagedFileDescriptor, assign a callback and register with the // SelectServer. - int fd = m_coap_context->sockfd; +// int fd = m_coap_context->sockfd; + int fd = coap_context_get_coap_fd(m_coap_context); + if (fd != -1) { + // if coap_fd is -1, then epoll is not supported within libcoap + } #ifdef _WIN32 m_descriptor.reset(new UnmanagedSocketDescriptor(fd)); #else @@ -527,6 +548,20 @@ bool SigNetNode::Init() { m_descriptor->SetOnData(NewCallback(this, &SigNetNode::DescriptorReady)); m_ss->AddReadDescriptor(m_descriptor.get()); + IPV4Address node_beacon = IPV4Address(HostToNetwork(ola::utils::JoinUInt8(239, 254, 255, 255))); + OLA_DEBUG << "Joining via handler " << node_beacon; + if (!m_socket.JoinMulticast(m_interface.ip_address, node_beacon)) { + OLA_WARN << "Failed to join multicast group " << node_beacon; + return false; + } + + IPV4Address node_lost = IPV4Address(HostToNetwork(ola::utils::JoinUInt8(239, 254, 255, 254))); + OLA_DEBUG << "Joining via handler " << node_lost; + if (!m_socket.JoinMulticast(m_interface.ip_address, node_lost)) { + OLA_WARN << "Failed to join multicast group " << node_lost; + return false; + } + return true; } @@ -582,10 +617,14 @@ bool SigNetNode::SetHandler(const uint16_t universe, // OLA_DEBUG << "Universe URI: " << *uri1; - r = coap_resource_init((unsigned char *)uri1->c_str(), uri1->length(), - COAP_RESOURCE_FLAGS_NOTIFY_NON); + coap_str_const_t *uri; + uri = coap_new_str_const((unsigned char *)uri1->c_str(), uri1->length()); + +// r = coap_resource_init((unsigned char *)uri1->c_str(), uri1->length(), +// COAP_RESOURCE_FLAGS_NOTIFY_NON); + r = coap_resource_init(uri, COAP_RESOURCE_FLAGS_NOTIFY_NON); - string *rname = new string("ola-sig-net-node-ptr"); +/* string *rname = new string("ola-sig-net-node-ptr"); string val = string(reinterpret_cast(this), 4); OLA_INFO << "Orig Attr val: " << val; @@ -595,9 +634,11 @@ bool SigNetNode::SetHandler(const uint16_t universe, rname->length(), (unsigned char *)this, 4, - 0); + 0);*/ + + coap_resource_set_userdata(r, this); - coap_register_handler(r, COAP_REQUEST_POST, SigNetUniverseHandler); + coap_register_handler(r, COAP_REQUEST_POST, (coap_method_handler_t)SigNetUniverseHandler); coap_add_resource(m_coap_context, r); @@ -708,7 +749,8 @@ uint16_t SigNetNode::ListeningPort() const { */ void SigNetNode::DescriptorReady() { // Call into libcoap to read the received data - coap_read(m_coap_context); +// coap_read(m_coap_context); + coap_run_once(m_coap_context, COAP_RUN_NONBLOCK); } @@ -753,16 +795,10 @@ bool SigNetNode::SendCoapMessage(const std::string uri, coap_address_t dst; coap_pdu_t *pdu; - if (!(pdu = coap_pdu_init(COAP_MESSAGE_NON, COAP_REQUEST_POST, - coap_new_message_id(m_coap_context), - COAP_MAX_PDU_SIZE))) { - return false; - } - - if (LOG_DEBUG <= coap_get_log_level()) { - debug("sending CoAP request:\n"); - coap_show_pdu(pdu); - } +// if (LOG_DEBUG <= coap_get_log_level()) { +// debug("sending CoAP request:\n"); +// coap_show_pdu(pdu); +// } struct addrinfo hints; struct addrinfo *result, *rp; @@ -793,6 +829,19 @@ bool SigNetNode::SendCoapMessage(const std::string uri, dst.addr.sin.sin_port = HostToNetwork(m_listen_port); +// if (!(pdu = coap_pdu_init(COAP_MESSAGE_NON, COAP_REQUEST_POST, +// coap_new_message_id(m_coap_context,), +// COAP_MAX_PDU_SIZE))) { +// COAP_DEFAULT_MAX_PDU_RX_SIZE))) { + + coap_session_t *coap_session = coap_new_client_session(m_coap_context, NULL, &dst, COAP_PROTO_UDP); + + if (!(pdu = coap_pdu_init(COAP_MESSAGE_NON, COAP_REQUEST_POST, + coap_new_message_id(coap_session), + COAP_DEFAULT_MAX_PDU_RX_SIZE))) { + return false; + } + #define BUFSIZE 40 unsigned char _buf[BUFSIZE]; unsigned char *buf = _buf; @@ -827,9 +876,10 @@ bool SigNetNode::SendCoapMessage(const std::string uri, coap_add_data(pdu, payload_length, payload); - coap_send(m_coap_context, m_coap_context->endpoint, &dst, pdu); + coap_send(coap_session, pdu); - coap_delete_pdu(pdu); + // TODO(Peter): Fixme so this doesn't seg-fault on libcoap2 +// coap_delete_pdu(pdu); return true; } diff --git a/plugins/signet/SigNetNode.h b/plugins/signet/SigNetNode.h index f32ec7168f..1259b777d1 100644 --- a/plugins/signet/SigNetNode.h +++ b/plugins/signet/SigNetNode.h @@ -23,7 +23,8 @@ // Required for libcoap #define WITH_POSIX -#include +// #include +#include #include #include #include @@ -177,6 +178,7 @@ class SigNetNode { ola::SequenceNumber m_seq_num; std::auto_ptr m_descriptor; coap_context_t *m_coap_context; + coap_session_t *m_coap_session; OutputGroupMap m_output_map; InputUniverseMap m_input_map; From bd735019fd2ce684524b2273beacd33c01412ce7 Mon Sep 17 00:00:00 2001 From: Peter Newman Date: Tue, 1 Sep 2026 21:58:21 +0100 Subject: [PATCH 10/12] Add and use more TIDs and Enums --- plugins/signet/SigNetEnums.h | 19 +++++++++++ plugins/signet/SigNetPort.cpp | 10 +++--- plugins/signet/SigNetPort.h | 2 -- plugins/signet/SigNetTIDs.h | 62 +++++++++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 6 deletions(-) diff --git a/plugins/signet/SigNetEnums.h b/plugins/signet/SigNetEnums.h index 666b0a9304..4acba5e869 100644 --- a/plugins/signet/SigNetEnums.h +++ b/plugins/signet/SigNetEnums.h @@ -39,6 +39,12 @@ namespace signet { * @{ */ +static const uint16_t SIGNET_ROOT_ENDPOINT = 0x0000; +static const uint16_t SIGNET_BROADCAST_ENDPOINT = 0xFFFF; + +static const uint16_t SIGNET_MIN_UNIVERSE = 1; +static const uint16_t SIGNET_MAX_UNIVERSE = 63999; + /** * @brief A set of values representing Sig-Net COAP Options. * @note See section 8.3 of Sig-Net for more information. @@ -61,6 +67,19 @@ typedef enum { SIGNET_SECURITY_MODE_OPEN = 0x01, /**< Open, unauthenticated packets */ SIGNET_SECURITY_MODE_OFFBOARDED = 0xFF, /**< Offboarded device */ } signet_security_mode; + + +/** + * @brief A set of values representing Sig-Net query levels. + * @note See section 10.2.3 of Sig-Net for more information. + */ +typedef enum { + SIGNET_QUERY_HEARTBEAT = 0x00, /**< Heartbeat */ + SIGNET_QUERY_CONFIG = 0x01, /**< Config */ + SIGNET_QUERY_FULL = 0x02, /**< Full */ + SIGNET_QUERY_EXTENDED = 0x03, /**< Extended */ +} signet_query_mode; + /** * @} */ diff --git a/plugins/signet/SigNetPort.cpp b/plugins/signet/SigNetPort.cpp index b97453617a..6f1f9dc2eb 100644 --- a/plugins/signet/SigNetPort.cpp +++ b/plugins/signet/SigNetPort.cpp @@ -24,6 +24,7 @@ #include "ola/Logging.h" #include "olad/Port.h" #include "plugins/signet/SigNetDevice.h" +#include "plugins/signet/SigNetEnums.h" #include "plugins/signet/SigNetNode.h" #include "plugins/signet/SigNetPort.h" @@ -37,10 +38,11 @@ using std::vector; bool SigNetPortHelper::PreSetUniverse(Universe *old_universe, Universe *new_universe) { - if (new_universe && (new_universe->UniverseId() == 0 || - new_universe->UniverseId() > MAX_SIGNET_UNIVERSE)) { - OLA_WARN << "Universe id " << new_universe->UniverseId() << " is 0 or > " - << MAX_SIGNET_UNIVERSE; + if (new_universe && ((new_universe->UniverseId() < SIGNET_MIN_UNIVERSE) || + (new_universe->UniverseId() > SIGNET_MAX_UNIVERSE))) { + OLA_WARN << "Universe id " << new_universe->UniverseId() + << " is < " << SIGNET_MIN_UNIVERSE + << " or > " << SIGNET_MAX_UNIVERSE; return false; } (void) old_universe; diff --git a/plugins/signet/SigNetPort.h b/plugins/signet/SigNetPort.h index bd049126e5..e7ec633e38 100644 --- a/plugins/signet/SigNetPort.h +++ b/plugins/signet/SigNetPort.h @@ -35,8 +35,6 @@ class SigNetPortHelper { public: bool PreSetUniverse(Universe *old_universe, Universe *new_universe); std::string Description(Universe *universe) const; - private: - static const unsigned int MAX_SIGNET_UNIVERSE = 63999; }; diff --git a/plugins/signet/SigNetTIDs.h b/plugins/signet/SigNetTIDs.h index 597adf0f3b..7aa6c9dc3d 100644 --- a/plugins/signet/SigNetTIDs.h +++ b/plugins/signet/SigNetTIDs.h @@ -45,8 +45,70 @@ namespace signet { * @brief Sig-Net TIDs used at the root level. */ enum RootTID { + TID_POLL = 0x0001, /**< TID Poll */ + TID_POLL_REPLY = 0x0002, /**< TID Poll Reply */ + TID_SET_REPLY = 0x0003, /**< TID Set Reply */ + TID_LEVEL = 0x0101, /**< TID Level */ TID_PRIORITY = 0x0102, /**< TID Priority */ + + TID_SYNC = 0x0201, /**< TID Sync */ + TID_TIMECODE = 0x0202, /**< TID Timecode */ + TID_UNIVERSE = 0x0203, /**< TID Universe */ + TID_OSC = 0x0204, /**< TID OSC */ + + TID_RDM_COMMAND = 0x0301, /**< TID RDM Command */ + TID_RDM_RESPONSE = 0x0302, /**< TID RDM Response */ + TID_RDM_TOD_CONTROL = 0x0303, /**< TID ToD Control */ + TID_RDM_TOD_DATA = 0x0304, /**< TID RDM ToD Data */ + TID_RDM_EP_CONFIG = 0x0305, /**< TID RDM Endpoint Config */ + TID_RDM_FLOW_CONTROL = 0x0306, /**< TID RDM Flow Control */ + + TID_RT_OFFBOARD = 0x0401, /**< TID Root Offboard */ + + TID_NW_MAC_ADDRESS = 0x0501, /**< TID Network MAC Address */ + TID_NW_IPV4_MODE = 0x0502, /**< TID Network IPv4 Mode */ + TID_NW_IPV4_ADDRESS = 0x0503, /**< TID Network IPv4 Address */ + TID_NW_IPV4_NETMASK = 0x0504, /**< TID Network IPv4 Netmask */ + TID_NW_IPV4_GATEWAY = 0x0505, /**< TID Network IPv4 Gateway */ + TID_NW_IPV4_CURRENT = 0x0506, /**< TID Network IPv4 Current Details */ + + TID_NW_IPV6_MODE = 0x0581, /**< TID Network IPv6 Mode */ + TID_NW_IPV6_ADDRESS = 0x0582, /**< TID Network IPv6 Address */ + TID_NW_IPV6_PREFIX = 0x0583, /**< TID Network IPv6 Prefix */ + TID_NW_IPV6_GATEWAY = 0x0584, /**< TID Network IPv6 Gateway */ + TID_NW_IPV6_CURRENT = 0x0585, /**< TID Network IPv6 Current Details */ + + TID_RT_SUPPORTED_TIDS = 0x0601, /**< TID Root Supported TIDs */ + TID_RT_ENDPOINT_COUNT = 0x0602, /**< TID Root Endpoint Count */ + TID_RT_PROTOCOL_VERSION = 0x0603, /**< TID Root Protocol Version */ + TID_RT_FIRMWARE_VERSION = 0x0604, /**< TID Root Firmware Version */ + TID_RT_DEVICE_LABEL = 0x0605, /**< TID Root Device Label */ + TID_RT_MULT_OVERRIDE = 0x0606, /**< TID Root Multicast Override */ + TID_RT_IDENTIFY = 0x0607, /**< TID Root Identify*/ + TID_RT_STATUS = 0x0608, /**< TID Root Status */ + TID_RT_ROLE_CAPABILITY = 0x0609, /**< TID Root Role Capability */ + TID_RT_REBOOT = 0x060A, /**< TID Root Reboot */ + TID_RT_MODEL_NAME = 0x060B, /**< TID Root Model Name */ + TID_RT_SCOPE = 0x060C, /**< TID Root URI Scope */ + TID_RT_OTW_CAPABILITY = 0x060D, /**< TID Root Over-the-Wire Capability */ + + TID_EP_UNIVERSE = 0x0901, /**< TID Endpoint Universe */ + TID_EP_LABEL = 0x0902, /**< TID Endpoint Label */ + TID_EP_MULT_OVERRIDE = 0x0903, /**< TID Endpoint Multicast IPv4 Override */ + TID_EP_CAPABILITY = 0x0904, /**< TID Endpoint Port Capability */ + TID_EP_DIRECTION = 0x0905, /**< TID Endpoint Direction */ + TID_EP_INPUT_PRIORITY = 0x0906, /**< TID Endpoint Input Priority */ + TID_EP_STATUS = 0x0907, /**< TID Endpoint Status */ + TID_EP_FAILOVER = 0x0908, /**< TID Endpoint Failover */ + TID_EP_DMX_TIMING = 0x0909, /**< TID Endpoint DMX Timing */ + TID_EP_REFRESH_CAPABILITY = 0x090A, /**< TID Endpoint Refresh Capability */ + TID_EP_PROTOCOL = 0x090B, /**< TID Endpoint Protocol */ + TID_EP_IDENTIFY = 0x090C, /**< TID Endpoint Identify */ + + TID_DG_SECURITY_EVENT = 0xFF01, /**< TID Diagnostics Security Event */ + TID_DG_MESSAGE = 0xFF02, /**< TID Diagnostics Message */ + TID_DG_LEVEL_FOLDBACK = 0xFF03, /**< TID Diagnostics Level Foldback */ }; /** * @} From ac4e6565312c5963d8a3bd938b278cc1a530a8d9 Mon Sep 17 00:00:00 2001 From: Peter Newman Date: Thu, 3 Sep 2026 14:18:08 +0100 Subject: [PATCH 11/12] Fix a typo --- include/ola/rdm/RDMCommand.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ola/rdm/RDMCommand.h b/include/ola/rdm/RDMCommand.h index 1096546f9a..3649abd892 100644 --- a/include/ola/rdm/RDMCommand.h +++ b/include/ola/rdm/RDMCommand.h @@ -181,7 +181,7 @@ class RDMCommand { /** * @brief Extract a RDMCommand from raw data. - * @param data The data excluding the state code. + * @param data The data excluding the start code. * @param length The length of the data. * @returns NULL if the RDM command is invalid. */ From 445e1e8cb7bf9fbd5ebc0272d078b0531ef1a436 Mon Sep 17 00:00:00 2001 From: Peter Newman Date: Tue, 8 Sep 2026 22:57:53 +0100 Subject: [PATCH 12/12] More Sig-Net Enums --- plugins/signet/SigNetEnums.h | 52 ++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/plugins/signet/SigNetEnums.h b/plugins/signet/SigNetEnums.h index 4acba5e869..f21a1a11fe 100644 --- a/plugins/signet/SigNetEnums.h +++ b/plugins/signet/SigNetEnums.h @@ -39,12 +39,49 @@ namespace signet { * @{ */ +static const uint8_t MAX_SIGNET_STRING_LENGTH = 64; + static const uint16_t SIGNET_ROOT_ENDPOINT = 0x0000; static const uint16_t SIGNET_BROADCAST_ENDPOINT = 0xFFFF; static const uint16_t SIGNET_MIN_UNIVERSE = 1; static const uint16_t SIGNET_MAX_UNIVERSE = 63999; +static const uint32_t SIGNET_ROOT_STATUS_HARDWARE_FAULT = 0x00000001; +static const uint32_t SIGNET_ROOT_STATUS_FACTORY_DEFAULT = 0x00000002; +static const uint32_t SIGNET_ROOT_STATUS_CONFIG_LOCKED = 0x00000004; +static const uint32_t SIGNET_ROOT_STATUS_OPEN_MODE = 0x00000008; + +static const uint32_t SIGNET_ROOT_ROLE_CAPABILITY_NODE = 0x00000001; +static const uint32_t SIGNET_ROOT_ROLE_CAPABILITY_SENDER = 0x00000002; +static const uint32_t SIGNET_ROOT_ROLE_CAPABILITY_MANAGER = 0x00000004; +static const uint32_t SIGNET_ROOT_ROLE_CAPABILITY_VISUALISER = 0x00000008; +static const uint32_t SIGNET_ROOT_ROLE_CAPABILITY_RESERVED_1 = 0x00000010; +static const uint32_t SIGNET_ROOT_ROLE_CAPABILITY_RESERVED_2 = 0x00000020; +static const uint32_t SIGNET_ROOT_ROLE_CAPABILITY_ROOT_FIRMWARE_SUPPORT = 0x00000040; +static const uint32_t SIGNET_ROOT_ROLE_CAPABILITY_OPEN_MODE_SUPPORT = 0x00000080; + +static const uint32_t SIGNET_ENDPOINT_CAPABILITY_CONSUME_LEVEL = 0x00000001; +static const uint32_t SIGNET_ENDPOINT_CAPABILITY_SUPPLY_LEVEL = 0x00000002; +static const uint32_t SIGNET_ENDPOINT_CAPABILITY_CONSUME_RDM = 0x00000004; +static const uint32_t SIGNET_ENDPOINT_CAPABILITY_SUPPLY_RDM = 0x00000008; +static const uint32_t SIGNET_ENDPOINT_CAPABILITY_VIRTUAL = 0x00000010; + +static const uint8_t SIGNET_ENDPOINT_DIRECTION_DIRECTION_BITMASK = 0x03; +static const uint8_t SIGNET_ENDPOINT_DIRECTION_DISABLED = 0x00; +static const uint8_t SIGNET_ENDPOINT_DIRECTION_CONSUMER = 0x01; +static const uint8_t SIGNET_ENDPOINT_DIRECTION_SUPPLIER = 0x02; +static const uint8_t SIGNET_ENDPOINT_DIRECTION_FALLBACK = 0x03; +static const uint8_t SIGNET_ENDPOINT_DIRECTION_RDM_ENABLE = 0x04; + +static const uint32_t SIGNET_ENDPOINT_STATUS_TX_RX = 0x00000001; +static const uint32_t SIGNET_ENDPOINT_STATUS_HARDWARE_FAULT = 0x00000002; +static const uint32_t SIGNET_ENDPOINT_STATUS_CONFIG_LOCKED = 0x00000004; +static const uint32_t SIGNET_ENDPOINT_STATUS_RX_TID_LEVEL = 0x00000008; +static const uint32_t SIGNET_ENDPOINT_STATUS_RX_MULTI_TID_LEVEL = 0x00000010; +static const uint32_t SIGNET_ENDPOINT_STATUS_FALLBACK = 0x00000020; +static const uint32_t SIGNET_ENDPOINT_STATUS_FAILOVER = 0x00000040; + /** * @brief A set of values representing Sig-Net COAP Options. * @note See section 8.3 of Sig-Net for more information. @@ -80,6 +117,21 @@ typedef enum { SIGNET_QUERY_EXTENDED = 0x03, /**< Extended */ } signet_query_mode; + +/** + * @brief A set of values representing Sig-Net TOD control commands. + * @note See section 11.3.3 of Sig-Net for more information. + */ +typedef enum { + SIGNET_TOD_CONTROL_SEND = 0x00, /**< Send */ + SIGNET_TOD_CONTROL_FORCE = 0x01, /**< Force */ +} signet_tod_control; + + +typedef enum { + SIGNET_TEXT_ENCODING_UTF8_ASCII = 0x00, /**< UTF-8/ASCII */ +} signet_text_encoding; + /** * @} */