ethertap.cc revision 146
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    close(socket);
167    socket = -1;
168}
169
170bool
171EtherTap::recvPacket(PacketPtr packet)
172{
173    if (dump)
174        dump->dump(packet);
175
176    DPRINTF(Ethernet, "EtherTap output len=%d\n", packet->length);
177    DDUMP(EthernetData, packet->data, packet->length);
178    u_int32_t len = htonl(packet->length);
179    write(socket, &len, sizeof(len));
180    write(socket, packet->data, packet->length);
181
182    recvDone();
183
184    return true;
185}
186
187void
188EtherTap::sendDone()
189{}
190
191void
192EtherTap::process(int revent)
193{
194    if (revent & POLLERR) {
195        detach();
196        return;
197    }
198
199    char *data = buffer + sizeof(u_int32_t);
200    if (!(revent & POLLIN))
201        return;
202
203    if (buffer_offset < data_len + sizeof(u_int32_t)) {
204        int len = read(socket, buffer + buffer_offset, buflen - buffer_offset);
205        if (len == 0) {
206            detach();
207            return;
208        }
209
210        buffer_offset += len;
211
212        if (data_len == 0)
213            data_len = ntohl(*(u_int32_t *)buffer);
214
215        DPRINTF(Ethernet, "Received data from peer: len=%d buffer_offset=%d "
216                "data_len=%d\n", len, buffer_offset, data_len);
217    }
218
219    while (data_len != 0 && buffer_offset >= data_len + sizeof(u_int32_t)) {
220        PacketPtr packet;
221        packet = new EtherPacket;
222        packet->data = new uint8_t[data_len];
223        packet->length = data_len;
224        memcpy(packet->data, data, data_len);
225
226        buffer_offset -= data_len + sizeof(u_int32_t);
227        assert(buffer_offset >= 0);
228        if (buffer_offset > 0) {
229            memmove(buffer, data + data_len, buffer_offset);
230            data_len = ntohl(*(u_int32_t *)buffer);
231        } else
232            data_len = 0;
233
234        DPRINTF(Ethernet, "EtherTap input len=%d\n", packet->length);
235        DDUMP(EthernetData, packet->data, packet->length);
236        if (!sendPacket(packet)) {
237            DPRINTF(Ethernet, "bus busy...buffer for retransmission\n");
238            packetBuffer.push(packet);
239            if (!txEvent.scheduled())
240                txEvent.schedule(curTick + 1000);
241        } else if (dump)
242            dump->dump(packet);
243    }
244}
245
246void
247EtherTap::retransmit()
248{
249    if (packetBuffer.empty())
250        return;
251
252    PacketPtr packet = packetBuffer.front();
253    if (sendPacket(packet)) {
254        if (dump)
255            dump->dump(packet);
256        DPRINTF(Ethernet, "EtherTap retransmit\n");
257        packetBuffer.front() = NULL;
258        packetBuffer.pop();
259    }
260
261    if (!packetBuffer.empty() && !txEvent.scheduled())
262        txEvent.schedule(curTick + 1000);
263}
264
265BEGIN_DECLARE_SIM_OBJECT_PARAMS(EtherTap)
266
267    SimObjectParam<EtherInt *> peer;
268    SimObjectParam<EtherDump *> packet_dump;
269    Param<uint16_t> port;
270    Param<uint16_t> bufsz;
271
272END_DECLARE_SIM_OBJECT_PARAMS(EtherTap)
273
274BEGIN_INIT_SIM_OBJECT_PARAMS(EtherTap)
275
276    INIT_PARAM_DFLT(peer, "peer interface", NULL),
277    INIT_PARAM_DFLT(packet_dump, "object to dump network packets to", NULL),
278    INIT_PARAM_DFLT(port, "tap port", 3500),
279    INIT_PARAM_DFLT(bufsz, "tap buffer size", 10000)
280
281END_INIT_SIM_OBJECT_PARAMS(EtherTap)
282
283
284CREATE_SIM_OBJECT(EtherTap)
285{
286    EtherTap *tap = new EtherTap(getInstanceName(), packet_dump, port, bufsz);
287
288    if (peer) {
289        tap->setPeer(peer);
290        peer->setPeer(tap);
291    }
292
293    return tap;
294}
295
296REGISTER_SIM_OBJECT("EtherTap", EtherTap)
297