ethertap.cc revision 253
1/*
2 * Copyright (c) 2003 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#if defined(__OpenBSD__)
34#include <sys/param.h>
35#endif
36#include <netinet/in.h>
37
38#include <unistd.h>
39
40#include <deque>
41#include <string>
42
43#include "base/misc.hh"
44#include "base/pollevent.hh"
45#include "base/socket.hh"
46#include "base/trace.hh"
47#include "dev/etherdump.hh"
48#include "dev/etherint.hh"
49#include "dev/etherpkt.hh"
50#include "dev/ethertap.hh"
51#include "sim/builder.hh"
52
53using namespace std;
54
55/**
56 */
57class TapListener
58{
59  protected:
60    /**
61     */
62    class Event : public PollEvent
63    {
64      protected:
65        TapListener *listener;
66
67      public:
68        Event(TapListener *l, int fd, int e)
69            : PollEvent(fd, e), listener(l) {}
70
71        virtual void process(int revent) { listener->accept(); }
72    };
73
74    friend class Event;
75    Event *event;
76
77  protected:
78    ListenSocket listener;
79    EtherTap *tap;
80    int port;
81
82  public:
83    TapListener(EtherTap *t, int p)
84        : event(NULL), tap(t), port(p) {}
85    ~TapListener() { if (event) delete event; }
86
87    void accept();
88    void listen();
89};
90
91void
92TapListener::listen()
93{
94    while (!listener.listen(port, true)) {
95        DPRINTF(Ethernet, "TapListener(listen): Can't bind port %d\n", port);
96        port++;
97    }
98
99    ccprintf(cerr, "Listening for tap connection on port %d\n", port);
100    event = new Event(this, listener.getfd(), POLLIN|POLLERR);
101    pollQueue.schedule(event);
102}
103
104void
105TapListener::accept()
106{
107    if (!listener.islistening())
108        panic("TapListener(accept): cannot accept if we're not listening!");
109
110    int sfd = listener.accept(true);
111    if (sfd != -1)
112        tap->attach(sfd);
113}
114
115/**
116 */
117class TapEvent : public PollEvent
118{
119  protected:
120    EtherTap *tap;
121
122  public:
123    TapEvent(EtherTap *_tap, int fd, int e)
124        : PollEvent(fd, e), tap(_tap) {}
125    virtual void process(int revent) { tap->process(revent); }
126};
127
128EtherTap::EtherTap(const string &name, EtherDump *d, int port, int bufsz)
129    : EtherInt(name), event(NULL), socket(-1), buflen(bufsz), dump(d),
130      txEvent(this)
131{
132    buffer = new char[buflen];
133    listener = new TapListener(this, port);
134    listener->listen();
135}
136
137EtherTap::~EtherTap()
138{
139    if (event)
140        delete event;
141    if (buffer)
142        delete [] buffer;
143
144    delete listener;
145}
146
147void
148EtherTap::attach(int fd)
149{
150    if (socket != -1)
151        close(fd);
152
153    buffer_offset = 0;
154    data_len = 0;
155    socket = fd;
156    DPRINTF(Ethernet, "EtherTap attached\n");
157    event = new TapEvent(this, socket, POLLIN|POLLERR);
158    pollQueue.schedule(event);
159}
160
161void
162EtherTap::detach()
163{
164    DPRINTF(Ethernet, "EtherTap detached\n");
165    delete event;
166    event = 0;
167    close(socket);
168    socket = -1;
169}
170
171bool
172EtherTap::recvPacket(PacketPtr packet)
173{
174    if (dump)
175        dump->dump(packet);
176
177    DPRINTF(Ethernet, "EtherTap output len=%d\n", packet->length);
178    DDUMP(EthernetData, packet->data, packet->length);
179    u_int32_t len = htonl(packet->length);
180    write(socket, &len, sizeof(len));
181    write(socket, packet->data, packet->length);
182
183    recvDone();
184
185    return true;
186}
187
188void
189EtherTap::sendDone()
190{}
191
192void
193EtherTap::process(int revent)
194{
195    if (revent & POLLERR) {
196        detach();
197        return;
198    }
199
200    char *data = buffer + sizeof(u_int32_t);
201    if (!(revent & POLLIN))
202        return;
203
204    if (buffer_offset < data_len + sizeof(u_int32_t)) {
205        int len = read(socket, buffer + buffer_offset, buflen - buffer_offset);
206        if (len == 0) {
207            detach();
208            return;
209        }
210
211        buffer_offset += len;
212
213        if (data_len == 0)
214            data_len = ntohl(*(u_int32_t *)buffer);
215
216        DPRINTF(Ethernet, "Received data from peer: len=%d buffer_offset=%d "
217                "data_len=%d\n", len, buffer_offset, data_len);
218    }
219
220    while (data_len != 0 && buffer_offset >= data_len + sizeof(u_int32_t)) {
221        PacketPtr packet;
222        packet = new EtherPacket;
223        packet->data = new uint8_t[data_len];
224        packet->length = data_len;
225        memcpy(packet->data, data, data_len);
226
227        buffer_offset -= data_len + sizeof(u_int32_t);
228        assert(buffer_offset >= 0);
229        if (buffer_offset > 0) {
230            memmove(buffer, data + data_len, buffer_offset);
231            data_len = ntohl(*(u_int32_t *)buffer);
232        } else
233            data_len = 0;
234
235        DPRINTF(Ethernet, "EtherTap input len=%d\n", packet->length);
236        DDUMP(EthernetData, packet->data, packet->length);
237        if (!sendPacket(packet)) {
238            DPRINTF(Ethernet, "bus busy...buffer for retransmission\n");
239            packetBuffer.push(packet);
240            if (!txEvent.scheduled())
241                txEvent.schedule(curTick + 1000);
242        } else if (dump)
243            dump->dump(packet);
244    }
245}
246
247void
248EtherTap::retransmit()
249{
250    if (packetBuffer.empty())
251        return;
252
253    PacketPtr packet = packetBuffer.front();
254    if (sendPacket(packet)) {
255        if (dump)
256            dump->dump(packet);
257        DPRINTF(Ethernet, "EtherTap retransmit\n");
258        packetBuffer.front() = NULL;
259        packetBuffer.pop();
260    }
261
262    if (!packetBuffer.empty() && !txEvent.scheduled())
263        txEvent.schedule(curTick + 1000);
264}
265
266//=====================================================================
267
268void
269EtherTap::serialize(ostream &os)
270{
271    SERIALIZE_SCALAR(socket);
272    SERIALIZE_SCALAR(buflen);
273    SERIALIZE_ARRAY((uint8_t *)buffer,buflen);
274    SERIALIZE_SCALAR(buffer_offset);
275    SERIALIZE_SCALAR(data_len);
276
277    bool tapevent_present = false;
278    if (event) {
279        tapevent_present = true;
280        SERIALIZE_SCALAR(tapevent_present);
281        event->serialize(os);
282    }
283    else {
284        SERIALIZE_SCALAR(tapevent_present);
285    }
286}
287
288void
289EtherTap::unserialize(Checkpoint *cp, const std::string &section)
290{
291    UNSERIALIZE_SCALAR(socket);
292    UNSERIALIZE_SCALAR(buflen);
293    UNSERIALIZE_ARRAY((uint8_t *)buffer,buflen);
294    UNSERIALIZE_SCALAR(buffer_offset);
295    UNSERIALIZE_SCALAR(data_len);
296
297    bool tapevent_present;
298    UNSERIALIZE_SCALAR(tapevent_present);
299    if (tapevent_present) {
300        event = new TapEvent(this, socket, POLLIN|POLLERR);
301
302        event->unserialize(cp,section);
303
304        if (event->queued()) {
305            pollQueue.schedule(event);
306        }
307    }
308}
309
310//=====================================================================
311
312BEGIN_DECLARE_SIM_OBJECT_PARAMS(EtherTap)
313
314    SimObjectParam<EtherInt *> peer;
315    SimObjectParam<EtherDump *> packet_dump;
316    Param<uint16_t> port;
317    Param<uint16_t> bufsz;
318
319END_DECLARE_SIM_OBJECT_PARAMS(EtherTap)
320
321BEGIN_INIT_SIM_OBJECT_PARAMS(EtherTap)
322
323    INIT_PARAM_DFLT(peer, "peer interface", NULL),
324    INIT_PARAM_DFLT(packet_dump, "object to dump network packets to", NULL),
325    INIT_PARAM_DFLT(port, "tap port", 3500),
326    INIT_PARAM_DFLT(bufsz, "tap buffer size", 10000)
327
328END_INIT_SIM_OBJECT_PARAMS(EtherTap)
329
330
331CREATE_SIM_OBJECT(EtherTap)
332{
333    EtherTap *tap = new EtherTap(getInstanceName(), packet_dump, port, bufsz);
334
335    if (peer) {
336        tap->setPeer(peer);
337        peer->setPeer(tap);
338    }
339
340    return tap;
341}
342
343REGISTER_SIM_OBJECT("EtherTap", EtherTap)
344