vslib/HostInterfaceInfo.cpp (216 lines of code) (raw):
#include "HostInterfaceInfo.h"
#include "SwitchStateBase.h"
#include "SelectableFd.h"
#include "EventPayloadPacket.h"
#include "swss/logger.h"
#include "swss/select.h"
#include "meta/sai_serialize.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <linux/if_tun.h>
#include <sys/ioctl.h>
#include <net/if_arp.h>
#include <unistd.h>
#include <net/ethernet.h>
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
using namespace saivs;
HostInterfaceInfo::HostInterfaceInfo(
_In_ int ifindex,
_In_ int socket,
_In_ int tapfd,
_In_ const std::string& tapname,
_In_ sai_object_id_t portId,
_In_ std::shared_ptr<EventQueue> eventQueue):
m_ifindex(ifindex),
m_packet_socket(socket),
m_name(tapname),
m_portId(portId),
m_eventQueue(eventQueue),
m_tapfd(tapfd)
{
SWSS_LOG_ENTER();
m_run_thread = true;
m_e2t = std::make_shared<std::thread>(&HostInterfaceInfo::veth2tap_fun, this);
m_t2e = std::make_shared<std::thread>(&HostInterfaceInfo::tap2veth_fun, this);
}
HostInterfaceInfo::~HostInterfaceInfo()
{
SWSS_LOG_ENTER();
m_run_thread = false;
m_e2tEvent.notify();
m_t2eEvent.notify();
if (m_t2e)
{
m_t2e->join();
}
if (m_e2t)
{
m_e2t->join();
}
// remove tap device
int err = close(m_tapfd);
if (err)
{
SWSS_LOG_ERROR("failed to remove tap device: %s, err: %d", m_name.c_str(), err);
}
SWSS_LOG_NOTICE("joined threads for hostif: %s", m_name.c_str());
}
void HostInterfaceInfo::async_process_packet_for_fdb_event(
_In_ const uint8_t *data,
_In_ size_t size) const
{
SWSS_LOG_ENTER();
auto buffer = Buffer(data, size);
auto payload = std::make_shared<EventPayloadPacket>(m_portId, m_ifindex, m_name, buffer);
m_eventQueue->enqueue(std::make_shared<Event>(EventType::EVENT_TYPE_PACKET, payload));
}
bool HostInterfaceInfo::installEth2TapFilter(
_In_ int priority,
_In_ std::shared_ptr<TrafficFilter> filter)
{
SWSS_LOG_ENTER();
return m_e2tFilters.installFilter(priority, filter);
}
bool HostInterfaceInfo::uninstallEth2TapFilter(
_In_ std::shared_ptr<TrafficFilter> filter)
{
SWSS_LOG_ENTER();
return m_e2tFilters.uninstallFilter(filter);
}
bool HostInterfaceInfo::installTap2EthFilter(
_In_ int priority,
_In_ std::shared_ptr<TrafficFilter> filter)
{
SWSS_LOG_ENTER();
return m_t2eFilters.installFilter(priority, filter);
}
bool HostInterfaceInfo::uninstallTap2EthFilter(
_In_ std::shared_ptr<TrafficFilter> filter)
{
SWSS_LOG_ENTER();
return m_t2eFilters.uninstallFilter(filter);
}
void HostInterfaceInfo::veth2tap_fun()
{
SWSS_LOG_ENTER();
unsigned char buffer[ETH_FRAME_BUFFER_SIZE];
swss::Select s;
SelectableFd fd(m_packet_socket);
s.addSelectable(&m_e2tEvent);
s.addSelectable(&fd);
while (m_run_thread)
{
struct msghdr msg;
memset(&msg, 0, sizeof(struct msghdr));
struct sockaddr_storage src_addr;
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 = &src_addr;
msg.msg_namelen = sizeof(src_addr);
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_name.c_str());
return;
}
if (sel == &m_e2tEvent) // thread end event
break;
ssize_t size = recvmsg(m_packet_socket, &msg, 0);
if (size < 0)
{
if (errno != ENETDOWN)
{
SWSS_LOG_ERROR("failed to read from socket fd %d, errno(%d): %s",
m_packet_socket, errno, strerror(errno));
}
continue;
}
if (size < (ssize_t)sizeof(ethhdr))
{
SWSS_LOG_ERROR("invalid ethernet frame length: %zu", msg.msg_controllen);
continue;
}
// Buffer include the ingress packets
// MACsec scenario: EAPOL packets and encrypted packets
size_t length = static_cast<size_t>(size);
auto ret = m_e2tFilters.execute(buffer, length);
if (ret == TrafficFilter::TERMINATE)
{
continue;
}
else if (ret == TrafficFilter::ERROR)
{
// Error log should be recorded in filter
return;
}
addVlanTag(buffer, length, msg);
async_process_packet_for_fdb_event(buffer, length);
if (!sendTo(m_tapfd, buffer, length))
{
break;
}
}
SWSS_LOG_NOTICE("ending thread proc for %s", m_name.c_str());
}
void HostInterfaceInfo::tap2veth_fun()
{
SWSS_LOG_ENTER();
unsigned char buffer[ETH_FRAME_BUFFER_SIZE];
swss::Select s;
SelectableFd fd(m_tapfd);
s.addSelectable(&m_t2eEvent);
s.addSelectable(&fd);
while (m_run_thread)
{
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_name.c_str());
return;
}
if (sel == &m_t2eEvent) // thread end event
break;
ssize_t size = read(m_tapfd, buffer, sizeof(buffer));
if (size < 0)
{
SWSS_LOG_ERROR("failed to read from tapfd fd %d, errno(%d): %s",
m_tapfd, errno, strerror(errno));
if (errno == EBADF)
{
// bad file descriptor, just close the thread
SWSS_LOG_NOTICE("ending thread for tap fd %d", m_tapfd);
return;
}
continue;
}
// Buffer include the egress packets
// MACsec scenario: EAPOL packets and plaintext packets
size_t length = static_cast<size_t>(size);
auto ret = m_t2eFilters.execute(buffer, length);
size = static_cast<ssize_t>(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)
{
SWSS_LOG_ERROR("failed to write to socket fd %d, errno(%d): %s",
m_packet_socket, errno, strerror(errno));
}
continue;
}
}
SWSS_LOG_NOTICE("ending thread proc for %s", m_name.c_str());
}