diff --git a/vslib/inc/HostInterfaceInfo.h b/vslib/inc/HostInterfaceInfo.h index dc97e1dad..77575fd41 100644 --- a/vslib/inc/HostInterfaceInfo.h +++ b/vslib/inc/HostInterfaceInfo.h @@ -5,6 +5,8 @@ extern "C" { } #include "EventQueue.h" +#include "TrafficFilterPipes.h" +#include "TrafficForwarder.h" #include "swss/selectableevent.h" @@ -14,7 +16,8 @@ extern "C" { namespace saivs { - class HostInterfaceInfo + class HostInterfaceInfo : + public TrafficForwarder { private: @@ -38,6 +41,20 @@ namespace saivs _In_ const uint8_t *buffer, _In_ size_t size) const; + bool installEth2TapFilter( + _In_ int priority, + _In_ std::shared_ptr filter); + + bool uninstallEth2TapFilter( + _In_ std::shared_ptr filter); + + bool installTap2EthFilter( + _In_ int priority, + _In_ std::shared_ptr filter); + + bool uninstallTap2EthFilter( + _In_ std::shared_ptr filter); + private: void veth2tap_fun(); @@ -58,13 +75,16 @@ namespace saivs std::shared_ptr m_eventQueue; - private: - int m_tapfd; + private: + std::shared_ptr m_e2t; std::shared_ptr m_t2e; + TrafficFilterPipes m_e2tFilters; + TrafficFilterPipes m_t2eFilters; + swss::SelectableEvent m_e2tEvent; swss::SelectableEvent m_t2eEvent; }; diff --git a/vslib/inc/MACsecFilter.h b/vslib/inc/MACsecFilter.h index 3cd06f5c6..6e272bb21 100644 --- a/vslib/inc/MACsecFilter.h +++ b/vslib/inc/MACsecFilter.h @@ -30,10 +30,10 @@ namespace saivs _In_ const void *buffer, _In_ size_t length) = 0; - bool m_macsec_device_enable; + bool m_macsecDeviceEnable; int m_macsecfd; - const std::string m_macsec_interface_name; + const std::string m_macsecInterfaceName; }; } diff --git a/vslib/inc/MACsecForwarder.h b/vslib/inc/MACsecForwarder.h index 3d0a5a7f2..d5aea1212 100644 --- a/vslib/inc/MACsecForwarder.h +++ b/vslib/inc/MACsecForwarder.h @@ -1,5 +1,8 @@ #pragma once +#include "HostInterfaceInfo.h" +#include "TrafficForwarder.h" + #include "swss/sal.h" #include "swss/selectableevent.h" @@ -9,12 +12,13 @@ namespace saivs { - class MACsecForwarder + class MACsecForwarder : + public TrafficForwarder { public: MACsecForwarder( _In_ const std::string &macsecInterfaceName, - _In_ int tapfd); + _In_ std::shared_ptr info); virtual ~MACsecForwarder(); @@ -23,7 +27,6 @@ namespace saivs void forward(); private: - int m_tapfd; int m_macsecfd; const std::string m_macsecInterfaceName; @@ -33,5 +36,7 @@ namespace saivs swss::SelectableEvent m_exitEvent; std::shared_ptr m_forwardThread; + + std::shared_ptr m_info; }; } diff --git a/vslib/inc/SwitchStateBase.h b/vslib/inc/SwitchStateBase.h index 443e5a00a..acc832f59 100644 --- a/vslib/inc/SwitchStateBase.h +++ b/vslib/inc/SwitchStateBase.h @@ -363,6 +363,11 @@ namespace saivs sai_status_t removeDebugCounter( _In_ sai_object_id_t objectId); + public: + + static int promisc( + _In_ const char *dev); + protected: // custom hostif sai_status_t createHostif( @@ -394,9 +399,6 @@ namespace saivs _In_ const char *dev, _In_ const sai_mac_t& mac); - static int promisc( - _In_ const char *dev); - static int get_default_gw_mac_address( _Out_ sai_mac_t& mac); diff --git a/vslib/inc/TrafficForwarder.h b/vslib/inc/TrafficForwarder.h new file mode 100644 index 000000000..c5dbf4324 --- /dev/null +++ b/vslib/inc/TrafficForwarder.h @@ -0,0 +1,32 @@ +#pragma once + +#include "swss/sal.h" + +#include +#include + +namespace saivs +{ + static constexpr size_t ETH_FRAME_BUFFER_SIZE = 0x4000; + static constexpr size_t CONTROL_MESSAGE_BUFFER_SIZE = 0x1000; + + class TrafficForwarder + { + public: + virtual ~TrafficForwarder() = default; + + protected: + TrafficForwarder() = default; + + static void addVlanTag( + _Inout_ unsigned char *buffer, + _Inout_ size_t &length, + _Inout_ struct msghdr &msg); + + virtual bool sendTo( + _In_ int fd, + _In_ const unsigned char *buffer, + _In_ size_t length) const; + + }; +} diff --git a/vslib/src/HostInterfaceInfo.cpp b/vslib/src/HostInterfaceInfo.cpp index 02cb88c76..ff96b50fd 100644 --- a/vslib/src/HostInterfaceInfo.cpp +++ b/vslib/src/HostInterfaceInfo.cpp @@ -90,11 +90,39 @@ void HostInterfaceInfo::async_process_packet_for_fdb_event( m_eventQueue->enqueue(std::make_shared(EventType::EVENT_TYPE_PACKET, payload)); } -#define ETH_FRAME_BUFFER_SIZE (0x4000) -#define CONTROL_MESSAGE_BUFFER_SIZE (0x1000) -#define IEEE_8021Q_ETHER_TYPE (0x8100) -#define MAC_ADDRESS_SIZE (6) -#define VLAN_TAG_SIZE (4) +bool HostInterfaceInfo::installEth2TapFilter( + _In_ int priority, + _In_ std::shared_ptr filter) +{ + SWSS_LOG_ENTER(); + + return m_e2tFilters.installFilter(priority, filter); +} + +bool HostInterfaceInfo::uninstallEth2TapFilter( + _In_ std::shared_ptr filter) +{ + SWSS_LOG_ENTER(); + + return m_e2tFilters.uninstallFilter(filter); +} + +bool HostInterfaceInfo::installTap2EthFilter( + _In_ int priority, + _In_ std::shared_ptr filter) +{ + SWSS_LOG_ENTER(); + + return m_t2eFilters.installFilter(priority, filter); +} + +bool HostInterfaceInfo::uninstallTap2EthFilter( + _In_ std::shared_ptr filter) +{ + SWSS_LOG_ENTER(); + + return m_t2eFilters.uninstallFilter(filter); +} void HostInterfaceInfo::veth2tap_fun() { @@ -161,63 +189,28 @@ void HostInterfaceInfo::veth2tap_fun() continue; } - struct cmsghdr *cmsg; + // Buffer include the ingress packets + // MACsec scenario: EAPOL packets and encrypted packets + size_t length = static_cast(size); + auto ret = m_e2tFilters.execute(buffer, length); - for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) + if (ret == TrafficFilter::TERMINATE) { - if (cmsg->cmsg_level != SOL_PACKET || cmsg->cmsg_type != PACKET_AUXDATA) - continue; - - struct tpacket_auxdata* aux = (struct tpacket_auxdata*)CMSG_DATA(cmsg); - - if ((aux->tp_status & TP_STATUS_VLAN_VALID) && - (aux->tp_status & TP_STATUS_VLAN_TPID_VALID)) - { - SWSS_LOG_DEBUG("got vlan tci: 0x%x, vlanid: %d", aux->tp_vlan_tci, aux->tp_vlan_tci & 0xFFF); - - // inject vlan tag into frame - - // for overlapping buffers - memmove(buffer + 2 * MAC_ADDRESS_SIZE + VLAN_TAG_SIZE, - buffer + 2 * MAC_ADDRESS_SIZE, - size - (2 * MAC_ADDRESS_SIZE)); - - uint16_t tci = htons(aux->tp_vlan_tci); - uint16_t tpid = htons(IEEE_8021Q_ETHER_TYPE); - - uint8_t* pvlan = (uint8_t *)(buffer + 2 * MAC_ADDRESS_SIZE); - memcpy(pvlan, &tpid, sizeof(uint16_t)); - memcpy(pvlan + sizeof(uint16_t), &tci, sizeof(uint16_t)); - - size += VLAN_TAG_SIZE; - - break; - } + continue; } - - async_process_packet_for_fdb_event(buffer, size); - - if (write(m_tapfd, buffer, size) < 0) + else if (ret == TrafficFilter::ERROR) { - /* - * We filter out EIO because of this patch: - * https://github.com/torvalds/linux/commit/1bd4978a88ac2589f3105f599b1d404a312fb7f6 - */ + // Error log should be recorded in filter + return; + } - if (errno != ENETDOWN && errno != EIO) - { - SWSS_LOG_ERROR("failed to write to tap device fd %d, errno(%d): %s", - m_tapfd, errno, strerror(errno)); - } + addVlanTag(buffer, length, msg); - if (errno == EBADF) - { - // bad file descriptor, just end thread - SWSS_LOG_NOTICE("ending thread for tap fd %d", m_tapfd); - return; - } + async_process_packet_for_fdb_event(buffer, length); - continue; + if (!sendTo(m_tapfd, buffer, length)) + { + break; } } @@ -268,6 +261,22 @@ void HostInterfaceInfo::tap2veth_fun() continue; } + // Buffer include the egress packets + // MACsec scenario: EAPOL packets and plaintext packets + size_t length = static_cast(size); + auto ret = m_t2eFilters.execute(buffer, length); + size = static_cast(length); + + if (ret == TrafficFilter::TERMINATE) + { + continue; + } + else if (ret == TrafficFilter::ERROR) + { + // Error log should be recorded in filter + return; + } + if (write(m_packet_socket, buffer, (int)size) < 0) { if (errno != ENETDOWN) diff --git a/vslib/src/MACsecForwarder.cpp b/vslib/src/MACsecForwarder.cpp index 72fcb500e..c58e10167 100644 --- a/vslib/src/MACsecForwarder.cpp +++ b/vslib/src/MACsecForwarder.cpp @@ -14,17 +14,20 @@ using namespace saivs; -#define ETH_FRAME_BUFFER_SIZE (0x4000) - MACsecForwarder::MACsecForwarder( _In_ const std::string &macsecInterfaceName, - _In_ int tapfd): - m_tapfd(tapfd), + _In_ std::shared_ptr info): m_macsecInterfaceName(macsecInterfaceName), - m_runThread(true) + m_runThread(true), + m_info(info) { SWSS_LOG_ENTER(); + if (m_info == nullptr) + { + SWSS_LOG_THROW("The HostInterfaceInfo on the MACsec port %s is empty", m_macsecInterfaceName.c_str()); + } + m_macsecfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (m_macsecfd < 0) @@ -112,6 +115,25 @@ void MACsecForwarder::forward() while (m_runThread) { + struct msghdr msg; + memset(&msg, 0, sizeof(struct msghdr)); + + struct sockaddr_storage srcAddr; + + struct iovec iov[1]; + + iov[0].iov_base = buffer; // buffer for message + iov[0].iov_len = sizeof(buffer); + + char control[CONTROL_MESSAGE_BUFFER_SIZE]; // buffer for control messages + + msg.msg_name = &srcAddr; + msg.msg_namelen = sizeof(srcAddr); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + swss::Selectable *sel = NULL; int result = s.select(&sel); @@ -128,7 +150,7 @@ void MACsecForwarder::forward() if (sel == &m_exitEvent) // thread end event break; - ssize_t size = read(m_macsecfd, buffer, sizeof(buffer)); + ssize_t size = recvmsg(m_macsecfd, &msg, 0); if (size < 0) { @@ -151,35 +173,26 @@ void MACsecForwarder::forward() continue; } - - if (write(m_tapfd, buffer, static_cast(size)) < 0) + else if (size < (ssize_t)sizeof(ethhdr)) { - if (errno != ENETDOWN && errno != EIO) - { - SWSS_LOG_ERROR( - "failed to write to macsec device %s fd %d, errno(%d): %s", - m_macsecInterfaceName.c_str(), - m_macsecfd, - errno, - strerror(errno)); - } - - if (errno == EBADF) - { - // bad file descriptor, just end thread - SWSS_LOG_ERROR( - "ending thread for macsec device %s fd %d", - m_macsecInterfaceName.c_str(), - m_macsecfd); - return; - } + SWSS_LOG_ERROR("invalid ethernet frame length: %zu", msg.msg_controllen); continue; } + + size_t length = static_cast(size); + + addVlanTag(buffer, length, msg); + + m_info->async_process_packet_for_fdb_event(buffer, length); + + if (!sendTo(m_info->m_tapfd, buffer, length)) + { + break; + } } SWSS_LOG_NOTICE( "ending thread proc for %s", m_macsecInterfaceName.c_str()); } - diff --git a/vslib/src/MACsecManager.cpp b/vslib/src/MACsecManager.cpp index f9c5a47dc..06b743a98 100644 --- a/vslib/src/MACsecManager.cpp +++ b/vslib/src/MACsecManager.cpp @@ -579,7 +579,7 @@ bool MACsecManager::add_macsec_forwarder( auto &manager = itr->second; - manager.m_forwarder = std::make_shared(macsecInterface, manager.m_info->m_tapfd); + manager.m_forwarder = std::make_shared(macsecInterface, manager.m_info); return true; } diff --git a/vslib/src/Makefile.am b/vslib/src/Makefile.am index 81f2e40fb..afcb80bca 100644 --- a/vslib/src/Makefile.am +++ b/vslib/src/Makefile.am @@ -53,7 +53,13 @@ libSaiVS_a_SOURCES = \ SwitchMLNX2700.cpp \ CorePortIndexMap.cpp \ CorePortIndexMapContainer.cpp \ - CorePortIndexMapFileParser.cpp + CorePortIndexMapFileParser.cpp \ + MACsecFilter.cpp \ + MACsecEgressFilter.cpp \ + MACsecIngressFilter.cpp \ + TrafficForwarder.cpp \ + MACsecForwarder.cpp \ + TrafficFilterPipes.cpp libsaivs_la_SOURCES = \ sai_vs_fdb.cpp \ diff --git a/vslib/src/TrafficForwarder.cpp b/vslib/src/TrafficForwarder.cpp new file mode 100644 index 000000000..9a5ba8ddd --- /dev/null +++ b/vslib/src/TrafficForwarder.cpp @@ -0,0 +1,105 @@ +#include "TrafficForwarder.h" + +#include "swss/logger.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace saivs; + +#define IEEE_8021Q_ETHER_TYPE (0x8100) +#define MAC_ADDRESS_SIZE (6) +#define VLAN_TAG_SIZE (4) + +void TrafficForwarder::addVlanTag( + _Inout_ unsigned char *buffer, + _Inout_ size_t &length, + _Inout_ struct msghdr &msg) +{ + SWSS_LOG_ENTER(); + + struct cmsghdr *cmsg; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) + { + if (cmsg->cmsg_level != SOL_PACKET || cmsg->cmsg_type != PACKET_AUXDATA) + continue; + + struct tpacket_auxdata* aux = (struct tpacket_auxdata*)CMSG_DATA(cmsg); + + if ((aux->tp_status & TP_STATUS_VLAN_VALID) && + (aux->tp_status & TP_STATUS_VLAN_TPID_VALID)) + { + SWSS_LOG_DEBUG("got vlan tci: 0x%x, vlanid: %d", aux->tp_vlan_tci, aux->tp_vlan_tci & 0xFFF); + + if ((length + VLAN_TAG_SIZE) > ETH_FRAME_BUFFER_SIZE) + { + SWSS_LOG_THROW("The VLAN packet size %lu exceeds the ETH_FRAME_BUFFER_SIZE", length + VLAN_TAG_SIZE); + } + + // inject vlan tag into frame + + // for overlapping buffers + memmove(buffer + 2 * MAC_ADDRESS_SIZE + VLAN_TAG_SIZE, + buffer + 2 * MAC_ADDRESS_SIZE, + length - (2 * MAC_ADDRESS_SIZE)); + + uint16_t tci = htons(aux->tp_vlan_tci); + uint16_t tpid = htons(IEEE_8021Q_ETHER_TYPE); + + uint8_t* pvlan = (uint8_t *)(buffer + 2 * MAC_ADDRESS_SIZE); + memcpy(pvlan, &tpid, sizeof(uint16_t)); + memcpy(pvlan + sizeof(uint16_t), &tci, sizeof(uint16_t)); + + length += VLAN_TAG_SIZE; + + break; + } + } +} + +bool TrafficForwarder::sendTo( + _In_ int fd, + _In_ const unsigned char *buffer, + _In_ size_t length) const +{ + SWSS_LOG_ENTER(); + + if (write(fd, buffer, static_cast(length)) < 0) + { + /* + * We filter out EIO because of this patch: + * https://github.com/torvalds/linux/commit/1bd4978a88ac2589f3105f599b1d404a312fb7f6 + */ + + if (errno != ENETDOWN && errno != EIO) + { + SWSS_LOG_ERROR("failed to write to device fd %d, errno(%d): %s", + fd, + errno, + strerror(errno)); + } + + if (errno == EBADF) + { + // bad file descriptor, just end thread + SWSS_LOG_NOTICE("ending forward for fd %d", fd); + + return false; + } + } + + return true; +}