gem5  v22.1.0.0
ethertap.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2003-2005 The Regents of The University of Michigan
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met: redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer;
9  * redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution;
12  * neither the name of the copyright holders nor the names of its
13  * contributors may be used to endorse or promote products derived from
14  * this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /* @file
30  * Interface to connect a simulated ethernet device to the real world
31  */
32 
33 #include "dev/net/ethertap.hh"
34 
35 #if defined(__OpenBSD__) || defined(__APPLE__)
36 #include <sys/param.h>
37 
38 #endif
39 
40 #if HAVE_TUNTAP && defined(__linux__)
41 #if 1 // Hide from the style checker since these have to be out of order.
42 #include <sys/socket.h> // Has to be included before if.h for some reason.
43 
44 #endif
45 
46 #include <linux/if.h>
47 #include <linux/if_tun.h>
48 
49 #endif
50 
51 #include <fcntl.h>
52 #include <netinet/in.h>
53 #include <sys/ioctl.h>
54 #include <unistd.h>
55 
56 #include <cstring>
57 #include <deque>
58 #include <string>
59 
60 #include "base/logging.hh"
61 #include "base/pollevent.hh"
62 #include "base/socket.hh"
63 #include "base/trace.hh"
64 #include "debug/Ethernet.hh"
65 #include "debug/EthernetData.hh"
66 #include "dev/net/etherdump.hh"
67 #include "dev/net/etherint.hh"
68 #include "dev/net/etherpkt.hh"
69 #include "sim/core.hh"
70 #include "sim/cur_tick.hh"
71 
72 namespace gem5
73 {
74 
75 class TapEvent : public PollEvent
76 {
77  protected:
79 
80  public:
81  TapEvent(EtherTapBase *_tap, int fd, int e)
82  : PollEvent(fd, e), tap(_tap) {}
83 
84  void
85  process(int revent) override
86  {
87  // Ensure that our event queue is active. It may not be since we get
88  // here from the PollQueue whenever a real packet happens to arrive.
90 
91  tap->recvReal(revent);
92  }
93 };
94 
96  : SimObject(p), buflen(p.bufsz), dump(p.dump), event(NULL),
97  interface(NULL),
98  txEvent([this]{ retransmit(); }, "EtherTapBase retransmit")
99 {
100  buffer = new uint8_t[buflen];
101  interface = new EtherTapInt(name() + ".interface", this);
102 }
103 
105 {
106  delete buffer;
107  delete event;
108  delete interface;
109 }
110 
111 void
113 {
115  uint8_t *buffer = (uint8_t *)this->buffer;
117 
118  bool tapevent_present = false;
119  if (event) {
120  tapevent_present = true;
121  SERIALIZE_SCALAR(tapevent_present);
122  event->serialize(cp);
123  } else {
124  SERIALIZE_SCALAR(tapevent_present);
125  }
126 }
127 
128 void
130 {
132  uint8_t *buffer = (uint8_t *)this->buffer;
134 
135  bool tapevent_present;
136  UNSERIALIZE_SCALAR(tapevent_present);
137  if (tapevent_present) {
138  event = new TapEvent(this, 0, 0);
139  event->unserialize(cp);
140  if (event->queued())
142  }
143 }
144 
145 
146 void
148 {
149  assert(!event);
150  event = new TapEvent(this, fd, POLLIN|POLLERR);
152 }
153 
154 void
156 {
157  assert(event);
158  delete event;
159  event = NULL;
160 }
161 
162 
163 Port &
164 EtherTapBase::getPort(const std::string &if_name, PortID idx)
165 {
166  if (if_name == "tap")
167  return *interface;
168  return SimObject::getPort(if_name, idx);
169 }
170 
171 bool
173 {
174  if (dump)
175  dump->dump(packet);
176 
177  DPRINTF(Ethernet, "EtherTap sim->real len=%d\n", packet->length);
178  DDUMP(EthernetData, packet->data, packet->length);
179 
180  bool success = sendReal(packet->data, packet->length);
181 
182  interface->recvDone();
183 
184  return success;
185 }
186 
187 void
189 {
190  EthPacketPtr packet;
191  packet = std::make_shared<EthPacketData>(len);
192  packet->length = len;
193  packet->simLength = len;
194  memcpy(packet->data, data, len);
195 
196  DPRINTF(Ethernet, "EtherTap real->sim len=%d\n", packet->length);
197  DDUMP(EthernetData, packet->data, packet->length);
198  if (!packetBuffer.empty() || !interface->sendPacket(packet)) {
199  DPRINTF(Ethernet, "bus busy...buffer for retransmission\n");
200  packetBuffer.push(packet);
201  if (!txEvent.scheduled())
203  } else if (dump) {
204  dump->dump(packet);
205  }
206 }
207 
208 void
210 {
211  if (packetBuffer.empty())
212  return;
213 
214  EthPacketPtr packet = packetBuffer.front();
215  if (interface->sendPacket(packet)) {
216  if (dump)
217  dump->dump(packet);
218  DPRINTF(Ethernet, "EtherTap retransmit\n");
219  packetBuffer.front() = NULL;
220  packetBuffer.pop();
221  }
222 
223  if (!packetBuffer.empty() && !txEvent.scheduled())
225 }
226 
227 
229 {
230  protected:
231  class Event : public PollEvent
232  {
233  protected:
235 
236  public:
237  Event(TapListener *l, int fd, int e) : PollEvent(fd, e), listener(l) {}
238 
239  void process(int revent) override { listener->accept(); }
240  };
241 
242  friend class Event;
244 
245  void accept();
246 
247  protected:
250  int port;
251 
252  public:
253  TapListener(EtherTapStub *t, int p) : event(NULL), tap(t), port(p) {}
254  ~TapListener() { delete event; }
255 
256  void listen();
257 };
258 
259 void
261 {
262  while (!listener.listen(port, true)) {
263  DPRINTF(Ethernet, "TapListener(listen): Can't bind port %d\n", port);
264  port++;
265  }
266 
267  ccprintf(std::cerr, "Listening for tap connection on port %d\n", port);
268  event = new Event(this, listener.getfd(), POLLIN|POLLERR);
270 }
271 
272 void
274 {
275  // As a consequence of being called from the PollQueue, we might
276  // have been called from a different thread. Migrate to "our"
277  // thread.
279 
280  if (!listener.islistening())
281  panic("TapListener(accept): cannot accept if we're not listening!");
282 
283  int sfd = listener.accept(true);
284  if (sfd != -1)
285  tap->attach(sfd);
286 }
287 
288 
290 {
292  fatal("All listeners are disabled! EtherTapStub can't work!");
293 
294  listener = new TapListener(this, p.port);
295  listener->listen();
296 }
297 
299 {
300  delete listener;
301 }
302 
303 void
305 {
307 
311 }
312 
313 void
315 {
317 
321 }
322 
323 
324 void
326 {
327  if (socket != -1)
328  close(fd);
329 
330  buffer_used = 0;
331  frame_len = 0;
332  socket = fd;
333  DPRINTF(Ethernet, "EtherTapStub attached\n");
334  pollFd(socket);
335 }
336 
337 void
339 {
340  DPRINTF(Ethernet, "EtherTapStub detached\n");
341  stopPolling();
342  close(socket);
343  socket = -1;
344 }
345 
346 void
348 {
349  if (revent & POLLERR) {
350  detach();
351  return;
352  }
353 
354  if (!(revent & POLLIN))
355  return;
356 
357  // Read in as much of the new data as we can.
358  int len = read(socket, buffer + buffer_used, buflen - buffer_used);
359  if (len == 0) {
360  detach();
361  return;
362  }
363  buffer_used += len;
364 
365  // If there's not enough data for the frame length, wait for more.
366  if (buffer_used < sizeof(uint32_t))
367  return;
368 
369  if (frame_len == 0)
370  frame_len = ntohl(*(uint32_t *)buffer);
371 
372  DPRINTF(Ethernet, "Received data from peer: len=%d buffer_used=%d "
373  "frame_len=%d\n", len, buffer_used, frame_len);
374 
375  uint8_t *frame_start = &buffer[sizeof(uint32_t)];
376  while (frame_len != 0 && buffer_used >= frame_len + sizeof(uint32_t)) {
377  sendSimulated(frame_start, frame_len);
378 
379  // Bookkeeping.
380  buffer_used -= frame_len + sizeof(uint32_t);
381  if (buffer_used > 0) {
382  // If there's still any data left, move it into position.
383  memmove(buffer, frame_start + frame_len, buffer_used);
384  }
385  frame_len = 0;
386 
387  if (buffer_used >= sizeof(uint32_t))
388  frame_len = ntohl(*(uint32_t *)buffer);
389  }
390 }
391 
392 bool
393 EtherTapStub::sendReal(const void *data, size_t len)
394 {
395  uint32_t frame_len = htonl(len);
396  ssize_t ret = write(socket, &frame_len, sizeof(frame_len));
397  if (ret != sizeof(frame_len))
398  return false;
399  return write(socket, data, len) == len;
400 }
401 
402 
403 #if HAVE_TUNTAP
404 
405 EtherTap::EtherTap(const Params &p) : EtherTapBase(p)
406 {
407  int fd = open(p.tun_clone_device.c_str(), O_RDWR | O_NONBLOCK);
408  if (fd < 0)
409  panic("Couldn't open %s.\n", p.tun_clone_device);
410 
411  struct ifreq ifr;
412  memset(&ifr, 0, sizeof(ifr));
413  ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
414  strncpy(ifr.ifr_name, p.tap_device_name.c_str(), IFNAMSIZ - 1);
415 
416  if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0)
417  panic("Failed to access tap device %s.\n", ifr.ifr_name);
418  // fd now refers to the tap device.
419  tap = fd;
420  pollFd(tap);
421 }
422 
423 EtherTap::~EtherTap()
424 {
425  stopPolling();
426  close(tap);
427  tap = -1;
428 }
429 
430 void
431 EtherTap::recvReal(int revent)
432 {
433  if (revent & POLLERR)
434  panic("Error polling for tap data.\n");
435 
436  if (!(revent & POLLIN))
437  return;
438 
439  ssize_t ret;
440  while ((ret = read(tap, buffer, buflen))) {
441  if (ret < 0) {
442  if (errno == EAGAIN)
443  break;
444  panic("Failed to read from tap device.\n");
445  }
446 
447  sendSimulated(buffer, ret);
448  }
449 }
450 
451 bool
452 EtherTap::sendReal(const void *data, size_t len)
453 {
454  int n;
455  pollfd pfd[1];
456  pfd->fd = tap;
457  pfd->events = POLLOUT;
458 
459  // `tap` is a nonblock fd. Here we try to write until success, and use
460  // poll to make a blocking wait.
461  while ((n = write(tap, data, len)) != len) {
462  if (errno != EAGAIN)
463  panic("Failed to write data to tap device.\n");
464  pfd->revents = 0;
465  int ret = poll(pfd, 1, -1);
466  // timeout is set to inf, we shouldn't get 0 in any case.
467  assert(ret != 0);
468  if (ret == -1 || (ret == 1 && (pfd->revents & POLLERR))) {
469  panic("Failed when polling to write data to tap device.\n");
470  }
471  }
472  return true;
473 }
474 
475 #endif // HAVE_TUNTAP
476 
477 } // namespace gem5
#define DDUMP(x, data, count)
DPRINTF is a debugging trace facility that allows one to selectively enable tracing statements.
Definition: trace.hh:180
#define DPRINTF(x,...)
Definition: trace.hh:186
const char data[]
void dump(EthPacketPtr &pkt)
Definition: etherdump.hh:60
bool sendPacket(EthPacketPtr packet)
Definition: etherint.hh:73
void recvDone()
Definition: etherint.hh:70
Port & getPort(const std::string &if_name, PortID idx=InvalidPortID) override
Get a port with a given name and index.
Definition: ethertap.cc:164
void sendSimulated(void *data, size_t len)
Definition: ethertap.cc:188
void unserialize(CheckpointIn &cp) override
Unserialize an object.
Definition: ethertap.cc:129
TapEvent * event
Definition: ethertap.hh:81
virtual ~EtherTapBase()
Definition: ethertap.cc:104
EtherTapBaseParams Params
Definition: ethertap.hh:62
virtual void recvReal(int revent)=0
void pollFd(int fd)
Definition: ethertap.cc:147
EtherTapInt * interface
Definition: ethertap.hh:95
uint8_t * buffer
Definition: ethertap.hh:70
bool recvSimulated(EthPacketPtr packet)
Definition: ethertap.cc:172
void serialize(CheckpointOut &cp) const override
Serialize an object.
Definition: ethertap.cc:112
std::queue< EthPacketPtr > packetBuffer
Definition: ethertap.hh:105
friend class TapEvent
Definition: ethertap.hh:80
virtual bool sendReal(const void *data, size_t len)=0
EventFunctionWrapper txEvent
Definition: ethertap.hh:107
EtherTapBase(const Params &p)
Definition: ethertap.cc:95
EtherDump * dump
Definition: ethertap.hh:73
EtherTapStub(const Params &p)
Definition: ethertap.cc:289
void serialize(CheckpointOut &cp) const override
Serialize an object.
Definition: ethertap.cc:304
friend class TapListener
Definition: ethertap.hh:145
void attach(int fd)
Definition: ethertap.cc:325
void recvReal(int revent) override
Definition: ethertap.cc:347
uint32_t buffer_used
Definition: ethertap.hh:153
void unserialize(CheckpointIn &cp) override
Unserialize an object.
Definition: ethertap.cc:314
uint32_t frame_len
Definition: ethertap.hh:154
bool sendReal(const void *data, size_t len) override
Definition: ethertap.cc:393
TapListener * listener
Definition: ethertap.hh:146
virtual bool listen(int port, bool reuse=true)
Definition: socket.cc:122
virtual int accept(bool nodelay=false)
Definition: socket.cc:176
static bool allDisabled()
Definition: socket.cc:71
bool islistening() const
Definition: socket.hh:78
int getfd() const
Definition: socket.hh:77
Ports are used to interface objects to each other.
Definition: port.hh:62
Abstract superclass for simulation objects.
Definition: sim_object.hh:148
TapEvent(EtherTapBase *_tap, int fd, int e)
Definition: ethertap.cc:81
void process(int revent) override
Definition: ethertap.cc:85
EtherTapBase * tap
Definition: ethertap.cc:78
void process(int revent) override
Definition: ethertap.cc:239
Event(TapListener *l, int fd, int e)
Definition: ethertap.cc:237
TapListener * listener
Definition: ethertap.cc:234
TapListener(EtherTapStub *t, int p)
Definition: ethertap.cc:253
friend class Event
Definition: ethertap.cc:242
EtherTapStub * tap
Definition: ethertap.cc:249
ListenSocket listener
Definition: ethertap.cc:248
EventQueue * eventQueue() const
Definition: eventq.hh:1010
bool scheduled() const
Determine if the current event is scheduled.
Definition: eventq.hh:465
void schedule(Event &event, Tick when)
Definition: eventq.hh:1019
#define panic(...)
This implements a cprintf based panic() function.
Definition: logging.hh:178
#define fatal(...)
This implements a cprintf based fatal() function.
Definition: logging.hh:190
PollQueue pollQueue
Definition: pollevent.cc:55
void schedule(PollEvent *event)
Definition: pollevent.cc:159
#define UNSERIALIZE_ARRAY(member, size)
Definition: serialize.hh:618
#define SERIALIZE_ARRAY(member, size)
Definition: serialize.hh:610
virtual Port & getPort(const std::string &if_name, PortID idx=InvalidPortID)
Get a port with a given name and index.
Definition: sim_object.cc:126
uint16_t len
Definition: helpers.cc:62
Bitfield< 14, 12 > fd
Definition: types.hh:150
Bitfield< 31 > n
Definition: misc_types.hh:462
Bitfield< 9 > e
Definition: misc_types.hh:65
Bitfield< 10, 5 > event
Bitfield< 55 > l
Definition: pagetable.hh:54
Bitfield< 51 > t
Definition: pagetable.hh:56
Bitfield< 54 > p
Definition: pagetable.hh:70
Tick ns
nanosecond
Definition: core.cc:71
void dump()
Dump all statistics data to the registered outputs.
Definition: statistics.cc:301
Reference material can be found at the JEDEC website: UFS standard http://www.jedec....
Tick curTick()
The universal simulation clock.
Definition: cur_tick.hh:46
std::ostream CheckpointOut
Definition: serialize.hh:66
int16_t PortID
Port index/ID type, and a symbolic name for an invalid port id.
Definition: types.hh:245
void ccprintf(cp::Print &print)
Definition: cprintf.hh:130
std::shared_ptr< EthPacketData > EthPacketPtr
Definition: etherpkt.hh:90
#define UNSERIALIZE_SCALAR(scalar)
Definition: serialize.hh:575
#define SERIALIZE_SCALAR(scalar)
Definition: serialize.hh:568
const std::string & name()
Definition: trace.cc:49

Generated on Wed Dec 21 2022 10:22:34 for gem5 by doxygen 1.9.1