2 * Copyright (c) 2003-2005 The Regents of The University of Michigan
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.
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.
28 * Authors: Nathan Binkert
32 * Interface to connect a simulated ethernet device to the real world
35 #include "dev/net/ethertap.hh"
37 #if defined(__OpenBSD__) || defined(__APPLE__)
38 #include <sys/param.h>
42 #if USE_TUNTAP && defined(__linux__)
43 #if 1 // Hide from the style checker since these have to be out of order.
44 #include <sys/socket.h> // Has to be included before if.h for some reason.
49 #include <linux/if_tun.h>
54 #include <netinet/in.h>
55 #include <sys/ioctl.h>
62 #include "base/logging.hh"
63 #include "base/pollevent.hh"
64 #include "base/socket.hh"
65 #include "base/trace.hh"
66 #include "debug/Ethernet.hh"
67 #include "debug/EthernetData.hh"
68 #include "dev/net/etherdump.hh"
69 #include "dev/net/etherint.hh"
70 #include "dev/net/etherpkt.hh"
74 class TapEvent
: public PollEvent
80 TapEvent(EtherTapBase
*_tap
, int fd
, int e
)
81 : PollEvent(fd
, e
), tap(_tap
) {}
84 process(int revent
) override
86 // Ensure that our event queue is active. It may not be since we get
87 // here from the PollQueue whenever a real packet happens to arrive.
88 EventQueue::ScopedMigration
migrate(tap
->eventQueue());
90 tap
->recvReal(revent
);
94 EtherTapBase::EtherTapBase(const Params
*p
)
95 : SimObject(p
), buflen(p
->bufsz
), dump(p
->dump
), event(NULL
),
97 txEvent([this]{ retransmit(); }, "EtherTapBase retransmit")
99 buffer
= new uint8_t[buflen
];
100 interface
= new EtherTapInt(name() + ".interface", this);
103 EtherTapBase::~EtherTapBase()
111 EtherTapBase::serialize(CheckpointOut
&cp
) const
113 SERIALIZE_SCALAR(buflen
);
114 uint8_t *buffer
= (uint8_t *)this->buffer
;
115 SERIALIZE_ARRAY(buffer
, buflen
);
117 bool tapevent_present
= false;
119 tapevent_present
= true;
120 SERIALIZE_SCALAR(tapevent_present
);
121 event
->serialize(cp
);
123 SERIALIZE_SCALAR(tapevent_present
);
128 EtherTapBase::unserialize(CheckpointIn
&cp
)
130 UNSERIALIZE_SCALAR(buflen
);
131 uint8_t *buffer
= (uint8_t *)this->buffer
;
132 UNSERIALIZE_ARRAY(buffer
, buflen
);
134 bool tapevent_present
;
135 UNSERIALIZE_SCALAR(tapevent_present
);
136 if (tapevent_present
) {
137 event
= new TapEvent(this, 0, 0);
138 event
->unserialize(cp
);
140 pollQueue
.schedule(event
);
146 EtherTapBase::pollFd(int fd
)
149 event
= new TapEvent(this, fd
, POLLIN
|POLLERR
);
150 pollQueue
.schedule(event
);
154 EtherTapBase::stopPolling()
163 EtherTapBase::getEthPort(const std::string
&if_name
, int idx
)
165 if (if_name
== "tap") {
166 if (interface
->getPeer())
167 panic("Interface already connected to\n");
174 EtherTapBase::recvSimulated(EthPacketPtr packet
)
179 DPRINTF(Ethernet
, "EtherTap sim->real len=%d\n", packet
->length
);
180 DDUMP(EthernetData
, packet
->data
, packet
->length
);
182 bool success
= sendReal(packet
->data
, packet
->length
);
184 interface
->recvDone();
190 EtherTapBase::sendSimulated(void *data
, size_t len
)
193 packet
= make_shared
<EthPacketData
>(len
);
194 packet
->length
= len
;
195 packet
->simLength
= len
;
196 memcpy(packet
->data
, data
, len
);
198 DPRINTF(Ethernet
, "EtherTap real->sim len=%d\n", packet
->length
);
199 DDUMP(EthernetData
, packet
->data
, packet
->length
);
200 if (!packetBuffer
.empty() || !interface
->sendPacket(packet
)) {
201 DPRINTF(Ethernet
, "bus busy...buffer for retransmission\n");
202 packetBuffer
.push(packet
);
203 if (!txEvent
.scheduled())
204 schedule(txEvent
, curTick() + retryTime
);
211 EtherTapBase::retransmit()
213 if (packetBuffer
.empty())
216 EthPacketPtr packet
= packetBuffer
.front();
217 if (interface
->sendPacket(packet
)) {
220 DPRINTF(Ethernet
, "EtherTap retransmit\n");
221 packetBuffer
.front() = NULL
;
225 if (!packetBuffer
.empty() && !txEvent
.scheduled())
226 schedule(txEvent
, curTick() + retryTime
);
233 class Event
: public PollEvent
236 TapListener
*listener
;
239 Event(TapListener
*l
, int fd
, int e
) : PollEvent(fd
, e
), listener(l
) {}
241 void process(int revent
) override
{ listener
->accept(); }
250 ListenSocket listener
;
255 TapListener(EtherTapStub
*t
, int p
) : event(NULL
), tap(t
), port(p
) {}
256 ~TapListener() { delete event
; }
262 TapListener::listen()
264 while (!listener
.listen(port
, true)) {
265 DPRINTF(Ethernet
, "TapListener(listen): Can't bind port %d\n", port
);
269 ccprintf(cerr
, "Listening for tap connection on port %d\n", port
);
270 event
= new Event(this, listener
.getfd(), POLLIN
|POLLERR
);
271 pollQueue
.schedule(event
);
275 TapListener::accept()
277 // As a consequence of being called from the PollQueue, we might
278 // have been called from a different thread. Migrate to "our"
280 EventQueue::ScopedMigration
migrate(tap
->eventQueue());
282 if (!listener
.islistening())
283 panic("TapListener(accept): cannot accept if we're not listening!");
285 int sfd
= listener
.accept(true);
291 EtherTapStub::EtherTapStub(const Params
*p
) : EtherTapBase(p
), socket(-1)
293 if (ListenSocket::allDisabled())
294 fatal("All listeners are disabled! EtherTapStub can't work!");
296 listener
= new TapListener(this, p
->port
);
300 EtherTapStub::~EtherTapStub()
306 EtherTapStub::serialize(CheckpointOut
&cp
) const
308 EtherTapBase::serialize(cp
);
310 SERIALIZE_SCALAR(socket
);
311 SERIALIZE_SCALAR(buffer_used
);
312 SERIALIZE_SCALAR(frame_len
);
316 EtherTapStub::unserialize(CheckpointIn
&cp
)
318 EtherTapBase::unserialize(cp
);
320 UNSERIALIZE_SCALAR(socket
);
321 UNSERIALIZE_SCALAR(buffer_used
);
322 UNSERIALIZE_SCALAR(frame_len
);
327 EtherTapStub::attach(int fd
)
335 DPRINTF(Ethernet
, "EtherTapStub attached\n");
340 EtherTapStub::detach()
342 DPRINTF(Ethernet
, "EtherTapStub detached\n");
349 EtherTapStub::recvReal(int revent
)
351 if (revent
& POLLERR
) {
356 if (!(revent
& POLLIN
))
359 // Read in as much of the new data as we can.
360 int len
= read(socket
, buffer
+ buffer_used
, buflen
- buffer_used
);
367 // If there's not enough data for the frame length, wait for more.
368 if (buffer_used
< sizeof(uint32_t))
372 frame_len
= ntohl(*(uint32_t *)buffer
);
374 DPRINTF(Ethernet
, "Received data from peer: len=%d buffer_used=%d "
375 "frame_len=%d\n", len
, buffer_used
, frame_len
);
377 uint8_t *frame_start
= &buffer
[sizeof(uint32_t)];
378 while (frame_len
!= 0 && buffer_used
>= frame_len
+ sizeof(uint32_t)) {
379 sendSimulated(frame_start
, frame_len
);
382 buffer_used
-= frame_len
+ sizeof(uint32_t);
383 if (buffer_used
> 0) {
384 // If there's still any data left, move it into position.
385 memmove(buffer
, frame_start
+ frame_len
, buffer_used
);
389 if (buffer_used
>= sizeof(uint32_t))
390 frame_len
= ntohl(*(uint32_t *)buffer
);
395 EtherTapStub::sendReal(const void *data
, size_t len
)
397 uint32_t frame_len
= htonl(len
);
398 ssize_t ret
= write(socket
, &frame_len
, sizeof(frame_len
));
399 if (ret
!= sizeof(frame_len
))
401 return write(socket
, data
, len
) == len
;
407 EtherTap::EtherTap(const Params
*p
) : EtherTapBase(p
)
409 int fd
= open(p
->tun_clone_device
.c_str(), O_RDWR
| O_NONBLOCK
);
411 panic("Couldn't open %s.\n", p
->tun_clone_device
);
414 memset(&ifr
, 0, sizeof(ifr
));
415 ifr
.ifr_flags
= IFF_TAP
| IFF_NO_PI
;
416 strncpy(ifr
.ifr_name
, p
->tap_device_name
.c_str(), IFNAMSIZ
- 1);
418 if (ioctl(fd
, TUNSETIFF
, (void *)&ifr
) < 0)
419 panic("Failed to access tap device %s.\n", ifr
.ifr_name
);
420 // fd now refers to the tap device.
425 EtherTap::~EtherTap()
433 EtherTap::recvReal(int revent
)
435 if (revent
& POLLERR
)
436 panic("Error polling for tap data.\n");
438 if (!(revent
& POLLIN
))
442 while ((ret
= read(tap
, buffer
, buflen
))) {
446 panic("Failed to read from tap device.\n");
449 sendSimulated(buffer
, ret
);
454 EtherTap::sendReal(const void *data
, size_t len
)
459 pfd
->events
= POLLOUT
;
461 // `tap` is a nonblock fd. Here we try to write until success, and use
462 // poll to make a blocking wait.
463 while ((n
= write(tap
, data
, len
)) != len
) {
465 panic("Failed to write data to tap device.\n");
467 int ret
= poll(pfd
, 1, -1);
468 // timeout is set to inf, we shouldn't get 0 in any case.
470 if (ret
== -1 || (ret
== 1 && (pfd
->revents
& POLLERR
))) {
471 panic("Failed when polling to write data to tap device.\n");
478 EtherTapParams::create()
480 return new EtherTap(this);
486 EtherTapStubParams::create()
488 return new EtherTapStub(this);