Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/viface/private/viface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#include <linux/if.h>
#include <linux/if_tun.h>
#include <linux/if_arp.h>
#include <sys/epoll.h>

// Interfaces
#include <ifaddrs.h> // getifaddrs
Expand Down Expand Up @@ -77,6 +78,7 @@ class VIfaceImpl
int kernel_socket;
int kernel_socket_ipv6;
vector<uint8_t> pktbuff;
int m_epoll_fd;

string name;
uint id;
Expand Down Expand Up @@ -151,6 +153,8 @@ class VIfaceImpl

vector<uint8_t> receive();

vector<uint8_t> receive(int timeout);

void send(vector<uint8_t>& packet) const;

set<string> listStats();
Expand Down
15 changes: 15 additions & 0 deletions include/viface/viface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,21 @@ class VIface
*/
std::vector<uint8_t> receive();

/**
* Receive a packet from the virtual interface.
*
* Note: Receive a packet from a virtual interface means that another
* userspace program (or the kernel) sent a packet to the network
* interface with the name of the instance of this class. If not packet
* was available, and empty vector is returned.
*
* @param[in] timeout epoll wait timeout, unit: millionseconds
*
* @return the packet (if tun) or frame (if tap) as a binary blob
* (array of bytes).
*/
std::vector<uint8_t> receive(int timeout);

/**
* Send a packet to this virtual interface.
*
Expand Down
104 changes: 103 additions & 1 deletion src/viface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,41 @@ VIfaceImpl::VIfaceImpl(string name, bool tap, int id)

this->queues = queues;

// epoll create
m_epoll_fd = epoll_create1(0);
if (m_epoll_fd == -1) {
ostringstream what;
what << "--- epoll_create1 fail.";
what << endl;
what << " Error: " << strerror(errno);
what << " (" << errno << ")." << endl;
throw runtime_error(what.str());
}

struct epoll_event ev = {
.events = EPOLLIN,
.data = { .fd = this->queues.rx },
};

if (epoll_ctl(m_epoll_fd, EPOLL_CTL_ADD, ev.data.fd, &ev) == -1) {
ostringstream what;
what << "--- epoll_ctl fail.";
what << "fd:" << ev.data.fd << endl;
what << " Error: " << strerror(errno);
what << " (" << errno << ")." << endl;
throw runtime_error(what.str());
}

ev.data.fd = this->queues.tx;
if (epoll_ctl(m_epoll_fd, EPOLL_CTL_ADD, ev.data.fd, &ev) == -1) {
ostringstream what;
what << "--- epoll_ctl fail.";
what << "fd:" << ev.data.fd << endl;
what << " Error: " << strerror(errno);
what << " (" << errno << ")." << endl;
throw runtime_error(what.str());
}

// Create socket channels to the NET kernel for later ioctl
this->kernel_socket = -1;
this->kernel_socket = socket(AF_INET, SOCK_STREAM, 0);
Expand Down Expand Up @@ -865,6 +900,68 @@ vector<uint8_t> VIfaceImpl::receive()
return packet;
}

vector<uint8_t> VIfaceImpl::receive(int timeout)
{
int nread = 0;
struct epoll_event wait_event;
int ret = 0;

ret = epoll_wait(m_epoll_fd, &wait_event, 1, timeout);
if ((ret < 0) && (errno != EINTR)) {
ostringstream what;
what << "--- epoll_wait fail.";
what << endl;
what << " Error: " << strerror(errno);
what << " (" << errno << ")." << endl;
throw runtime_error(what.str());
}

if (ret == 0) {
return vector<uint8_t>(0);
}

if (ret > 0) {
// Read packet into our buffer
nread = read(wait_event.data.fd, &(this->pktbuff[0]), this->mtu);
}

// Handle errors
if (nread == -1) {
// Nothing was read for this fd (non-blocking).
// This could happen, as http://linux.die.net/man/2/select states:
//
// Under Linux, select() may report a socket file descriptor as
// "ready for reading", while nevertheless a subsequent read
// blocks. This could for example happen when data has arrived
// but upon examination has wrong checksum and is discarded.
// There may be other circumstances in which a file descriptor
// is spuriously reported as ready. Thus it may be safer to
// use O_NONBLOCK on sockets that should not block.
//
// I know this is not a socket, but the "There may be other
// circumstances in which a file descriptor is spuriously reported
// as ready" warns it, and so, it better to do this that to have
// an application that frozes for no apparent reason.
//
if (errno == EAGAIN) {
return vector<uint8_t>(0);
}

// Something bad happened
ostringstream what;
what << "--- IO error while reading from " << this->name;
what << "." << endl;
what << " Error: " << strerror(errno);
what << " (" << errno << ")." << endl;
throw runtime_error(what.str());
}

// Copy packet from buffer and return
vector<uint8_t> packet(nread);
packet.assign(&(this->pktbuff[0]), &(this->pktbuff[nread]));
return packet;
}

void VIfaceImpl::send(vector<uint8_t>& packet) const
{
ostringstream what;
Expand Down Expand Up @@ -1207,6 +1304,11 @@ vector<uint8_t> VIface::receive()
return this->pimpl->receive();
}

vector<uint8_t> VIface::receive(int timeout)
{
return this->pimpl->receive(timeout);
}

void VIface::send(vector<uint8_t>& packet) const
{
return this->pimpl->send(packet);
Expand All @@ -1226,4 +1328,4 @@ void VIface::clearStat(std::string const& stat)
{
return this->pimpl->clearStat(stat);
}
}
}