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