From 6db0918dfaa2d2747fe1431ed573151a962bd21f Mon Sep 17 00:00:00 2001 From: wangfs Date: Thu, 21 Jul 2022 13:50:14 +0800 Subject: [PATCH 1/3] add epoll read --- include/viface/private/viface.hpp | 4 ++ include/viface/viface.hpp | 15 +++++ src/viface.cpp | 98 +++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+) 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..100e771 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,64 @@ 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) { + ostringstream what; + what << "--- epoll_wait fail."; + what << endl; + what << " Error: " << strerror(errno); + what << " (" << errno << ")." << endl; + throw runtime_error(what.str()); + } + + 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 +1300,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); From 4e2e94313030b6b9737c896d61443261c48f05a9 Mon Sep 17 00:00:00 2001 From: wangfs Date: Tue, 26 Jul 2022 17:58:12 +0800 Subject: [PATCH 2/3] ignore epoll_wait INTR fail when gdb --- src/viface.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/viface.cpp b/src/viface.cpp index 100e771..2e725bc 100644 --- a/src/viface.cpp +++ b/src/viface.cpp @@ -907,7 +907,7 @@ vector VIfaceImpl::receive(int timeout) int ret = 0; ret = epoll_wait(m_epoll_fd, &wait_event, 1, timeout); - if (ret < 0) { + if ((ret < 0) && (errno != EINTR)) { ostringstream what; what << "--- epoll_wait fail."; what << endl; @@ -1324,4 +1324,4 @@ void VIface::clearStat(std::string const& stat) { return this->pimpl->clearStat(stat); } -} \ No newline at end of file +} From bc72afd1ea3bd8869d86a4802842dda2951f97c2 Mon Sep 17 00:00:00 2001 From: wangfs Date: Wed, 27 Jul 2022 10:33:20 +0800 Subject: [PATCH 3/3] enhance epoll ret == 0 --- src/viface.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/viface.cpp b/src/viface.cpp index 2e725bc..3bbfcae 100644 --- a/src/viface.cpp +++ b/src/viface.cpp @@ -916,6 +916,10 @@ vector VIfaceImpl::receive(int timeout) 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);