ethertap.cc revision 12056:1ad5b3161819
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#include "dev/net/ethertap.hh" 36 37#if defined(__OpenBSD__) || defined(__APPLE__) 38#include <sys/param.h> 39 40#endif 41 42#if USE_TUNTAP && defined(__linux__) 43#if 1 // Hide from the style checker since these have to be out of order. 44#include <sys/socket.h> // Has to be included before if.h for some reason. 45 46#endif 47 48#include <linux/if.h> 49#include <linux/if_tun.h> 50 51#endif 52 53#include <fcntl.h> 54#include <netinet/in.h> 55#include <sys/ioctl.h> 56#include <unistd.h> 57 58#include <cstring> 59#include <deque> 60#include <string> 61 62#include "base/misc.hh" 63#include "base/pollevent.hh" 64#include "base/socket.hh" 65#include "base/trace.hh" 66#include "debug/Ethernet.hh" 67#include "debug/EthernetData.hh" 68#include "dev/net/etherdump.hh" 69#include "dev/net/etherint.hh" 70#include "dev/net/etherpkt.hh" 71 72using namespace std; 73 74class TapEvent : public PollEvent 75{ 76 protected: 77 EtherTapBase *tap; 78 79 public: 80 TapEvent(EtherTapBase *_tap, int fd, int e) 81 : PollEvent(fd, e), tap(_tap) {} 82 virtual void process(int revent) { tap->recvReal(revent); } 83}; 84 85EtherTapBase::EtherTapBase(const Params *p) 86 : EtherObject(p), buflen(p->bufsz), dump(p->dump), event(NULL), 87 interface(NULL), txEvent(this) 88{ 89 buffer = new uint8_t[buflen]; 90 interface = new EtherTapInt(name() + ".interface", this); 91} 92 93EtherTapBase::~EtherTapBase() 94{ 95 delete buffer; 96 delete event; 97 delete interface; 98} 99 100void 101EtherTapBase::serialize(CheckpointOut &cp) const 102{ 103 SERIALIZE_SCALAR(buflen); 104 uint8_t *buffer = (uint8_t *)this->buffer; 105 SERIALIZE_ARRAY(buffer, buflen); 106 107 bool tapevent_present = false; 108 if (event) { 109 tapevent_present = true; 110 SERIALIZE_SCALAR(tapevent_present); 111 event->serialize(cp); 112 } else { 113 SERIALIZE_SCALAR(tapevent_present); 114 } 115} 116 117void 118EtherTapBase::unserialize(CheckpointIn &cp) 119{ 120 UNSERIALIZE_SCALAR(buflen); 121 uint8_t *buffer = (uint8_t *)this->buffer; 122 UNSERIALIZE_ARRAY(buffer, buflen); 123 124 bool tapevent_present; 125 UNSERIALIZE_SCALAR(tapevent_present); 126 if (tapevent_present) { 127 event = new TapEvent(this, 0, 0); 128 event->unserialize(cp); 129 if (event->queued()) 130 pollQueue.schedule(event); 131 } 132} 133 134 135void 136EtherTapBase::pollFd(int fd) 137{ 138 assert(!event); 139 event = new TapEvent(this, fd, POLLIN|POLLERR); 140 pollQueue.schedule(event); 141} 142 143void 144EtherTapBase::stopPolling() 145{ 146 assert(event); 147 delete event; 148 event = NULL; 149} 150 151 152EtherInt* 153EtherTapBase::getEthPort(const std::string &if_name, int idx) 154{ 155 if (if_name == "tap") { 156 if (interface->getPeer()) 157 panic("Interface already connected to\n"); 158 return interface; 159 } 160 return NULL; 161} 162 163bool 164EtherTapBase::recvSimulated(EthPacketPtr packet) 165{ 166 if (dump) 167 dump->dump(packet); 168 169 DPRINTF(Ethernet, "EtherTap sim->real len=%d\n", packet->length); 170 DDUMP(EthernetData, packet->data, packet->length); 171 172 bool success = sendReal(packet->data, packet->length); 173 174 interface->recvDone(); 175 176 return success; 177} 178 179void 180EtherTapBase::sendSimulated(void *data, size_t len) 181{ 182 EthPacketPtr packet; 183 packet = make_shared<EthPacketData>(len); 184 packet->length = len; 185 packet->simLength = len; 186 memcpy(packet->data, data, len); 187 188 DPRINTF(Ethernet, "EtherTap real->sim len=%d\n", packet->length); 189 DDUMP(EthernetData, packet->data, packet->length); 190 if (!packetBuffer.empty() || !interface->sendPacket(packet)) { 191 DPRINTF(Ethernet, "bus busy...buffer for retransmission\n"); 192 packetBuffer.push(packet); 193 if (!txEvent.scheduled()) 194 schedule(txEvent, curTick() + retryTime); 195 } else if (dump) { 196 dump->dump(packet); 197 } 198} 199 200void 201EtherTapBase::retransmit() 202{ 203 if (packetBuffer.empty()) 204 return; 205 206 EthPacketPtr packet = packetBuffer.front(); 207 if (interface->sendPacket(packet)) { 208 if (dump) 209 dump->dump(packet); 210 DPRINTF(Ethernet, "EtherTap retransmit\n"); 211 packetBuffer.front() = NULL; 212 packetBuffer.pop(); 213 } 214 215 if (!packetBuffer.empty() && !txEvent.scheduled()) 216 schedule(txEvent, curTick() + retryTime); 217} 218 219 220class TapListener 221{ 222 protected: 223 class Event : public PollEvent 224 { 225 protected: 226 TapListener *listener; 227 228 public: 229 Event(TapListener *l, int fd, int e) : PollEvent(fd, e), listener(l) {} 230 231 void process(int revent) override { listener->accept(); } 232 }; 233 234 friend class Event; 235 Event *event; 236 237 void accept(); 238 239 protected: 240 ListenSocket listener; 241 EtherTapStub *tap; 242 int port; 243 244 public: 245 TapListener(EtherTapStub *t, int p) : event(NULL), tap(t), port(p) {} 246 ~TapListener() { delete event; } 247 248 void listen(); 249}; 250 251void 252TapListener::listen() 253{ 254 while (!listener.listen(port, true)) { 255 DPRINTF(Ethernet, "TapListener(listen): Can't bind port %d\n", port); 256 port++; 257 } 258 259 ccprintf(cerr, "Listening for tap connection on port %d\n", port); 260 event = new Event(this, listener.getfd(), POLLIN|POLLERR); 261 pollQueue.schedule(event); 262} 263 264void 265TapListener::accept() 266{ 267 // As a consequence of being called from the PollQueue, we might 268 // have been called from a different thread. Migrate to "our" 269 // thread. 270 EventQueue::ScopedMigration migrate(tap->eventQueue()); 271 272 if (!listener.islistening()) 273 panic("TapListener(accept): cannot accept if we're not listening!"); 274 275 int sfd = listener.accept(true); 276 if (sfd != -1) 277 tap->attach(sfd); 278} 279 280 281EtherTapStub::EtherTapStub(const Params *p) : EtherTapBase(p), socket(-1) 282{ 283 if (ListenSocket::allDisabled()) 284 fatal("All listeners are disabled! EtherTapStub can't work!"); 285 286 listener = new TapListener(this, p->port); 287 listener->listen(); 288} 289 290EtherTapStub::~EtherTapStub() 291{ 292 delete listener; 293} 294 295void 296EtherTapStub::serialize(CheckpointOut &cp) const 297{ 298 EtherTapBase::serialize(cp); 299 300 SERIALIZE_SCALAR(socket); 301 SERIALIZE_SCALAR(buffer_used); 302 SERIALIZE_SCALAR(frame_len); 303} 304 305void 306EtherTapStub::unserialize(CheckpointIn &cp) 307{ 308 EtherTapBase::unserialize(cp); 309 310 UNSERIALIZE_SCALAR(socket); 311 UNSERIALIZE_SCALAR(buffer_used); 312 UNSERIALIZE_SCALAR(frame_len); 313} 314 315 316void 317EtherTapStub::attach(int fd) 318{ 319 if (socket != -1) 320 close(fd); 321 322 buffer_used = 0; 323 frame_len = 0; 324 socket = fd; 325 DPRINTF(Ethernet, "EtherTapStub attached\n"); 326 pollFd(socket); 327} 328 329void 330EtherTapStub::detach() 331{ 332 DPRINTF(Ethernet, "EtherTapStub detached\n"); 333 stopPolling(); 334 close(socket); 335 socket = -1; 336} 337 338void 339EtherTapStub::recvReal(int revent) 340{ 341 if (revent & POLLERR) { 342 detach(); 343 return; 344 } 345 346 if (!(revent & POLLIN)) 347 return; 348 349 // Read in as much of the new data as we can. 350 int len = read(socket, buffer + buffer_used, buflen - buffer_used); 351 if (len == 0) { 352 detach(); 353 return; 354 } 355 buffer_used += len; 356 357 // If there's not enough data for the frame length, wait for more. 358 if (buffer_used < sizeof(uint32_t)) 359 return; 360 361 if (frame_len == 0) 362 frame_len = ntohl(*(uint32_t *)buffer); 363 364 DPRINTF(Ethernet, "Received data from peer: len=%d buffer_used=%d " 365 "frame_len=%d\n", len, buffer_used, frame_len); 366 367 uint8_t *frame_start = &buffer[sizeof(uint32_t)]; 368 while (frame_len != 0 && buffer_used >= frame_len + sizeof(uint32_t)) { 369 sendSimulated(frame_start, frame_len); 370 371 // Bookkeeping. 372 buffer_used -= frame_len + sizeof(uint32_t); 373 if (buffer_used > 0) { 374 // If there's still any data left, move it into position. 375 memmove(buffer, frame_start + frame_len, buffer_used); 376 } 377 frame_len = 0; 378 379 if (buffer_used >= sizeof(uint32_t)) 380 frame_len = ntohl(*(uint32_t *)buffer); 381 } 382} 383 384bool 385EtherTapStub::sendReal(const void *data, size_t len) 386{ 387 uint32_t frame_len = htonl(len); 388 ssize_t ret = write(socket, &frame_len, sizeof(frame_len)); 389 if (ret != sizeof(frame_len)) 390 return false; 391 return write(socket, data, len) == len; 392} 393 394 395#if USE_TUNTAP 396 397EtherTap::EtherTap(const Params *p) : EtherTapBase(p) 398{ 399 int fd = open(p->tun_clone_device.c_str(), O_RDWR); 400 if (fd < 0) 401 panic("Couldn't open %s.\n", p->tun_clone_device); 402 403 struct ifreq ifr; 404 memset(&ifr, 0, sizeof(ifr)); 405 ifr.ifr_flags = IFF_TAP | IFF_NO_PI; 406 strncpy(ifr.ifr_name, p->tap_device_name.c_str(), IFNAMSIZ); 407 408 if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) 409 panic("Failed to access tap device %s.\n", ifr.ifr_name); 410 // fd now refers to the tap device. 411 tap = fd; 412 pollFd(tap); 413} 414 415EtherTap::~EtherTap() 416{ 417 stopPolling(); 418 close(tap); 419 tap = -1; 420} 421 422void 423EtherTap::recvReal(int revent) 424{ 425 if (revent & POLLERR) 426 panic("Error polling for tap data.\n"); 427 428 if (!(revent & POLLIN)) 429 return; 430 431 ssize_t ret = read(tap, buffer, buflen); 432 if (ret < 0) 433 panic("Failed to read from tap device.\n"); 434 435 sendSimulated(buffer, ret); 436} 437 438bool 439EtherTap::sendReal(const void *data, size_t len) 440{ 441 if (write(tap, data, len) != len) 442 panic("Failed to write data to tap device.\n"); 443 return true; 444} 445 446EtherTap * 447EtherTapParams::create() 448{ 449 return new EtherTap(this); 450} 451 452#endif 453 454EtherTapStub * 455EtherTapStubParams::create() 456{ 457 return new EtherTapStub(this); 458} 459