diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b03ebe2..b2eb1c7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -440,6 +440,7 @@ build python/linux/amd64: DOCKER_HOST: unix:///var/run/docker.sock DOCKER_DRIVER: overlay2 DOCKER_TLS_CERTDIR: "" + DOCKER_HOST: unix:///var/run/docker.sock CIBW_ENVIRONMENT: CMAKE_PREFIX_PATH=/project/libpcap/install:/project/libusb/install script: - curl -sSL https://get.docker.com/ | sh diff --git a/CMakeLists.txt b/CMakeLists.txt index a3c6c30..54f0745 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -249,6 +249,7 @@ set(SRC_FILES communication/message/tc10statusmessage.cpp communication/message/gptpstatusmessage.cpp communication/message/ethernetstatusmessage.cpp + communication/message/macsecmessage.cpp communication/packet/flexraypacket.cpp communication/packet/canpacket.cpp communication/packet/a2bpacket.cpp @@ -276,6 +277,7 @@ set(SRC_FILES communication/driver.cpp communication/livedata.cpp communication/ringbuffer.cpp + communication/crc32.cpp device/extensions/flexray/extension.cpp device/extensions/flexray/controller.cpp device/idevicesettings.cpp diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index c65f611..bee412b 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -30,6 +30,7 @@ pybind11_add_module(icsneopy icsneopy/communication/message/mdiomessage.cpp icsneopy/communication/message/gptpstatusmessage.cpp icsneopy/communication/message/ethernetstatusmessage.cpp + icsneopy/communication/message/macsecmessage.cpp icsneopy/communication/message/callback/messagecallback.cpp icsneopy/communication/message/filter/messagefilter.cpp icsneopy/device/device.cpp diff --git a/bindings/python/icsneopy/communication/message/macsecmessage.cpp b/bindings/python/icsneopy/communication/message/macsecmessage.cpp new file mode 100644 index 0000000..b2ef80d --- /dev/null +++ b/bindings/python/icsneopy/communication/message/macsecmessage.cpp @@ -0,0 +1,160 @@ +#include +#include +#include + +#include "icsneo/communication/message/macsecmessage.h" + +namespace icsneo { + +void init_macsecmessage(pybind11::module_ & m) +{ + pybind11::enum_(m, "MACsecPacketType") + .value("NoVLAN_OrMPLS", MACsecPacketType::NoVLANOrMPLS) + .value("SingleVLAN", MACsecPacketType::SingleVLAN) + .value("DualVLAN", MACsecPacketType::DualVLAN) + .value("MPLS", MACsecPacketType::MPLS) + .value("SingleVLAN_FollowByMPLS", MACsecPacketType::SingleVLANFollowedByMPLS) + .value("DualVLAN_FollowByMPLS", MACsecPacketType::DualVLANFollowedByMPLS) + .value("Unsupported", MACsecPacketType::Unsupported); + + pybind11::enum_(m, "MACsecValidateFrameType") + .value("Disabled", MACsecValidateFrameType::Disabled) + .value("Check", MACsecValidateFrameType::Check) + .value("Strict", MACsecValidateFrameType::Strict) + .value("NA", MACsecValidateFrameType::NA); + + pybind11::enum_(m, "MACsecSecTagIcvStripType") + .value("StripBoth", MACsecSecTagIcvStripType::StripBoth) + .value("StripSecTagPreserveICV", MACsecSecTagIcvStripType::StripSecTagPreserveICV) + .value("PreserveSecTagStripICV", MACsecSecTagIcvStripType::PreserveSecTagStripICV) + .value("PreserveBoth", MACsecSecTagIcvStripType::PreserveBoth); + + pybind11::enum_(m, "MACsecCipherSuiteType") + .value("AES_128", MACsecCipherSuiteType::GcmAes128) + .value("AES_256", MACsecCipherSuiteType::GcmAes256) + .value("AES_128_XPN", MACsecCipherSuiteType::GcmAes128Xpn) + .value("AES_256_XPN", MACsecCipherSuiteType::GcmAes256Xpn); + + pybind11::class_>(m, "MACsecVLANTag") + .def(pybind11::init()) + .def_readwrite("vid", &MACsecVLANTag::vid) + .def_readwrite("pri_cfi", &MACsecVLANTag::priCfi); + + pybind11::class_>(m, "MACsecMPLSOuter") + .def(pybind11::init()) + .def_readwrite("mpls_label", &MACsecMPLSOuter::mplsLabel) + .def_readwrite("exp", &MACsecMPLSOuter::exp); + + pybind11::class_>(m, "MACsecRule") + .def(pybind11::init()) + .def_readwrite("index", &MACsecRule::index) + .def_readwrite("keyMacDa", &MACsecRule::keyMacDa) + .def_readwrite("maskMacDa", &MACsecRule::maskMacDa) + .def_readwrite("keyMacSa", &MACsecRule::keyMacSa) + .def_readwrite("maskMacSa", &MACsecRule::maskMacSa) + .def_readwrite("keyEthertype", &MACsecRule::keyEthertype) + .def_readwrite("maskEthertype", &MACsecRule::maskEthertype) + .def_readwrite("keyVlanTagOuter1", &MACsecRule::keyVlanTagOuter1) + .def_readwrite("keyMplsOuter1", &MACsecRule::keyMplsOuter1) + .def_readwrite("maskVlanTagOuter1", &MACsecRule::maskVlanTagOuter1) + .def_readwrite("maskMplsOuter1", &MACsecRule::maskMplsOuter1) + .def_readwrite("keyVlanTagOuter2", &MACsecRule::keyVlanTagOuter2) + .def_readwrite("keyMplsOuter2", &MACsecRule::keyMplsOuter2) + .def_readwrite("maskVlanTagOuter2", &MACsecRule::maskVlanTagOuter2) + .def_readwrite("maskMplsOuter2", &MACsecRule::maskMplsOuter2) + .def_readwrite("keyBonusData", &MACsecRule::keyBonusData) + .def_readwrite("maskBonusData", &MACsecRule::maskBonusData) + .def_readwrite("keyTagMatchBitmap", &MACsecRule::keyTagMatchBitmap) + .def_readwrite("maskTagMatchBitmap", &MACsecRule::maskTagMatchBitmap) + .def_readwrite("keyPacketType", &MACsecRule::keyPacketType) + .def_readwrite("maskPacketType", &MACsecRule::maskPacketType) + .def_readwrite("keyInnerVlanType", &MACsecRule::keyInnerVlanType) + .def_readwrite("maskInnerVlanType", &MACsecRule::maskInnerVlanType) + .def_readwrite("keyOuterVlanType", &MACsecRule::keyOuterVlanType) + .def_readwrite("maskOuterVlanType", &MACsecRule::maskOuterVlanType) + .def_readwrite("keyNumTags", &MACsecRule::keyNumTags) + .def_readwrite("maskNumTags", &MACsecRule::maskNumTags) + .def_readwrite("keyExpress", &MACsecRule::keyExpress) + .def_readwrite("maskExpress", &MACsecRule::maskExpress) + .def_readwrite("isMpls", &MACsecRule::isMpls) + .def_readwrite("enable", &MACsecRule::enable); + + pybind11::class_>(m, "MACsecMap") + .def(pybind11::init()) + .def_readwrite("index", &MACsecMap::index) + .def_readwrite("secTagSci", &MACsecMap::secTagSci) + .def_readwrite("secYIndex", &MACsecMap::secYIndex) + .def_readwrite("isControlPacket", &MACsecMap::isControlPacket) + .def_readwrite("scIndex", &MACsecMap::scIndex) + .def_readwrite("auxiliaryPlcy", &MACsecMap::auxiliaryPlcy) + .def_readwrite("ruleId", &MACsecMap::ruleId) + .def_readwrite("enable", &MACsecMap::enable); + + pybind11::class_>(m, "MACsecSecY") + .def(pybind11::init()) + .def_readwrite("index", &MACsecSecY::index) + .def_readwrite("controlledPortEnabled", &MACsecSecY::controlledPortEnabled) + .def_readwrite("frameValidationType", &MACsecSecY::frameValidationType) + .def_readwrite("secTagIcvStripType", &MACsecSecY::secTagIcvStripType) + .def_readwrite("cipher", &MACsecSecY::cipher) + .def_readwrite("confidentialOffset", &MACsecSecY::confidentialOffset) + .def_readwrite("icvIncludesDaSa", &MACsecSecY::icvIncludesDaSa) + .def_readwrite("replayProtect", &MACsecSecY::replayProtect) + .def_readwrite("replayWindow", &MACsecSecY::replayWindow) + .def_readwrite("protectFrames", &MACsecSecY::protectFrames) + .def_readwrite("secTagOffset", &MACsecSecY::secTagOffset) + .def_readwrite("secTagTci", &MACsecSecY::secTagTci) + .def_readwrite("mtu", &MACsecSecY::mtu) + .def_readwrite("enable", &MACsecSecY::enable); + + pybind11::class_>(m, "MACsecSc") + .def(pybind11::init()) + .def_readwrite("index", &MACsecSc::index) + .def_readwrite("secYIndex", &MACsecSc::secYIndex) + .def_readwrite("sci", &MACsecSc::sci) + .def_readwrite("saIndex0", &MACsecSc::saIndex0) + .def_readwrite("saIndex1", &MACsecSc::saIndex1) + .def_readwrite("saIndex0InUse", &MACsecSc::saIndex0InUse) + .def_readwrite("saIndex1InUse", &MACsecSc::saIndex1InUse) + .def_readwrite("enableAutoRekey", &MACsecSc::enableAutoRekey) + .def_readwrite("isActiveSa1", &MACsecSc::isActiveSa1) + .def_readwrite("enable", &MACsecSc::enable); + + pybind11::class_>(m, "MACsecSa") + .def(pybind11::init()) + .def_readwrite("index", &MACsecSa::index) + .def_readwrite("sak", &MACsecSa::sak) + .def_readwrite("hashKey", &MACsecSa::hashKey) + .def_readwrite("salt", &MACsecSa::salt) + .def_readwrite("ssci", &MACsecSa::ssci) + .def_readwrite("an", &MACsecSa::an) + .def_readwrite("nextPn", &MACsecSa::nextPn) + .def_readwrite("enable", &MACsecSa::enable); + + pybind11::class_>(m, "MACSecFlags") + .def(pybind11::init()) + .def_readwrite("en", &MACSecFlags::en); + + pybind11::class_>(m, "MACsecConfig") + .def(pybind11::init()) + .def_readwrite("flags", &MACsecConfig::flags) + .def_readwrite("rule", &MACsecConfig::rule) + .def_readwrite("map", &MACsecConfig::map) + .def_readwrite("secy", &MACsecConfig::secy) + .def_readwrite("sc", &MACsecConfig::sc) + .def_readwrite("sa", &MACsecConfig::sa); + + pybind11::class_>(m, "MACSecGlobalFlags") + .def(pybind11::init()) + .def_readwrite("en", &MACSecGlobalFlags::en) + .def_readwrite("nvm", &MACSecGlobalFlags::nvm); + + pybind11::class_>(m, "MACsecMessage") + .def(pybind11::init()) + .def_readwrite("flags", &MACsecMessage::flags) + .def_readwrite("rx", &MACsecMessage::rx) + .def_readwrite("tx", &MACsecMessage::tx); +} + +} // namespace icsneo + diff --git a/bindings/python/icsneopy/device/device.cpp b/bindings/python/icsneopy/device/device.cpp index 08ab186..ab4b8e8 100644 --- a/bindings/python/icsneopy/device/device.cpp +++ b/bindings/python/icsneopy/device/device.cpp @@ -40,6 +40,7 @@ void init_device(pybind11::module_& m) { .def("request_tc10_sleep", &Device::requestTC10Sleep, pybind11::call_guard()) .def("get_tc10_status", &Device::getTC10Status, pybind11::call_guard()) .def("get_gptp_status", &Device::getGPTPStatus, pybind11::arg("timeout") = std::chrono::milliseconds(100), pybind11::call_guard()) + .def("write_macsec_config", &Device::writeMACsecConfig) .def("__repr__", &Device::describe); } diff --git a/bindings/python/icsneopy/icsneocpp.cpp b/bindings/python/icsneopy/icsneocpp.cpp index bfc72d5..2260f35 100644 --- a/bindings/python/icsneopy/icsneocpp.cpp +++ b/bindings/python/icsneopy/icsneocpp.cpp @@ -20,6 +20,7 @@ void init_tc10statusmessage(pybind11::module_&); void init_gptpstatusmessage(pybind11::module_&); void init_mdiomessage(pybind11::module_&); void init_ethernetstatusmessage(pybind11::module_&); +void init_macsecmessage(pybind11::module_&); void init_device(pybind11::module_&); void init_messagefilter(pybind11::module_&); void init_messagecallback(pybind11::module_&); @@ -45,6 +46,7 @@ PYBIND11_MODULE(icsneopy, m) { init_gptpstatusmessage(m); init_mdiomessage(m); init_ethernetstatusmessage(m); + init_macsecmessage(m); init_messagefilter(m); init_messagecallback(m); init_device(m); diff --git a/communication/crc32.cpp b/communication/crc32.cpp new file mode 100644 index 0000000..0c50e2e --- /dev/null +++ b/communication/crc32.cpp @@ -0,0 +1,69 @@ +/* + * crc32.c + * + * Created on: Jun 22, 2020 + * Author: BJones + */ +#include "icsneo/communication/crc32.h" + +static const unsigned long crc32_table[256] = { 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, + 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, + 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, + 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, + 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, + 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, + 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, + 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, + 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, + 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, + 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, + 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, + 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, + 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, + 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, + 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, + 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, + 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, + 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, + 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D }; + +uint32_t crc32(uint32_t crc, const unsigned char* buf, uint32_t len) +{ + unsigned char octet; + const unsigned char* p = buf; + + crc = ~crc; + while (len--) + { + octet = *p++; /* Cast to unsigned octet. */ + crc = (crc >> 8) ^ crc32_table[(crc & 0xff) ^ octet]; + } + return ~crc; +} + +static unsigned char rev_crc32_table[256]; + +static void revgen(void) +{ + unsigned char k; + + for (k = 0; k < 256; k++) + rev_crc32_table[crc32_table[k] >> 24] = k; +} + +uint32_t revcrc32(uint32_t crc, const unsigned char* buf, uint32_t len) +{ + unsigned char k; + revgen(); + crc = ~crc; + while (len--) + { + k = rev_crc32_table[crc >> 24]; + crc = ((crc ^ crc32_table[k]) << 8) | (k ^ buf[len]); + } + return ~crc; +} diff --git a/communication/message/macsecmessage.cpp b/communication/message/macsecmessage.cpp new file mode 100644 index 0000000..67e697f --- /dev/null +++ b/communication/message/macsecmessage.cpp @@ -0,0 +1,531 @@ +#include "icsneo/communication/message/macsecmessage.h" + +#include "icsneo/communication/crc32.h" +#include +#include +#include +#include + +namespace icsneo +{ + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4201) // nameless struct/union +#endif + +#pragma pack(push, 1) +/* MACsec Rule */ +/** + * @brief Structure of Vlan tag + * + */ +typedef struct +{ + uint16_t vid; /*!< 12 bits */ + uint8_t priCfi; /*!< PRI - 3 bits, CFI - 1bit */ +} MACSEC_VLANTAG_t; +/** + * @brief Structure of MPLS + * + */ +typedef struct +{ + uint32_t mplsLabel; /*!< 20 bits */ + uint8_t exp; /*!< 3 bits */ +} MACSEC_MPLS_OUTER_t; +/** + * @brief Define Encoded Packet Type from the parser + * + */ +static constexpr int MACSEC_SETTINGS_RULE_SIZE = 88; +typedef union _MACSecRule +{ + struct + { + uint8_t index; + uint8_t keyMacDa[6]; /*!< MAC DA field extracted from the packet */ + uint8_t maskMacDa[6]; /*!< Set bits to 1 to mask/exclude corresponding flowid_tcam_data bit from compare */ + uint8_t keyMacSa[6]; /*!< MAC SA field extracted from the packet */ + uint8_t maskMacSa[6]; /*!< Set bits to 1 to mask/exclude corresponding flowid_tcam_data bit from compare */ + uint16_t keyEthertype; /*!< First E-Type found in the packet that doesn't match one of the preconfigured custom tag. */ + uint16_t maskEthertype; /*!< Set bits to 1 to mask/exclude corresponding flowid_tcam_data bit from compare */ + MACSEC_VLANTAG_t keyVlanTagOuter1; /*!< outermost/1st VLAN ID {8'd0, VLAN_ID[11:0]}, or 20-bit MPLS label. */ + MACSEC_MPLS_OUTER_t keyMplsOuter1; + MACSEC_VLANTAG_t maskVlanTagOuter1; /*!< Set bits to 1 to mask/exclude corresponding flowid_tcam_data bit from compare */ + MACSEC_MPLS_OUTER_t maskMplsOuter1; + MACSEC_VLANTAG_t keyVlanTagOuter2; /*!< 2nd outermost VLAN ID {8'd0, VLAN_ID[11:0]}, or 20-bit MPLS label. */ + MACSEC_MPLS_OUTER_t keyMplsOuter2; + MACSEC_VLANTAG_t maskVlanTagOuter2; /*!< Set bits to 1 to mask/exclude corresponding flowid_tcam_data bit from compare */ + MACSEC_MPLS_OUTER_t maskMplsOuter2; + uint16_t keyBonusData; /*!< 2 bytes of additional bonus data extracted from one of the custom tags. */ + uint16_t maskBonusData; /*!< Set bits to 1 to mask/exclude corresponding flowid_tcam_data bit from compare */ + uint8_t + keyTagMatchBitmap; /*!< 8 bits total. Maps 1 to 1 bitwise with the set of custom tags. (set bit[N]=1 if check Nth custom tag) */ + uint8_t maskTagMatchBitmap; /*!< Set bits to 1 to mask/exclude corresponding flowid_tcam_data bit from compare */ + uint8_t keyPacketType; /*!< Encoded Packet Type, see MACSEC_PACKET_TYPE */ + uint8_t maskPacketType; /*!< Set bits to 1 to mask/exclude corresponding flowid_tcam_data bit from compare */ + uint16_t + keyInnerVlanType; /*!< 3 bits total. Encoded value indicating which VLAN TPID value matched for the second outermost VLAN Tag. */ + uint16_t maskInnerVlanType; /*!< Set bits to 1 to mask/exclude corresponding flowid_tcam_data bit from compare */ + uint16_t keyOuterVlanType; /*!< 3 bits total. Encoded value indicating which VLAN TPID value matched for the outermost VLAN Tag. */ + uint16_t maskOuterVlanType; /*!< Set bits to 1 to mask/exclude corresponding flowid_tcam_data bit from compare */ + uint8_t + keyNumTags; /*!< 7 bits total. Number of VLAN/custom tags or MPLS lables detected. Ingress: before SecTag; Egress: total detected. Exclude MCS header tags. i.e. Bit 2: 2 tags/labels before SecTAG...Bit 6: 6 or more tags/labels before SecTAG. */ + uint8_t maskNumTags; /*!< Set bits to 1 to mask/exclude corresponding flowid_tcam_data bit from compare */ + uint8_t keyExpress; /*!< 1 bits. Express packet. */ + uint8_t maskExpress; /*!< Set bits to 1 to mask/exclude corresponding flowid_tcam_data bit from compare */ + uint8_t isMpls; + uint8_t rsvd[5]; + uint8_t enable; + }; + uint8_t byte[MACSEC_SETTINGS_RULE_SIZE]; +} MACSecRule_t; +/* MACsec Map */ +static constexpr int MACSEC_SETTINGS_MAP_SIZE = 20; +typedef union _MACSecMap +{ + struct + { + uint8_t index; + uint64_t secTagSci; /*!< Identifies the SecTAG SCI for this Flow. */ + uint8_t secYIndex; /*!< index for entry in Egress secY Policy */ + uint8_t isControlPacket; /*!< Identifies all packets matching this index lookup as control packets. */ + uint8_t scIndex; /*!< Identifies the SC for this Flow. */ + uint8_t auxiliaryPlcy; /*!< Auxiliary policy bits. */ + uint8_t ruleId; /*!< Identifies the Rule for this Flow. */ + uint8_t rsvd[5]; + uint8_t enable; + }; + uint8_t byte[MACSEC_SETTINGS_MAP_SIZE]; +} MACSecMap_t; +/* MACsec SecY */ +/** + * @brief Define the permit police for frames as defined in 802.1ae + * + */ +static constexpr int MACSEC_SETTINGS_SECY_SIZE = 24; +typedef union _MACSecSecY +{ + struct + { + uint8_t index; /*!< Identifies the SecY for this Flow. */ + uint8_t controlledPortEnabled; /*!< Enable (or disable) operation of the Controlled port associated with this SecY */ + uint8_t frameValidationType; /*!< see MACSEC_VALIDATEFRAME */ + uint8_t secTagIcvStripType; /*!< see MACSEC_STRIP_SECTAG_ICV */ + uint8_t cipher; /*!< Define the cipher suite to use for this SecY see MACSEC_CIPHER_SUITE */ + uint8_t confidentialOffset; /*!< Define the number of bytes that are unencrypted following the SecTag. */ + uint8_t icvIncludesDaSa; /*!< When set, the outer DA/SA bytes are included in the authentication GHASH calculation */ + uint8_t replayProtect; /*!< Enables Anti-Replay protection */ + uint32_t replayWindow; /*!< Unsigned value indicating the size of the anti-replay window. */ + uint8_t + protectFrames; /*!< 0 = do not encrypt or authenticate this packet; 1 = always Authenticate frame and if SecTag.TCI.E = 1 encrypt the packet as well. */ + uint8_t + secTagOffset; /*!< Define the offset in bytes from either the start of the packet or a matching Etype depending on SecTag_Insertion_Mode. */ + uint8_t secTagTci; /*!< Tag Control Information excluding the AN field which originates from the SA Policy table */ + uint16_t mtu; /*!< Specifies the outgoing MTU for this SecY */ + uint8_t rsvd[6]; + uint8_t enable; + }; + uint8_t byte[MACSEC_SETTINGS_SECY_SIZE]; +} MACSecSecY_t; +/* MACsec SC */ +static constexpr int MACSEC_SETTINGS_SC_SIZE = 24; +typedef union _MACSecSc +{ + struct + { + uint8_t index; /*!< SC index. */ + uint8_t secYIndex; /*!< SecY associated with this packet. */ + uint64_t sci; /*!< The Secure Channel Identifier. */ + uint8_t saIndex0; /*!< Define the 1st SA to use */ + uint8_t saIndex1; /*!< Define the 2nd SA to use */ + uint8_t saIndex0InUse; /*!< Specifies whether 1st SA is in use or not. */ + uint8_t saIndex1InUse; /*!< Specifies whether 2nd SA is in use or not. */ + uint8_t enableAutoRekey; /*!< If enabled, then once the pn_threshold is reached, auto rekey will happen. */ + uint8_t + isActiveSa1; /*!< If set, then sa_index1 is the currently active SA index. If cleared, the sa_index0 is the currently active SA index). */ + uint8_t rsvd[7]; + uint8_t enable; + }; + uint8_t byte[MACSEC_SETTINGS_SC_SIZE]; +} MACSecSc_t; +/* MACsec SA */ +static constexpr int MACSEC_SETTINGS_SA_SIZE = 80; +typedef union _MACSecSa +{ + struct + { + uint8_t index; /*!< SA index */ + uint8_t + sak[32]; /*!< 256b SAK: Define the encryption key to be used to encrypte this packet. The lower 128 bits are used for 128-bit ciphers. */ + uint8_t hashKey[16]; /*!< 128b Hash Key: Key used for authentication. */ + uint8_t salt[12]; /*!< 96b Salt value: Salt value used in XPN ciphers. */ + uint32_t ssci; /*!< 32b SSCI value: Short Secure Channel Identifier, used in XPN ciphers. */ + uint8_t an; /*!< 2b SecTag Association Number (AN) */ + uint64_t nextPn; /*!< 64b next_pn value: Next packet number to insert into outgoing packet on a particular SA. */ + uint8_t rsvd[5]; + uint8_t enable; + }; + uint8_t byte[MACSEC_SETTINGS_SA_SIZE]; +} MACSecSa_t; +/* MACsec Flags */ +static constexpr int MACSEC_SETTINGS_FLAGS_SIZE = 4; +typedef union _MACSecFlags +{ + struct + { + uint32_t en : 1; // '1' = enable; '0' = disable + uint32_t reserved : 31; + }; + uint32_t flags_32b; +} MACSecFlags_t; +/* MACSec Settings for 1 port/phy */ +typedef struct MACSEC_CONFIG_t +{ + MACSecFlags_t flags; + MACSecRule_t rule[MACsecConfig::NumRules]; + MACSecMap_t map[MACsecConfig::NumMaps]; + MACSecSecY_t secy[MACsecConfig::NumSecY]; + MACSecSc_t sc[MACsecConfig::NumSc]; + MACSecSa_t sa[MACsecConfig::NumSa]; +} MACSEC_CONFIG; +typedef union _MACSecGlobalFlags +{ + struct + { + uint32_t en : 1; // '1' = enable; '0' = disable + uint32_t nvm : 1; // store macsec config in non-volatile memory + uint32_t reserved : 30; + }; + uint32_t flags_32b; +} MACSecGlobalFlags_t; +#define MACSEC_SETTINGS_SIZE (2040) // leave space for expansion and keep nicely aligned for flashing +typedef union _MACSEC_SETTINGS +{ + struct + { + MACSecGlobalFlags_t flags; + MACSEC_CONFIG rx; + MACSEC_CONFIG tx; + }; + uint8_t byte[MACSEC_SETTINGS_SIZE]; +} MACSEC_SETTINGS; + +#define MACSEC_SETTINGS_VERSION 1 +struct MACSEC_SETTINGS_W_HDR +{ + uint16_t version; + uint16_t len; + uint32_t crc32; + MACSEC_SETTINGS macsec; +}; + +#pragma pack(pop) + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +std::shared_ptr MACsecMessage::DecodeToMessage(const std::vector& bytestream, const device_eventhandler_t& report) +{ + if (bytestream.empty() || (bytestream.size() < sizeof(MACSEC_SETTINGS_W_HDR))) + { + report(APIEvent::Type::RequiredParameterNull, APIEvent::Severity::Error); + return nullptr; + } + + MACSEC_SETTINGS_W_HDR* macsecArgs = (MACSEC_SETTINGS_W_HDR*)bytestream.data(); + if (macsecArgs->version != MACSEC_SETTINGS_VERSION) + { + report(APIEvent::Type::SettingsVersionError, APIEvent::Severity::Error); + return nullptr; + } + if (macsecArgs->len != sizeof(MACSEC_SETTINGS)) + { + report(APIEvent::Type::SettingsLengthError, APIEvent::Severity::Error); + return nullptr; + } + const auto crcCalculted = crc32(0, (uint8_t*)&macsecArgs->macsec, macsecArgs->len); + if (macsecArgs->crc32 != crcCalculted) + { + report(APIEvent::Type::SettingsChecksumError, APIEvent::Severity::Error); + return nullptr; + } + + auto msg = std::make_shared(); + + const auto& copyConfig = [](const MACSEC_CONFIG& source, MACsecConfig& dest) + { + dest.flags.en = source.flags.en; + for (int index = 0; index < MACsecConfig::NumRules; index++) + { + if(!source.rule[index].enable) continue; +#undef __COPY_ITEM +#define __COPY_ITEM(___name__) dest.rule[index].___name__ = (decltype(dest.rule[index].___name__))source.rule[index].___name__ +#undef __COPY_ARR +#define __COPY_ARR(___name__) (void)memcpy(dest.rule[index].___name__.data(), source.rule[index].___name__, dest.rule[index].___name__.size()) + __COPY_ITEM(index); + __COPY_ARR(keyMacDa); + __COPY_ARR(maskMacDa); + __COPY_ARR(keyMacSa); + __COPY_ARR(maskMacSa); + __COPY_ITEM(keyVlanTagOuter1.vid); + __COPY_ITEM(keyVlanTagOuter1.priCfi); + __COPY_ITEM(keyMplsOuter1.mplsLabel); + __COPY_ITEM(keyMplsOuter1.exp); + __COPY_ITEM(maskVlanTagOuter1.vid); + __COPY_ITEM(maskVlanTagOuter1.priCfi); + __COPY_ITEM(maskMplsOuter1.mplsLabel); + __COPY_ITEM(maskMplsOuter1.exp); + __COPY_ITEM(keyVlanTagOuter2.vid); + __COPY_ITEM(keyVlanTagOuter2.priCfi); + __COPY_ITEM(keyMplsOuter2.mplsLabel); + __COPY_ITEM(keyMplsOuter2.exp); + __COPY_ITEM(maskVlanTagOuter2.vid); + __COPY_ITEM(maskVlanTagOuter2.priCfi); + __COPY_ITEM(maskMplsOuter2.mplsLabel); + __COPY_ITEM(maskMplsOuter2.exp); + __COPY_ITEM(keyEthertype); + __COPY_ITEM(maskEthertype); + __COPY_ITEM(keyBonusData); + __COPY_ITEM(maskBonusData); + __COPY_ITEM(keyTagMatchBitmap); + __COPY_ITEM(maskTagMatchBitmap); + __COPY_ITEM(keyPacketType); + __COPY_ITEM(maskPacketType); + __COPY_ITEM(keyInnerVlanType); + __COPY_ITEM(maskInnerVlanType); + __COPY_ITEM(keyOuterVlanType); + __COPY_ITEM(maskOuterVlanType); + __COPY_ITEM(keyNumTags); + __COPY_ITEM(maskNumTags); + __COPY_ITEM(keyExpress); + __COPY_ITEM(maskExpress); + __COPY_ITEM(isMpls); + __COPY_ITEM(enable); + } + + for (int index = 0; index < MACsecConfig::NumMaps; index++) + { + if(!source.map[index].enable) continue; +#undef __COPY_ITEM +#define __COPY_ITEM(___name__) dest.map[index].___name__ = source.map[index].___name__ + __COPY_ITEM(index); + __COPY_ITEM(secTagSci); + __COPY_ITEM(secYIndex); + __COPY_ITEM(isControlPacket); + __COPY_ITEM(scIndex); + __COPY_ITEM(auxiliaryPlcy); + __COPY_ITEM(ruleId); + __COPY_ITEM(enable); + } + + for (int index = 0; index < MACsecConfig::NumSecY; index++) + { + if(!source.secy[index].enable) continue; +#undef __COPY_ITEM +#define __COPY_ITEM(___name__) dest.secy[index].___name__ = (decltype(dest.secy[index].___name__))source.secy[index].___name__ + __COPY_ITEM(index); + __COPY_ITEM(controlledPortEnabled); + __COPY_ITEM(frameValidationType); + __COPY_ITEM(secTagIcvStripType); + __COPY_ITEM(cipher); + __COPY_ITEM(confidentialOffset); + __COPY_ITEM(icvIncludesDaSa); + __COPY_ITEM(replayProtect); + __COPY_ITEM(replayWindow); + __COPY_ITEM(protectFrames); + __COPY_ITEM(secTagOffset); + __COPY_ITEM(secTagTci); + __COPY_ITEM(mtu); + __COPY_ITEM(enable); + } + + for (int index = 0; index < MACsecConfig::NumSc; index++) + { + if(!source.sc[index].enable) continue; +#undef __COPY_ITEM +#define __COPY_ITEM(___name__) dest.sc[index].___name__ = source.sc[index].___name__ + __COPY_ITEM(index); + __COPY_ITEM(secYIndex); + __COPY_ITEM(sci); + __COPY_ITEM(saIndex0); + __COPY_ITEM(saIndex1); + __COPY_ITEM(saIndex0InUse); + __COPY_ITEM(saIndex1InUse); + __COPY_ITEM(enableAutoRekey); + __COPY_ITEM(isActiveSa1); + __COPY_ITEM(enable); + } + + for (int index = 0; index < MACsecConfig::NumSa; index++) + { + if(!source.sa[index].enable) continue; +#undef __COPY_ITEM +#define __COPY_ITEM(___name__) dest.sa[index].___name__ = source.sa[index].___name__ +#undef __COPY_ARR +#define __COPY_ARR(___name__) (void)memcpy(dest.sa[index].___name__.data(), source.sa[index].___name__, dest.sa[index].___name__.size()) + __COPY_ITEM(index); + __COPY_ARR(sak); + __COPY_ARR(hashKey); + __COPY_ARR(salt); + __COPY_ITEM(ssci); + __COPY_ITEM(an); + __COPY_ITEM(nextPn); + __COPY_ITEM(enable); + } + }; + + msg->flags.en = macsecArgs->macsec.flags.en; + copyConfig(macsecArgs->macsec.rx, msg->rx); + copyConfig(macsecArgs->macsec.tx, msg->tx); + + return msg; +} + +bool MACsecMessage::EncodeFromMessage(std::vector& bytestream, const device_eventhandler_t& report) const +{ + MACSEC_SETTINGS_W_HDR* macsecArgs; + + bytestream.resize(sizeof(MACSEC_SETTINGS_W_HDR), 0); + macsecArgs = (MACSEC_SETTINGS_W_HDR*)bytestream.data(); + + macsecArgs->version = MACSEC_SETTINGS_VERSION; + macsecArgs->len = sizeof(MACSEC_SETTINGS_W_HDR); + + const auto& copyConfig = [](const MACsecConfig& source, MACSEC_CONFIG& dest) + { + dest.flags.en = (uint8_t)source.flags.en; + for (int index = 0; index < MACsecConfig::NumRules; index++) + { + if(!source.rule[index].enable) continue; +#undef __COPY_ITEM +#define __COPY_ITEM(___name__) dest.rule[index].___name__ = (decltype(dest.rule[index].___name__))source.rule[index].___name__ +#undef __COPY_ARR +#define __COPY_ARR(___name__) (void)memcpy(dest.rule[index].___name__, source.rule[index].___name__.data(), source.rule[index].___name__.size()) + __COPY_ITEM(index); + __COPY_ARR(keyMacDa); + __COPY_ARR(maskMacDa); + __COPY_ARR(keyMacSa); + __COPY_ARR(maskMacSa); + __COPY_ITEM(keyVlanTagOuter1.vid); + __COPY_ITEM(keyVlanTagOuter1.priCfi); + __COPY_ITEM(keyMplsOuter1.mplsLabel); + __COPY_ITEM(keyMplsOuter1.exp); + __COPY_ITEM(maskVlanTagOuter1.vid); + __COPY_ITEM(maskVlanTagOuter1.priCfi); + __COPY_ITEM(maskMplsOuter1.mplsLabel); + __COPY_ITEM(maskMplsOuter1.exp); + __COPY_ITEM(keyVlanTagOuter2.vid); + __COPY_ITEM(keyVlanTagOuter2.priCfi); + __COPY_ITEM(keyMplsOuter2.mplsLabel); + __COPY_ITEM(keyMplsOuter2.exp); + __COPY_ITEM(maskVlanTagOuter2.vid); + __COPY_ITEM(maskVlanTagOuter2.priCfi); + __COPY_ITEM(maskMplsOuter2.mplsLabel); + __COPY_ITEM(maskMplsOuter2.exp); + __COPY_ITEM(keyEthertype); + __COPY_ITEM(maskEthertype); + __COPY_ITEM(keyBonusData); + __COPY_ITEM(maskBonusData); + __COPY_ITEM(keyTagMatchBitmap); + __COPY_ITEM(maskTagMatchBitmap); + __COPY_ITEM(keyPacketType); + __COPY_ITEM(maskPacketType); + __COPY_ITEM(keyInnerVlanType); + __COPY_ITEM(maskInnerVlanType); + __COPY_ITEM(keyOuterVlanType); + __COPY_ITEM(maskOuterVlanType); + __COPY_ITEM(keyNumTags); + __COPY_ITEM(maskNumTags); + __COPY_ITEM(keyExpress); + __COPY_ITEM(maskExpress); + __COPY_ITEM(isMpls); + __COPY_ITEM(enable); + } + + for (int index = 0; index < MACsecConfig::NumMaps; index++) + { + if(!source.map[index].enable) continue; +#undef __COPY_ITEM +#define __COPY_ITEM(___name__) dest.map[index].___name__ = source.map[index].___name__ + __COPY_ITEM(index); + __COPY_ITEM(secTagSci); + __COPY_ITEM(secYIndex); + __COPY_ITEM(isControlPacket); + __COPY_ITEM(scIndex); + __COPY_ITEM(auxiliaryPlcy); + __COPY_ITEM(ruleId); + __COPY_ITEM(enable); + } + + for (int index = 0; index < MACsecConfig::NumSecY; index++) + { + if(!source.secy[index].enable) continue; +#undef __COPY_ITEM +#define __COPY_ITEM(___name__) dest.secy[index].___name__ = (decltype(dest.secy[index].___name__))source.secy[index].___name__ + __COPY_ITEM(index); + __COPY_ITEM(controlledPortEnabled); + __COPY_ITEM(frameValidationType); + __COPY_ITEM(secTagIcvStripType); + __COPY_ITEM(cipher); + __COPY_ITEM(confidentialOffset); + __COPY_ITEM(icvIncludesDaSa); + __COPY_ITEM(replayProtect); + __COPY_ITEM(replayWindow); + __COPY_ITEM(protectFrames); + __COPY_ITEM(secTagOffset); + __COPY_ITEM(secTagTci); + __COPY_ITEM(mtu); + __COPY_ITEM(enable); + } + + for (int index = 0; index < MACsecConfig::NumSc; index++) + { + if(!source.sc[index].enable) continue; +#undef __COPY_ITEM +#define __COPY_ITEM(___name__) dest.sc[index].___name__ = source.sc[index].___name__ + __COPY_ITEM(index); + __COPY_ITEM(secYIndex); + __COPY_ITEM(sci); + __COPY_ITEM(saIndex0); + __COPY_ITEM(saIndex1); + __COPY_ITEM(saIndex0InUse); + __COPY_ITEM(saIndex1InUse); + __COPY_ITEM(enableAutoRekey); + __COPY_ITEM(isActiveSa1); + __COPY_ITEM(enable); + } + + for (int index = 0; index < MACsecConfig::NumSa; index++) + { + if(!source.sa[index].enable) continue; +#undef __COPY_ITEM +#define __COPY_ITEM(___name__) dest.sa[index].___name__ = source.sa[index].___name__ +#undef __COPY_ARR +#define __COPY_ARR(___name__) (void)memcpy(dest.sa[index].___name__, source.sa[index].___name__.data(), source.sa[index].___name__.size()) + __COPY_ITEM(index); + __COPY_ARR(sak); + __COPY_ARR(hashKey); + __COPY_ARR(salt); + __COPY_ITEM(ssci); + __COPY_ITEM(an); + __COPY_ITEM(nextPn); + __COPY_ITEM(enable); + } + }; + macsecArgs->macsec.flags.en = this->flags.en; + macsecArgs->macsec.flags.nvm = this->flags.nvm; + + copyConfig(this->rx, macsecArgs->macsec.rx); + copyConfig(this->tx, macsecArgs->macsec.tx); + + macsecArgs->crc32 = crc32(0, (uint8_t*)&macsecArgs->macsec, sizeof(MACSEC_SETTINGS)); + + (void)report; + + return true; +} + +} // namespace icsneo diff --git a/device/device.cpp b/device/device.cpp index 351044d..a60d83f 100644 --- a/device/device.cpp +++ b/device/device.cpp @@ -2054,6 +2054,40 @@ bool Device::readBinaryFile(std::ostream& stream, uint16_t binaryIndex) { return true; } +bool Device::writeBinaryFile(const std::vector& in, uint16_t binaryIndex) +{ + auto timeout = std::chrono::milliseconds(100); + + auto size = in.size(); + + std::vector arguments(sizeof(ExtendedDataMessage::ExtendedDataHeader) + ExtendedDataMessage::MaxExtendedDataBufferSize); + ExtendedDataMessage::ExtendedDataHeader& parameters = *reinterpret_cast(arguments.data()); + + auto filter = std::make_shared(Network::NetID::ExtendedData); + + for (size_t offset = 0; offset < size; offset += ExtendedDataMessage::MaxExtendedDataBufferSize) + { + parameters.subCommand = ExtendedDataSubCommand::GenericBinaryWrite; + parameters.userValue = static_cast(binaryIndex); + parameters.offset = static_cast(offset); + parameters.length = static_cast(std::min(ExtendedDataMessage::MaxExtendedDataBufferSize, size - offset)); + (void)memcpy(&arguments[sizeof(ExtendedDataMessage::ExtendedDataHeader)], &in[offset], parameters.length); + + std::shared_ptr response = com->waitForMessageSync( + [this, arguments]() { + return com->sendCommand(Command::ExtendedData, arguments); + }, + filter, + timeout + ); + if (!response) { + report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error); + return false; + } + } + return true; +} + bool Device::subscribeLiveData(std::shared_ptr message) { if(!supportsLiveData()) { report(APIEvent::Type::LiveDataNotSupported, APIEvent::Severity::Error); @@ -3369,3 +3403,12 @@ std::optional Device::getGPTPStatus(std::chrono::milliseconds timeou return *retMsg; } + +bool Device::writeMACsecConfig(const MACsecMessage& message, uint16_t binaryIndex) +{ + std::vector raw; + + message.EncodeFromMessage(raw, report); + + return writeBinaryFile(raw, binaryIndex); +} diff --git a/include/icsneo/communication/command.h b/include/icsneo/communication/command.h index 9700fd2..d2efd15 100644 --- a/include/icsneo/communication/command.h +++ b/include/icsneo/communication/command.h @@ -73,6 +73,7 @@ enum class ExtendedResponse : int32_t { enum class ExtendedDataSubCommand : uint32_t { GenericBinaryRead = 13, + GenericBinaryWrite = 14, }; #pragma pack(push,1) diff --git a/include/icsneo/communication/crc32.h b/include/icsneo/communication/crc32.h new file mode 100644 index 0000000..66c95d0 --- /dev/null +++ b/include/icsneo/communication/crc32.h @@ -0,0 +1,27 @@ +/* + * crc32.h + * + * Created on: Jun 22, 2020 + * Author: BJones + */ + +#pragma once + +#ifndef CRC32_H_ +#define CRC32_H_ + +#include + +/* + * When any data/buffer is run through calCRC(), then the resulting CRC value + * is appended to the end of the data/buffer and the data/buffer is rerun + * through calCRC(), the result will be CRC32_IDENT + */ +#define CRC32_IDENT (0x2144DF1C) /**< CRC check value */ + +#define CRC32_ISVALID(crc) ((crc) == CRC32_IDENT) + +uint32_t crc32(uint32_t crc, const unsigned char* buf, uint32_t len); +uint32_t revcrc32(uint32_t crc, const unsigned char* buf, uint32_t len); + +#endif // CRC32_H_ diff --git a/include/icsneo/communication/message/macsecmessage.h b/include/icsneo/communication/message/macsecmessage.h new file mode 100644 index 0000000..2255500 --- /dev/null +++ b/include/icsneo/communication/message/macsecmessage.h @@ -0,0 +1,200 @@ +#ifndef __MACSECMESSAGE_H__ +#define __MACSECMESSAGE_H__ + +#ifdef __cplusplus + +#include "icsneo/communication/message/message.h" +#include "icsneo/communication/packet.h" +#include "icsneo/api/eventmanager.h" +#include +#include +#include +#include + +namespace icsneo { + +struct MACsecVLANTag +{ + uint16_t vid; /*!< 12 bits */ + uint8_t priCfi; /*!< PRI - 3 bits, CFI - 1bit */ +}; + +struct MACsecMPLSOuter +{ + uint32_t mplsLabel; /*!< 20 bits */ + uint8_t exp; /*!< 3 bits */ +}; + +enum class MACsecPacketType : uint8_t +{ + NoVLANOrMPLS = 0, + SingleVLAN = 1, + DualVLAN = 2, + MPLS = 3, + SingleVLANFollowedByMPLS = 4, + DualVLANFollowedByMPLS = 5, + Unsupported = 6, +}; + +struct MACsecRule +{ + uint8_t index; + std::array keyMacDa; /*!< MAC DA field extracted from the packet */ + std::array maskMacDa; /*!< Set bits to 1 to mask/exclude corresponding flowid_tcam_data bit from compare */ + std::array keyMacSa; /*!< MAC SA field extracted from the packet */ + std::array maskMacSa; /*!< Set bits to 1 to mask/exclude corresponding flowid_tcam_data bit from compare */ + uint16_t keyEthertype; /*!< First E-Type found in the packet that doesn't match one of the preconfigured custom tag. */ + uint16_t maskEthertype; /*!< Set bits to 1 to mask/exclude corresponding flowid_tcam_data bit from compare */ + MACsecVLANTag keyVlanTagOuter1; /*!< outermost/1st VLAN ID {8'd0, VLAN_ID[11:0]}, or 20-bit MPLS label. */ + MACsecMPLSOuter keyMplsOuter1; + MACsecVLANTag maskVlanTagOuter1; /*!< Set bits to 1 to mask/exclude corresponding flowid_tcam_data bit from compare */ + MACsecMPLSOuter maskMplsOuter1; + MACsecVLANTag keyVlanTagOuter2; /*!< 2nd outermost VLAN ID {8'd0, VLAN_ID[11:0]}, or 20-bit MPLS label. */ + MACsecMPLSOuter keyMplsOuter2; + MACsecVLANTag maskVlanTagOuter2; /*!< Set bits to 1 to mask/exclude corresponding flowid_tcam_data bit from compare */ + MACsecMPLSOuter maskMplsOuter2; + uint16_t keyBonusData; /*!< 2 bytes of additional bonus data extracted from one of the custom tags. */ + uint16_t maskBonusData; /*!< Set bits to 1 to mask/exclude corresponding flowid_tcam_data bit from compare */ + uint8_t keyTagMatchBitmap; /*!< 8 bits total. Maps 1 to 1 bitwise with the set of custom tags. (set bit[N]=1 if check Nth custom tag) */ + uint8_t maskTagMatchBitmap; /*!< Set bits to 1 to mask/exclude corresponding flowid_tcam_data bit from compare */ + MACsecPacketType keyPacketType; /*!< Encoded Packet Type, see MACSEC_PACKET_TYPE */ + uint8_t maskPacketType; /*!< Set bits to 1 to mask/exclude corresponding flowid_tcam_data bit from compare */ + uint16_t keyInnerVlanType; /*!< 3 bits total. Encoded value indicating which VLAN TPID value matched for the second outermost VLAN Tag. */ + uint16_t maskInnerVlanType; /*!< Set bits to 1 to mask/exclude corresponding flowid_tcam_data bit from compare */ + uint16_t keyOuterVlanType; /*!< 3 bits total. Encoded value indicating which VLAN TPID value matched for the outermost VLAN Tag. */ + uint16_t maskOuterVlanType; /*!< Set bits to 1 to mask/exclude corresponding flowid_tcam_data bit from compare */ + uint8_t keyNumTags; /*!< 7 bits total. Number of VLAN/custom tags or MPLS lables detected. Ingress: before SecTag; Egress: total detected. Exclude MCS header tags. i.e. Bit 2: 2 tags/labels before SecTAG...Bit 6: 6 or more tags/labels before SecTAG. */ + uint8_t maskNumTags; /*!< Set bits to 1 to mask/exclude corresponding flowid_tcam_data bit from compare */ + bool keyExpress; /*!< 1 bits. Express packet. */ + uint8_t maskExpress; /*!< Set bits to 1 to mask/exclude corresponding flowid_tcam_data bit from compare */ + bool isMpls; + bool enable; +}; + +struct MACsecMap +{ + uint8_t index; + uint64_t secTagSci; /*!< Identifies the SecTAG SCI for this Flow. */ + uint8_t secYIndex; /*!< index for entry in Egress secY Policy */ + bool isControlPacket; /*!< Identifies all packets matching this index lookup as control packets. */ + uint8_t scIndex; /*!< Identifies the SC for this Flow. */ + uint8_t auxiliaryPlcy; /*!< Auxiliary policy bits. */ + uint8_t ruleId; /*!< Identifies the Rule for this Flow. */ + bool enable; +}; + +enum class MACsecValidateFrameType : uint8_t +{ + Disabled = 0, /*!< Disable validation */ + Check = 1, /*!< Enable validation, do not discard invalid frames*/ + Strict = 2, /*!< Enable validation and discard invalid frames */ + NA = 3 /*!< No processing or accounting */ +}; + +enum class MACsecSecTagIcvStripType : uint8_t +{ + StripBoth = 0, /*!< Strip both SecTag and ICV from packet */ + StripSecTagPreserveICV = 1, + PreserveSecTagStripICV = 2, /*!< Preserve SecTag, Strip ICV */ + PreserveBoth = 3 /*!< Preserve both SecTag and ICV */ +}; + +enum class MACsecCipherSuiteType : uint8_t +{ + GcmAes128 = 0, + GcmAes256 = 1, + GcmAes128Xpn = 2, + GcmAes256Xpn = 3 +}; + +struct MACsecSecY +{ + uint8_t index; /*!< Identifies the SecY for this Flow. */ + bool controlledPortEnabled; /*!< Enable (or disable) operation of the Controlled port associated with this SecY */ + MACsecValidateFrameType frameValidationType; /*!< see MACSEC_VALIDATEFRAME */ + MACsecSecTagIcvStripType secTagIcvStripType; /*!< see MACSEC_STRIP_SECTAG_ICV */ + MACsecCipherSuiteType cipher; /*!< Define the cipher suite to use for this SecY see MACSEC_CIPHER_SUITE */ + uint8_t confidentialOffset; /*!< Define the number of bytes that are unencrypted following the SecTag. */ + bool icvIncludesDaSa; /*!< When set, the outer DA/SA bytes are included in the authentication GHASH calculation */ + bool replayProtect; /*!< Enables Anti-Replay protection */ + uint32_t replayWindow; /*!< Unsigned value indicating the size of the anti-replay window. */ + bool protectFrames; /*!< 0 = do not encrypt or authenticate this packet; 1 = always Authenticate frame and if SecTag.TCI.E = 1 encrypt the packet as well. */ + uint8_t secTagOffset; /*!< Define the offset in bytes from either the start of the packet or a matching Etype depending on SecTag_Insertion_Mode. */ + uint8_t secTagTci; /*!< Tag Control Information excluding the AN field which originates from the SA Policy table */ + uint16_t mtu; /*!< Specifies the outgoing MTU for this SecY */ + bool enable; +}; + +struct MACsecSc +{ + uint8_t index; /*!< SC index. */ + uint8_t secYIndex; /*!< SecY associated with this packet. */ + uint64_t sci; /*!< The Secure Channel Identifier. */ + uint8_t saIndex0; /*!< Define the 1st SA to use */ + uint8_t saIndex1; /*!< Define the 2nd SA to use */ + bool saIndex0InUse; /*!< Specifies whether 1st SA is in use or not. */ + bool saIndex1InUse; /*!< Specifies whether 2nd SA is in use or not. */ + bool enableAutoRekey; /*!< If enabled, then once the pn_threshold is reached, auto rekey will happen. */ + bool isActiveSa1; /*!< If set, then sa_index1 is the currently active SA index. If cleared, the sa_index0 is the currently active SA index). */ + bool enable; +}; + +struct MACsecSa +{ + uint8_t index; /*!< SA index */ + std::array sak; /*!< 256b SAK: Define the encryption key to be used to encrypte this packet. The lower 128 bits are used for 128-bit ciphers. */ + std::array hashKey; /*!< 128b Hash Key: Key used for authentication. */ + std::array salt; /*!< 96b Salt value: Salt value used in XPN ciphers. */ + uint32_t ssci; /*!< 32b SSCI value: Short Secure Channel Identifier, used in XPN ciphers. */ + uint8_t an; /*!< 2b SecTag Association Number (AN) */ + uint64_t nextPn; /*!< 64b next_pn value: Next packet number to insert into outgoing packet on a particular SA. */ + bool enable; +}; + +struct MACSecFlags +{ + bool en; // '1' = enable; '0' = disable +}; + +/* MACSec Settings for 1 port/phy */ +struct MACsecConfig +{ + static constexpr int NumFlags = 1; + static constexpr int NumRules = 2; + static constexpr int NumMaps = 2; + static constexpr int NumSecY = 2; + static constexpr int NumSc = 2; + static constexpr int NumSa = 4; + + MACSecFlags flags; + std::array rule; + std::array map; + std::array secy; + std::array sc; + std::array sa; +}; + +struct MACSecGlobalFlags +{ + bool en; // '1' = enable; '0' = disable + bool nvm; // store macsec config in non-volatile memory +}; + +class MACsecMessage : public Message +{ +public: + MACsecMessage(void) : Message(Message::Type::RawMessage) {} + + MACSecGlobalFlags flags; + MACsecConfig rx; + MACsecConfig tx; + + static std::shared_ptr DecodeToMessage(const std::vector& bytestream, const device_eventhandler_t& report); + bool EncodeFromMessage(std::vector& bytestream, const device_eventhandler_t& report) const; +}; + +} + +#endif // __cplusplus + +#endif \ No newline at end of file diff --git a/include/icsneo/device/device.h b/include/icsneo/device/device.h index 8a67c5b..e55cb36 100644 --- a/include/icsneo/device/device.h +++ b/include/icsneo/device/device.h @@ -40,6 +40,7 @@ #include "icsneo/communication/message/extendeddatamessage.h" #include "icsneo/communication/message/livedatamessage.h" #include "icsneo/communication/message/tc10statusmessage.h" +#include "icsneo/communication/message/macsecmessage.h" #include "icsneo/communication/packet/genericbinarystatuspacket.h" #include "icsneo/communication/packet/livedatapacket.h" #include "icsneo/device/extensions/flexray/controller.h" @@ -624,6 +625,7 @@ public: std::optional getGenericBinarySize(uint16_t binaryIndex); bool readBinaryFile(std::ostream& stream, uint16_t binaryIndex); + bool writeBinaryFile(const std::vector& in, uint16_t binaryIndex); bool subscribeLiveData(std::shared_ptr message); bool unsubscribeLiveData(const LiveDataHandle& handle); bool clearAllLiveData(); @@ -738,6 +740,9 @@ public: std::optional getTC10Status(Network::NetID network); std::optional getGPTPStatus(std::chrono::milliseconds timeout = std::chrono::milliseconds(100)); + /* MACsec support */ + virtual bool writeMACsecConfig(const MACsecMessage& message, uint16_t binaryIndex); + protected: bool online = false; int messagePollingCallbackID = 0;