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