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