common/nfnetlink.cpp (138 lines of code) (raw):
#include <string.h>
#include <errno.h>
#include <system_error>
#include "common/logger.h"
#include "common/netmsg.h"
#include "common/netdispatcher.h"
#include "common/netlink.h"
#include "common/nfnetlink.h"
using namespace swss;
using namespace std;
NfNetlink::NfNetlink(int pri) :
Selectable(pri)
{
m_socket = nl_socket_alloc();
if (!m_socket)
{
SWSS_LOG_ERROR("Unable to allocated nfnetlink socket");
throw system_error(make_error_code(errc::address_not_available),
"Unable to allocate nfnetlink socket");
}
nl_socket_disable_seq_check(m_socket);
nl_socket_disable_auto_ack(m_socket);
int err = nfnl_connect(m_socket);
if (err < 0)
{
SWSS_LOG_ERROR("Unable to connect nfnetlink socket: %s", nl_geterror(err));
nl_socket_free(m_socket);
m_socket = NULL;
throw system_error(make_error_code(errc::address_not_available),
"Unable to connect nfnetlink socket");
}
nl_socket_set_nonblocking(m_socket);
/* Set socket buffer size to 10 MB */
nl_socket_set_buffer_size(m_socket, 10485760, 0);
#ifdef NETFILTER_UNIT_TEST
/* For netlink packet tracing purposes */
nfPktsLogFile = fopen("/var/log/nf_netlink_pkts.log", "w");
if (nfPktsLogFile == NULL)
{
SWSS_LOG_ERROR("Unable to open netfilter netlink packets log file: %s", strerror(errno));
}
#endif
}
NfNetlink::~NfNetlink()
{
if (m_socket != NULL)
{
nl_close(m_socket);
nl_socket_free(m_socket);
}
}
bool NfNetlink::setSockBufSize(uint32_t sockBufSize)
{
if (nl_socket_set_buffer_size(m_socket, sockBufSize, 0) < 0)
{
return false;
}
return true;
}
void NfNetlink::registerRecvCallbacks()
{
#ifdef NETFILTER_UNIT_TEST
nl_socket_modify_cb(m_socket, NL_CB_MSG_IN, NL_CB_CUSTOM, onNetlinkRcv, this);
#endif
nl_socket_modify_cb(m_socket, NL_CB_VALID, NL_CB_CUSTOM, onNetlinkMsg, this);
}
void NfNetlink::registerGroup(int nfnlGroup)
{
int err = nl_socket_add_membership(m_socket, nfnlGroup);
if (err < 0)
{
SWSS_LOG_THROW("Unable to register to netfilter group %d: %s", nfnlGroup,
nl_geterror(err));
}
}
void NfNetlink::dumpRequest(int getCommand)
{
int err = nfnl_ct_dump_request(m_socket);
if (err < 0)
{
SWSS_LOG_THROW("Unable to request netfilter conntrack dump: %s",
nl_geterror(err));
}
}
bool NfNetlink::updateConnTrackEntry(struct nfnl_ct *ct)
{
if (nfnl_ct_add(m_socket, ct, NLM_F_REPLACE) < 0)
{
SWSS_LOG_ERROR("Failed to update conntrack object in the kernel");
return false;
}
return true;
}
bool NfNetlink::deleteConnTrackEntry(struct nfnl_ct *ct)
{
if (nfnl_ct_del(m_socket, ct, 0) < 0)
{
SWSS_LOG_ERROR("Failed to delete conntrack object in the kernel");
return false;
}
return true;
}
int NfNetlink::getFd()
{
return nl_socket_get_fd(m_socket);
}
uint64_t NfNetlink::readData()
{
int err;
do
{
err = nl_recvmsgs_default(m_socket);
}
while(err == -NLE_INTR); // Retry if the process was interrupted by a signal
if (err < 0)
{
if (err == -NLE_NOMEM)
SWSS_LOG_ERROR("netlink reports out of memory on reading a netfilter netlink socket. High possibility of a lost message");
else if (err == -NLE_AGAIN)
SWSS_LOG_DEBUG("netlink reports NLE_AGAIN on reading a netfilter netlink socket");
else
SWSS_LOG_ERROR("netlink reports an error=%d on reading a netfilter netlink socket", err);
}
return 0;
}
int NfNetlink::onNetlinkMsg(struct nl_msg *msg, void *arg)
{
NetDispatcher::getInstance().onNetlinkMessage(msg);
return NL_OK;
}
#ifdef NETFILTER_UNIT_TEST
int NfNetlink::onNetlinkRcv(struct nl_msg *msg, void *arg)
{
NfNetlink *nfnlink = (NfNetlink *)arg;
if ((nfnlink == NULL) || (nfnlink->nfPktsLogFile == NULL))
return NL_OK;
nl_msg_dump(msg, nfnlink->nfPktsLogFile);
return NL_OK;
}
#endif