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