ethertap.cc revision 12632
12SN/A/* 21762SN/A * Copyright (c) 2003-2005 The Regents of The University of Michigan 32SN/A * All rights reserved. 42SN/A * 52SN/A * Redistribution and use in source and binary forms, with or without 62SN/A * modification, are permitted provided that the following conditions are 72SN/A * met: redistributions of source code must retain the above copyright 82SN/A * notice, this list of conditions and the following disclaimer; 92SN/A * redistributions in binary form must reproduce the above copyright 102SN/A * notice, this list of conditions and the following disclaimer in the 112SN/A * documentation and/or other materials provided with the distribution; 122SN/A * neither the name of the copyright holders nor the names of its 132SN/A * contributors may be used to endorse or promote products derived from 142SN/A * this software without specific prior written permission. 152SN/A * 162SN/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 172SN/A * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 182SN/A * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 192SN/A * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 202SN/A * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 212SN/A * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 222SN/A * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 232SN/A * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 242SN/A * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 252SN/A * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 262SN/A * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 272665SN/A * 282665SN/A * Authors: Nathan Binkert 292SN/A */ 302SN/A 312SN/A/* @file 322SN/A * Interface to connect a simulated ethernet device to the real world 332SN/A */ 342SN/A 3511263Sandreas.sandberg@arm.com#include "dev/net/ethertap.hh" 3611263Sandreas.sandberg@arm.com 37873SN/A#if defined(__OpenBSD__) || defined(__APPLE__) 382SN/A#include <sys/param.h> 3911263Sandreas.sandberg@arm.com 402SN/A#endif 4112056Sgabeblack@google.com 4212056Sgabeblack@google.com#if USE_TUNTAP && defined(__linux__) 4312056Sgabeblack@google.com#if 1 // Hide from the style checker since these have to be out of order. 4412056Sgabeblack@google.com#include <sys/socket.h> // Has to be included before if.h for some reason. 4512056Sgabeblack@google.com 4612056Sgabeblack@google.com#endif 4712056Sgabeblack@google.com 4812056Sgabeblack@google.com#include <linux/if.h> 4912056Sgabeblack@google.com#include <linux/if_tun.h> 5012056Sgabeblack@google.com 5112056Sgabeblack@google.com#endif 5212056Sgabeblack@google.com 5312056Sgabeblack@google.com#include <fcntl.h> 542SN/A#include <netinet/in.h> 5512056Sgabeblack@google.com#include <sys/ioctl.h> 562SN/A#include <unistd.h> 572SN/A 5812056Sgabeblack@google.com#include <cstring> 592SN/A#include <deque> 602SN/A#include <string> 612SN/A 6212334Sgabeblack@google.com#include "base/logging.hh" 63146SN/A#include "base/pollevent.hh" 64146SN/A#include "base/socket.hh" 65146SN/A#include "base/trace.hh" 668232SN/A#include "debug/Ethernet.hh" 678232SN/A#include "debug/EthernetData.hh" 6811263Sandreas.sandberg@arm.com#include "dev/net/etherdump.hh" 6911263Sandreas.sandberg@arm.com#include "dev/net/etherint.hh" 7011263Sandreas.sandberg@arm.com#include "dev/net/etherpkt.hh" 712SN/A 722SN/Ausing namespace std; 732SN/A 7412055Sgabeblack@google.comclass TapEvent : public PollEvent 7512055Sgabeblack@google.com{ 7612055Sgabeblack@google.com protected: 7712055Sgabeblack@google.com EtherTapBase *tap; 7812055Sgabeblack@google.com 7912055Sgabeblack@google.com public: 8012055Sgabeblack@google.com TapEvent(EtherTapBase *_tap, int fd, int e) 8112055Sgabeblack@google.com : PollEvent(fd, e), tap(_tap) {} 8212632Sgabeblack@google.com 8312632Sgabeblack@google.com void 8412632Sgabeblack@google.com process(int revent) override 8512632Sgabeblack@google.com { 8612632Sgabeblack@google.com // Ensure that our event queue is active. It may not be since we get 8712632Sgabeblack@google.com // here from the PollQueue whenever a real packet happens to arrive. 8812632Sgabeblack@google.com EventQueue::ScopedMigration migrate(tap->eventQueue()); 8912632Sgabeblack@google.com 9012632Sgabeblack@google.com tap->recvReal(revent); 9112632Sgabeblack@google.com } 9212055Sgabeblack@google.com}; 9312055Sgabeblack@google.com 9412055Sgabeblack@google.comEtherTapBase::EtherTapBase(const Params *p) 9512055Sgabeblack@google.com : EtherObject(p), buflen(p->bufsz), dump(p->dump), event(NULL), 9612130Sspwilson2@wisc.edu interface(NULL), 9712130Sspwilson2@wisc.edu txEvent([this]{ retransmit(); }, "EtherTapBase retransmit") 9812055Sgabeblack@google.com{ 9912055Sgabeblack@google.com buffer = new uint8_t[buflen]; 10012055Sgabeblack@google.com interface = new EtherTapInt(name() + ".interface", this); 10112055Sgabeblack@google.com} 10212055Sgabeblack@google.com 10312055Sgabeblack@google.comEtherTapBase::~EtherTapBase() 10412055Sgabeblack@google.com{ 10512055Sgabeblack@google.com delete buffer; 10612055Sgabeblack@google.com delete event; 10712055Sgabeblack@google.com delete interface; 10812055Sgabeblack@google.com} 10912055Sgabeblack@google.com 11012055Sgabeblack@google.comvoid 11112055Sgabeblack@google.comEtherTapBase::serialize(CheckpointOut &cp) const 11212055Sgabeblack@google.com{ 11312055Sgabeblack@google.com SERIALIZE_SCALAR(buflen); 11412055Sgabeblack@google.com uint8_t *buffer = (uint8_t *)this->buffer; 11512055Sgabeblack@google.com SERIALIZE_ARRAY(buffer, buflen); 11612055Sgabeblack@google.com 11712055Sgabeblack@google.com bool tapevent_present = false; 11812055Sgabeblack@google.com if (event) { 11912055Sgabeblack@google.com tapevent_present = true; 12012055Sgabeblack@google.com SERIALIZE_SCALAR(tapevent_present); 12112055Sgabeblack@google.com event->serialize(cp); 12212055Sgabeblack@google.com } else { 12312055Sgabeblack@google.com SERIALIZE_SCALAR(tapevent_present); 12412055Sgabeblack@google.com } 12512055Sgabeblack@google.com} 12612055Sgabeblack@google.com 12712055Sgabeblack@google.comvoid 12812055Sgabeblack@google.comEtherTapBase::unserialize(CheckpointIn &cp) 12912055Sgabeblack@google.com{ 13012055Sgabeblack@google.com UNSERIALIZE_SCALAR(buflen); 13112055Sgabeblack@google.com uint8_t *buffer = (uint8_t *)this->buffer; 13212055Sgabeblack@google.com UNSERIALIZE_ARRAY(buffer, buflen); 13312055Sgabeblack@google.com 13412055Sgabeblack@google.com bool tapevent_present; 13512055Sgabeblack@google.com UNSERIALIZE_SCALAR(tapevent_present); 13612055Sgabeblack@google.com if (tapevent_present) { 13712055Sgabeblack@google.com event = new TapEvent(this, 0, 0); 13812055Sgabeblack@google.com event->unserialize(cp); 13912055Sgabeblack@google.com if (event->queued()) 14012055Sgabeblack@google.com pollQueue.schedule(event); 14112055Sgabeblack@google.com } 14212055Sgabeblack@google.com} 14312055Sgabeblack@google.com 14412055Sgabeblack@google.com 14512055Sgabeblack@google.comvoid 14612055Sgabeblack@google.comEtherTapBase::pollFd(int fd) 14712055Sgabeblack@google.com{ 14812055Sgabeblack@google.com assert(!event); 14912055Sgabeblack@google.com event = new TapEvent(this, fd, POLLIN|POLLERR); 15012055Sgabeblack@google.com pollQueue.schedule(event); 15112055Sgabeblack@google.com} 15212055Sgabeblack@google.com 15312055Sgabeblack@google.comvoid 15412055Sgabeblack@google.comEtherTapBase::stopPolling() 15512055Sgabeblack@google.com{ 15612055Sgabeblack@google.com assert(event); 15712055Sgabeblack@google.com delete event; 15812055Sgabeblack@google.com event = NULL; 15912055Sgabeblack@google.com} 16012055Sgabeblack@google.com 16112055Sgabeblack@google.com 16212055Sgabeblack@google.comEtherInt* 16312055Sgabeblack@google.comEtherTapBase::getEthPort(const std::string &if_name, int idx) 16412055Sgabeblack@google.com{ 16512055Sgabeblack@google.com if (if_name == "tap") { 16612055Sgabeblack@google.com if (interface->getPeer()) 16712055Sgabeblack@google.com panic("Interface already connected to\n"); 16812055Sgabeblack@google.com return interface; 16912055Sgabeblack@google.com } 17012055Sgabeblack@google.com return NULL; 17112055Sgabeblack@google.com} 17212055Sgabeblack@google.com 17312055Sgabeblack@google.combool 17412055Sgabeblack@google.comEtherTapBase::recvSimulated(EthPacketPtr packet) 17512055Sgabeblack@google.com{ 17612055Sgabeblack@google.com if (dump) 17712055Sgabeblack@google.com dump->dump(packet); 17812055Sgabeblack@google.com 17912055Sgabeblack@google.com DPRINTF(Ethernet, "EtherTap sim->real len=%d\n", packet->length); 18012055Sgabeblack@google.com DDUMP(EthernetData, packet->data, packet->length); 18112055Sgabeblack@google.com 18212055Sgabeblack@google.com bool success = sendReal(packet->data, packet->length); 18312055Sgabeblack@google.com 18412055Sgabeblack@google.com interface->recvDone(); 18512055Sgabeblack@google.com 18612055Sgabeblack@google.com return success; 18712055Sgabeblack@google.com} 18812055Sgabeblack@google.com 18912055Sgabeblack@google.comvoid 19012055Sgabeblack@google.comEtherTapBase::sendSimulated(void *data, size_t len) 19112055Sgabeblack@google.com{ 19212055Sgabeblack@google.com EthPacketPtr packet; 19312055Sgabeblack@google.com packet = make_shared<EthPacketData>(len); 19412055Sgabeblack@google.com packet->length = len; 19512055Sgabeblack@google.com packet->simLength = len; 19612055Sgabeblack@google.com memcpy(packet->data, data, len); 19712055Sgabeblack@google.com 19812055Sgabeblack@google.com DPRINTF(Ethernet, "EtherTap real->sim len=%d\n", packet->length); 19912055Sgabeblack@google.com DDUMP(EthernetData, packet->data, packet->length); 20012055Sgabeblack@google.com if (!packetBuffer.empty() || !interface->sendPacket(packet)) { 20112055Sgabeblack@google.com DPRINTF(Ethernet, "bus busy...buffer for retransmission\n"); 20212055Sgabeblack@google.com packetBuffer.push(packet); 20312055Sgabeblack@google.com if (!txEvent.scheduled()) 20412055Sgabeblack@google.com schedule(txEvent, curTick() + retryTime); 20512055Sgabeblack@google.com } else if (dump) { 20612055Sgabeblack@google.com dump->dump(packet); 20712055Sgabeblack@google.com } 20812055Sgabeblack@google.com} 20912055Sgabeblack@google.com 21012055Sgabeblack@google.comvoid 21112055Sgabeblack@google.comEtherTapBase::retransmit() 21212055Sgabeblack@google.com{ 21312055Sgabeblack@google.com if (packetBuffer.empty()) 21412055Sgabeblack@google.com return; 21512055Sgabeblack@google.com 21612055Sgabeblack@google.com EthPacketPtr packet = packetBuffer.front(); 21712055Sgabeblack@google.com if (interface->sendPacket(packet)) { 21812055Sgabeblack@google.com if (dump) 21912055Sgabeblack@google.com dump->dump(packet); 22012055Sgabeblack@google.com DPRINTF(Ethernet, "EtherTap retransmit\n"); 22112055Sgabeblack@google.com packetBuffer.front() = NULL; 22212055Sgabeblack@google.com packetBuffer.pop(); 22312055Sgabeblack@google.com } 22412055Sgabeblack@google.com 22512055Sgabeblack@google.com if (!packetBuffer.empty() && !txEvent.scheduled()) 22612055Sgabeblack@google.com schedule(txEvent, curTick() + retryTime); 22712055Sgabeblack@google.com} 22812055Sgabeblack@google.com 22912055Sgabeblack@google.com 2302SN/Aclass TapListener 2312SN/A{ 2322SN/A protected: 2332SN/A class Event : public PollEvent 2342SN/A { 2352SN/A protected: 2362SN/A TapListener *listener; 2372SN/A 2382SN/A public: 23912055Sgabeblack@google.com Event(TapListener *l, int fd, int e) : PollEvent(fd, e), listener(l) {} 2402SN/A 24112055Sgabeblack@google.com void process(int revent) override { listener->accept(); } 2422SN/A }; 2432SN/A 2442SN/A friend class Event; 2452SN/A Event *event; 2462SN/A 24712055Sgabeblack@google.com void accept(); 24812055Sgabeblack@google.com 2492SN/A protected: 2502SN/A ListenSocket listener; 25112054Sgabeblack@google.com EtherTapStub *tap; 2522SN/A int port; 2532SN/A 2542SN/A public: 25512055Sgabeblack@google.com TapListener(EtherTapStub *t, int p) : event(NULL), tap(t), port(p) {} 25612055Sgabeblack@google.com ~TapListener() { delete event; } 2572SN/A 2582SN/A void listen(); 2592SN/A}; 2602SN/A 2612SN/Avoid 2622SN/ATapListener::listen() 2632SN/A{ 2642SN/A while (!listener.listen(port, true)) { 2652SN/A DPRINTF(Ethernet, "TapListener(listen): Can't bind port %d\n", port); 2662SN/A port++; 2672SN/A } 2682SN/A 2692SN/A ccprintf(cerr, "Listening for tap connection on port %d\n", port); 2702SN/A event = new Event(this, listener.getfd(), POLLIN|POLLERR); 2712SN/A pollQueue.schedule(event); 2722SN/A} 2732SN/A 2742SN/Avoid 2752SN/ATapListener::accept() 2762SN/A{ 27710156SN/A // As a consequence of being called from the PollQueue, we might 27810156SN/A // have been called from a different thread. Migrate to "our" 27910156SN/A // thread. 28010156SN/A EventQueue::ScopedMigration migrate(tap->eventQueue()); 28110156SN/A 2822SN/A if (!listener.islistening()) 2832SN/A panic("TapListener(accept): cannot accept if we're not listening!"); 2842SN/A 2852SN/A int sfd = listener.accept(true); 2862SN/A if (sfd != -1) 2872SN/A tap->attach(sfd); 2882SN/A} 2892SN/A 2902SN/A 29112055Sgabeblack@google.comEtherTapStub::EtherTapStub(const Params *p) : EtherTapBase(p), socket(-1) 2922SN/A{ 2935523SN/A if (ListenSocket::allDisabled()) 29412054Sgabeblack@google.com fatal("All listeners are disabled! EtherTapStub can't work!"); 2955523SN/A 2964981SN/A listener = new TapListener(this, p->port); 2972SN/A listener->listen(); 2982SN/A} 2992SN/A 30012054Sgabeblack@google.comEtherTapStub::~EtherTapStub() 3012SN/A{ 3022SN/A delete listener; 3032SN/A} 3042SN/A 3052SN/Avoid 30612055Sgabeblack@google.comEtherTapStub::serialize(CheckpointOut &cp) const 30712055Sgabeblack@google.com{ 30812055Sgabeblack@google.com EtherTapBase::serialize(cp); 30912055Sgabeblack@google.com 31012055Sgabeblack@google.com SERIALIZE_SCALAR(socket); 31112055Sgabeblack@google.com SERIALIZE_SCALAR(buffer_used); 31212055Sgabeblack@google.com SERIALIZE_SCALAR(frame_len); 31312055Sgabeblack@google.com} 31412055Sgabeblack@google.com 31512055Sgabeblack@google.comvoid 31612055Sgabeblack@google.comEtherTapStub::unserialize(CheckpointIn &cp) 31712055Sgabeblack@google.com{ 31812055Sgabeblack@google.com EtherTapBase::unserialize(cp); 31912055Sgabeblack@google.com 32012055Sgabeblack@google.com UNSERIALIZE_SCALAR(socket); 32112055Sgabeblack@google.com UNSERIALIZE_SCALAR(buffer_used); 32212055Sgabeblack@google.com UNSERIALIZE_SCALAR(frame_len); 32312055Sgabeblack@google.com} 32412055Sgabeblack@google.com 32512055Sgabeblack@google.com 32612055Sgabeblack@google.comvoid 32712054Sgabeblack@google.comEtherTapStub::attach(int fd) 3282SN/A{ 3292SN/A if (socket != -1) 3302SN/A close(fd); 3312SN/A 33212055Sgabeblack@google.com buffer_used = 0; 33312055Sgabeblack@google.com frame_len = 0; 3342SN/A socket = fd; 33512054Sgabeblack@google.com DPRINTF(Ethernet, "EtherTapStub attached\n"); 33612055Sgabeblack@google.com pollFd(socket); 3372SN/A} 3382SN/A 3392SN/Avoid 34012054Sgabeblack@google.comEtherTapStub::detach() 3412SN/A{ 34212054Sgabeblack@google.com DPRINTF(Ethernet, "EtherTapStub detached\n"); 34312055Sgabeblack@google.com stopPolling(); 3442SN/A close(socket); 3452SN/A socket = -1; 3462SN/A} 3472SN/A 3482SN/Avoid 34912055Sgabeblack@google.comEtherTapStub::recvReal(int revent) 3502SN/A{ 3512SN/A if (revent & POLLERR) { 3522SN/A detach(); 3532SN/A return; 3542SN/A } 3552SN/A 3562SN/A if (!(revent & POLLIN)) 3572SN/A return; 3582SN/A 35912055Sgabeblack@google.com // Read in as much of the new data as we can. 36012055Sgabeblack@google.com int len = read(socket, buffer + buffer_used, buflen - buffer_used); 36112055Sgabeblack@google.com if (len == 0) { 36212055Sgabeblack@google.com detach(); 36312055Sgabeblack@google.com return; 36412055Sgabeblack@google.com } 36512055Sgabeblack@google.com buffer_used += len; 36612055Sgabeblack@google.com 36712055Sgabeblack@google.com // If there's not enough data for the frame length, wait for more. 36812055Sgabeblack@google.com if (buffer_used < sizeof(uint32_t)) 36912055Sgabeblack@google.com return; 37012055Sgabeblack@google.com 37112055Sgabeblack@google.com if (frame_len == 0) 37212055Sgabeblack@google.com frame_len = ntohl(*(uint32_t *)buffer); 37312055Sgabeblack@google.com 37412055Sgabeblack@google.com DPRINTF(Ethernet, "Received data from peer: len=%d buffer_used=%d " 37512055Sgabeblack@google.com "frame_len=%d\n", len, buffer_used, frame_len); 37612055Sgabeblack@google.com 37712055Sgabeblack@google.com uint8_t *frame_start = &buffer[sizeof(uint32_t)]; 37812055Sgabeblack@google.com while (frame_len != 0 && buffer_used >= frame_len + sizeof(uint32_t)) { 37912055Sgabeblack@google.com sendSimulated(frame_start, frame_len); 38012055Sgabeblack@google.com 38112055Sgabeblack@google.com // Bookkeeping. 38212055Sgabeblack@google.com buffer_used -= frame_len + sizeof(uint32_t); 38312055Sgabeblack@google.com if (buffer_used > 0) { 38412055Sgabeblack@google.com // If there's still any data left, move it into position. 38512055Sgabeblack@google.com memmove(buffer, frame_start + frame_len, buffer_used); 3862SN/A } 38712055Sgabeblack@google.com frame_len = 0; 3882SN/A 38912055Sgabeblack@google.com if (buffer_used >= sizeof(uint32_t)) 39012055Sgabeblack@google.com frame_len = ntohl(*(uint32_t *)buffer); 3912SN/A } 3922SN/A} 3932SN/A 39412055Sgabeblack@google.combool 39512055Sgabeblack@google.comEtherTapStub::sendReal(const void *data, size_t len) 3962SN/A{ 39712055Sgabeblack@google.com uint32_t frame_len = htonl(len); 39812055Sgabeblack@google.com ssize_t ret = write(socket, &frame_len, sizeof(frame_len)); 39912055Sgabeblack@google.com if (ret != sizeof(frame_len)) 40012055Sgabeblack@google.com return false; 40112055Sgabeblack@google.com return write(socket, data, len) == len; 4022SN/A} 4032SN/A 404253SN/A 40512056Sgabeblack@google.com#if USE_TUNTAP 40612056Sgabeblack@google.com 40712056Sgabeblack@google.comEtherTap::EtherTap(const Params *p) : EtherTapBase(p) 40812056Sgabeblack@google.com{ 40912056Sgabeblack@google.com int fd = open(p->tun_clone_device.c_str(), O_RDWR); 41012056Sgabeblack@google.com if (fd < 0) 41112056Sgabeblack@google.com panic("Couldn't open %s.\n", p->tun_clone_device); 41212056Sgabeblack@google.com 41312056Sgabeblack@google.com struct ifreq ifr; 41412056Sgabeblack@google.com memset(&ifr, 0, sizeof(ifr)); 41512056Sgabeblack@google.com ifr.ifr_flags = IFF_TAP | IFF_NO_PI; 41612559Ssiddhesh.poyarekar@gmail.com strncpy(ifr.ifr_name, p->tap_device_name.c_str(), IFNAMSIZ - 1); 41712056Sgabeblack@google.com 41812056Sgabeblack@google.com if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) 41912056Sgabeblack@google.com panic("Failed to access tap device %s.\n", ifr.ifr_name); 42012056Sgabeblack@google.com // fd now refers to the tap device. 42112056Sgabeblack@google.com tap = fd; 42212056Sgabeblack@google.com pollFd(tap); 42312056Sgabeblack@google.com} 42412056Sgabeblack@google.com 42512056Sgabeblack@google.comEtherTap::~EtherTap() 42612056Sgabeblack@google.com{ 42712056Sgabeblack@google.com stopPolling(); 42812056Sgabeblack@google.com close(tap); 42912056Sgabeblack@google.com tap = -1; 43012056Sgabeblack@google.com} 43112056Sgabeblack@google.com 43212056Sgabeblack@google.comvoid 43312056Sgabeblack@google.comEtherTap::recvReal(int revent) 43412056Sgabeblack@google.com{ 43512056Sgabeblack@google.com if (revent & POLLERR) 43612056Sgabeblack@google.com panic("Error polling for tap data.\n"); 43712056Sgabeblack@google.com 43812056Sgabeblack@google.com if (!(revent & POLLIN)) 43912056Sgabeblack@google.com return; 44012056Sgabeblack@google.com 44112056Sgabeblack@google.com ssize_t ret = read(tap, buffer, buflen); 44212056Sgabeblack@google.com if (ret < 0) 44312056Sgabeblack@google.com panic("Failed to read from tap device.\n"); 44412056Sgabeblack@google.com 44512056Sgabeblack@google.com sendSimulated(buffer, ret); 44612056Sgabeblack@google.com} 44712056Sgabeblack@google.com 44812056Sgabeblack@google.combool 44912056Sgabeblack@google.comEtherTap::sendReal(const void *data, size_t len) 45012056Sgabeblack@google.com{ 45112056Sgabeblack@google.com if (write(tap, data, len) != len) 45212056Sgabeblack@google.com panic("Failed to write data to tap device.\n"); 45312056Sgabeblack@google.com return true; 45412056Sgabeblack@google.com} 45512056Sgabeblack@google.com 45612056Sgabeblack@google.comEtherTap * 45712056Sgabeblack@google.comEtherTapParams::create() 45812056Sgabeblack@google.com{ 45912056Sgabeblack@google.com return new EtherTap(this); 46012056Sgabeblack@google.com} 46112056Sgabeblack@google.com 46212056Sgabeblack@google.com#endif 46312056Sgabeblack@google.com 46412054Sgabeblack@google.comEtherTapStub * 46512054Sgabeblack@google.comEtherTapStubParams::create() 4662SN/A{ 46712054Sgabeblack@google.com return new EtherTapStub(this); 4682SN/A} 469