ruby: move all statistics to stats.txt, eliminate ruby.stats
[gem5.git] / util / tap / tap.cc
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
31 extern "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
58 char *program = "ethertap";
59 void
60 usage()
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
70 int 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
81 void
82 dump(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
113 bool quit = false;
114 void
115 quit_now(int sigtype)
116 {
117 DPRINTF("User requested exit\n");
118 quit = true;
119 }
120
121
122 int
123 Socket(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
139 void
140 Listen(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.
157 int
158 Accept(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
173 void
174 Connect(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
194 class 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
207 class 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
220 class 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
233 PCap::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
263 PCap::~PCap()
264 {
265 pcap_close(pcap);
266 eth_close(ethernet);
267 }
268
269 bool
270 PCap::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
281 bool
282 PCap::write(const char *data, int len)
283 {
284 eth_send(ethernet, data, len);
285 }
286
287 Tap::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
294 Tap::~Tap()
295 {
296 close(fd);
297 }
298
299 bool
300 Tap::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
311 bool
312 Tap::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
321 int
322 main(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 }