ethertap.cc revision 12130:5e0dd4d7b730
16365Sgblack@eecs.umich.edu/* 26365Sgblack@eecs.umich.edu * Copyright (c) 2003-2005 The Regents of The University of Michigan 36365Sgblack@eecs.umich.edu * All rights reserved. 46365Sgblack@eecs.umich.edu * 56365Sgblack@eecs.umich.edu * Redistribution and use in source and binary forms, with or without 66365Sgblack@eecs.umich.edu * modification, are permitted provided that the following conditions are 76365Sgblack@eecs.umich.edu * met: redistributions of source code must retain the above copyright 86365Sgblack@eecs.umich.edu * notice, this list of conditions and the following disclaimer; 96365Sgblack@eecs.umich.edu * redistributions in binary form must reproduce the above copyright 106365Sgblack@eecs.umich.edu * notice, this list of conditions and the following disclaimer in the 116365Sgblack@eecs.umich.edu * documentation and/or other materials provided with the distribution; 126365Sgblack@eecs.umich.edu * neither the name of the copyright holders nor the names of its 136365Sgblack@eecs.umich.edu * contributors may be used to endorse or promote products derived from 146365Sgblack@eecs.umich.edu * this software without specific prior written permission. 156365Sgblack@eecs.umich.edu * 166365Sgblack@eecs.umich.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 176365Sgblack@eecs.umich.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 186365Sgblack@eecs.umich.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 196365Sgblack@eecs.umich.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 206365Sgblack@eecs.umich.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 216365Sgblack@eecs.umich.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 226365Sgblack@eecs.umich.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 236365Sgblack@eecs.umich.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 246365Sgblack@eecs.umich.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 256365Sgblack@eecs.umich.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 266365Sgblack@eecs.umich.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 276365Sgblack@eecs.umich.edu * 286365Sgblack@eecs.umich.edu * Authors: Nathan Binkert 296365Sgblack@eecs.umich.edu */ 306365Sgblack@eecs.umich.edu 316365Sgblack@eecs.umich.edu/* @file 326365Sgblack@eecs.umich.edu * Interface to connect a simulated ethernet device to the real world 336365Sgblack@eecs.umich.edu */ 346365Sgblack@eecs.umich.edu 356365Sgblack@eecs.umich.edu#include "dev/net/ethertap.hh" 366365Sgblack@eecs.umich.edu 376365Sgblack@eecs.umich.edu#if defined(__OpenBSD__) || defined(__APPLE__) 386365Sgblack@eecs.umich.edu#include <sys/param.h> 396365Sgblack@eecs.umich.edu 406365Sgblack@eecs.umich.edu#endif 416365Sgblack@eecs.umich.edu 426365Sgblack@eecs.umich.edu#if USE_TUNTAP && defined(__linux__) 436365Sgblack@eecs.umich.edu#if 1 // Hide from the style checker since these have to be out of order. 446365Sgblack@eecs.umich.edu#include <sys/socket.h> // Has to be included before if.h for some reason. 456365Sgblack@eecs.umich.edu 466365Sgblack@eecs.umich.edu#endif 476365Sgblack@eecs.umich.edu 486365Sgblack@eecs.umich.edu#include <linux/if.h> 496365Sgblack@eecs.umich.edu#include <linux/if_tun.h> 506365Sgblack@eecs.umich.edu 516365Sgblack@eecs.umich.edu#endif 526365Sgblack@eecs.umich.edu 536365Sgblack@eecs.umich.edu#include <fcntl.h> 546365Sgblack@eecs.umich.edu#include <netinet/in.h> 556365Sgblack@eecs.umich.edu#include <sys/ioctl.h> 566365Sgblack@eecs.umich.edu#include <unistd.h> 576365Sgblack@eecs.umich.edu 586365Sgblack@eecs.umich.edu#include <cstring> 596365Sgblack@eecs.umich.edu#include <deque> 606365Sgblack@eecs.umich.edu#include <string> 616365Sgblack@eecs.umich.edu 626365Sgblack@eecs.umich.edu#include "base/misc.hh" 636365Sgblack@eecs.umich.edu#include "base/pollevent.hh" 646365Sgblack@eecs.umich.edu#include "base/socket.hh" 656365Sgblack@eecs.umich.edu#include "base/trace.hh" 666365Sgblack@eecs.umich.edu#include "debug/Ethernet.hh" 676365Sgblack@eecs.umich.edu#include "debug/EthernetData.hh" 686365Sgblack@eecs.umich.edu#include "dev/net/etherdump.hh" 696365Sgblack@eecs.umich.edu#include "dev/net/etherint.hh" 706365Sgblack@eecs.umich.edu#include "dev/net/etherpkt.hh" 716365Sgblack@eecs.umich.edu 726365Sgblack@eecs.umich.eduusing namespace std; 736365Sgblack@eecs.umich.edu 746365Sgblack@eecs.umich.educlass TapEvent : public PollEvent 756365Sgblack@eecs.umich.edu{ 766365Sgblack@eecs.umich.edu protected: 776365Sgblack@eecs.umich.edu EtherTapBase *tap; 786365Sgblack@eecs.umich.edu 796365Sgblack@eecs.umich.edu public: 806365Sgblack@eecs.umich.edu TapEvent(EtherTapBase *_tap, int fd, int e) 816365Sgblack@eecs.umich.edu : PollEvent(fd, e), tap(_tap) {} 826365Sgblack@eecs.umich.edu virtual void process(int revent) { tap->recvReal(revent); } 836365Sgblack@eecs.umich.edu}; 846365Sgblack@eecs.umich.edu 856365Sgblack@eecs.umich.eduEtherTapBase::EtherTapBase(const Params *p) 866365Sgblack@eecs.umich.edu : EtherObject(p), buflen(p->bufsz), dump(p->dump), event(NULL), 876365Sgblack@eecs.umich.edu interface(NULL), 886365Sgblack@eecs.umich.edu txEvent([this]{ retransmit(); }, "EtherTapBase retransmit") 896365Sgblack@eecs.umich.edu{ 906365Sgblack@eecs.umich.edu buffer = new uint8_t[buflen]; 91 interface = new EtherTapInt(name() + ".interface", this); 92} 93 94EtherTapBase::~EtherTapBase() 95{ 96 delete buffer; 97 delete event; 98 delete interface; 99} 100 101void 102EtherTapBase::serialize(CheckpointOut &cp) const 103{ 104 SERIALIZE_SCALAR(buflen); 105 uint8_t *buffer = (uint8_t *)this->buffer; 106 SERIALIZE_ARRAY(buffer, buflen); 107 108 bool tapevent_present = false; 109 if (event) { 110 tapevent_present = true; 111 SERIALIZE_SCALAR(tapevent_present); 112 event->serialize(cp); 113 } else { 114 SERIALIZE_SCALAR(tapevent_present); 115 } 116} 117 118void 119EtherTapBase::unserialize(CheckpointIn &cp) 120{ 121 UNSERIALIZE_SCALAR(buflen); 122 uint8_t *buffer = (uint8_t *)this->buffer; 123 UNSERIALIZE_ARRAY(buffer, buflen); 124 125 bool tapevent_present; 126 UNSERIALIZE_SCALAR(tapevent_present); 127 if (tapevent_present) { 128 event = new TapEvent(this, 0, 0); 129 event->unserialize(cp); 130 if (event->queued()) 131 pollQueue.schedule(event); 132 } 133} 134 135 136void 137EtherTapBase::pollFd(int fd) 138{ 139 assert(!event); 140 event = new TapEvent(this, fd, POLLIN|POLLERR); 141 pollQueue.schedule(event); 142} 143 144void 145EtherTapBase::stopPolling() 146{ 147 assert(event); 148 delete event; 149 event = NULL; 150} 151 152 153EtherInt* 154EtherTapBase::getEthPort(const std::string &if_name, int idx) 155{ 156 if (if_name == "tap") { 157 if (interface->getPeer()) 158 panic("Interface already connected to\n"); 159 return interface; 160 } 161 return NULL; 162} 163 164bool 165EtherTapBase::recvSimulated(EthPacketPtr packet) 166{ 167 if (dump) 168 dump->dump(packet); 169 170 DPRINTF(Ethernet, "EtherTap sim->real len=%d\n", packet->length); 171 DDUMP(EthernetData, packet->data, packet->length); 172 173 bool success = sendReal(packet->data, packet->length); 174 175 interface->recvDone(); 176 177 return success; 178} 179 180void 181EtherTapBase::sendSimulated(void *data, size_t len) 182{ 183 EthPacketPtr packet; 184 packet = make_shared<EthPacketData>(len); 185 packet->length = len; 186 packet->simLength = len; 187 memcpy(packet->data, data, len); 188 189 DPRINTF(Ethernet, "EtherTap real->sim len=%d\n", packet->length); 190 DDUMP(EthernetData, packet->data, packet->length); 191 if (!packetBuffer.empty() || !interface->sendPacket(packet)) { 192 DPRINTF(Ethernet, "bus busy...buffer for retransmission\n"); 193 packetBuffer.push(packet); 194 if (!txEvent.scheduled()) 195 schedule(txEvent, curTick() + retryTime); 196 } else if (dump) { 197 dump->dump(packet); 198 } 199} 200 201void 202EtherTapBase::retransmit() 203{ 204 if (packetBuffer.empty()) 205 return; 206 207 EthPacketPtr packet = packetBuffer.front(); 208 if (interface->sendPacket(packet)) { 209 if (dump) 210 dump->dump(packet); 211 DPRINTF(Ethernet, "EtherTap retransmit\n"); 212 packetBuffer.front() = NULL; 213 packetBuffer.pop(); 214 } 215 216 if (!packetBuffer.empty() && !txEvent.scheduled()) 217 schedule(txEvent, curTick() + retryTime); 218} 219 220 221class TapListener 222{ 223 protected: 224 class Event : public PollEvent 225 { 226 protected: 227 TapListener *listener; 228 229 public: 230 Event(TapListener *l, int fd, int e) : PollEvent(fd, e), listener(l) {} 231 232 void process(int revent) override { listener->accept(); } 233 }; 234 235 friend class Event; 236 Event *event; 237 238 void accept(); 239 240 protected: 241 ListenSocket listener; 242 EtherTapStub *tap; 243 int port; 244 245 public: 246 TapListener(EtherTapStub *t, int p) : event(NULL), tap(t), port(p) {} 247 ~TapListener() { delete event; } 248 249 void listen(); 250}; 251 252void 253TapListener::listen() 254{ 255 while (!listener.listen(port, true)) { 256 DPRINTF(Ethernet, "TapListener(listen): Can't bind port %d\n", port); 257 port++; 258 } 259 260 ccprintf(cerr, "Listening for tap connection on port %d\n", port); 261 event = new Event(this, listener.getfd(), POLLIN|POLLERR); 262 pollQueue.schedule(event); 263} 264 265void 266TapListener::accept() 267{ 268 // As a consequence of being called from the PollQueue, we might 269 // have been called from a different thread. Migrate to "our" 270 // thread. 271 EventQueue::ScopedMigration migrate(tap->eventQueue()); 272 273 if (!listener.islistening()) 274 panic("TapListener(accept): cannot accept if we're not listening!"); 275 276 int sfd = listener.accept(true); 277 if (sfd != -1) 278 tap->attach(sfd); 279} 280 281 282EtherTapStub::EtherTapStub(const Params *p) : EtherTapBase(p), socket(-1) 283{ 284 if (ListenSocket::allDisabled()) 285 fatal("All listeners are disabled! EtherTapStub can't work!"); 286 287 listener = new TapListener(this, p->port); 288 listener->listen(); 289} 290 291EtherTapStub::~EtherTapStub() 292{ 293 delete listener; 294} 295 296void 297EtherTapStub::serialize(CheckpointOut &cp) const 298{ 299 EtherTapBase::serialize(cp); 300 301 SERIALIZE_SCALAR(socket); 302 SERIALIZE_SCALAR(buffer_used); 303 SERIALIZE_SCALAR(frame_len); 304} 305 306void 307EtherTapStub::unserialize(CheckpointIn &cp) 308{ 309 EtherTapBase::unserialize(cp); 310 311 UNSERIALIZE_SCALAR(socket); 312 UNSERIALIZE_SCALAR(buffer_used); 313 UNSERIALIZE_SCALAR(frame_len); 314} 315 316 317void 318EtherTapStub::attach(int fd) 319{ 320 if (socket != -1) 321 close(fd); 322 323 buffer_used = 0; 324 frame_len = 0; 325 socket = fd; 326 DPRINTF(Ethernet, "EtherTapStub attached\n"); 327 pollFd(socket); 328} 329 330void 331EtherTapStub::detach() 332{ 333 DPRINTF(Ethernet, "EtherTapStub detached\n"); 334 stopPolling(); 335 close(socket); 336 socket = -1; 337} 338 339void 340EtherTapStub::recvReal(int revent) 341{ 342 if (revent & POLLERR) { 343 detach(); 344 return; 345 } 346 347 if (!(revent & POLLIN)) 348 return; 349 350 // Read in as much of the new data as we can. 351 int len = read(socket, buffer + buffer_used, buflen - buffer_used); 352 if (len == 0) { 353 detach(); 354 return; 355 } 356 buffer_used += len; 357 358 // If there's not enough data for the frame length, wait for more. 359 if (buffer_used < sizeof(uint32_t)) 360 return; 361 362 if (frame_len == 0) 363 frame_len = ntohl(*(uint32_t *)buffer); 364 365 DPRINTF(Ethernet, "Received data from peer: len=%d buffer_used=%d " 366 "frame_len=%d\n", len, buffer_used, frame_len); 367 368 uint8_t *frame_start = &buffer[sizeof(uint32_t)]; 369 while (frame_len != 0 && buffer_used >= frame_len + sizeof(uint32_t)) { 370 sendSimulated(frame_start, frame_len); 371 372 // Bookkeeping. 373 buffer_used -= frame_len + sizeof(uint32_t); 374 if (buffer_used > 0) { 375 // If there's still any data left, move it into position. 376 memmove(buffer, frame_start + frame_len, buffer_used); 377 } 378 frame_len = 0; 379 380 if (buffer_used >= sizeof(uint32_t)) 381 frame_len = ntohl(*(uint32_t *)buffer); 382 } 383} 384 385bool 386EtherTapStub::sendReal(const void *data, size_t len) 387{ 388 uint32_t frame_len = htonl(len); 389 ssize_t ret = write(socket, &frame_len, sizeof(frame_len)); 390 if (ret != sizeof(frame_len)) 391 return false; 392 return write(socket, data, len) == len; 393} 394 395 396#if USE_TUNTAP 397 398EtherTap::EtherTap(const Params *p) : EtherTapBase(p) 399{ 400 int fd = open(p->tun_clone_device.c_str(), O_RDWR); 401 if (fd < 0) 402 panic("Couldn't open %s.\n", p->tun_clone_device); 403 404 struct ifreq ifr; 405 memset(&ifr, 0, sizeof(ifr)); 406 ifr.ifr_flags = IFF_TAP | IFF_NO_PI; 407 strncpy(ifr.ifr_name, p->tap_device_name.c_str(), IFNAMSIZ); 408 409 if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) 410 panic("Failed to access tap device %s.\n", ifr.ifr_name); 411 // fd now refers to the tap device. 412 tap = fd; 413 pollFd(tap); 414} 415 416EtherTap::~EtherTap() 417{ 418 stopPolling(); 419 close(tap); 420 tap = -1; 421} 422 423void 424EtherTap::recvReal(int revent) 425{ 426 if (revent & POLLERR) 427 panic("Error polling for tap data.\n"); 428 429 if (!(revent & POLLIN)) 430 return; 431 432 ssize_t ret = read(tap, buffer, buflen); 433 if (ret < 0) 434 panic("Failed to read from tap device.\n"); 435 436 sendSimulated(buffer, ret); 437} 438 439bool 440EtherTap::sendReal(const void *data, size_t len) 441{ 442 if (write(tap, data, len) != len) 443 panic("Failed to write data to tap device.\n"); 444 return true; 445} 446 447EtherTap * 448EtherTapParams::create() 449{ 450 return new EtherTap(this); 451} 452 453#endif 454 455EtherTapStub * 456EtherTapStubParams::create() 457{ 458 return new EtherTapStub(this); 459} 460