tap.cc revision 1803:9ddfa35ac314
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
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}
541