ca19b4884f1c4152c7ebbd63e3cbf2c19430a8c0
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
) {}
82 virtual void process(int revent
) { tap
->recvReal(revent
); }
85 EtherTapBase::EtherTapBase(const Params
*p
)
86 : EtherObject(p
), buflen(p
->bufsz
), dump(p
->dump
), event(NULL
),
88 txEvent([this]{ retransmit(); }, "EtherTapBase retransmit")
90 buffer
= new uint8_t[buflen
];
91 interface
= new EtherTapInt(name() + ".interface", this);
94 EtherTapBase::~EtherTapBase()
102 EtherTapBase::serialize(CheckpointOut
&cp
) const
104 SERIALIZE_SCALAR(buflen
);
105 uint8_t *buffer
= (uint8_t *)this->buffer
;
106 SERIALIZE_ARRAY(buffer
, buflen
);
108 bool tapevent_present
= false;
110 tapevent_present
= true;
111 SERIALIZE_SCALAR(tapevent_present
);
112 event
->serialize(cp
);
114 SERIALIZE_SCALAR(tapevent_present
);
119 EtherTapBase::unserialize(CheckpointIn
&cp
)
121 UNSERIALIZE_SCALAR(buflen
);
122 uint8_t *buffer
= (uint8_t *)this->buffer
;
123 UNSERIALIZE_ARRAY(buffer
, buflen
);
125 bool tapevent_present
;
126 UNSERIALIZE_SCALAR(tapevent_present
);
127 if (tapevent_present
) {
128 event
= new TapEvent(this, 0, 0);
129 event
->unserialize(cp
);
131 pollQueue
.schedule(event
);
137 EtherTapBase::pollFd(int fd
)
140 event
= new TapEvent(this, fd
, POLLIN
|POLLERR
);
141 pollQueue
.schedule(event
);
145 EtherTapBase::stopPolling()
154 EtherTapBase::getEthPort(const std::string
&if_name
, int idx
)
156 if (if_name
== "tap") {
157 if (interface
->getPeer())
158 panic("Interface already connected to\n");
165 EtherTapBase::recvSimulated(EthPacketPtr packet
)
170 DPRINTF(Ethernet
, "EtherTap sim->real len=%d\n", packet
->length
);
171 DDUMP(EthernetData
, packet
->data
, packet
->length
);
173 bool success
= sendReal(packet
->data
, packet
->length
);
175 interface
->recvDone();
181 EtherTapBase::sendSimulated(void *data
, size_t len
)
184 packet
= make_shared
<EthPacketData
>(len
);
185 packet
->length
= len
;
186 packet
->simLength
= len
;
187 memcpy(packet
->data
, data
, len
);
189 DPRINTF(Ethernet
, "EtherTap real->sim len=%d\n", packet
->length
);
190 DDUMP(EthernetData
, packet
->data
, packet
->length
);
191 if (!packetBuffer
.empty() || !interface
->sendPacket(packet
)) {
192 DPRINTF(Ethernet
, "bus busy...buffer for retransmission\n");
193 packetBuffer
.push(packet
);
194 if (!txEvent
.scheduled())
195 schedule(txEvent
, curTick() + retryTime
);
202 EtherTapBase::retransmit()
204 if (packetBuffer
.empty())
207 EthPacketPtr packet
= packetBuffer
.front();
208 if (interface
->sendPacket(packet
)) {
211 DPRINTF(Ethernet
, "EtherTap retransmit\n");
212 packetBuffer
.front() = NULL
;
216 if (!packetBuffer
.empty() && !txEvent
.scheduled())
217 schedule(txEvent
, curTick() + retryTime
);
224 class Event
: public PollEvent
227 TapListener
*listener
;
230 Event(TapListener
*l
, int fd
, int e
) : PollEvent(fd
, e
), listener(l
) {}
232 void process(int revent
) override
{ listener
->accept(); }
241 ListenSocket listener
;
246 TapListener(EtherTapStub
*t
, int p
) : event(NULL
), tap(t
), port(p
) {}
247 ~TapListener() { delete event
; }
253 TapListener::listen()
255 while (!listener
.listen(port
, true)) {
256 DPRINTF(Ethernet
, "TapListener(listen): Can't bind port %d\n", port
);
260 ccprintf(cerr
, "Listening for tap connection on port %d\n", port
);
261 event
= new Event(this, listener
.getfd(), POLLIN
|POLLERR
);
262 pollQueue
.schedule(event
);
266 TapListener::accept()
268 // As a consequence of being called from the PollQueue, we might
269 // have been called from a different thread. Migrate to "our"
271 EventQueue::ScopedMigration
migrate(tap
->eventQueue());
273 if (!listener
.islistening())
274 panic("TapListener(accept): cannot accept if we're not listening!");
276 int sfd
= listener
.accept(true);
282 EtherTapStub::EtherTapStub(const Params
*p
) : EtherTapBase(p
), socket(-1)
284 if (ListenSocket::allDisabled())
285 fatal("All listeners are disabled! EtherTapStub can't work!");
287 listener
= new TapListener(this, p
->port
);
291 EtherTapStub::~EtherTapStub()
297 EtherTapStub::serialize(CheckpointOut
&cp
) const
299 EtherTapBase::serialize(cp
);
301 SERIALIZE_SCALAR(socket
);
302 SERIALIZE_SCALAR(buffer_used
);
303 SERIALIZE_SCALAR(frame_len
);
307 EtherTapStub::unserialize(CheckpointIn
&cp
)
309 EtherTapBase::unserialize(cp
);
311 UNSERIALIZE_SCALAR(socket
);
312 UNSERIALIZE_SCALAR(buffer_used
);
313 UNSERIALIZE_SCALAR(frame_len
);
318 EtherTapStub::attach(int fd
)
326 DPRINTF(Ethernet
, "EtherTapStub attached\n");
331 EtherTapStub::detach()
333 DPRINTF(Ethernet
, "EtherTapStub detached\n");
340 EtherTapStub::recvReal(int revent
)
342 if (revent
& POLLERR
) {
347 if (!(revent
& POLLIN
))
350 // Read in as much of the new data as we can.
351 int len
= read(socket
, buffer
+ buffer_used
, buflen
- buffer_used
);
358 // If there's not enough data for the frame length, wait for more.
359 if (buffer_used
< sizeof(uint32_t))
363 frame_len
= ntohl(*(uint32_t *)buffer
);
365 DPRINTF(Ethernet
, "Received data from peer: len=%d buffer_used=%d "
366 "frame_len=%d\n", len
, buffer_used
, frame_len
);
368 uint8_t *frame_start
= &buffer
[sizeof(uint32_t)];
369 while (frame_len
!= 0 && buffer_used
>= frame_len
+ sizeof(uint32_t)) {
370 sendSimulated(frame_start
, frame_len
);
373 buffer_used
-= frame_len
+ sizeof(uint32_t);
374 if (buffer_used
> 0) {
375 // If there's still any data left, move it into position.
376 memmove(buffer
, frame_start
+ frame_len
, buffer_used
);
380 if (buffer_used
>= sizeof(uint32_t))
381 frame_len
= ntohl(*(uint32_t *)buffer
);
386 EtherTapStub::sendReal(const void *data
, size_t len
)
388 uint32_t frame_len
= htonl(len
);
389 ssize_t ret
= write(socket
, &frame_len
, sizeof(frame_len
));
390 if (ret
!= sizeof(frame_len
))
392 return write(socket
, data
, len
) == len
;
398 EtherTap::EtherTap(const Params
*p
) : EtherTapBase(p
)
400 int fd
= open(p
->tun_clone_device
.c_str(), O_RDWR
);
402 panic("Couldn't open %s.\n", p
->tun_clone_device
);
405 memset(&ifr
, 0, sizeof(ifr
));
406 ifr
.ifr_flags
= IFF_TAP
| IFF_NO_PI
;
407 strncpy(ifr
.ifr_name
, p
->tap_device_name
.c_str(), IFNAMSIZ
- 1);
409 if (ioctl(fd
, TUNSETIFF
, (void *)&ifr
) < 0)
410 panic("Failed to access tap device %s.\n", ifr
.ifr_name
);
411 // fd now refers to the tap device.
416 EtherTap::~EtherTap()
424 EtherTap::recvReal(int revent
)
426 if (revent
& POLLERR
)
427 panic("Error polling for tap data.\n");
429 if (!(revent
& POLLIN
))
432 ssize_t ret
= read(tap
, buffer
, buflen
);
434 panic("Failed to read from tap device.\n");
436 sendSimulated(buffer
, ret
);
440 EtherTap::sendReal(const void *data
, size_t len
)
442 if (write(tap
, data
, len
) != len
)
443 panic("Failed to write data to tap device.\n");
448 EtherTapParams::create()
450 return new EtherTap(this);
456 EtherTapStubParams::create()
458 return new EtherTapStub(this);