summaryrefslogtreecommitdiff
path: root/src/dev/net/ethertap.cc
diff options
context:
space:
mode:
authorGabe Black <gabeblack@google.com>2017-06-03 05:03:18 -0700
committerGabe Black <gabeblack@google.com>2017-06-03 15:23:46 +0000
commit2ce045341b73166cad2ef95b37c87d8d4c590d80 (patch)
treeeb46ce8c910cd04d39477da459915802105c5729 /src/dev/net/ethertap.cc
parentf9ad4066d71748829c11868b5b92ba092ee9513b (diff)
downloadgem5-2ce045341b73166cad2ef95b37c87d8d4c590d80.tar.xz
dev: Refactor the EtherTapStub to make room for using tap.
A lot of the implementation of EtherTapStub can be shared with a version which uses a tap device directly. This change factors out those parts to accommodate that. Change-Id: I9c2e31f1be139ca73859a83f05457cef90101006 Reviewed-on: https://gem5-review.googlesource.com/3645 Reviewed-by: Nathan Binkert <nate@binkert.org> Maintainer: Nathan Binkert <nate@binkert.org>
Diffstat (limited to 'src/dev/net/ethertap.cc')
-rw-r--r--src/dev/net/ethertap.cc385
1 files changed, 210 insertions, 175 deletions
diff --git a/src/dev/net/ethertap.cc b/src/dev/net/ethertap.cc
index e7b928972..f08de0ebf 100644
--- a/src/dev/net/ethertap.cc
+++ b/src/dev/net/ethertap.cc
@@ -56,39 +56,180 @@
using namespace std;
-/**
- */
+class TapEvent : public PollEvent
+{
+ protected:
+ EtherTapBase *tap;
+
+ public:
+ TapEvent(EtherTapBase *_tap, int fd, int e)
+ : PollEvent(fd, e), tap(_tap) {}
+ virtual void process(int revent) { tap->recvReal(revent); }
+};
+
+EtherTapBase::EtherTapBase(const Params *p)
+ : EtherObject(p), buflen(p->bufsz), dump(p->dump), event(NULL),
+ interface(NULL), txEvent(this)
+{
+ 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;
+}
+
+
+EtherInt*
+EtherTapBase::getEthPort(const std::string &if_name, int idx)
+{
+ if (if_name == "tap") {
+ if (interface->getPeer())
+ panic("Interface already connected to\n");
+ return interface;
+ }
+ return NULL;
+}
+
+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<EthPacketData>(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 +262,120 @@ 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;
- }
-
- buffer_offset += len;
-
- if (data_len == 0)
- data_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);
- }
-
- while (data_len != 0 && buffer_offset >= data_len + sizeof(uint32_t)) {
- EthPacketPtr packet;
- packet = make_shared<EthPacketData>(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);
- }
- }
-}
-
-void
-EtherTapStub::retransmit()
-{
- if (packetBuffer.empty())
+ // 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;
-
- EthPacketPtr packet = packetBuffer.front();
- if (interface->sendPacket(packet)) {
- if (dump)
- dump->dump(packet);
- DPRINTF(Ethernet, "EtherTapStub retransmit\n");
- packetBuffer.front() = NULL;
- packetBuffer.pop();
}
+ buffer_used += len;
- if (!packetBuffer.empty() && !txEvent.scheduled())
- schedule(txEvent, curTick() + retryTime);
-}
+ // If there's not enough data for the frame length, wait for more.
+ if (buffer_used < sizeof(uint32_t))
+ return;
-EtherInt*
-EtherTapStub::getEthPort(const std::string &if_name, int idx)
-{
- if (if_name == "tap") {
- if (interface->getPeer())
- panic("Interface already connected to\n");
- return interface;
- }
- return NULL;
-}
+ if (frame_len == 0)
+ frame_len = ntohl(*(uint32_t *)buffer);
+ DPRINTF(Ethernet, "Received data from peer: len=%d buffer_used=%d "
+ "frame_len=%d\n", len, buffer_used, frame_len);
-//=====================================================================
+ 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::serialize(CheckpointOut &cp) const
-{
- 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);
+ // 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;
- bool tapevent_present = false;
- if (event) {
- tapevent_present = true;
- SERIALIZE_SCALAR(tapevent_present);
- event->serialize(cp);
- }
- else {
- SERIALIZE_SCALAR(tapevent_present);
+ if (buffer_used >= sizeof(uint32_t))
+ frame_len = ntohl(*(uint32_t *)buffer);
}
}
-void
-EtherTapStub::unserialize(CheckpointIn &cp)
+bool
+EtherTapStub::sendReal(const void *data, size_t len)
{
- 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);
-
- bool tapevent_present;
- UNSERIALIZE_SCALAR(tapevent_present);
- if (tapevent_present) {
- event = new TapEvent(this, socket, POLLIN|POLLERR);
-
- event->unserialize(cp);
-
- if (event->queued()) {
- pollQueue.schedule(event);
- }
- }
+ 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;
}
-//=====================================================================
EtherTapStub *
EtherTapStubParams::create()