ethertap.cc revision 146
112855Sgabeblack@google.com/*
212855Sgabeblack@google.com * Copyright (c) 2003 The Regents of The University of Michigan
312855Sgabeblack@google.com * All rights reserved.
412855Sgabeblack@google.com *
512855Sgabeblack@google.com * Redistribution and use in source and binary forms, with or without
612855Sgabeblack@google.com * modification, are permitted provided that the following conditions are
712855Sgabeblack@google.com * met: redistributions of source code must retain the above copyright
812855Sgabeblack@google.com * notice, this list of conditions and the following disclaimer;
912855Sgabeblack@google.com * redistributions in binary form must reproduce the above copyright
1012855Sgabeblack@google.com * notice, this list of conditions and the following disclaimer in the
1112855Sgabeblack@google.com * documentation and/or other materials provided with the distribution;
1212855Sgabeblack@google.com * neither the name of the copyright holders nor the names of its
1312855Sgabeblack@google.com * contributors may be used to endorse or promote products derived from
1412855Sgabeblack@google.com * this software without specific prior written permission.
1512855Sgabeblack@google.com *
1612855Sgabeblack@google.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1712855Sgabeblack@google.com * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1812855Sgabeblack@google.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1912855Sgabeblack@google.com * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2012855Sgabeblack@google.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2112855Sgabeblack@google.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2212855Sgabeblack@google.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2312855Sgabeblack@google.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2412855Sgabeblack@google.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2512855Sgabeblack@google.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2612855Sgabeblack@google.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2712855Sgabeblack@google.com */
2812855Sgabeblack@google.com
2912855Sgabeblack@google.com/* @file
3012855Sgabeblack@google.com * Interface to connect a simulated ethernet device to the real world
3112855Sgabeblack@google.com */
3212855Sgabeblack@google.com
3312855Sgabeblack@google.com#if defined(__OpenBSD__)
3412855Sgabeblack@google.com#include <sys/param.h>
3512855Sgabeblack@google.com#endif
3612855Sgabeblack@google.com#include <netinet/in.h>
3712855Sgabeblack@google.com
3812855Sgabeblack@google.com#include <unistd.h>
3912855Sgabeblack@google.com
4012855Sgabeblack@google.com#include <deque>
4112855Sgabeblack@google.com#include <string>
4212855Sgabeblack@google.com
4312855Sgabeblack@google.com#include "base/misc.hh"
4412855Sgabeblack@google.com#include "base/pollevent.hh"
4512855Sgabeblack@google.com#include "base/socket.hh"
4612855Sgabeblack@google.com#include "base/trace.hh"
4712855Sgabeblack@google.com#include "dev/etherdump.hh"
4812855Sgabeblack@google.com#include "dev/etherint.hh"
4912855Sgabeblack@google.com#include "dev/etherpkt.hh"
5012855Sgabeblack@google.com#include "dev/ethertap.hh"
5112855Sgabeblack@google.com#include "sim/builder.hh"
5212855Sgabeblack@google.com
5312855Sgabeblack@google.comusing namespace std;
5412855Sgabeblack@google.com
5512855Sgabeblack@google.com/**
5612855Sgabeblack@google.com */
5712855Sgabeblack@google.comclass TapListener
5812855Sgabeblack@google.com{
5912855Sgabeblack@google.com  protected:
6012855Sgabeblack@google.com    /**
6112855Sgabeblack@google.com     */
6212855Sgabeblack@google.com    class Event : public PollEvent
6312855Sgabeblack@google.com    {
6412855Sgabeblack@google.com      protected:
6512855Sgabeblack@google.com        TapListener *listener;
6612855Sgabeblack@google.com
6712855Sgabeblack@google.com      public:
6812855Sgabeblack@google.com        Event(TapListener *l, int fd, int e)
6912855Sgabeblack@google.com            : PollEvent(fd, e), listener(l) {}
7012855Sgabeblack@google.com
7112855Sgabeblack@google.com        virtual void process(int revent) { listener->accept(); }
7212855Sgabeblack@google.com    };
7312855Sgabeblack@google.com
7412855Sgabeblack@google.com    friend class Event;
7512855Sgabeblack@google.com    Event *event;
7612855Sgabeblack@google.com
7712855Sgabeblack@google.com  protected:
7812855Sgabeblack@google.com    ListenSocket listener;
7912855Sgabeblack@google.com    EtherTap *tap;
8012855Sgabeblack@google.com    int port;
8112855Sgabeblack@google.com
8212855Sgabeblack@google.com  public:
8312855Sgabeblack@google.com    TapListener(EtherTap *t, int p)
8412855Sgabeblack@google.com        : event(NULL), tap(t), port(p) {}
8512855Sgabeblack@google.com    ~TapListener() { if (event) delete event; }
8612855Sgabeblack@google.com
8712855Sgabeblack@google.com    void accept();
8812855Sgabeblack@google.com    void listen();
8912855Sgabeblack@google.com};
9012855Sgabeblack@google.com
9112855Sgabeblack@google.comvoid
9212855Sgabeblack@google.comTapListener::listen()
9312855Sgabeblack@google.com{
9412855Sgabeblack@google.com    while (!listener.listen(port, true)) {
9512855Sgabeblack@google.com        DPRINTF(Ethernet, "TapListener(listen): Can't bind port %d\n", port);
9612855Sgabeblack@google.com        port++;
9712855Sgabeblack@google.com    }
9812855Sgabeblack@google.com
9912855Sgabeblack@google.com    ccprintf(cerr, "Listening for tap connection on port %d\n", port);
10012855Sgabeblack@google.com    event = new Event(this, listener.getfd(), POLLIN|POLLERR);
10112855Sgabeblack@google.com    pollQueue.schedule(event);
10212855Sgabeblack@google.com}
10312855Sgabeblack@google.com
10412855Sgabeblack@google.comvoid
10512855Sgabeblack@google.comTapListener::accept()
10612855Sgabeblack@google.com{
10712855Sgabeblack@google.com    if (!listener.islistening())
10812855Sgabeblack@google.com        panic("TapListener(accept): cannot accept if we're not listening!");
10912855Sgabeblack@google.com
11012855Sgabeblack@google.com    int sfd = listener.accept(true);
11112855Sgabeblack@google.com    if (sfd != -1)
11212855Sgabeblack@google.com        tap->attach(sfd);
11312855Sgabeblack@google.com}
11412855Sgabeblack@google.com
11512855Sgabeblack@google.com/**
11612855Sgabeblack@google.com */
11712855Sgabeblack@google.comclass TapEvent : public PollEvent
11812855Sgabeblack@google.com{
11912855Sgabeblack@google.com  protected:
12012855Sgabeblack@google.com    EtherTap *tap;
12112855Sgabeblack@google.com
12212855Sgabeblack@google.com  public:
12312855Sgabeblack@google.com    TapEvent(EtherTap *_tap, int fd, int e)
12412855Sgabeblack@google.com        : PollEvent(fd, e), tap(_tap) {}
12512855Sgabeblack@google.com    virtual void process(int revent) { tap->process(revent); }
12612855Sgabeblack@google.com};
12712855Sgabeblack@google.com
12812855Sgabeblack@google.comEtherTap::EtherTap(const string &name, EtherDump *d, int port, int bufsz)
12912855Sgabeblack@google.com    : EtherInt(name), event(NULL), socket(-1), buflen(bufsz), dump(d),
13012855Sgabeblack@google.com      txEvent(this)
13112855Sgabeblack@google.com{
13212855Sgabeblack@google.com    buffer = new char[buflen];
13312855Sgabeblack@google.com    listener = new TapListener(this, port);
13412855Sgabeblack@google.com    listener->listen();
13512855Sgabeblack@google.com}
13612855Sgabeblack@google.com
13712855Sgabeblack@google.comEtherTap::~EtherTap()
13812855Sgabeblack@google.com{
13912855Sgabeblack@google.com    if (event)
14012855Sgabeblack@google.com        delete event;
14112855Sgabeblack@google.com    if (buffer)
14212855Sgabeblack@google.com        delete [] buffer;
14312855Sgabeblack@google.com
14412855Sgabeblack@google.com    delete listener;
14512855Sgabeblack@google.com}
14612855Sgabeblack@google.com
14712855Sgabeblack@google.comvoid
14812855Sgabeblack@google.comEtherTap::attach(int fd)
14912855Sgabeblack@google.com{
15012855Sgabeblack@google.com    if (socket != -1)
15112855Sgabeblack@google.com        close(fd);
15212855Sgabeblack@google.com
15312855Sgabeblack@google.com    buffer_offset = 0;
15412855Sgabeblack@google.com    data_len = 0;
15512855Sgabeblack@google.com    socket = fd;
15612855Sgabeblack@google.com    DPRINTF(Ethernet, "EtherTap attached\n");
15712855Sgabeblack@google.com    event = new TapEvent(this, socket, POLLIN|POLLERR);
15812855Sgabeblack@google.com    pollQueue.schedule(event);
15912855Sgabeblack@google.com}
16012855Sgabeblack@google.com
16112855Sgabeblack@google.comvoid
16212855Sgabeblack@google.comEtherTap::detach()
16312855Sgabeblack@google.com{
16412855Sgabeblack@google.com    DPRINTF(Ethernet, "EtherTap detached\n");
16512855Sgabeblack@google.com    delete event;
16612855Sgabeblack@google.com    close(socket);
16712855Sgabeblack@google.com    socket = -1;
16812855Sgabeblack@google.com}
16912855Sgabeblack@google.com
17012855Sgabeblack@google.combool
17112855Sgabeblack@google.comEtherTap::recvPacket(PacketPtr packet)
17212855Sgabeblack@google.com{
17312855Sgabeblack@google.com    if (dump)
17412855Sgabeblack@google.com        dump->dump(packet);
17512855Sgabeblack@google.com
17612855Sgabeblack@google.com    DPRINTF(Ethernet, "EtherTap output len=%d\n", packet->length);
17712855Sgabeblack@google.com    DDUMP(EthernetData, packet->data, packet->length);
17812855Sgabeblack@google.com    u_int32_t len = htonl(packet->length);
17912855Sgabeblack@google.com    write(socket, &len, sizeof(len));
18012855Sgabeblack@google.com    write(socket, packet->data, packet->length);
18112855Sgabeblack@google.com
18212855Sgabeblack@google.com    recvDone();
18312855Sgabeblack@google.com
18412855Sgabeblack@google.com    return true;
18512855Sgabeblack@google.com}
18612855Sgabeblack@google.com
18712855Sgabeblack@google.comvoid
18812855Sgabeblack@google.comEtherTap::sendDone()
18912855Sgabeblack@google.com{}
19012855Sgabeblack@google.com
19112855Sgabeblack@google.comvoid
19212855Sgabeblack@google.comEtherTap::process(int revent)
19312855Sgabeblack@google.com{
19412855Sgabeblack@google.com    if (revent & POLLERR) {
19512855Sgabeblack@google.com        detach();
19612855Sgabeblack@google.com        return;
19712855Sgabeblack@google.com    }
19812855Sgabeblack@google.com
19912855Sgabeblack@google.com    char *data = buffer + sizeof(u_int32_t);
20012855Sgabeblack@google.com    if (!(revent & POLLIN))
20112855Sgabeblack@google.com        return;
20212855Sgabeblack@google.com
20312855Sgabeblack@google.com    if (buffer_offset < data_len + sizeof(u_int32_t)) {
20412855Sgabeblack@google.com        int len = read(socket, buffer + buffer_offset, buflen - buffer_offset);
20512855Sgabeblack@google.com        if (len == 0) {
20612855Sgabeblack@google.com            detach();
20712855Sgabeblack@google.com            return;
20812855Sgabeblack@google.com        }
20912855Sgabeblack@google.com
21012855Sgabeblack@google.com        buffer_offset += len;
21112855Sgabeblack@google.com
21212855Sgabeblack@google.com        if (data_len == 0)
21312855Sgabeblack@google.com            data_len = ntohl(*(u_int32_t *)buffer);
21412855Sgabeblack@google.com
21512855Sgabeblack@google.com        DPRINTF(Ethernet, "Received data from peer: len=%d buffer_offset=%d "
21612855Sgabeblack@google.com                "data_len=%d\n", len, buffer_offset, data_len);
21712855Sgabeblack@google.com    }
21812855Sgabeblack@google.com
21912855Sgabeblack@google.com    while (data_len != 0 && buffer_offset >= data_len + sizeof(u_int32_t)) {
22012855Sgabeblack@google.com        PacketPtr packet;
22112855Sgabeblack@google.com        packet = new EtherPacket;
22212855Sgabeblack@google.com        packet->data = new uint8_t[data_len];
22312855Sgabeblack@google.com        packet->length = data_len;
22412855Sgabeblack@google.com        memcpy(packet->data, data, data_len);
22512855Sgabeblack@google.com
22612855Sgabeblack@google.com        buffer_offset -= data_len + sizeof(u_int32_t);
22712855Sgabeblack@google.com        assert(buffer_offset >= 0);
22812855Sgabeblack@google.com        if (buffer_offset > 0) {
22912855Sgabeblack@google.com            memmove(buffer, data + data_len, buffer_offset);
23012855Sgabeblack@google.com            data_len = ntohl(*(u_int32_t *)buffer);
23112855Sgabeblack@google.com        } else
23212855Sgabeblack@google.com            data_len = 0;
23312855Sgabeblack@google.com
23412855Sgabeblack@google.com        DPRINTF(Ethernet, "EtherTap input len=%d\n", packet->length);
23512855Sgabeblack@google.com        DDUMP(EthernetData, packet->data, packet->length);
23612855Sgabeblack@google.com        if (!sendPacket(packet)) {
23712855Sgabeblack@google.com            DPRINTF(Ethernet, "bus busy...buffer for retransmission\n");
23812855Sgabeblack@google.com            packetBuffer.push(packet);
23912855Sgabeblack@google.com            if (!txEvent.scheduled())
24012855Sgabeblack@google.com                txEvent.schedule(curTick + 1000);
24112855Sgabeblack@google.com        } else if (dump)
24212855Sgabeblack@google.com            dump->dump(packet);
24312855Sgabeblack@google.com    }
24412855Sgabeblack@google.com}
24512855Sgabeblack@google.com
24612855Sgabeblack@google.comvoid
24712855Sgabeblack@google.comEtherTap::retransmit()
24812855Sgabeblack@google.com{
24912855Sgabeblack@google.com    if (packetBuffer.empty())
25012855Sgabeblack@google.com        return;
25112855Sgabeblack@google.com
25212855Sgabeblack@google.com    PacketPtr packet = packetBuffer.front();
25312855Sgabeblack@google.com    if (sendPacket(packet)) {
25412855Sgabeblack@google.com        if (dump)
25512855Sgabeblack@google.com            dump->dump(packet);
25612855Sgabeblack@google.com        DPRINTF(Ethernet, "EtherTap retransmit\n");
25712855Sgabeblack@google.com        packetBuffer.front() = NULL;
25812855Sgabeblack@google.com        packetBuffer.pop();
25912855Sgabeblack@google.com    }
26012855Sgabeblack@google.com
26112855Sgabeblack@google.com    if (!packetBuffer.empty() && !txEvent.scheduled())
26212855Sgabeblack@google.com        txEvent.schedule(curTick + 1000);
26312855Sgabeblack@google.com}
26412855Sgabeblack@google.com
26512855Sgabeblack@google.comBEGIN_DECLARE_SIM_OBJECT_PARAMS(EtherTap)
26612855Sgabeblack@google.com
26712855Sgabeblack@google.com    SimObjectParam<EtherInt *> peer;
26812855Sgabeblack@google.com    SimObjectParam<EtherDump *> packet_dump;
26912855Sgabeblack@google.com    Param<uint16_t> port;
27012855Sgabeblack@google.com    Param<uint16_t> bufsz;
27112855Sgabeblack@google.com
27212855Sgabeblack@google.comEND_DECLARE_SIM_OBJECT_PARAMS(EtherTap)
27312855Sgabeblack@google.com
27412855Sgabeblack@google.comBEGIN_INIT_SIM_OBJECT_PARAMS(EtherTap)
27512855Sgabeblack@google.com
27612855Sgabeblack@google.com    INIT_PARAM_DFLT(peer, "peer interface", NULL),
27712855Sgabeblack@google.com    INIT_PARAM_DFLT(packet_dump, "object to dump network packets to", NULL),
27812855Sgabeblack@google.com    INIT_PARAM_DFLT(port, "tap port", 3500),
27912855Sgabeblack@google.com    INIT_PARAM_DFLT(bufsz, "tap buffer size", 10000)
28012855Sgabeblack@google.com
28112855Sgabeblack@google.comEND_INIT_SIM_OBJECT_PARAMS(EtherTap)
28212855Sgabeblack@google.com
28312855Sgabeblack@google.com
28412855Sgabeblack@google.comCREATE_SIM_OBJECT(EtherTap)
28512855Sgabeblack@google.com{
28612855Sgabeblack@google.com    EtherTap *tap = new EtherTap(getInstanceName(), packet_dump, port, bufsz);
28712855Sgabeblack@google.com
28812855Sgabeblack@google.com    if (peer) {
28912855Sgabeblack@google.com        tap->setPeer(peer);
29012855Sgabeblack@google.com        peer->setPeer(tap);
29112855Sgabeblack@google.com    }
29212855Sgabeblack@google.com
29312855Sgabeblack@google.com    return tap;
29412855Sgabeblack@google.com}
29512855Sgabeblack@google.com
29612855Sgabeblack@google.comREGISTER_SIM_OBJECT("EtherTap", EtherTap)
29712855Sgabeblack@google.com