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