vslib/MACsecForwarder.cpp (153 lines of code) (raw):

#include "MACsecForwarder.h" #include "SwitchStateBase.h" #include "SelectableFd.h" #include "swss/logger.h" #include "swss/select.h" #include <sys/socket.h> #include <linux/if_packet.h> #include <linux/if_ether.h> #include <arpa/inet.h> #include <net/if.h> #include <unistd.h> using namespace saivs; MACsecForwarder::MACsecForwarder( _In_ const std::string &macsecInterfaceName, _In_ std::shared_ptr<HostInterfaceInfo> info): m_macsecInterfaceName(macsecInterfaceName), 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) { SWSS_LOG_THROW( "failed to open macsec socket %s, errno: %d", m_macsecInterfaceName.c_str(), errno); } struct sockaddr_ll sockAddress; memset(&sockAddress, 0, sizeof(sockAddress)); sockAddress.sll_family = PF_PACKET; sockAddress.sll_protocol = htons(ETH_P_ALL); sockAddress.sll_ifindex = if_nametoindex(m_macsecInterfaceName.c_str()); if (sockAddress.sll_ifindex == 0) { close(m_macsecfd); SWSS_LOG_THROW( "failed to get interface index for %s", m_macsecInterfaceName.c_str()); } if (SwitchStateBase::promisc(m_macsecInterfaceName.c_str())) { close(m_macsecfd); SWSS_LOG_THROW( "promisc failed on %s", m_macsecInterfaceName.c_str()); } if (bind(m_macsecfd, (struct sockaddr *)&sockAddress, sizeof(sockAddress)) < 0) { close(m_macsecfd); SWSS_LOG_THROW( "bind failed on %s", m_macsecInterfaceName.c_str()); } m_forwardThread = std::make_shared<std::thread>(&MACsecForwarder::forward, this); SWSS_LOG_NOTICE( "setup MACsec forward rule for %s succeeded", m_macsecInterfaceName.c_str()); } MACsecForwarder::~MACsecForwarder() { SWSS_LOG_ENTER(); m_runThread = false; m_exitEvent.notify(); m_forwardThread->join(); int err = close(m_macsecfd); if (err != 0) { SWSS_LOG_ERROR( "failed to close macsec device: %s, err: %d", m_macsecInterfaceName.c_str(), err); } } int MACsecForwarder::get_macsecfd() const { SWSS_LOG_ENTER(); return m_macsecfd; } void MACsecForwarder::forward() { SWSS_LOG_ENTER(); unsigned char buffer[ETH_FRAME_BUFFER_SIZE]; swss::Select s; SelectableFd fd(m_macsecfd); s.addSelectable(&m_exitEvent); s.addSelectable(&fd); 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); if (result != swss::Select::OBJECT) { SWSS_LOG_ERROR( "selectable failed: %d, ending thread for %s", result, m_macsecInterfaceName.c_str()); break; } if (sel == &m_exitEvent) // thread end event break; ssize_t size = recvmsg(m_macsecfd, &msg, 0); if (size < 0) { SWSS_LOG_WARN( "failed to read from macsec device %s fd %d, errno(%d): %s", m_macsecInterfaceName.c_str(), m_macsecfd, errno, strerror(errno)); if (errno == EBADF) { // bad file descriptor, just close the thread SWSS_LOG_NOTICE( "ending thread for macsec device %s", m_macsecInterfaceName.c_str()); break; } continue; } else if (size < (ssize_t)sizeof(ethhdr)) { SWSS_LOG_ERROR("invalid ethernet frame length: %zu", msg.msg_controllen); continue; } size_t length = static_cast<size_t>(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()); }