]> CyberLeo.Net >> Repos - FreeBSD/releng/9.1.git/blob - tools/tools/netmap/pcap.c
Copy stable/9 to releng/9.1 as part of the 9.1-RELEASE release process.
[FreeBSD/releng/9.1.git] / tools / tools / netmap / pcap.c
1 /*
2  * (C) 2011 Luigi Rizzo
3  *
4  * BSD license
5  *
6  * A simple library that maps some pcap functions onto netmap
7  * This is not 100% complete but enough to let tcpdump, trafshow
8  * and other apps work.
9  *
10  * $FreeBSD$
11  */
12
13 #include <errno.h>
14 #include <signal.h> /* signal */
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <string.h> /* strcmp */
18 #include <fcntl.h> /* open */
19 #include <unistd.h> /* close */
20
21 #include <sys/endian.h> /* le64toh */
22 #include <sys/mman.h> /* PROT_* */
23 #include <sys/ioctl.h> /* ioctl */
24 #include <machine/param.h>
25 #include <sys/poll.h>
26 #include <sys/socket.h> /* sockaddr.. */
27 #include <arpa/inet.h> /* ntohs */
28
29 #include <net/if.h>     /* ifreq */
30 #include <net/ethernet.h>
31 #include <net/netmap.h>
32 #include <net/netmap_user.h>
33
34 #include <netinet/in.h> /* sockaddr_in */
35
36 #include <sys/socket.h>
37 #include <ifaddrs.h>
38
39 #define MIN(a, b) ((a) < (b) ? (a) : (b))
40
41 char *version = "$Id$";
42 int verbose = 0;
43
44 /* debug support */
45 #define ND(format, ...) do {} while (0)
46 #define D(format, ...) do {                             \
47     if (verbose)                                        \
48         fprintf(stderr, "--- %s [%d] " format "\n",     \
49         __FUNCTION__, __LINE__, ##__VA_ARGS__);         \
50         } while (0)
51
52 inline void prefetch (const void *x)
53 {
54         __asm volatile("prefetcht0 %0" :: "m" (*(const unsigned long *)x));
55 }
56
57 // XXX only for multiples of 64 bytes, non overlapped.
58 static inline void
59 pkt_copy(const void *_src, void *_dst, int l)
60 {
61         const uint64_t *src = _src;
62         uint64_t *dst = _dst;
63 #define likely(x)       __builtin_expect(!!(x), 1)
64 #define unlikely(x)       __builtin_expect(!!(x), 0)
65         if (unlikely(l >= 1024)) {
66                 bcopy(src, dst, l);
67                 return;
68         }
69         for (; l > 0; l-=64) {
70                 *dst++ = *src++;
71                 *dst++ = *src++;
72                 *dst++ = *src++;
73                 *dst++ = *src++;
74                 *dst++ = *src++;
75                 *dst++ = *src++;
76                 *dst++ = *src++;
77                 *dst++ = *src++;
78         }
79 }
80
81 /*
82  * We redefine here a number of structures that are in pcap.h
83  * so we can compile this file without the system header.
84  */
85 #ifndef PCAP_ERRBUF_SIZE
86 #define PCAP_ERRBUF_SIZE 128
87
88 /*
89  * Each packet is accompanied by a header including the timestamp,
90  * captured size and actual size.
91  */
92 struct pcap_pkthdr {
93         struct timeval ts;      /* time stamp */
94         uint32_t caplen;        /* length of portion present */
95         uint32_t len;           /* length this packet (off wire) */
96 };
97
98 typedef struct pcap_if pcap_if_t;
99
100 /*
101  * Representation of an interface address.
102  */
103 struct pcap_addr {
104         struct pcap_addr *next;
105         struct sockaddr *addr;          /* address */
106         struct sockaddr *netmask;       /* netmask for the above */
107         struct sockaddr *broadaddr;     /* broadcast addr for the above */
108         struct sockaddr *dstaddr;       /* P2P dest. address for the above */
109 };
110
111 struct pcap_if {
112         struct pcap_if *next;
113         char *name;             /* name to hand to "pcap_open_live()" */
114         char *description;      /* textual description of interface, or NULL */
115         struct pcap_addr *addresses;
116         uint32_t flags;      /* PCAP_IF_ interface flags */
117 };
118
119 /*
120  * We do not support stats (yet)
121  */
122 struct pcap_stat {
123         u_int ps_recv;          /* number of packets received */
124         u_int ps_drop;          /* number of packets dropped */
125         u_int ps_ifdrop;        /* drops by interface XXX not yet supported */
126 #ifdef WIN32
127         u_int bs_capt;          /* number of packets that reach the app. */
128 #endif /* WIN32 */
129 };
130
131 typedef void    pcap_t;
132 typedef enum {
133         PCAP_D_INOUT = 0,
134         PCAP_D_IN,
135         PCAP_D_OUT
136 } pcap_direction_t;
137  
138
139
140 typedef void (*pcap_handler)(u_char *user,
141                 const struct pcap_pkthdr *h, const u_char *bytes);
142
143 char errbuf[PCAP_ERRBUF_SIZE];
144
145 pcap_t *pcap_open_live(const char *device, int snaplen,
146                int promisc, int to_ms, char *errbuf);
147
148 int pcap_findalldevs(pcap_if_t **alldevsp, char *errbuf);
149 void pcap_close(pcap_t *p);
150 int pcap_get_selectable_fd(pcap_t *p);
151 int pcap_dispatch(pcap_t *p, int cnt, pcap_handler callback, u_char *user);
152 int pcap_setnonblock(pcap_t *p, int nonblock, char *errbuf);
153 int pcap_setdirection(pcap_t *p, pcap_direction_t d);
154 char *pcap_lookupdev(char *errbuf);
155 int pcap_inject(pcap_t *p, const void *buf, size_t size);
156 int pcap_fileno(pcap_t *p);
157 const char *pcap_lib_version(void);
158
159
160 struct eproto {
161         const char *s;
162         u_short p;
163 };
164 #endif /* !PCAP_ERRBUF_SIZE */
165
166 #ifdef __PIC__
167 /*
168  * build as a shared library
169  */
170
171 char pcap_version[] = "libnetmap version 0.3";
172
173 /*
174  * Our equivalent of pcap_t
175  */
176 struct my_ring {
177         struct nmreq nmr;
178
179         int fd;
180         char *mem;                      /* userspace mmap address */
181         u_int memsize;
182         u_int queueid;
183         u_int begin, end;               /* first..last+1 rings to check */
184         struct netmap_if *nifp;
185
186         int snaplen;
187         char *errbuf;
188         int promisc;
189         int to_ms;
190
191         struct pcap_pkthdr hdr;
192
193         uint32_t if_flags;
194         uint32_t if_reqcap;
195         uint32_t if_curcap;
196
197         struct pcap_stat st;
198
199         char msg[PCAP_ERRBUF_SIZE];
200 };
201
202
203 static int
204 do_ioctl(struct my_ring *me, int what)
205 {
206         struct ifreq ifr;
207         int error;
208
209         bzero(&ifr, sizeof(ifr));
210         strncpy(ifr.ifr_name, me->nmr.nr_name, sizeof(ifr.ifr_name));
211         switch (what) {
212         case SIOCSIFFLAGS:
213                 D("call SIOCSIFFLAGS 0x%x", me->if_flags);
214                 ifr.ifr_flagshigh = (me->if_flags >> 16) & 0xffff;
215                 ifr.ifr_flags = me->if_flags & 0xffff;
216                 break;
217         case SIOCSIFCAP:
218                 ifr.ifr_reqcap = me->if_reqcap;
219                 ifr.ifr_curcap = me->if_curcap;
220                 break;
221         }
222         error = ioctl(me->fd, what, &ifr);
223         if (error) {
224                 D("ioctl 0x%x error %d", what, error);
225                 return error;
226         }
227         switch (what) {
228         case SIOCSIFFLAGS:
229         case SIOCGIFFLAGS:
230                 me->if_flags = (ifr.ifr_flagshigh << 16) |
231                         (0xffff & ifr.ifr_flags);
232                 D("flags are L 0x%x H 0x%x 0x%x",
233                         (uint16_t)ifr.ifr_flags,
234                         (uint16_t)ifr.ifr_flagshigh, me->if_flags);
235                 break;
236
237         case SIOCGIFCAP:
238                 me->if_reqcap = ifr.ifr_reqcap;
239                 me->if_curcap = ifr.ifr_curcap;
240                 D("curcap are 0x%x", me->if_curcap);
241                 break;
242         }
243         return 0;
244 }
245
246
247 /*
248  * open a device. if me->mem is null then do an mmap.
249  */
250 static int
251 netmap_open(struct my_ring *me, int ringid)
252 {
253         int fd, err, l;
254         u_int i;
255         struct nmreq req;
256
257         me->fd = fd = open("/dev/netmap", O_RDWR);
258         if (fd < 0) {
259                 D("Unable to open /dev/netmap");
260                 return (-1);
261         }
262         bzero(&req, sizeof(req));
263         strncpy(req.nr_name, me->nmr.nr_name, sizeof(req.nr_name));
264         req.nr_ringid = ringid;
265         req.nr_version = NETMAP_API;
266         err = ioctl(fd, NIOCGINFO, &req);
267         if (err) {
268                 D("cannot get info on %s", me->nmr.nr_name);
269                 goto error;
270         }
271         me->memsize = l = req.nr_memsize;
272         ND("memsize is %d MB", l>>20);
273         err = ioctl(fd, NIOCREGIF, &req);
274         if (err) {
275                 D("Unable to register %s", me->nmr.nr_name);
276                 goto error;
277         }
278
279         if (me->mem == NULL) {
280                 me->mem = mmap(0, l, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0);
281                 if (me->mem == MAP_FAILED) {
282                         D("Unable to mmap");
283                         me->mem = NULL;
284                         goto error;
285                 }
286         }
287
288         me->nifp = NETMAP_IF(me->mem, req.nr_offset);
289         me->queueid = ringid;
290         if (ringid & NETMAP_SW_RING) {
291                 me->begin = req.nr_rx_rings;
292                 me->end = me->begin + 1;
293         } else if (ringid & NETMAP_HW_RING) {
294                 me->begin = ringid & NETMAP_RING_MASK;
295                 me->end = me->begin + 1;
296         } else {
297                 me->begin = 0;
298                 me->end = req.nr_rx_rings;
299         }
300         /* request timestamps for packets */
301         for (i = me->begin; i < me->end; i++) {
302                 struct netmap_ring *ring = NETMAP_RXRING(me->nifp, i);
303                 ring->flags = NR_TIMESTAMP;
304         }
305         //me->tx = NETMAP_TXRING(me->nifp, 0);
306         return (0);
307 error:
308         close(me->fd);
309         return -1;
310 }
311
312 /*
313  * There is a set of functions that tcpdump expects even if probably
314  * not used
315  */
316 struct eproto eproto_db[] = {
317         { "ip", ETHERTYPE_IP },
318         { "arp", ETHERTYPE_ARP },
319         { (char *)0, 0 }
320 };
321
322
323 const char *pcap_lib_version(void)
324 {
325         return pcap_version;
326 }
327
328 int
329 pcap_findalldevs(pcap_if_t **alldevsp, __unused char *errbuf)
330 {
331         struct ifaddrs *i_head, *i;
332         pcap_if_t *top = NULL, *cur;
333         struct pcap_addr *tail = NULL;
334         int l;
335
336         D("listing all devs");
337         *alldevsp = NULL;
338         i_head = NULL;
339
340         if (getifaddrs(&i_head)) {
341                 D("cannot get if addresses");
342                 return -1;
343         }
344         for (i = i_head; i; i = i->ifa_next) {
345                 //struct ifaddrs   *ifa;
346                 struct pcap_addr *pca;
347                 //struct sockaddr *sa;
348
349                 D("got interface %s", i->ifa_name);
350                 if (!top || strcmp(top->name, i->ifa_name)) {
351                         /* new interface */
352                         l = sizeof(*top) + strlen(i->ifa_name) + 1;
353                         cur = calloc(1, l);
354                         if (cur == NULL) {
355                                 D("no space for if descriptor");
356                                 continue;
357                         }
358                         cur->name = (char *)(cur + 1);
359                         //cur->flags = i->ifa_flags;
360                         strcpy(cur->name, i->ifa_name);
361                         cur->description = NULL;
362                         cur->next = top;
363                         top = cur;
364                         tail = NULL;
365                 }
366                 /* now deal with addresses */
367                 D("%s addr family %d len %d %s %s",
368                         top->name,
369                         i->ifa_addr->sa_family, i->ifa_addr->sa_len,
370                         i->ifa_netmask ? "Netmask" : "",
371                         i->ifa_broadaddr ? "Broadcast" : "");
372                 l = sizeof(struct pcap_addr) +
373                         (i->ifa_addr ? i->ifa_addr->sa_len:0) +
374                         (i->ifa_netmask ? i->ifa_netmask->sa_len:0) +
375                         (i->ifa_broadaddr? i->ifa_broadaddr->sa_len:0);
376                 pca = calloc(1, l);
377                 if (pca == NULL) {
378                         D("no space for if addr");
379                         continue;
380                 }
381 #define SA_NEXT(x) ((struct sockaddr *)((char *)(x) + (x)->sa_len))
382                 pca->addr = (struct sockaddr *)(pca + 1);
383                 bcopy(i->ifa_addr, pca->addr, i->ifa_addr->sa_len);
384                 if (i->ifa_netmask) {
385                         pca->netmask = SA_NEXT(pca->addr);
386                         bcopy(i->ifa_netmask, pca->netmask, i->ifa_netmask->sa_len);
387                         if (i->ifa_broadaddr) {
388                                 pca->broadaddr = SA_NEXT(pca->netmask);
389                                 bcopy(i->ifa_broadaddr, pca->broadaddr, i->ifa_broadaddr->sa_len);
390                         }
391                 }
392                 if (tail == NULL) {
393                         top->addresses = pca;
394                 } else {
395                         tail->next = pca;
396                 }
397                 tail = pca;
398
399         }
400         freeifaddrs(i_head);
401         *alldevsp = top;
402         return 0;
403 }
404
405 void pcap_freealldevs(__unused pcap_if_t *alldevs)
406 {
407         D("unimplemented");
408 }
409
410 char *
411 pcap_lookupdev(char *buf)
412 {
413         D("%s", buf);
414         strcpy(buf, "/dev/netmap");
415         return buf;
416 }
417
418 pcap_t *
419 pcap_create(const char *source, char *errbuf)
420 {
421         D("src %s (call open liveted)", source);
422         return pcap_open_live(source, 0, 1, 100, errbuf);
423 }
424
425 int
426 pcap_activate(pcap_t *p)
427 {
428         D("pcap %p running", p);
429         return 0;
430 }
431
432 int
433 pcap_can_set_rfmon(__unused pcap_t *p)
434 {
435         D("");
436         return 0;       /* no we can't */
437 }
438
439 int
440 pcap_set_snaplen(pcap_t *p, int snaplen)
441 {
442         struct my_ring *me = p;
443
444         D("len %d", snaplen);
445         me->snaplen = snaplen;
446         return 0;
447 }
448
449 int
450 pcap_snapshot(pcap_t *p)
451 {
452         struct my_ring *me = p;
453
454         D("len %d", me->snaplen);
455         return me->snaplen;
456 }
457
458 int
459 pcap_lookupnet(const char *device, uint32_t *netp,
460         uint32_t *maskp, __unused char *errbuf)
461 {
462
463         D("device %s", device);
464         inet_aton("10.0.0.255", (struct in_addr *)netp);
465         inet_aton("255.255.255.0",(struct in_addr *) maskp);
466         return 0;
467 }
468
469 int
470 pcap_set_promisc(pcap_t *p, int promisc)
471 {
472         struct my_ring *me = p;
473
474         D("promisc %d", promisc);
475         if (do_ioctl(me, SIOCGIFFLAGS))
476                 D("SIOCGIFFLAGS failed");
477         if (promisc) {
478                 me->if_flags |= IFF_PPROMISC;
479         } else {
480                 me->if_flags &= ~IFF_PPROMISC;
481         }
482         if (do_ioctl(me, SIOCSIFFLAGS))
483                 D("SIOCSIFFLAGS failed");
484         return 0;
485 }
486
487 int
488 pcap_set_timeout(pcap_t *p, int to_ms)
489 {
490         struct my_ring *me = p;
491
492         D("%d ms", to_ms);
493         me->to_ms = to_ms;
494         return 0;
495 }
496
497 struct bpf_program;
498
499 int
500 pcap_compile(__unused pcap_t *p, __unused struct bpf_program *fp,
501         const char *str, __unused int optimize, __unused uint32_t netmask)
502 {
503         D("%s", str);
504         return 0;
505 }
506
507 int
508 pcap_setfilter(__unused pcap_t *p, __unused  struct bpf_program *fp)
509 {
510         D("");
511         return 0;
512 }
513
514 int
515 pcap_datalink(__unused pcap_t *p)
516 {
517         D("returns 1");
518         return 1;       // ethernet
519 }
520
521 const char *
522 pcap_datalink_val_to_name(int dlt)
523 {
524         D("%d returns DLT_EN10MB", dlt);
525         return "DLT_EN10MB";
526 }
527
528 const char *
529 pcap_datalink_val_to_description(int dlt)
530 {
531         D("%d returns Ethernet link", dlt);
532         return "Ethernet link";
533 }
534
535 struct pcap_stat;
536 int
537 pcap_stats(pcap_t *p, struct pcap_stat *ps)
538 {
539         struct my_ring *me = p;
540         ND("");
541
542         *ps = me->st;
543         return 0;       /* accumulate from pcap_dispatch() */
544 };
545
546 char *
547 pcap_geterr(pcap_t *p)
548 {
549         struct my_ring *me = p;
550
551         D("");
552         return me->msg;
553 }
554
555 pcap_t *
556 pcap_open_live(const char *device, __unused int snaplen,
557                int promisc, int to_ms, __unused char *errbuf)
558 {
559         struct my_ring *me;
560
561         D("request to open %s snaplen %d promisc %d timeout %dms",
562                 device, snaplen, promisc, to_ms);
563         me = calloc(1, sizeof(*me));
564         if (me == NULL) {
565                 D("failed to allocate struct for %s", device);
566                 return NULL;
567         }
568         strncpy(me->nmr.nr_name, device, sizeof(me->nmr.nr_name));
569         if (netmap_open(me, 0)) {
570                 D("error opening %s", device);
571                 free(me);
572                 return NULL;
573         }
574         me->to_ms = to_ms;
575         if (do_ioctl(me, SIOCGIFFLAGS))
576                 D("SIOCGIFFLAGS failed");
577         if (promisc) {
578                 me->if_flags |= IFF_PPROMISC;
579                 if (do_ioctl(me, SIOCSIFFLAGS))
580                         D("SIOCSIFFLAGS failed");
581         }
582         if (do_ioctl(me, SIOCGIFCAP))
583                 D("SIOCGIFCAP failed");
584         me->if_reqcap &= ~(IFCAP_HWCSUM | IFCAP_TSO | IFCAP_TOE);
585         if (do_ioctl(me, SIOCSIFCAP))
586                 D("SIOCSIFCAP failed");
587
588         return (pcap_t *)me;
589 }
590
591 void
592 pcap_close(pcap_t *p)
593 {
594         struct my_ring *me = p;
595
596         D("");
597         if (!me)
598                 return;
599         if (me->mem)
600                 munmap(me->mem, me->memsize);
601         /* restore original flags ? */
602         ioctl(me->fd, NIOCUNREGIF, NULL);
603         close(me->fd);
604         bzero(me, sizeof(*me));
605         free(me);
606 }
607
608 int
609 pcap_fileno(pcap_t *p)
610 {
611         struct my_ring *me = p;
612         D("returns %d", me->fd);
613         return me->fd;
614 }
615
616 int
617 pcap_get_selectable_fd(pcap_t *p)
618 {
619         struct my_ring *me = p;
620
621         ND("");
622         return me->fd;
623 }
624
625 int
626 pcap_setnonblock(__unused pcap_t *p, int nonblock, __unused char *errbuf)
627 {
628         D("mode is %d", nonblock);
629         return 0;       /* ignore */
630 }
631
632 int
633 pcap_setdirection(__unused pcap_t *p, __unused pcap_direction_t d)
634 {
635         D("");
636         return 0;       /* ignore */
637 };
638
639 int
640 pcap_dispatch(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
641 {
642         struct my_ring *me = p;
643         int got = 0;
644         u_int si;
645
646         ND("cnt %d", cnt);
647         if (cnt == 0)
648                 cnt = -1;
649         /* scan all rings */
650         for (si = me->begin; si < me->end; si++) {
651                 struct netmap_ring *ring = NETMAP_RXRING(me->nifp, si);
652                 ND("ring has %d pkts", ring->avail);
653                 if (ring->avail == 0)
654                         continue;
655                 me->hdr.ts = ring->ts;
656                 /*
657                  * XXX a proper prefetch should be done as
658                  *      prefetch(i); callback(i-1); ...
659                  */
660                 while ((cnt == -1 || cnt != got) && ring->avail > 0) {
661                         u_int i = ring->cur;
662                         u_int idx = ring->slot[i].buf_idx;
663                         if (idx < 2) {
664                                 D("%s bogus RX index %d at offset %d",
665                                         me->nifp->ni_name, idx, i);
666                                 sleep(2);
667                         }
668                         u_char *buf = (u_char *)NETMAP_BUF(ring, idx);
669                         prefetch(buf);
670                         me->hdr.len = me->hdr.caplen = ring->slot[i].len;
671                         // D("call %p len %d", p, me->hdr.len);
672                         callback(user, &me->hdr, buf);
673                         ring->cur = NETMAP_RING_NEXT(ring, i);
674                         ring->avail--;
675                         got++;
676                 }
677         }
678         me->st.ps_recv += got;
679         return got;
680 }
681
682 int
683 pcap_inject(pcap_t *p, const void *buf, size_t size)
684 {
685         struct my_ring *me = p;
686         u_int si;
687  
688         ND("cnt %d", cnt);
689         /* scan all rings */
690         for (si = me->begin; si < me->end; si++) {
691                 struct netmap_ring *ring = NETMAP_TXRING(me->nifp, si);
692  
693                 ND("ring has %d pkts", ring->avail);
694                 if (ring->avail == 0)
695                         continue;
696                 u_int i = ring->cur;
697                 u_int idx = ring->slot[i].buf_idx;
698                 if (idx < 2) {
699                         D("%s bogus TX index %d at offset %d",
700                                 me->nifp->ni_name, idx, i);
701                         sleep(2);
702                 }
703                 u_char *dst = (u_char *)NETMAP_BUF(ring, idx);
704                 ring->slot[i].len = size;
705                 pkt_copy(buf, dst, size);
706                 ring->cur = NETMAP_RING_NEXT(ring, i);
707                 ring->avail--;
708                 // if (ring->avail == 0) ioctl(me->fd, NIOCTXSYNC, NULL);
709                 return size;
710         }
711         errno = ENOBUFS;
712         return -1;
713 }
714
715 int
716 pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
717 {
718         struct my_ring *me = p;
719         struct pollfd fds[1];
720         int i;
721
722         ND("cnt %d", cnt);
723         memset(fds, 0, sizeof(fds));
724         fds[0].fd = me->fd;
725         fds[0].events = (POLLIN);
726
727         while (cnt == -1 || cnt > 0) {
728                 if (poll(fds, 1, me->to_ms) <= 0) {
729                         D("poll error/timeout");
730                         continue;
731                 }
732                 i = pcap_dispatch(p, cnt, callback, user);
733                 if (cnt > 0)
734                         cnt -= i;
735         }
736         return 0;
737 }
738
739 #endif /* __PIC__ */
740
741 #ifndef __PIC__
742 void do_send(u_char *user, const struct pcap_pkthdr *h, const u_char *buf)
743 {
744         pcap_inject((pcap_t *)user, buf, h->caplen);
745 }
746
747 /*
748  * a simple pcap test program, bridge between two interfaces.
749  */
750 int
751 main(int argc, char **argv)
752 {
753         pcap_t *p0, *p1;
754         int burst = 1024;
755         struct pollfd pollfd[2];
756
757         fprintf(stderr, "%s %s built %s %s\n",
758                 argv[0], version, __DATE__, __TIME__);
759                 
760         while (argc > 1 && !strcmp(argv[1], "-v")) {
761                 verbose++;
762                 argv++;
763                 argc--;
764         }
765
766         if (argc < 3 || argc > 4 || !strcmp(argv[1], argv[2])) {
767                 D("Usage: %s IFNAME1 IFNAME2 [BURST]", argv[0]);
768                 return (1);
769         }
770         if (argc > 3)
771                 burst = atoi(argv[3]);
772
773         p0 = pcap_open_live(argv[1], 0, 1, 100, NULL);
774         p1 = pcap_open_live(argv[2], 0, 1, 100, NULL);
775         D("%s", version);
776         D("open returns %p %p", p0, p1);
777         if (!p0 || !p1)
778                 return(1);
779         bzero(pollfd, sizeof(pollfd));
780         pollfd[0].fd = pcap_fileno(p0);
781         pollfd[1].fd = pcap_fileno(p1);
782         pollfd[0].events = pollfd[1].events = POLLIN;
783         for (;;) {
784                 /* do i need to reset ? */
785                 pollfd[0].revents = pollfd[1].revents = 0;
786                 int ret = poll(pollfd, 2, 1000);
787                 if (ret <= 0 || verbose)
788                    D("poll %s [0] ev %x %x [1] ev %x %x",
789                         ret <= 0 ? "timeout" : "ok",
790                                 pollfd[0].events,
791                                 pollfd[0].revents,
792                                 pollfd[1].events,
793                                 pollfd[1].revents);
794                 if (ret < 0)
795                         continue;
796                 if (pollfd[0].revents & POLLIN)
797                         pcap_dispatch(p0, burst, do_send, p1);
798                 if (pollfd[1].revents & POLLIN)
799                         pcap_dispatch(p1, burst, do_send, p0);
800         }
801
802         return (0);
803 }
804 #endif /* !__PIC__ */