ethertap.cc revision 12054:ab04045965d1
16899SN/A/*
26899SN/A * Copyright (c) 2003-2005 The Regents of The University of Michigan
36899SN/A * All rights reserved.
46899SN/A *
56899SN/A * Redistribution and use in source and binary forms, with or without
66899SN/A * modification, are permitted provided that the following conditions are
76899SN/A * met: redistributions of source code must retain the above copyright
86899SN/A * notice, this list of conditions and the following disclaimer;
96899SN/A * redistributions in binary form must reproduce the above copyright
106899SN/A * notice, this list of conditions and the following disclaimer in the
116899SN/A * documentation and/or other materials provided with the distribution;
126899SN/A * neither the name of the copyright holders nor the names of its
136899SN/A * contributors may be used to endorse or promote products derived from
146899SN/A * this software without specific prior written permission.
156899SN/A *
166899SN/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
176899SN/A * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
186899SN/A * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
196899SN/A * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
206899SN/A * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
216899SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
226899SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
236899SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
246899SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
256899SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
266899SN/A * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
276899SN/A *
286899SN/A * Authors: Nathan Binkert
296899SN/A */
307632SBrad.Beckmann@amd.com
318232Snate@binkert.org/* @file
327053SN/A * Interface to connect a simulated ethernet device to the real world
336899SN/A */
346899SN/A
356899SN/A#include "dev/net/ethertap.hh"
367053SN/A
377053SN/A#if defined(__OpenBSD__) || defined(__APPLE__)
387053SN/A#include <sys/param.h>
398932SBrad.Beckmann@amd.com
408932SBrad.Beckmann@amd.com#endif
418932SBrad.Beckmann@amd.com#include <netinet/in.h>
426899SN/A#include <unistd.h>
437053SN/A
446899SN/A#include <deque>
457053SN/A#include <string>
467053SN/A
477053SN/A#include "base/misc.hh"
487053SN/A#include "base/pollevent.hh"
498164Snilay@cs.wisc.edu#include "base/socket.hh"
507053SN/A#include "base/trace.hh"
516899SN/A#include "debug/Ethernet.hh"
526899SN/A#include "debug/EthernetData.hh"
537053SN/A#include "dev/net/etherdump.hh"
547053SN/A#include "dev/net/etherint.hh"
556899SN/A#include "dev/net/etherpkt.hh"
567053SN/A
577053SN/Ausing namespace std;
586899SN/A
597053SN/A/**
607053SN/A */
617053SN/Aclass TapListener
627053SN/A{
636899SN/A  protected:
648184Ssomayeh@cs.wisc.edu    /**
658184Ssomayeh@cs.wisc.edu     */
668184Ssomayeh@cs.wisc.edu    class Event : public PollEvent
678184Ssomayeh@cs.wisc.edu    {
687053SN/A      protected:
697053SN/A        TapListener *listener;
707053SN/A
717053SN/A      public:
727053SN/A        Event(TapListener *l, int fd, int e)
737053SN/A            : PollEvent(fd, e), listener(l) {}
747053SN/A
757053SN/A        virtual void process(int revent) { listener->accept(); }
767053SN/A    };
776899SN/A
786899SN/A    friend class Event;
797053SN/A    Event *event;
807053SN/A
816899SN/A  protected:
827053SN/A    ListenSocket listener;
836899SN/A    EtherTapStub *tap;
848932SBrad.Beckmann@amd.com    int port;
858950Sandreas.hansson@arm.com
866899SN/A  public:
877053SN/A    TapListener(EtherTapStub *t, int p)
887053SN/A        : event(NULL), tap(t), port(p) {}
896899SN/A    ~TapListener() { if (event) delete event; }
907053SN/A
916899SN/A    void accept();
927053SN/A    void listen();
937053SN/A};
947053SN/A
957053SN/Avoid
968932SBrad.Beckmann@amd.comTapListener::listen()
978950Sandreas.hansson@arm.com{
987053SN/A    while (!listener.listen(port, true)) {
997053SN/A        DPRINTF(Ethernet, "TapListener(listen): Can't bind port %d\n", port);
1007053SN/A        port++;
1017053SN/A    }
1027053SN/A
1036899SN/A    ccprintf(cerr, "Listening for tap connection on port %d\n", port);
1046899SN/A    event = new Event(this, listener.getfd(), POLLIN|POLLERR);
1057568SN/A    pollQueue.schedule(event);
1068832SAli.Saidi@ARM.com}
1078832SAli.Saidi@ARM.com
1088190SLisa.Hsu@amd.comvoid
1097568SN/ATapListener::accept()
1108949Sandreas.hansson@arm.com{
1116899SN/A    // As a consequence of being called from the PollQueue, we might
1127053SN/A    // have been called from a different thread. Migrate to "our"
1137053SN/A    // thread.
1147053SN/A    EventQueue::ScopedMigration migrate(tap->eventQueue());
1157053SN/A
1166899SN/A    if (!listener.islistening())
1178975Sandreas.hansson@arm.com        panic("TapListener(accept): cannot accept if we're not listening!");
1187053SN/A
1197053SN/A    int sfd = listener.accept(true);
1207053SN/A    if (sfd != -1)
1217053SN/A        tap->attach(sfd);
1227053SN/A}
1237053SN/A
1247053SN/A/**
1257053SN/A */
1266899SN/Aclass TapEvent : public PollEvent
1277053SN/A{
1287053SN/A  protected:
1297053SN/A    EtherTapStub *tap;
1306899SN/A
1316899SN/A  public:
1327053SN/A    TapEvent(EtherTapStub *_tap, int fd, int e)
1338184Ssomayeh@cs.wisc.edu        : PollEvent(fd, e), tap(_tap) {}
1348184Ssomayeh@cs.wisc.edu    virtual void process(int revent) { tap->process(revent); }
1358184Ssomayeh@cs.wisc.edu};
1368184Ssomayeh@cs.wisc.edu
1378184Ssomayeh@cs.wisc.eduEtherTapStub::EtherTapStub(const Params *p)
1388932SBrad.Beckmann@amd.com    : EtherObject(p), event(NULL), socket(-1), buflen(p->bufsz), dump(p->dump),
1398950Sandreas.hansson@arm.com      interface(NULL), txEvent(this)
1408184Ssomayeh@cs.wisc.edu{
1418184Ssomayeh@cs.wisc.edu    if (ListenSocket::allDisabled())
1428184Ssomayeh@cs.wisc.edu        fatal("All listeners are disabled! EtherTapStub can't work!");
1438832SAli.Saidi@ARM.com
1448832SAli.Saidi@ARM.com    buffer = new char[buflen];
1458184Ssomayeh@cs.wisc.edu    listener = new TapListener(this, p->port);
1468184Ssomayeh@cs.wisc.edu    listener->listen();
1478184Ssomayeh@cs.wisc.edu    interface = new EtherTapInt(name() + ".interface", this);
1488184Ssomayeh@cs.wisc.edu}
1498184Ssomayeh@cs.wisc.edu
1508949Sandreas.hansson@arm.comEtherTapStub::~EtherTapStub()
1518184Ssomayeh@cs.wisc.edu{
1528184Ssomayeh@cs.wisc.edu    if (event)
1538184Ssomayeh@cs.wisc.edu        delete event;
1548184Ssomayeh@cs.wisc.edu    if (buffer)
1558184Ssomayeh@cs.wisc.edu        delete [] buffer;
1568184Ssomayeh@cs.wisc.edu
1578975Sandreas.hansson@arm.com    delete interface;
1588184Ssomayeh@cs.wisc.edu    delete listener;
1598184Ssomayeh@cs.wisc.edu}
1608184Ssomayeh@cs.wisc.edu
1618184Ssomayeh@cs.wisc.eduvoid
1628184Ssomayeh@cs.wisc.eduEtherTapStub::attach(int fd)
1637053SN/A{
1646899SN/A    if (socket != -1)
1657053SN/A        close(fd);
1667053SN/A
1676899SN/A    buffer_offset = 0;
1688932SBrad.Beckmann@amd.com    data_len = 0;
1698950Sandreas.hansson@arm.com    socket = fd;
1706899SN/A    DPRINTF(Ethernet, "EtherTapStub attached\n");
1717053SN/A    event = new TapEvent(this, socket, POLLIN|POLLERR);
1726899SN/A    pollQueue.schedule(event);
1737053SN/A}
1747053SN/A
1756899SN/Avoid
1767053SN/AEtherTapStub::detach()
1778832SAli.Saidi@ARM.com{
1788832SAli.Saidi@ARM.com    DPRINTF(Ethernet, "EtherTapStub detached\n");
1797053SN/A    delete event;
1806899SN/A    event = 0;
1818190SLisa.Hsu@amd.com    close(socket);
1827053SN/A    socket = -1;
1837053SN/A}
1847053SN/A
1857053SN/Abool
1867053SN/AEtherTapStub::recvPacket(EthPacketPtr packet)
1877053SN/A{
1886899SN/A    if (dump)
1897053SN/A        dump->dump(packet);
1906899SN/A
1918949Sandreas.hansson@arm.com    DPRINTF(Ethernet, "EtherTapStub output len=%d\n", packet->length);
1929208Snilay@cs.wisc.edu    DDUMP(EthernetData, packet->data, packet->length);
1937053SN/A    uint32_t len = htonl(packet->length);
1947053SN/A    ssize_t ret = write(socket, &len, sizeof(len));
1956899SN/A    if (ret != sizeof(len))
1967053SN/A        return false;
1977053SN/A    ret = write(socket, packet->data, packet->length);
1986899SN/A    if (ret != packet->length)
1997053SN/A        return false;
2007053SN/A
2017053SN/A    interface->recvDone();
2027053SN/A
2036899SN/A    return true;
2048975Sandreas.hansson@arm.com}
2057053SN/A
2067053SN/Avoid
2077053SN/AEtherTapStub::sendDone()
2087053SN/A{}
2097053SN/A
2107053SN/Avoid
2117053SN/AEtherTapStub::process(int revent)
2127053SN/A{
2137053SN/A    if (revent & POLLERR) {
2147053SN/A        detach();
2157053SN/A        return;
2167053SN/A    }
2177053SN/A
2187053SN/A    char *data = buffer + sizeof(uint32_t);
2197053SN/A    if (!(revent & POLLIN))
2207053SN/A        return;
2217053SN/A
2227053SN/A    if (buffer_offset < data_len + sizeof(uint32_t)) {
2236899SN/A        int len = read(socket, buffer + buffer_offset, buflen - buffer_offset);
2246899SN/A        if (len == 0) {
2256899SN/A            detach();
2267053SN/A            return;
2277053SN/A        }
2286899SN/A
2297053SN/A        buffer_offset += len;
2307053SN/A
2316899SN/A        if (data_len == 0)
2328932SBrad.Beckmann@amd.com            data_len = ntohl(*(uint32_t *)buffer);
2338950Sandreas.hansson@arm.com
2346899SN/A        DPRINTF(Ethernet, "Received data from peer: len=%d buffer_offset=%d "
2357053SN/A                "data_len=%d\n", len, buffer_offset, data_len);
2366899SN/A    }
2378932SBrad.Beckmann@amd.com
2388950Sandreas.hansson@arm.com    while (data_len != 0 && buffer_offset >= data_len + sizeof(uint32_t)) {
2397053SN/A        EthPacketPtr packet;
2407053SN/A        packet = make_shared<EthPacketData>(data_len);
2416899SN/A        packet->length = data_len;
2427568SN/A        packet->simLength = data_len;
2437568SN/A        memcpy(packet->data, data, data_len);
2448832SAli.Saidi@ARM.com
2457568SN/A        assert(buffer_offset >= data_len + sizeof(uint32_t));
2468190SLisa.Hsu@amd.com        buffer_offset -= data_len + sizeof(uint32_t);
2478949Sandreas.hansson@arm.com        if (buffer_offset > 0) {
2489208Snilay@cs.wisc.edu            memmove(buffer, data + data_len, buffer_offset);
2497053SN/A            data_len = ntohl(*(uint32_t *)buffer);
2506899SN/A        } else
2517053SN/A            data_len = 0;
2527053SN/A
2537053SN/A        DPRINTF(Ethernet, "EtherTapStub input len=%d\n", packet->length);
2547053SN/A        DDUMP(EthernetData, packet->data, packet->length);
2556899SN/A        if (!interface->sendPacket(packet)) {
2568975Sandreas.hansson@arm.com            DPRINTF(Ethernet, "bus busy...buffer for retransmission\n");
2577053SN/A            packetBuffer.push(packet);
2587053SN/A            if (!txEvent.scheduled())
2597053SN/A                schedule(txEvent, curTick() + retryTime);
2607053SN/A        } else if (dump) {
2617053SN/A            dump->dump(packet);
2627053SN/A        }
2637053SN/A    }
2647053SN/A}
2657053SN/A
2667053SN/Avoid
2677053SN/AEtherTapStub::retransmit()
2687053SN/A{
2697053SN/A    if (packetBuffer.empty())
2706899SN/A        return;
2717053SN/A
2727053SN/A    EthPacketPtr packet = packetBuffer.front();
2737053SN/A    if (interface->sendPacket(packet)) {
2747053SN/A        if (dump)
2757053SN/A            dump->dump(packet);
2766899SN/A        DPRINTF(Ethernet, "EtherTapStub retransmit\n");
2776899SN/A        packetBuffer.front() = NULL;
2787053SN/A        packetBuffer.pop();
2799475Snilay@cs.wisc.edu    }
2806899SN/A
2817053SN/A    if (!packetBuffer.empty() && !txEvent.scheduled())
2826899SN/A        schedule(txEvent, curTick() + retryTime);
2837053SN/A}
2847053SN/A
2856899SN/AEtherInt*
2867053SN/AEtherTapStub::getEthPort(const std::string &if_name, int idx)
2877053SN/A{
2887053SN/A    if (if_name == "tap") {
2897053SN/A        if (interface->getPeer())
2906899SN/A            panic("Interface already connected to\n");
2916899SN/A        return interface;
2927053SN/A    }
2937053SN/A    return NULL;
2947053SN/A}
2957053SN/A
2967053SN/A
2977053SN/A//=====================================================================
2987053SN/A
2997053SN/Avoid
3007053SN/AEtherTapStub::serialize(CheckpointOut &cp) const
3017053SN/A{
3027053SN/A    SERIALIZE_SCALAR(socket);
3037053SN/A    SERIALIZE_SCALAR(buflen);
3047053SN/A    uint8_t *buffer = (uint8_t *)this->buffer;
3057053SN/A    SERIALIZE_ARRAY(buffer, buflen);
3067053SN/A    SERIALIZE_SCALAR(buffer_offset);
3077053SN/A    SERIALIZE_SCALAR(data_len);
3087053SN/A
3099208Snilay@cs.wisc.edu    bool tapevent_present = false;
3107805Snilay@cs.wisc.edu    if (event) {
3117805Snilay@cs.wisc.edu        tapevent_present = true;
3127805Snilay@cs.wisc.edu        SERIALIZE_SCALAR(tapevent_present);
3137805Snilay@cs.wisc.edu        event->serialize(cp);
3147805Snilay@cs.wisc.edu    }
3159475Snilay@cs.wisc.edu    else {
3167053SN/A        SERIALIZE_SCALAR(tapevent_present);
3177053SN/A    }
3187053SN/A}
3197053SN/A
3206899SN/Avoid
3217053SN/AEtherTapStub::unserialize(CheckpointIn &cp)
3227053SN/A{
3236899SN/A    UNSERIALIZE_SCALAR(socket);
3247053SN/A    UNSERIALIZE_SCALAR(buflen);
3257053SN/A    uint8_t *buffer = (uint8_t *)this->buffer;
3267053SN/A    UNSERIALIZE_ARRAY(buffer, buflen);
3277053SN/A    UNSERIALIZE_SCALAR(buffer_offset);
3287805Snilay@cs.wisc.edu    UNSERIALIZE_SCALAR(data_len);
3299475Snilay@cs.wisc.edu
3307053SN/A    bool tapevent_present;
3317053SN/A    UNSERIALIZE_SCALAR(tapevent_present);
3327053SN/A    if (tapevent_present) {
3337053SN/A        event = new TapEvent(this, socket, POLLIN|POLLERR);
3347053SN/A
3357053SN/A        event->unserialize(cp);
3366899SN/A
3376899SN/A        if (event->queued()) {
3387053SN/A            pollQueue.schedule(event);
3397053SN/A        }
3406899SN/A    }
3417053SN/A}
3427053SN/A
3437053SN/A//=====================================================================
3447053SN/A
3456899SN/AEtherTapStub *
3466899SN/AEtherTapStubParams::create()
3477053SN/A{
3487053SN/A    return new EtherTapStub(this);
3496899SN/A}
3507053SN/A