tap.cc revision 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.
27 *
28 * Authors: Nathan Binkert
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}
543