X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fdev%2Fnet%2Fethertap.cc;h=552296d5c89aebb1c7377e9e6c916d4b23ae43f3;hb=40018b14a608b066a52c4baf86b4160820402c9c;hp=e7b928972dc412a2b371312e609d96da3491a570;hpb=f9ad4066d71748829c11868b5b92ba092ee9513b;p=gem5.git diff --git a/src/dev/net/ethertap.cc b/src/dev/net/ethertap.cc index e7b928972..552296d5c 100644 --- a/src/dev/net/ethertap.cc +++ b/src/dev/net/ethertap.cc @@ -38,13 +38,28 @@ #include #endif + +#if USE_TUNTAP && defined(__linux__) +#if 1 // Hide from the style checker since these have to be out of order. +#include // Has to be included before if.h for some reason. + +#endif + +#include +#include + +#endif + +#include #include +#include #include +#include #include #include -#include "base/misc.hh" +#include "base/logging.hh" #include "base/pollevent.hh" #include "base/socket.hh" #include "base/trace.hh" @@ -56,39 +71,187 @@ using namespace std; -/** - */ +class TapEvent : public PollEvent +{ + protected: + EtherTapBase *tap; + + public: + TapEvent(EtherTapBase *_tap, int fd, int e) + : PollEvent(fd, e), tap(_tap) {} + + 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) + : 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); +} + +EtherTapBase::~EtherTapBase() +{ + delete buffer; + delete event; + delete interface; +} + +void +EtherTapBase::serialize(CheckpointOut &cp) const +{ + SERIALIZE_SCALAR(buflen); + uint8_t *buffer = (uint8_t *)this->buffer; + SERIALIZE_ARRAY(buffer, buflen); + + bool tapevent_present = false; + if (event) { + tapevent_present = true; + SERIALIZE_SCALAR(tapevent_present); + event->serialize(cp); + } else { + SERIALIZE_SCALAR(tapevent_present); + } +} + +void +EtherTapBase::unserialize(CheckpointIn &cp) +{ + UNSERIALIZE_SCALAR(buflen); + uint8_t *buffer = (uint8_t *)this->buffer; + UNSERIALIZE_ARRAY(buffer, buflen); + + bool tapevent_present; + UNSERIALIZE_SCALAR(tapevent_present); + if (tapevent_present) { + event = new TapEvent(this, 0, 0); + event->unserialize(cp); + if (event->queued()) + pollQueue.schedule(event); + } +} + + +void +EtherTapBase::pollFd(int fd) +{ + assert(!event); + event = new TapEvent(this, fd, POLLIN|POLLERR); + pollQueue.schedule(event); +} + +void +EtherTapBase::stopPolling() +{ + assert(event); + delete event; + event = NULL; +} + + +Port & +EtherTapBase::getPort(const std::string &if_name, PortID idx) +{ + if (if_name == "tap") + return *interface; + return SimObject::getPort(if_name, idx); +} + +bool +EtherTapBase::recvSimulated(EthPacketPtr packet) +{ + if (dump) + dump->dump(packet); + + DPRINTF(Ethernet, "EtherTap sim->real len=%d\n", packet->length); + DDUMP(EthernetData, packet->data, packet->length); + + bool success = sendReal(packet->data, packet->length); + + interface->recvDone(); + + return success; +} + +void +EtherTapBase::sendSimulated(void *data, size_t len) +{ + EthPacketPtr packet; + packet = make_shared(len); + packet->length = len; + packet->simLength = len; + memcpy(packet->data, data, len); + + DPRINTF(Ethernet, "EtherTap real->sim len=%d\n", packet->length); + DDUMP(EthernetData, packet->data, packet->length); + if (!packetBuffer.empty() || !interface->sendPacket(packet)) { + DPRINTF(Ethernet, "bus busy...buffer for retransmission\n"); + packetBuffer.push(packet); + if (!txEvent.scheduled()) + schedule(txEvent, curTick() + retryTime); + } else if (dump) { + dump->dump(packet); + } +} + +void +EtherTapBase::retransmit() +{ + if (packetBuffer.empty()) + return; + + EthPacketPtr packet = packetBuffer.front(); + if (interface->sendPacket(packet)) { + if (dump) + dump->dump(packet); + DPRINTF(Ethernet, "EtherTap retransmit\n"); + packetBuffer.front() = NULL; + packetBuffer.pop(); + } + + if (!packetBuffer.empty() && !txEvent.scheduled()) + schedule(txEvent, curTick() + retryTime); +} + + class TapListener { protected: - /** - */ class Event : public PollEvent { protected: TapListener *listener; public: - Event(TapListener *l, int fd, int e) - : PollEvent(fd, e), listener(l) {} + Event(TapListener *l, int fd, int e) : PollEvent(fd, e), listener(l) {} - virtual void process(int revent) { listener->accept(); } + void process(int revent) override { listener->accept(); } }; friend class Event; Event *event; + void accept(); + protected: ListenSocket listener; EtherTapStub *tap; int port; public: - TapListener(EtherTapStub *t, int p) - : event(NULL), tap(t), port(p) {} - ~TapListener() { if (event) delete event; } + TapListener(EtherTapStub *t, int p) : event(NULL), tap(t), port(p) {} + ~TapListener() { delete event; } - void accept(); void listen(); }; @@ -121,226 +284,200 @@ TapListener::accept() tap->attach(sfd); } -/** - */ -class TapEvent : public PollEvent -{ - protected: - EtherTapStub *tap; - - public: - TapEvent(EtherTapStub *_tap, int fd, int e) - : PollEvent(fd, e), tap(_tap) {} - virtual void process(int revent) { tap->process(revent); } -}; -EtherTapStub::EtherTapStub(const Params *p) - : EtherObject(p), event(NULL), socket(-1), buflen(p->bufsz), dump(p->dump), - interface(NULL), txEvent(this) +EtherTapStub::EtherTapStub(const Params *p) : EtherTapBase(p), socket(-1) { if (ListenSocket::allDisabled()) fatal("All listeners are disabled! EtherTapStub can't work!"); - buffer = new char[buflen]; listener = new TapListener(this, p->port); listener->listen(); - interface = new EtherTapInt(name() + ".interface", this); } EtherTapStub::~EtherTapStub() { - if (event) - delete event; - if (buffer) - delete [] buffer; - - delete interface; delete listener; } +void +EtherTapStub::serialize(CheckpointOut &cp) const +{ + EtherTapBase::serialize(cp); + + SERIALIZE_SCALAR(socket); + SERIALIZE_SCALAR(buffer_used); + SERIALIZE_SCALAR(frame_len); +} + +void +EtherTapStub::unserialize(CheckpointIn &cp) +{ + EtherTapBase::unserialize(cp); + + UNSERIALIZE_SCALAR(socket); + UNSERIALIZE_SCALAR(buffer_used); + UNSERIALIZE_SCALAR(frame_len); +} + + void EtherTapStub::attach(int fd) { if (socket != -1) close(fd); - buffer_offset = 0; - data_len = 0; + buffer_used = 0; + frame_len = 0; socket = fd; DPRINTF(Ethernet, "EtherTapStub attached\n"); - event = new TapEvent(this, socket, POLLIN|POLLERR); - pollQueue.schedule(event); + pollFd(socket); } void EtherTapStub::detach() { DPRINTF(Ethernet, "EtherTapStub detached\n"); - delete event; - event = 0; + stopPolling(); close(socket); socket = -1; } -bool -EtherTapStub::recvPacket(EthPacketPtr packet) -{ - if (dump) - dump->dump(packet); - - DPRINTF(Ethernet, "EtherTapStub output len=%d\n", packet->length); - DDUMP(EthernetData, packet->data, packet->length); - uint32_t len = htonl(packet->length); - ssize_t ret = write(socket, &len, sizeof(len)); - if (ret != sizeof(len)) - return false; - ret = write(socket, packet->data, packet->length); - if (ret != packet->length) - return false; - - interface->recvDone(); - - return true; -} - -void -EtherTapStub::sendDone() -{} - void -EtherTapStub::process(int revent) +EtherTapStub::recvReal(int revent) { if (revent & POLLERR) { detach(); return; } - char *data = buffer + sizeof(uint32_t); if (!(revent & POLLIN)) return; - if (buffer_offset < data_len + sizeof(uint32_t)) { - int len = read(socket, buffer + buffer_offset, buflen - buffer_offset); - if (len == 0) { - detach(); - return; - } + // Read in as much of the new data as we can. + int len = read(socket, buffer + buffer_used, buflen - buffer_used); + if (len == 0) { + detach(); + return; + } + buffer_used += len; - buffer_offset += len; + // If there's not enough data for the frame length, wait for more. + if (buffer_used < sizeof(uint32_t)) + return; - if (data_len == 0) - data_len = ntohl(*(uint32_t *)buffer); + if (frame_len == 0) + frame_len = ntohl(*(uint32_t *)buffer); - DPRINTF(Ethernet, "Received data from peer: len=%d buffer_offset=%d " - "data_len=%d\n", len, buffer_offset, data_len); - } + DPRINTF(Ethernet, "Received data from peer: len=%d buffer_used=%d " + "frame_len=%d\n", len, buffer_used, frame_len); - while (data_len != 0 && buffer_offset >= data_len + sizeof(uint32_t)) { - EthPacketPtr packet; - packet = make_shared(data_len); - packet->length = data_len; - packet->simLength = data_len; - memcpy(packet->data, data, data_len); - - assert(buffer_offset >= data_len + sizeof(uint32_t)); - buffer_offset -= data_len + sizeof(uint32_t); - if (buffer_offset > 0) { - memmove(buffer, data + data_len, buffer_offset); - data_len = ntohl(*(uint32_t *)buffer); - } else - data_len = 0; - - DPRINTF(Ethernet, "EtherTapStub input len=%d\n", packet->length); - DDUMP(EthernetData, packet->data, packet->length); - if (!interface->sendPacket(packet)) { - DPRINTF(Ethernet, "bus busy...buffer for retransmission\n"); - packetBuffer.push(packet); - if (!txEvent.scheduled()) - schedule(txEvent, curTick() + retryTime); - } else if (dump) { - dump->dump(packet); - } - } -} + uint8_t *frame_start = &buffer[sizeof(uint32_t)]; + while (frame_len != 0 && buffer_used >= frame_len + sizeof(uint32_t)) { + sendSimulated(frame_start, frame_len); -void -EtherTapStub::retransmit() -{ - if (packetBuffer.empty()) - return; + // Bookkeeping. + buffer_used -= frame_len + sizeof(uint32_t); + if (buffer_used > 0) { + // If there's still any data left, move it into position. + memmove(buffer, frame_start + frame_len, buffer_used); + } + frame_len = 0; - EthPacketPtr packet = packetBuffer.front(); - if (interface->sendPacket(packet)) { - if (dump) - dump->dump(packet); - DPRINTF(Ethernet, "EtherTapStub retransmit\n"); - packetBuffer.front() = NULL; - packetBuffer.pop(); + if (buffer_used >= sizeof(uint32_t)) + frame_len = ntohl(*(uint32_t *)buffer); } - - if (!packetBuffer.empty() && !txEvent.scheduled()) - schedule(txEvent, curTick() + retryTime); } -EtherInt* -EtherTapStub::getEthPort(const std::string &if_name, int idx) +bool +EtherTapStub::sendReal(const void *data, size_t len) { - if (if_name == "tap") { - if (interface->getPeer()) - panic("Interface already connected to\n"); - return interface; - } - return NULL; + uint32_t frame_len = htonl(len); + ssize_t ret = write(socket, &frame_len, sizeof(frame_len)); + if (ret != sizeof(frame_len)) + return false; + return write(socket, data, len) == len; } -//===================================================================== +#if USE_TUNTAP -void -EtherTapStub::serialize(CheckpointOut &cp) const +EtherTap::EtherTap(const Params *p) : EtherTapBase(p) { - SERIALIZE_SCALAR(socket); - SERIALIZE_SCALAR(buflen); - uint8_t *buffer = (uint8_t *)this->buffer; - SERIALIZE_ARRAY(buffer, buflen); - SERIALIZE_SCALAR(buffer_offset); - SERIALIZE_SCALAR(data_len); + 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); +} - bool tapevent_present = false; - if (event) { - tapevent_present = true; - SERIALIZE_SCALAR(tapevent_present); - event->serialize(cp); - } - else { - SERIALIZE_SCALAR(tapevent_present); - } +EtherTap::~EtherTap() +{ + stopPolling(); + close(tap); + tap = -1; } void -EtherTapStub::unserialize(CheckpointIn &cp) +EtherTap::recvReal(int revent) { - UNSERIALIZE_SCALAR(socket); - UNSERIALIZE_SCALAR(buflen); - uint8_t *buffer = (uint8_t *)this->buffer; - UNSERIALIZE_ARRAY(buffer, buflen); - UNSERIALIZE_SCALAR(buffer_offset); - UNSERIALIZE_SCALAR(data_len); + if (revent & POLLERR) + panic("Error polling for tap data.\n"); - bool tapevent_present; - UNSERIALIZE_SCALAR(tapevent_present); - if (tapevent_present) { - event = new TapEvent(this, socket, POLLIN|POLLERR); + if (!(revent & POLLIN)) + return; - event->unserialize(cp); + ssize_t ret; + while ((ret = read(tap, buffer, buflen))) { + if (ret < 0) { + if (errno == EAGAIN) + break; + panic("Failed to read from tap device.\n"); + } - if (event->queued()) { - pollQueue.schedule(event); + 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()