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