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