dev-arm: Move GICv3 (Re)Ditributor address in Realview.py
[gem5.git] / src / dev / net / ethertap.cc
index f08de0ebf7f50358d6a1b5cca15771b9b2d3e08c..552296d5c89aebb1c7377e9e6c916d4b23ae43f3 100644 (file)
 #include <sys/param.h>
 
 #endif
+
+#if USE_TUNTAP && defined(__linux__)
+#if 1 // Hide from the style checker since these have to be out of order.
+#include <sys/socket.h> // Has to be included before if.h for some reason.
+
+#endif
+
+#include <linux/if.h>
+#include <linux/if_tun.h>
+
+#endif
+
+#include <fcntl.h>
 #include <netinet/in.h>
+#include <sys/ioctl.h>
 #include <unistd.h>
 
+#include <cstring>
 #include <deque>
 #include <string>
 
-#include "base/misc.hh"
+#include "base/logging.hh"
 #include "base/pollevent.hh"
 #include "base/socket.hh"
 #include "base/trace.hh"
@@ -64,12 +79,22 @@ class TapEvent : public PollEvent
   public:
     TapEvent(EtherTapBase *_tap, int fd, int e)
         : PollEvent(fd, e), tap(_tap) {}
-    virtual void process(int revent) { tap->recvReal(revent); }
+
+    void
+    process(int revent) override
+    {
+        // Ensure that our event queue is active. It may not be since we get
+        // here from the PollQueue whenever a real packet happens to arrive.
+        EventQueue::ScopedMigration migrate(tap->eventQueue());
+
+        tap->recvReal(revent);
+    }
 };
 
 EtherTapBase::EtherTapBase(const Params *p)
-    : EtherObject(p), buflen(p->bufsz), dump(p->dump), event(NULL),
-      interface(NULL), txEvent(this)
+    : SimObject(p), buflen(p->bufsz), dump(p->dump), event(NULL),
+      interface(NULL),
+      txEvent([this]{ retransmit(); }, "EtherTapBase retransmit")
 {
     buffer = new uint8_t[buflen];
     interface = new EtherTapInt(name() + ".interface", this);
@@ -134,15 +159,12 @@ EtherTapBase::stopPolling()
 }
 
 
-EtherInt*
-EtherTapBase::getEthPort(const std::string &if_name, int idx)
+Port &
+EtherTapBase::getPort(const std::string &if_name, PortID idx)
 {
-    if (if_name == "tap") {
-        if (interface->getPeer())
-            panic("Interface already connected to\n");
-        return interface;
-    }
-    return NULL;
+    if (if_name == "tap")
+        return *interface;
+    return SimObject::getPort(if_name, idx);
 }
 
 bool
@@ -377,6 +399,86 @@ EtherTapStub::sendReal(const void *data, size_t len)
 }
 
 
+#if USE_TUNTAP
+
+EtherTap::EtherTap(const Params *p) : EtherTapBase(p)
+{
+    int fd = open(p->tun_clone_device.c_str(), O_RDWR | O_NONBLOCK);
+    if (fd < 0)
+        panic("Couldn't open %s.\n", p->tun_clone_device);
+
+    struct ifreq ifr;
+    memset(&ifr, 0, sizeof(ifr));
+    ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+    strncpy(ifr.ifr_name, p->tap_device_name.c_str(), IFNAMSIZ - 1);
+
+    if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0)
+        panic("Failed to access tap device %s.\n", ifr.ifr_name);
+    // fd now refers to the tap device.
+    tap = fd;
+    pollFd(tap);
+}
+
+EtherTap::~EtherTap()
+{
+    stopPolling();
+    close(tap);
+    tap = -1;
+}
+
+void
+EtherTap::recvReal(int revent)
+{
+    if (revent & POLLERR)
+        panic("Error polling for tap data.\n");
+
+    if (!(revent & POLLIN))
+        return;
+
+    ssize_t ret;
+    while ((ret = read(tap, buffer, buflen))) {
+        if (ret < 0) {
+            if (errno == EAGAIN)
+                break;
+            panic("Failed to read from tap device.\n");
+        }
+
+        sendSimulated(buffer, ret);
+    }
+}
+
+bool
+EtherTap::sendReal(const void *data, size_t len)
+{
+    int n;
+    pollfd pfd[1];
+    pfd->fd = tap;
+    pfd->events = POLLOUT;
+
+    // `tap` is a nonblock fd. Here we try to write until success, and use
+    // poll to make a blocking wait.
+    while ((n = write(tap, data, len)) != len) {
+        if (errno != EAGAIN)
+            panic("Failed to write data to tap device.\n");
+        pfd->revents = 0;
+        int ret = poll(pfd, 1, -1);
+        // timeout is set to inf, we shouldn't get 0 in any case.
+        assert(ret != 0);
+        if (ret == -1 || (ret == 1 && (pfd->revents & POLLERR))) {
+            panic("Failed when polling to write data to tap device.\n");
+        }
+    }
+    return true;
+}
+
+EtherTap *
+EtherTapParams::create()
+{
+    return new EtherTap(this);
+}
+
+#endif
+
 EtherTapStub *
 EtherTapStubParams::create()
 {