face: introduce PcapHelper class
Change-Id: I26f7d43394e1b17f314c226ca6bce723c2410ae3
Refs: #4011
diff --git a/daemon/face/ethernet-transport.cpp b/daemon/face/ethernet-transport.cpp
index be44e0c..0773e85 100644
--- a/daemon/face/ethernet-transport.cpp
+++ b/daemon/face/ethernet-transport.cpp
@@ -34,11 +34,6 @@
#include <net/ethernet.h> // for struct ether_header
#include <net/if.h> // for struct ifreq
#include <sys/ioctl.h> // for ioctl()
-#include <unistd.h> // for dup()
-
-#if !defined(PCAP_NETMASK_UNKNOWN)
-#define PCAP_NETMASK_UNKNOWN 0xffffffff
-#endif
namespace nfd {
namespace face {
@@ -47,28 +42,22 @@
EthernetTransport::EthernetTransport(const NetworkInterfaceInfo& localEndpoint,
const ethernet::Address& remoteEndpoint)
- : m_pcap(nullptr, pcap_close)
- , m_socket(getGlobalIoService())
+ : m_socket(getGlobalIoService())
+ , m_pcap(localEndpoint.name)
, m_srcAddress(localEndpoint.etherAddress)
, m_destAddress(remoteEndpoint)
, m_interfaceName(localEndpoint.name)
-#if defined(__linux__)
- , m_interfaceIndex(localEndpoint.index)
-#endif
#ifdef _DEBUG
, m_nDropped(0)
#endif
{
- pcapInit();
-
- int fd = pcap_get_selectable_fd(m_pcap.get());
- if (fd < 0)
- BOOST_THROW_EXCEPTION(Error("pcap_get_selectable_fd failed"));
-
- // need to duplicate the fd, otherwise both pcap_close()
- // and stream_descriptor::close() will try to close the
- // same fd and one of them will fail
- m_socket.assign(::dup(fd));
+ try {
+ m_pcap.activate(DLT_EN10MB);
+ m_socket.assign(m_pcap.getFd());
+ }
+ catch (const PcapHelper::Error& e) {
+ BOOST_THROW_EXCEPTION(Error(e.what()));
+ }
// do this after assigning m_socket because getInterfaceMtu uses it
this->setMtu(getInterfaceMtu());
@@ -80,14 +69,6 @@
}
void
-EthernetTransport::doSend(Transport::Packet&& packet)
-{
- NFD_LOG_FACE_TRACE(__func__);
-
- sendPacket(packet.packet);
-}
-
-void
EthernetTransport::doClose()
{
NFD_LOG_FACE_TRACE(__func__);
@@ -99,7 +80,7 @@
m_socket.cancel(error);
m_socket.close(error);
}
- m_pcap.reset();
+ m_pcap.close();
// Ensure that the Transport stays alive at least
// until all pending handlers are dispatched
@@ -109,50 +90,16 @@
}
void
-EthernetTransport::pcapInit()
+EthernetTransport::doSend(Transport::Packet&& packet)
{
- char errbuf[PCAP_ERRBUF_SIZE] = {};
- m_pcap.reset(pcap_create(m_interfaceName.c_str(), errbuf));
- if (!m_pcap)
- BOOST_THROW_EXCEPTION(Error("pcap_create: " + std::string(errbuf)));
+ NFD_LOG_FACE_TRACE(__func__);
-#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
- // Enable "immediate mode", effectively disabling any read buffering in the kernel.
- // This corresponds to the BIOCIMMEDIATE ioctl on BSD-like systems (including macOS)
- // where libpcap uses a BPF device. On Linux this forces libpcap not to use TPACKET_V3,
- // even if the kernel supports it, thus preventing bug #1511.
- pcap_set_immediate_mode(m_pcap.get(), 1);
-#endif
-
- if (pcap_activate(m_pcap.get()) < 0)
- BOOST_THROW_EXCEPTION(Error("pcap_activate failed"));
-
- if (pcap_set_datalink(m_pcap.get(), DLT_EN10MB) < 0)
- BOOST_THROW_EXCEPTION(Error("pcap_set_datalink: " + std::string(pcap_geterr(m_pcap.get()))));
-
- if (pcap_setdirection(m_pcap.get(), PCAP_D_IN) < 0)
- // no need to throw on failure, BPF will filter unwanted packets anyway
- NFD_LOG_FACE_WARN("pcap_setdirection failed: " << pcap_geterr(m_pcap.get()));
-}
-
-void
-EthernetTransport::setPacketFilter(const char* filterString)
-{
- bpf_program filter;
- if (pcap_compile(m_pcap.get(), &filter, filterString, 1, PCAP_NETMASK_UNKNOWN) < 0)
- BOOST_THROW_EXCEPTION(Error("pcap_compile: " + std::string(pcap_geterr(m_pcap.get()))));
-
- int ret = pcap_setfilter(m_pcap.get(), &filter);
- pcap_freecode(&filter);
- if (ret < 0)
- BOOST_THROW_EXCEPTION(Error("pcap_setfilter: " + std::string(pcap_geterr(m_pcap.get()))));
+ sendPacket(packet.packet);
}
void
EthernetTransport::sendPacket(const ndn::Block& block)
{
- /// \todo Right now there is no reserve when packet is received, but
- /// we should reserve some space at the beginning and at the end
ndn::EncodingBuffer buffer(block);
// pad with zeroes if the payload is too short
@@ -168,9 +115,9 @@
buffer.prependByteArray(m_destAddress.data(), m_destAddress.size());
// send the packet
- int sent = pcap_inject(m_pcap.get(), buffer.buf(), buffer.size());
+ int sent = pcap_inject(m_pcap, buffer.buf(), buffer.size());
if (sent < 0)
- NFD_LOG_FACE_ERROR("pcap_inject failed: " << pcap_geterr(m_pcap.get()));
+ NFD_LOG_FACE_ERROR("pcap_inject failed: " << m_pcap.getLastError());
else if (static_cast<size_t>(sent) < buffer.size())
NFD_LOG_FACE_ERROR("Failed to send the full frame: bufsize=" << buffer.size() << " sent=" << sent);
else
@@ -184,29 +131,21 @@
if (error)
return processErrorCode(error);
- pcap_pkthdr* header;
- const uint8_t* packet;
+ const uint8_t* pkt;
+ size_t len;
+ std::string err;
+ std::tie(pkt, len, err) = m_pcap.readNextPacket();
- // read the pcap header and packet data
- int ret = pcap_next_ex(m_pcap.get(), &header, &packet);
- if (ret < 0)
- NFD_LOG_FACE_ERROR("pcap_next_ex failed: " << pcap_geterr(m_pcap.get()));
- else if (ret == 0)
- NFD_LOG_FACE_WARN("Read timeout");
+ if (pkt == nullptr)
+ NFD_LOG_FACE_ERROR("Read error: " << err);
else
- processIncomingPacket(header, packet);
+ processIncomingPacket(pkt, len);
#ifdef _DEBUG
- pcap_stat ps{};
- ret = pcap_stats(m_pcap.get(), &ps);
- if (ret < 0) {
- NFD_LOG_FACE_DEBUG("pcap_stats failed: " << pcap_geterr(m_pcap.get()));
- }
- else if (ret == 0) {
- if (ps.ps_drop - m_nDropped > 0)
- NFD_LOG_FACE_DEBUG("Detected " << ps.ps_drop - m_nDropped << " dropped packet(s)");
- m_nDropped = ps.ps_drop;
- }
+ size_t nDropped = m_pcap.getNDropped();
+ if (nDropped - m_nDropped > 0)
+ NFD_LOG_FACE_DEBUG("Detected " << nDropped - m_nDropped << " dropped packet(s)");
+ m_nDropped = nDropped;
#endif
m_socket.async_read_some(boost::asio::null_buffers(),
@@ -216,9 +155,8 @@
}
void
-EthernetTransport::processIncomingPacket(const pcap_pkthdr* header, const uint8_t* packet)
+EthernetTransport::processIncomingPacket(const uint8_t* packet, size_t length)
{
- size_t length = header->caplen;
if (length < ethernet::HDR_LEN + ethernet::MIN_DATA_LEN) {
NFD_LOG_FACE_WARN("Received frame is too short (" << length << " bytes)");
return;
@@ -274,7 +212,7 @@
NFD_LOG_FACE_WARN("Receive operation failed: " << error.message());
}
-size_t
+int
EthernetTransport::getInterfaceMtu()
{
#ifdef SIOCGIFMTU
@@ -288,11 +226,11 @@
#endif
ifreq ifr{};
- std::strncpy(ifr.ifr_name, m_interfaceName.c_str(), sizeof(ifr.ifr_name) - 1);
+ std::strncpy(ifr.ifr_name, m_interfaceName.data(), sizeof(ifr.ifr_name) - 1);
if (::ioctl(fd, SIOCGIFMTU, &ifr) == 0) {
NFD_LOG_FACE_DEBUG("Interface MTU is " << ifr.ifr_mtu);
- return static_cast<size_t>(ifr.ifr_mtu);
+ return ifr.ifr_mtu;
}
NFD_LOG_FACE_WARN("Failed to get interface MTU: " << std::strerror(errno));