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