From f0b1341cdff947ff242cc4f5314e12b1c31f79ca Mon Sep 17 00:00:00 2001 From: Thomas Stoddard Date: Thu, 24 Sep 2026 23:49:23 +0000 Subject: [PATCH] Device: Write MACsec configs by network ID --- CMakeLists.txt | 1 + bindings/python/icsneopy/core/macseccfg.cpp | 1 - bindings/python/icsneopy/device/device.cpp | 3 +- core/macseccfg.cpp | 8 +- device/device.cpp | 19 ++++- examples/cpp/macsec/src/macsec.cpp | 2 +- include/icsneo/core/macseccfg.h | 2 - include/icsneo/device/device.h | 11 ++- .../icsneo/device/tree/radcomet2/radcomet2.h | 7 ++ .../icsneo/device/tree/radcomet3/radcomet3.h | 7 ++ .../device/tree/radgigastar2/radgigastar2.h | 8 ++ .../icsneo/device/tree/radmoon2/radmoon2zl.h | 7 ++ .../icsneo/device/tree/radmoon3/radmoon3.h | 8 ++ test/unit/macsectest.cpp | 84 +++++++++++++++++++ 14 files changed, 154 insertions(+), 14 deletions(-) create mode 100644 test/unit/macsectest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ed303a9..3b40ab1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -570,6 +570,7 @@ if(LIBICSNEO_BUILD_UNIT_TESTS) test/unit/a2bencoderdecodertest.cpp test/unit/mdioencoderdecodertest.cpp test/unit/livedataencoderdecodertest.cpp + test/unit/macsectest.cpp test/unit/ringbuffertest.cpp test/unit/apperrordecodertest.cpp test/unit/icsneoc2.cpp diff --git a/bindings/python/icsneopy/core/macseccfg.cpp b/bindings/python/icsneopy/core/macseccfg.cpp index 5fa32bc..9960aa4 100644 --- a/bindings/python/icsneopy/core/macseccfg.cpp +++ b/bindings/python/icsneopy/core/macseccfg.cpp @@ -154,7 +154,6 @@ void init_macsecconfig(pybind11::module_ & m) .def("set_storage", &MACsecConfig::setStorage, pybind11::call_guard()) .def("clear", &MACsecConfig::clear, pybind11::call_guard()) .def("serialize", &MACsecConfig::serialize, pybind11::call_guard()) - .def("get_bin_index", &MACsecConfig::getBinIndex, pybind11::call_guard()) .def("get_type", &MACsecConfig::getType, pybind11::call_guard()) .def("get_max_num_rule", &MACsecConfig::getMaxNumRule, pybind11::call_guard()) .def("get_max_num_secy", &MACsecConfig::getMaxNumSecY, pybind11::call_guard()) diff --git a/bindings/python/icsneopy/device/device.cpp b/bindings/python/icsneopy/device/device.cpp index 3b16546..3176784 100644 --- a/bindings/python/icsneopy/device/device.cpp +++ b/bindings/python/icsneopy/device/device.cpp @@ -34,6 +34,7 @@ void init_device(pybind11::module_& m) { .def("get_serial", &Device::getSerial) .def("get_pcb_serial", &Device::getPCBSerial, pybind11::call_guard()) .def("get_mac_addresses", &Device::getMACAddresses, pybind11::call_guard()) + .def("get_macsec_networks", &Device::getMACsecNetworks) .def("get_supported_rx_networks", &Device::getSupportedRXNetworks, pybind11::return_value_policy::reference) .def("get_supported_tx_networks", &Device::getSupportedTXNetworks, pybind11::return_value_policy::reference) .def("get_tc10_status", &Device::getTC10Status, pybind11::call_guard()) @@ -64,7 +65,7 @@ void init_device(pybind11::module_& m) { .def("set_value_live_data", &Device::setValueLiveData, pybind11::arg("message"), pybind11::call_guard()) .def("transmit", pybind11::overload_cast>(&Device::transmit), pybind11::call_guard()) .def("upload_coremini", [](Device& device, std::string& path, Disk::MemoryType memType) { std::ifstream ifs(path, std::ios::binary); return device.uploadCoremini(ifs, memType); }, pybind11::call_guard()) - .def("write_macsec_config", &Device::writeMACsecConfig, pybind11::call_guard()) + .def("write_macsec_config", &Device::writeMACsecConfig, pybind11::arg("config"), pybind11::arg("network"), pybind11::call_guard()) .def("send_eth_phy_msg", &Device::sendEthPhyMsg, pybind11::arg("message"), pybind11::arg("timeout") = std::chrono::milliseconds(50), pybind11::call_guard()) .def("get_chip_versions", &Device::getChipVersions, pybind11::arg("refreshComponents") = true, pybind11::call_guard()) .def("supports_disk_formatting", &Device::supportsDiskFormatting, pybind11::call_guard()) diff --git a/core/macseccfg.cpp b/core/macseccfg.cpp index 8defd0f..ea97fb3 100644 --- a/core/macseccfg.cpp +++ b/core/macseccfg.cpp @@ -231,16 +231,16 @@ MACsecConfig::MACsecConfig(const DeviceType& deviceType) : type(deviceType) { case icsneo::DeviceType::Enum::RADMoon3: case icsneo::DeviceType::Enum::RADEpsilon: case icsneo::DeviceType::Enum::RADGigastar2: + case icsneo::DeviceType::Enum::RADComet2: + case icsneo::DeviceType::Enum::RADComet3: maxSecY = 2; maxRule = 2; maxSa = 4; - binIndex = 0; break; default: maxSecY = 0; maxSa = 0; maxRule = 0; - binIndex = 0; ReportEvent(APIEvent::Type::MACsecNotSupported, APIEvent::Severity::Error); return; } @@ -515,10 +515,6 @@ MACsecConfig::operator bool() const { return (maxSa != 0) || (maxSecY != 0) || (maxRule != 0); } -uint16_t MACsecConfig::getBinIndex() const { - return binIndex; -} - DeviceType MACsecConfig::getType() const { return type; } diff --git a/device/device.cpp b/device/device.cpp index 949659a..c087b8d 100644 --- a/device/device.cpp +++ b/device/device.cpp @@ -3761,7 +3761,15 @@ std::optional Device::getGPTPStatus(std::chrono::milliseconds timeou return *retMsg; } -bool Device::writeMACsecConfig(const MACsecConfig& cfg) { +std::vector Device::getMACsecNetworks() const { + std::vector networks; + for(const auto& network : getMACsecNetworkMap()) { + networks.emplace_back(network.first); + } + return networks; +} + +bool Device::writeMACsecConfig(const MACsecConfig& cfg, Network::NetID network) { if(!cfg) { report(APIEvent::Type::MACsecNotSupported, APIEvent::Severity::Error); return false; @@ -3772,8 +3780,15 @@ bool Device::writeMACsecConfig(const MACsecConfig& cfg) { return false; } + const auto& networks = getMACsecNetworkMap(); + const auto binaryIndex = networks.find(network); + if(binaryIndex == networks.end()) { + report(APIEvent::Type::MACsecNotSupported, APIEvent::Severity::Error); + return false; + } + std::vector raw = cfg.serialize(); - return writeBinaryFile(raw, cfg.getBinIndex()); + return writeBinaryFile(raw, binaryIndex->second); } bool Device::enableNetworkCommunication(bool enable, uint32_t timeout) { diff --git a/examples/cpp/macsec/src/macsec.cpp b/examples/cpp/macsec/src/macsec.cpp index e30ef7d..09935b4 100644 --- a/examples/cpp/macsec/src/macsec.cpp +++ b/examples/cpp/macsec/src/macsec.cpp @@ -77,7 +77,7 @@ int main(int, char**) { cfg.setTxEnable(true); // Write config to the device - if(!device->writeMACsecConfig(cfg)) { + if(!device->writeMACsecConfig(cfg, icsneo::Network::NetID::AE_01)) { std::cout << "Failed to write MACsec config" << std::endl; std::cout << icsneo::GetLastError() << std::endl; return -1; diff --git a/include/icsneo/core/macseccfg.h b/include/icsneo/core/macseccfg.h index 47ba84e..2882836 100644 --- a/include/icsneo/core/macseccfg.h +++ b/include/icsneo/core/macseccfg.h @@ -150,7 +150,6 @@ private: uint8_t maxSecY; uint8_t maxSa; uint8_t maxRule; - uint16_t binIndex; DeviceType type; std::vector rxRule; @@ -205,7 +204,6 @@ public: void clear(); std::vector serialize() const; operator bool() const; - uint16_t getBinIndex() const; DeviceType getType() const; uint8_t getMaxNumRule() const; uint8_t getMaxNumSecY() const; diff --git a/include/icsneo/device/device.h b/include/icsneo/device/device.h index fadafef..1a41962 100644 --- a/include/icsneo/device/device.h +++ b/include/icsneo/device/device.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -897,7 +898,8 @@ public: std::optional getGPTPStatus(std::chrono::milliseconds timeout = std::chrono::milliseconds(100)); /* MACsec support */ - virtual bool writeMACsecConfig(const MACsecConfig& cfg); + std::vector getMACsecNetworks() const; + bool writeMACsecConfig(const MACsecConfig& cfg, Network::NetID network); std::shared_ptr getExtension(const std::string& name) const; @@ -917,6 +919,13 @@ public: bool iso15765SetupRxFlowControl(const Network& network, const Iso15765MessageArgs& msg); protected: + using MACsecNetworkMap = std::map; + + virtual const MACsecNetworkMap& getMACsecNetworkMap() const { + static const MACsecNetworkMap networks; + return networks; + } + bool online = false; int messagePollingCallbackID = 0; int internalHandlerCallbackID = 0; diff --git a/include/icsneo/device/tree/radcomet2/radcomet2.h b/include/icsneo/device/tree/radcomet2/radcomet2.h index 1c2220c..30966bd 100644 --- a/include/icsneo/device/tree/radcomet2/radcomet2.h +++ b/include/icsneo/device/tree/radcomet2/radcomet2.h @@ -70,6 +70,13 @@ public: } protected: + const MACsecNetworkMap& getMACsecNetworkMap() const override { + static const MACsecNetworkMap networks = { + {Network::NetID::AE_01, 0}, + }; + return networks; + } + RADComet2(neodevice_t neodevice, const driver_factory_t& makeDriver) : Device(neodevice) { initialize(makeDriver); } diff --git a/include/icsneo/device/tree/radcomet3/radcomet3.h b/include/icsneo/device/tree/radcomet3/radcomet3.h index 8468327..929678a 100644 --- a/include/icsneo/device/tree/radcomet3/radcomet3.h +++ b/include/icsneo/device/tree/radcomet3/radcomet3.h @@ -77,6 +77,13 @@ public: } protected: + const MACsecNetworkMap& getMACsecNetworkMap() const override { + static const MACsecNetworkMap networks = { + {Network::NetID::AE_01, 0}, + }; + return networks; + } + RADComet3(neodevice_t neodevice, const driver_factory_t& makeDriver) : Device(neodevice) { initialize(makeDriver); } diff --git a/include/icsneo/device/tree/radgigastar2/radgigastar2.h b/include/icsneo/device/tree/radgigastar2/radgigastar2.h index 934d836..2f9b02d 100644 --- a/include/icsneo/device/tree/radgigastar2/radgigastar2.h +++ b/include/icsneo/device/tree/radgigastar2/radgigastar2.h @@ -217,6 +217,14 @@ public: return chipVersions; } protected: + const MACsecNetworkMap& getMACsecNetworkMap() const override { + static const MACsecNetworkMap networks = { + {Network::NetID::AE_01, 0}, + {Network::NetID::AE_02, 1}, + }; + return networks; + } + RADGigastar2(neodevice_t neodevice, const driver_factory_t &makeDriver) : Device(neodevice) { initialize(makeDriver); diff --git a/include/icsneo/device/tree/radmoon2/radmoon2zl.h b/include/icsneo/device/tree/radmoon2/radmoon2zl.h index a0e35e0..88c830a 100644 --- a/include/icsneo/device/tree/radmoon2/radmoon2zl.h +++ b/include/icsneo/device/tree/radmoon2/radmoon2zl.h @@ -38,6 +38,13 @@ public: .add(); } protected: + const MACsecNetworkMap& getMACsecNetworkMap() const override { + static const MACsecNetworkMap networks = { + {Network::NetID::AE_01, 0}, + }; + return networks; + } + RADMoon2ZL(neodevice_t neodevice, const driver_factory_t& makeDriver) : RADMoon2Base(neodevice) { initialize(makeDriver); } diff --git a/include/icsneo/device/tree/radmoon3/radmoon3.h b/include/icsneo/device/tree/radmoon3/radmoon3.h index 95936fb..c4d7f88 100644 --- a/include/icsneo/device/tree/radmoon3/radmoon3.h +++ b/include/icsneo/device/tree/radmoon3/radmoon3.h @@ -52,6 +52,14 @@ public: .add(); } protected: + const MACsecNetworkMap& getMACsecNetworkMap() const override { + static const MACsecNetworkMap networks = { + {Network::NetID::AE_01, 0}, + {Network::NetID::ETHERNET_01, 1}, + }; + return networks; + } + RADMoon3(neodevice_t neodevice, const driver_factory_t& makeDriver) : Device(neodevice) { initialize(makeDriver); } diff --git a/test/unit/macsectest.cpp b/test/unit/macsectest.cpp new file mode 100644 index 0000000..9d76e12 --- /dev/null +++ b/test/unit/macsectest.cpp @@ -0,0 +1,84 @@ +#include + +#include + +#include "icsneo/communication/driver.h" +#include "icsneo/core/macseccfg.h" +#include "icsneo/disk/neomemorydiskdriver.h" +#include "icsneo/device/tree/radcomet2/radcomet2.h" +#include "icsneo/device/tree/radcomet3/radcomet3.h" +#include "icsneo/device/tree/radgigastar2/radgigastar2.h" +#include "icsneo/device/tree/radmoon2/radmoon2.h" +#include "icsneo/device/tree/radmoon2/radmoon2zl.h" +#include "icsneo/device/tree/radmoon3/radmoon3.h" + +using namespace icsneo; + +namespace { + +class MACsecTestDriver : public Driver { +public: + MACsecTestDriver(const device_eventhandler_t& handler) : Driver(handler) {} + + bool open() override { return true; } + bool isOpen() override { return true; } + bool close() override { return true; } + driver_finder_t getFinder() override { + return [](std::vector&) {}; + } +}; + +FoundDevice makeFoundDevice(const char* serial) { + FoundDevice found; + std::memcpy(found.serial, serial, sizeof(found.serial) - 1); + found.serial[sizeof(found.serial) - 1] = '\0'; + found.makeDriver = [](device_eventhandler_t handler, neodevice_t&) { + return std::make_unique(handler); + }; + return found; +} + +std::vector getMACsecNetIDs(const Device& device) { + std::vector netIDs; + for(const auto& network : device.getMACsecNetworks()) + netIDs.push_back(network.getNetID()); + return netIDs; +} + +TEST(MACsec, RADMoon2ZLOnlyMapsAutomotiveEthernet) { + RADMoon2 moon2(makeFoundDevice("RM0001")); + RADMoon2ZL moon2zl(makeFoundDevice("RN0001")); + + EXPECT_TRUE(getMACsecNetIDs(moon2).empty()); + EXPECT_EQ(getMACsecNetIDs(moon2zl), std::vector({Network::NetID::AE_01})); +} + +TEST(MACsec, RADMoon3MapsIndependentPHYs) { + RADMoon3 moon3(makeFoundDevice("R30001")); + + EXPECT_EQ( + getMACsecNetIDs(moon3), + std::vector({Network::NetID::AE_01, Network::NetID::ETHERNET_01}) + ); +} + +TEST(MACsec, RADGigastar2MapsBothMACsecPorts) { + RADGigastar2 gigastar2(makeFoundDevice("GT0001")); + + EXPECT_EQ( + getMACsecNetIDs(gigastar2), + std::vector({Network::NetID::AE_01, Network::NetID::AE_02}) + ); +} + +TEST(MACsec, RADCometsMapOneMACsecPort) { + RADComet2 comet2(makeFoundDevice("RC0300")); + RADComet3 comet3(makeFoundDevice("C30001")); + + EXPECT_EQ(getMACsecNetIDs(comet2), std::vector({Network::NetID::AE_01})); + EXPECT_EQ(getMACsecNetIDs(comet3), std::vector({Network::NetID::AE_01})); + EXPECT_TRUE(MACsecConfig(comet2.getType())); + EXPECT_TRUE(MACsecConfig(comet3.getType())); +} + +} // namespace