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