diff --git a/include/viface/private/viface.hpp b/include/viface/private/viface.hpp index e621e80..e134333 100644 --- a/include/viface/private/viface.hpp +++ b/include/viface/private/viface.hpp @@ -44,6 +44,7 @@ #include #include #include +#include // Interfaces #include // getifaddrs @@ -77,6 +78,7 @@ class VIfaceImpl int kernel_socket; int kernel_socket_ipv6; vector pktbuff; + int m_epoll_fd; string name; uint id; @@ -151,6 +153,8 @@ class VIfaceImpl vector receive(); + vector receive(int timeout); + void send(vector& packet) const; set listStats(); diff --git a/include/viface/viface.hpp b/include/viface/viface.hpp index 722d004..eea86cf 100644 --- a/include/viface/viface.hpp +++ b/include/viface/viface.hpp @@ -308,6 +308,21 @@ class VIface */ std::vector 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 receive(int timeout); + /** * Send a packet to this virtual interface. * diff --git a/src/viface.cpp b/src/viface.cpp index 1251312..3bbfcae 100644 --- a/src/viface.cpp +++ b/src/viface.cpp @@ -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); @@ -865,6 +900,68 @@ vector VIfaceImpl::receive() return packet; } +vector 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(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(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 packet(nread); + packet.assign(&(this->pktbuff[0]), &(this->pktbuff[nread])); + return packet; +} + void VIfaceImpl::send(vector& packet) const { ostringstream what; @@ -1207,6 +1304,11 @@ vector VIface::receive() return this->pimpl->receive(); } +vector VIface::receive(int timeout) +{ + return this->pimpl->receive(timeout); +} + void VIface::send(vector& packet) const { return this->pimpl->send(packet); @@ -1226,4 +1328,4 @@ void VIface::clearStat(std::string const& stat) { return this->pimpl->clearStat(stat); } -} \ No newline at end of file +}