tap.cc revision 105:5440d7a4f376
1/*
2 * Copyright (c) 2003 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!");
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!");
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!");
152
153    if (::listen(fd, 1) == -1)
154        panic("listen() failed!");
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!");
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 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", host, port);
192
193    DPRINTF("connected to %s on port %d\n", host, port);
194}
195
196int
197main(int argc, char *argv[])
198{
199    int port = 3500;
200    int bufsize = 2000;
201    bool listening = false;
202    char *device = NULL;
203    char *filter = "";
204    char c;
205    int daemon = false;
206    string host;
207
208    program = basename(argv[0]);
209
210    while ((c = getopt(argc, argv, "b:df:lp:v")) != -1) {
211        switch (c) {
212          case 'b':
213            bufsize = atoi(optarg);
214            break;
215          case 'd':
216            daemon = true;
217            break;
218          case 'f':
219            filter = optarg;
220            break;
221          case 'l':
222            listening = true;
223            break;
224          case 'p':
225            port = atoi(optarg);
226            break;
227          case 'v':
228            verbose++;
229            break;
230          default:
231            usage();
232            break;
233        }
234    }
235
236    signal(SIGINT, quit_now);
237    signal(SIGTERM, quit_now);
238    signal(SIGHUP, quit_now);
239
240    if (daemon) {
241        verbose = 0;
242        switch(fork()) {
243          case -1:
244            panic("Fork failed\n");
245          case 0:
246            break;
247          default:
248            exit(0);
249        }
250    }
251
252    char *buffer = new char[bufsize];
253    argc -= optind;
254    argv += optind;
255
256    if (argc-- == 0)
257        usage();
258
259    device = *argv++;
260
261    if (listening) {
262        if (argc)
263            usage();
264    } else {
265        if (argc != 1)
266            usage();
267
268        host = *argv;
269    }
270
271    char errbuf[PCAP_ERRBUF_SIZE];
272    pcap_t *pcap = pcap_open_live(device, 1500, 1, -1, errbuf);
273    if (pcap == NULL)
274        panic("pcap_open_live failed: %s\n", errbuf);
275
276    bpf_program program;
277    bpf_u_int32 localnet, netmask;
278    if (!pcap_lookupnet(device, &localnet, &netmask, errbuf))
279        panic("pcap_lookupnet failed: %s\n", errbuf);
280
281    if (pcap_compile(pcap, &program, filter, 1, netmask) == -1)
282        panic("pcap_compile failed, invalid filter:\n%s\n", filter);
283
284    if (pcap_setfilter(pcap, &program) == -1)
285        panic("pcap_setfilter failed\n");
286
287    eth_t *ethernet = eth_open(device);
288
289    pollfd pfds[3];
290    pfds[0].fd = Socket(true);
291    pfds[0].events = POLLIN;
292    pfds[0].revents = 0;
293
294    if (listening)
295        Listen(pfds[0].fd, port);
296    else
297        Connect(pfds[0].fd, host, port);
298
299    pfds[1].fd = pcap_fileno(pcap);
300    pfds[1].events = POLLIN;
301    pfds[1].revents = 0;
302
303    pfds[2].fd = 0;
304    pfds[2].events = POLLIN|POLLERR;
305    pfds[2].revents = 0;
306
307    pollfd *listen_pfd = listening ? &pfds[0] : NULL;
308    pollfd *tap_pfd = &pfds[1];
309    pollfd *client_pfd = listening ? NULL : &pfds[0];
310    int npfds = 2;
311
312    int32_t buffer_offset = 0;
313    int32_t data_len = 0;
314
315    while (!quit) {
316        int ret = ::poll(pfds, npfds, INFTIM);
317        if (ret < 0)
318            continue;
319
320        if (listen_pfd && listen_pfd->revents) {
321            if (listen_pfd->revents & POLLIN) {
322                int fd = Accept(listen_pfd->fd, false);
323                if (client_pfd) {
324                    DPRINTF("Connection rejected\n");
325                    close(fd);
326                } else {
327                    DPRINTF("Connection accepted\n");
328                    client_pfd = &pfds[2];
329                    client_pfd->fd = fd;
330                    npfds++;
331                }
332            }
333            listen_pfd->revents = 0;
334        }
335
336        if (tap_pfd && tap_pfd->revents) {
337            if (tap_pfd->revents & POLLIN) {
338                pcap_pkthdr hdr;
339                const u_char *data = pcap_next(pcap, &hdr);
340                if (data && client_pfd) {
341                    DPRINTF("Received packet from ethernet len=%d\n", hdr.len);
342                    DDUMP(data, hdr.len);
343                    u_int32_t len = htonl(hdr.len);
344                    write(client_pfd->fd, &len, sizeof(len));
345                    write(client_pfd->fd, data, hdr.len);
346                }
347            }
348
349            tap_pfd->revents = 0;
350        }
351
352        if (client_pfd && client_pfd->revents) {
353            if (client_pfd->revents & POLLIN) {
354                if (buffer_offset < data_len + sizeof(u_int32_t)) {
355                    int len = read(client_pfd->fd, buffer + buffer_offset,
356                                   bufsize - buffer_offset);
357
358                    if (len <= 0) {
359                        perror("read");
360                        goto error;
361                    }
362
363                    buffer_offset += len;
364                    if (data_len == 0)
365                        data_len = ntohl(*(u_int32_t *)buffer);
366
367                    DPRINTF("Received data from peer: len=%d buffer_offset=%d "
368                            "data_len=%d\n", len, buffer_offset, data_len);
369                }
370
371                while (data_len != 0 &&
372                       buffer_offset >= data_len + sizeof(u_int32_t)) {
373                    char *data = buffer + sizeof(u_int32_t);
374                    eth_send(ethernet, data, data_len);
375                    DPRINTF("Sent packet to ethernet len = %d\n", data_len);
376                    DDUMP(data, data_len);
377
378                    buffer_offset -= data_len + sizeof(u_int32_t);
379                    if (buffer_offset > 0 && data_len > 0) {
380                        memmove(buffer, data + data_len, buffer_offset);
381                        data_len = ntohl(*(u_int32_t *)buffer);
382                    } else
383                        data_len = 0;
384                }
385            }
386
387            if (client_pfd->revents & POLLERR) {
388              error:
389                DPRINTF("Error on client socket\n");
390                close(client_pfd->fd);
391                client_pfd = NULL;
392
393                if (listening)
394                    npfds--;
395                else
396                    quit = true;
397            }
398
399            if (client_pfd)
400                client_pfd->revents = 0;
401        }
402
403    }
404
405    delete [] buffer;
406    pcap_close(pcap);
407    eth_close(ethernet);
408    if (listen_pfd)
409        close(listen_pfd->fd);
410
411    if (client_pfd)
412        close(client_pfd->fd);
413
414    return 0;
415}
416