tap.cc revision 291:ba3bdac49128
1/* 2 * Copyright (c) 2003 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 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 196int 197main(int argc, char *argv[]) 198{ 199 int port = 3500; 200 int bufsize = 2000; 201 bool listening = false; 202 char *device = NULL; 203 char *filter = NULL; 204 char c; 205 int daemon = false; 206 string host; 207 208 program = basename(argv[0]); 209 210 while ((c = getopt(argc, argv, "b:df:lp:v")) != -1) { 211 switch (c) { 212 case 'b': 213 bufsize = atoi(optarg); 214 break; 215 case 'd': 216 daemon = true; 217 break; 218 case 'f': 219 filter = optarg; 220 break; 221 case 'l': 222 listening = true; 223 break; 224 case 'p': 225 port = atoi(optarg); 226 break; 227 case 'v': 228 verbose++; 229 break; 230 default: 231 usage(); 232 break; 233 } 234 } 235 236 signal(SIGINT, quit_now); 237 signal(SIGTERM, quit_now); 238 signal(SIGHUP, quit_now); 239 240 if (daemon) { 241 verbose = 0; 242 switch(fork()) { 243 case -1: 244 panic("Fork failed\n"); 245 case 0: 246 break; 247 default: 248 exit(0); 249 } 250 } 251 252 char *buffer = new char[bufsize]; 253 argc -= optind; 254 argv += optind; 255 256 if (argc-- == 0) 257 usage(); 258 259 device = *argv++; 260 261 if (listening) { 262 if (argc) 263 usage(); 264 } else { 265 if (argc != 1) 266 usage(); 267 268 host = *argv; 269 } 270 271 char errbuf[PCAP_ERRBUF_SIZE]; 272 memset(errbuf, 0, sizeof errbuf); 273 pcap_t *pcap = pcap_open_live(device, 1500, 1, -1, errbuf); 274 if (pcap == NULL) 275 panic("pcap_open_live failed: %s\n", errbuf); 276 277 if (filter) { 278 bpf_program program; 279 bpf_u_int32 localnet, netmask; 280 if (pcap_lookupnet(device, &localnet, &netmask, errbuf) == -1) { 281 DPRINTF("pcap_lookupnet failed: %s\n", errbuf); 282 netmask = 0xffffff00; 283 } 284 285 if (pcap_compile(pcap, &program, filter, 1, netmask) == -1) 286 panic("pcap_compile failed, invalid filter:\n%s\n", filter); 287 288 if (pcap_setfilter(pcap, &program) == -1) 289 panic("pcap_setfilter failed\n"); 290 } 291 292 eth_t *ethernet = eth_open(device); 293 if (!ethernet) 294 panic("cannot open the ethernet device for writing\n"); 295 296 pollfd pfds[3]; 297 pfds[0].fd = Socket(true); 298 pfds[0].events = POLLIN; 299 pfds[0].revents = 0; 300 301 if (listening) 302 Listen(pfds[0].fd, port); 303 else 304 Connect(pfds[0].fd, host, port); 305 306 pfds[1].fd = pcap_fileno(pcap); 307 pfds[1].events = POLLIN; 308 pfds[1].revents = 0; 309 310 pfds[2].fd = 0; 311 pfds[2].events = POLLIN|POLLERR; 312 pfds[2].revents = 0; 313 314 pollfd *listen_pfd = listening ? &pfds[0] : NULL; 315 pollfd *tap_pfd = &pfds[1]; 316 pollfd *client_pfd = listening ? NULL : &pfds[0]; 317 int npfds = 2; 318 319 int32_t buffer_offset = 0; 320 int32_t data_len = 0; 321 322 DPRINTF("Begin poll loop\n"); 323 while (!quit) { 324 int ret = ::poll(pfds, npfds, INFTIM); 325 if (ret < 0) 326 continue; 327 328 if (listen_pfd && listen_pfd->revents) { 329 if (listen_pfd->revents & POLLIN) { 330 int fd = Accept(listen_pfd->fd, false); 331 if (client_pfd) { 332 DPRINTF("Connection rejected\n"); 333 close(fd); 334 } else { 335 DPRINTF("Connection accepted\n"); 336 client_pfd = &pfds[2]; 337 client_pfd->fd = fd; 338 npfds++; 339 } 340 } 341 listen_pfd->revents = 0; 342 } 343 344 if (tap_pfd && tap_pfd->revents) { 345 if (tap_pfd->revents & POLLIN) { 346 pcap_pkthdr hdr; 347 const u_char *data = pcap_next(pcap, &hdr); 348 if (data && client_pfd) { 349 DPRINTF("Received packet from ethernet len=%d\n", hdr.len); 350 DDUMP(data, hdr.len); 351 u_int32_t len = htonl(hdr.len); 352 write(client_pfd->fd, &len, sizeof(len)); 353 write(client_pfd->fd, data, hdr.len); 354 } 355 } 356 357 tap_pfd->revents = 0; 358 } 359 360 if (client_pfd && client_pfd->revents) { 361 if (client_pfd->revents & POLLIN) { 362 if (buffer_offset < data_len + sizeof(u_int32_t)) { 363 int len = read(client_pfd->fd, buffer + buffer_offset, 364 bufsize - buffer_offset); 365 366 if (len <= 0) { 367 perror("read"); 368 goto error; 369 } 370 371 buffer_offset += len; 372 if (data_len == 0) 373 data_len = ntohl(*(u_int32_t *)buffer); 374 375 DPRINTF("Received data from peer: len=%d buffer_offset=%d " 376 "data_len=%d\n", len, buffer_offset, data_len); 377 } 378 379 while (data_len != 0 && 380 buffer_offset >= data_len + sizeof(u_int32_t)) { 381 char *data = buffer + sizeof(u_int32_t); 382 eth_send(ethernet, data, data_len); 383 DPRINTF("Sent packet to ethernet len = %d\n", data_len); 384 DDUMP(data, data_len); 385 386 buffer_offset -= data_len + sizeof(u_int32_t); 387 if (buffer_offset > 0 && data_len > 0) { 388 memmove(buffer, data + data_len, buffer_offset); 389 data_len = ntohl(*(u_int32_t *)buffer); 390 } else 391 data_len = 0; 392 } 393 } 394 395 if (client_pfd->revents & POLLERR) { 396 error: 397 DPRINTF("Error on client socket\n"); 398 close(client_pfd->fd); 399 client_pfd = NULL; 400 401 if (listening) 402 npfds--; 403 else { 404 DPRINTF("Calling it quits because of poll error\n"); 405 quit = true; 406 } 407 } 408 409 if (client_pfd) 410 client_pfd->revents = 0; 411 } 412 } 413 414 delete [] buffer; 415 pcap_close(pcap); 416 eth_close(ethernet); 417 if (listen_pfd) 418 close(listen_pfd->fd); 419 420 if (client_pfd) 421 close(client_pfd->fd); 422 423 return 0; 424} 425