tap.cc revision 8229:78bf55f23338
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 31extern "C" { 32#include <pcap.h> 33} 34 35#include <arpa/inet.h> 36#include <netinet/in.h> 37#include <netinet/tcp.h> 38#include <sys/ioctl.h> 39#include <sys/socket.h> 40#include <sys/types.h> 41#include <dnet.h> 42#include <fcntl.h> 43#include <libgen.h> 44#include <netdb.h> 45#include <poll.h> 46#include <unistd.h> 47 48#include <cerrno> 49#include <csignal> 50#include <list> 51#include <string> 52 53#include "base/cprintf.hh" 54 55#define panic(arg...) \ 56 do { cprintf("Panic: " arg); exit(1); } while (0) 57 58char *program = "ethertap"; 59void 60usage() 61{ 62 cprintf( 63 "usage: \n" 64 "\t%s [-b bufsize] [-d] [-f filter] [-p port] [-v] <device> <host>\n" 65 "\t%s [-b bufsize] [-d] [-f filter] [-l] [-p port] [-v] <device>\n", 66 program, program); 67 exit(2); 68} 69 70int verbose = 0; 71#define DPRINTF(args...) do { \ 72 if (verbose >= 1) \ 73 cprintf(args); \ 74} while (0) 75 76#define DDUMP(args...) do { \ 77 if (verbose >= 2) \ 78 dump((const u_char *)args); \ 79} while (0) 80 81void 82dump(const u_char *data, int len) 83{ 84 int c, i, j; 85 86 for (i = 0; i < len; i += 16) { 87 cprintf("%08x ", i); 88 c = len - i; 89 if (c > 16) c = 16; 90 91 for (j = 0; j < c; j++) { 92 cprintf("%02x ", data[i + j] & 0xff); 93 if ((j & 0xf) == 7 && j > 0) 94 cprintf(" "); 95 } 96 97 for (; j < 16; j++) 98 cprintf(" "); 99 cprintf(" "); 100 101 for (j = 0; j < c; j++) { 102 int ch = data[i + j] & 0x7f; 103 cprintf("%c", (char)(isprint(ch) ? ch : ' ')); 104 } 105 106 cprintf("\n"); 107 108 if (c < 16) 109 break; 110 } 111} 112 113bool quit = false; 114void 115quit_now(int sigtype) 116{ 117 DPRINTF("User requested exit\n"); 118 quit = true; 119} 120 121 122int 123Socket(int reuse) 124{ 125 int fd = ::socket(PF_INET, SOCK_STREAM, 0); 126 if (fd < 0) 127 panic("Can't create socket!\n"); 128 129 if (reuse) { 130 int i = 1; 131 if (::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&i, 132 sizeof(i)) < 0) 133 panic("setsockopt() SO_REUSEADDR failed!\n"); 134 } 135 136 return fd; 137} 138 139void 140Listen(int fd, int port) 141{ 142 struct sockaddr_in sockaddr; 143 sockaddr.sin_family = PF_INET; 144 sockaddr.sin_addr.s_addr = INADDR_ANY; 145 146 sockaddr.sin_port = htons(port); 147 int ret = ::bind(fd, (struct sockaddr *)&sockaddr, sizeof (sockaddr)); 148 if (ret == -1) 149 panic("bind() failed!\n"); 150 151 if (::listen(fd, 1) == -1) 152 panic("listen() failed!\n"); 153} 154 155// Open a connection. Accept will block, so if you don't want it to, 156// make sure a connection is ready before you call accept. 157int 158Accept(int fd, bool nodelay) 159{ 160 struct sockaddr_in sockaddr; 161 socklen_t slen = sizeof (sockaddr); 162 int sfd = ::accept(fd, (struct sockaddr *)&sockaddr, &slen); 163 if (sfd == -1) 164 panic("accept() failed!\n"); 165 166 if (nodelay) { 167 int i = 1; 168 ::setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, (char *)&i, sizeof(i)); 169 } 170 return sfd; 171} 172 173void 174Connect(int fd, const std::string &host, int port) 175{ 176 struct sockaddr_in sockaddr; 177 if (::inet_aton(host.c_str(), &sockaddr.sin_addr) == 0) { 178 struct hostent *hp; 179 hp = ::gethostbyname(host.c_str()); 180 if (!hp) 181 panic("Host %s not found\n", host); 182 183 sockaddr.sin_family = hp->h_addrtype; 184 memcpy(&sockaddr.sin_addr, hp->h_addr, hp->h_length); 185 } 186 187 sockaddr.sin_port = htons(port); 188 if (::connect(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != 0) 189 panic("could not connect to %s on port %d\n", host, port); 190 191 DPRINTF("connected to %s on port %d\n", host, port); 192} 193 194class Ethernet 195{ 196 protected: 197 int fd; 198 199 public: 200 virtual ~Ethernet() {} 201 202 int getfd() const { return fd; } 203 virtual bool read(const char *&data, int &len) = 0; 204 virtual bool write(const char *data, int len) = 0; 205}; 206 207class Tap : public Ethernet 208{ 209 private: 210 char buffer[65536]; 211 int fd; 212 213 public: 214 Tap(char *device); 215 ~Tap(); 216 virtual bool read(const char *&data, int &len); 217 virtual bool write(const char *data, int len); 218}; 219 220class PCap : public Ethernet 221{ 222 private: 223 pcap_t *pcap; 224 eth_t *ethernet; 225 226 public: 227 PCap(char *device, char *filter = NULL); 228 ~PCap(); 229 virtual bool read(const char *&data, int &len); 230 virtual bool write(const char *data, int len); 231}; 232 233PCap::PCap(char *device, char *filter) 234{ 235 char errbuf[PCAP_ERRBUF_SIZE]; 236 memset(errbuf, 0, sizeof errbuf); 237 pcap = pcap_open_live(device, 1500, 1, -1, errbuf); 238 if (pcap == NULL) 239 panic("pcap_open_live failed: %s\n", errbuf); 240 241 if (filter) { 242 bpf_program program; 243 bpf_u_int32 localnet, netmask; 244 if (pcap_lookupnet(device, &localnet, &netmask, errbuf) == -1) { 245 DPRINTF("pcap_lookupnet failed: %s\n", errbuf); 246 netmask = 0xffffff00; 247 } 248 249 if (pcap_compile(pcap, &program, filter, 1, netmask) == -1) 250 panic("pcap_compile failed, invalid filter:\n%s\n", filter); 251 252 if (pcap_setfilter(pcap, &program) == -1) 253 panic("pcap_setfilter failed\n"); 254 } 255 256 ethernet = eth_open(device); 257 if (!ethernet) 258 panic("cannot open the ethernet device for writing\n"); 259 260 fd = pcap_fileno(pcap); 261} 262 263PCap::~PCap() 264{ 265 pcap_close(pcap); 266 eth_close(ethernet); 267} 268 269bool 270PCap::read(const char *&data, int &len) 271{ 272 pcap_pkthdr hdr; 273 data = (const char *)pcap_next(pcap, &hdr); 274 if (!data) 275 return false; 276 277 len = hdr.len; 278 return true; 279} 280 281bool 282PCap::write(const char *data, int len) 283{ 284 eth_send(ethernet, data, len); 285} 286 287Tap::Tap(char *device) 288{ 289 fd = open(device, O_RDWR, 0); 290 if (fd < 0) 291 panic("could not open %s: %s\n", device, strerror(errno)); 292} 293 294Tap::~Tap() 295{ 296 close(fd); 297} 298 299bool 300Tap::read(const char *&data, int &len) 301{ 302 DPRINTF("tap read!\n"); 303 data = buffer; 304 len = ::read(fd, buffer, sizeof(buffer)); 305 if (len < 0) 306 return false; 307 308 return true; 309} 310 311bool 312Tap::write(const char *data, int len) 313{ 314 int result = ::write(fd, data, len); 315 if (result < 0) 316 return false; 317 318 return true; 319} 320 321int 322main(int argc, char *argv[]) 323{ 324 int port = 3500; 325 int bufsize = 2000; 326 bool listening = false; 327 char *device = NULL; 328 char *filter = NULL; 329 Ethernet *tap = NULL; 330 bool usetap = false; 331 char c; 332 int daemon = false; 333 std::string host; 334 int devfd; 335 336 program = basename(argv[0]); 337 338 while ((c = getopt(argc, argv, "b:df:lp:tv")) != -1) { 339 switch (c) { 340 case 'b': 341 bufsize = atoi(optarg); 342 break; 343 case 'd': 344 daemon = true; 345 break; 346 case 'f': 347 filter = optarg; 348 break; 349 case 'l': 350 listening = true; 351 break; 352 case 'p': 353 port = atoi(optarg); 354 break; 355 case 't': 356 usetap = true; 357 break; 358 case 'v': 359 verbose++; 360 break; 361 default: 362 usage(); 363 break; 364 } 365 } 366 367 signal(SIGINT, quit_now); 368 signal(SIGTERM, quit_now); 369 signal(SIGHUP, quit_now); 370 371 if (daemon) { 372 verbose = 0; 373 switch(fork()) { 374 case -1: 375 panic("Fork failed\n"); 376 case 0: 377 break; 378 default: 379 exit(0); 380 } 381 } 382 383 char *buffer = new char[bufsize]; 384 argc -= optind; 385 argv += optind; 386 387 if (argc-- == 0) 388 usage(); 389 390 device = *argv++; 391 392 if (listening) { 393 if (argc) 394 usage(); 395 } else { 396 if (argc != 1) 397 usage(); 398 399 host = *argv; 400 } 401 402 if (usetap) { 403 if (filter) 404 panic("-f parameter not valid with a tap device!"); 405 tap = new Tap(device); 406 } else { 407 tap = new PCap(device, filter); 408 } 409 410 pollfd pfds[3]; 411 pfds[0].fd = Socket(true); 412 pfds[0].events = POLLIN; 413 pfds[0].revents = 0; 414 415 if (listening) 416 Listen(pfds[0].fd, port); 417 else 418 Connect(pfds[0].fd, host, port); 419 420 pfds[1].fd = tap->getfd(); 421 pfds[1].events = POLLIN; 422 pfds[1].revents = 0; 423 424 pfds[2].fd = 0; 425 pfds[2].events = POLLIN|POLLERR; 426 pfds[2].revents = 0; 427 428 pollfd *listen_pfd = listening ? &pfds[0] : NULL; 429 pollfd *tap_pfd = &pfds[1]; 430 pollfd *client_pfd = listening ? NULL : &pfds[0]; 431 int npfds = 2; 432 433 int32_t buffer_offset = 0; 434 int32_t data_len = 0; 435 436 DPRINTF("Begin poll loop\n"); 437 while (!quit) { 438 int ret = ::poll(pfds, npfds, INFTIM); 439 if (ret < 0) 440 continue; 441 442 if (listen_pfd && listen_pfd->revents) { 443 if (listen_pfd->revents & POLLIN) { 444 int fd = Accept(listen_pfd->fd, false); 445 if (client_pfd) { 446 DPRINTF("Connection rejected\n"); 447 close(fd); 448 } else { 449 DPRINTF("Connection accepted\n"); 450 client_pfd = &pfds[2]; 451 client_pfd->fd = fd; 452 npfds++; 453 } 454 } 455 listen_pfd->revents = 0; 456 } 457 458 DPRINTF("tap events: %x\n", tap_pfd->revents); 459 if (tap_pfd && tap_pfd->revents) { 460 if (tap_pfd->revents & POLLIN) { 461 const char *data; int len; 462 if (tap->read(data, len) && client_pfd) { 463 DPRINTF("Received packet from ethernet len=%d\n", len); 464 DDUMP(data, len); 465 u_int32_t swaplen = htonl(len); 466 write(client_pfd->fd, &swaplen, sizeof(swaplen)); 467 write(client_pfd->fd, data, len); 468 } 469 } 470 471 tap_pfd->revents = 0; 472 } 473 474 if (client_pfd && client_pfd->revents) { 475 if (client_pfd->revents & POLLIN) { 476 if (buffer_offset < data_len + sizeof(u_int32_t)) { 477 int len = read(client_pfd->fd, buffer + buffer_offset, 478 bufsize - buffer_offset); 479 480 if (len <= 0) { 481 perror("read"); 482 goto error; 483 } 484 485 buffer_offset += len; 486 if (data_len == 0) 487 data_len = ntohl(*(u_int32_t *)buffer); 488 489 DPRINTF("Received data from peer: len=%d buffer_offset=%d " 490 "data_len=%d\n", len, buffer_offset, data_len); 491 } 492 493 while (data_len != 0 && 494 buffer_offset >= data_len + sizeof(u_int32_t)) { 495 char *data = buffer + sizeof(u_int32_t); 496 tap->write(data, data_len); 497 DPRINTF("Sent packet to ethernet len = %d\n", data_len); 498 DDUMP(data, data_len); 499 500 buffer_offset -= data_len + sizeof(u_int32_t); 501 if (buffer_offset > 0 && data_len > 0) { 502 memmove(buffer, data + data_len, buffer_offset); 503 data_len = ntohl(*(u_int32_t *)buffer); 504 } else 505 data_len = 0; 506 } 507 } 508 509 if (client_pfd->revents & POLLERR) { 510 error: 511 DPRINTF("Error on client socket\n"); 512 close(client_pfd->fd); 513 client_pfd = NULL; 514 515 if (listening) 516 npfds--; 517 else { 518 DPRINTF("Calling it quits because of poll error\n"); 519 quit = true; 520 } 521 } 522 523 if (client_pfd) 524 client_pfd->revents = 0; 525 } 526 } 527 528 delete [] buffer; 529 delete tap; 530 531 if (listen_pfd) 532 close(listen_pfd->fd); 533 534 if (client_pfd) 535 close(client_pfd->fd); 536 537 return 0; 538} 539