1 /////////////////////////////////////////////////////////////////////////
2 // $Id: eth_fbsd.cc,v 1.35 2008/12/11 18:01:56 vruppert Exp $
3 /////////////////////////////////////////////////////////////////////////
5 // Copyright (C) 2001 MandrakeSoft S.A.
9 // 75002 Paris - France
10 // http://www.linux-mandrake.com/
11 // http://www.mandrakesoft.com/
13 // This library is free software; you can redistribute it and/or
14 // modify it under the terms of the GNU Lesser General Public
15 // License as published by the Free Software Foundation; either
16 // version 2 of the License, or (at your option) any later version.
18 // This library is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 // Lesser General Public License for more details.
23 // You should have received a copy of the GNU Lesser General Public
24 // License along with this library; if not, write to the Free Software
25 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 // Peter Grehan (grehan@iprg.nokia.com) coded all of this
28 // NE2000/ether stuff.
30 // eth_fbsd.cc - A FreeBSD packet filter implementation of
31 // an ethernet pktmover. There are some problems and limitations
33 // - the source address of the frame is overwritten by
34 // the hosts's source address. This causes problems with
35 // learning bridges - since they cannot determine where
36 // BOCHS 'is', they broadcast the frame to all ports.
37 // - packets cannot be sent from BOCHS to the host
38 // - TCP performance seems to be abysmal; I think this is
39 // a timing problem somewhere.
40 // - I haven't handled the case where multiple frames arrive
41 // in a single BPF read.
43 // The /dev/bpf* devices need to be set up with the appropriate
44 // permissions for this to work.
46 // The config line in .bochsrc should look something like:
48 // ne2k: ioaddr=0x280, irq=9, mac=00:a:b:c:1:2, ethmod=fbsd, ethdev=fxp0
51 // Define BX_PLUGGABLE in files that can be compiled into plugins. For
52 // platforms that require a special tag on exported symbols, BX_PLUGGABLE
53 // is used to know when we are exporting symbols and when we are importing.
56 #define NO_DEVICE_INCLUDES
59 #if BX_NETWORKING && defined(ETH_FBSD)
63 #define LOG_THIS bx_devices.pluginNE2kDevice->
67 #include <sys/ioctl.h>
68 #include <sys/socket.h>
74 #define BX_BPF_POLL 1000 // Poll for a frame every 250 usecs
76 #define BX_BPF_INSNSIZ 8 // number of bpf insns
78 // template filter for a unicast mac address and all
79 // multicast/broadcast frames
80 static const struct bpf_insn macfilter
[] = {
81 BPF_STMT(BPF_LD
|BPF_W
|BPF_ABS
, 2), // A <- P[2:4]
82 BPF_JUMP(BPF_JMP
|BPF_JEQ
|BPF_K
, 0xaaaaaaaa, 0, 2), // if A != 0xaaaaaaa GOTO LABEL-1
83 BPF_STMT(BPF_LD
|BPF_H
|BPF_ABS
, 0), // A <- P[0:2]
84 BPF_JUMP(BPF_JMP
|BPF_JEQ
|BPF_K
, 0x0000aaaa, 2, 0), // if A == 0xaaaa GOTO ACCEPT
86 BPF_STMT(BPF_LD
|BPF_B
|BPF_ABS
, 0), // A <- P[0:1]
87 BPF_JUMP(BPF_JMP
|BPF_JSET
|BPF_K
, 0x01, 0, 1), // if !(A & 1) GOTO LABEL-REJECT
89 BPF_STMT(BPF_RET
, 1514), // Accept packet
91 BPF_STMT(BPF_RET
, 0), // Reject packet
94 // template filter for all frames
95 static const struct bpf_insn promiscfilter
[] = {
96 BPF_STMT(BPF_RET
, 1514)
100 // Define the class. This is private to this module
102 class bx_fbsd_pktmover_c
: public eth_pktmover_c
{
104 bx_fbsd_pktmover_c(const char *netif
,
106 eth_rx_handler_t rxh
,
107 void *rxarg
, const char *script
);
108 void sendpkt(void *buf
, unsigned io_len
);
111 char *fbsd_macaddr
[6];
113 static void rx_timer_handler(void *);
116 struct bpf_insn filter
[BX_BPF_INSNSIZ
];
117 FILE *ne2klog
, *ne2klog_txt
;
122 // Define the static class that registers the derived pktmover class,
123 // and allocates one on request.
125 class bx_fbsd_locator_c
: public eth_locator_c
{
127 bx_fbsd_locator_c(void) : eth_locator_c("fbsd") {}
129 eth_pktmover_c
*allocate(const char *netif
,
131 eth_rx_handler_t rxh
,
132 void *rxarg
, const char *script
) {
133 return (new bx_fbsd_pktmover_c(netif
, macaddr
, rxh
, rxarg
, script
));
139 // Define the methods for the bx_fbsd_pktmover derived class
144 // Open a bpf file descriptor, and attempt to bind to
145 // the specified netif (Replicates libpcap open code)
147 bx_fbsd_pktmover_c::bx_fbsd_pktmover_c(const char *netif
,
149 eth_rx_handler_t rxh
,
153 char device
[sizeof "/dev/bpf000"];
157 struct bpf_version bv
;
158 struct bpf_program bp
;
161 memcpy(fbsd_macaddr
, macaddr
, 6);
164 (void)sprintf(device
, "/dev/bpf%d", n
++);
165 this->bpf_fd
= open(device
, O_RDWR
);
166 BX_DEBUG(("tried %s, returned %d (%s)",device
,this->bpf_fd
,strerror(errno
)));
169 } while (this->bpf_fd
== -1);
171 if (this->bpf_fd
== -1) {
172 BX_PANIC(("eth_freebsd: could not open packet filter: %s", strerror(errno
)));
176 if (ioctl(this->bpf_fd
, BIOCVERSION
, (caddr_t
)&bv
) < 0) {
177 BX_PANIC(("eth_freebsd: could not retrieve bpf version"));
182 if (bv
.bv_major
!= BPF_MAJOR_VERSION
|| bv
.bv_minor
< BPF_MINOR_VERSION
) {
183 BX_PANIC(("eth_freebsd: bpf version mismatch between compilation and runtime"));
190 v
= BX_PACKET_BUFSIZE
;
191 if (ioctl(this->bpf_fd
, BIOCSBLEN
, (caddr_t
)&v
) < 0) {
192 BX_PANIC(("eth_freebsd: could not set buffer size: %s", strerror(errno
)));
198 (void)strncpy(ifr
.ifr_name
, netif
, sizeof(ifr
.ifr_name
));
199 if (ioctl(this->bpf_fd
, BIOCSETIF
, (caddr_t
)&ifr
) < 0) {
200 BX_PANIC(("eth_freebsd: could not enable interface '%s': %s", netif
, strerror(errno
)));
205 // Verify that the device is an ethernet.
206 if (ioctl(this->bpf_fd
, BIOCGDLT
, (caddr_t
)&v
) < 0) {
207 BX_PANIC(("eth_freebsd: could not retrieve datalink type: %s", strerror(errno
)));
212 if (v
!= DLT_EN10MB
) {
213 BX_PANIC(("eth_freebsd: incorrect datalink type %d, expected 10mb ethernet", v
));
219 // Put the device into promisc mode. This could be optimised
220 // to filter on a MAC address, broadcast, and all-multi,
221 // but this will do for now.
223 if (ioctl(this->bpf_fd
, BIOCPROMISC
, NULL
) < 0) {
224 BX_PANIC(("eth_freebsd: could not enable promisc mode: %s", strerror(errno
)));
231 if (ioctl(this->bpf_fd
, BIOCIMMEDIATE
, &v
) < 0) {
232 BX_PANIC(("eth_freebsd: could not enable immediate mode"));
238 // Set up non-blocking i/o
240 if (ioctl(this->bpf_fd
, FIONBIO
, &v
) < 0) {
241 BX_PANIC(("eth_freebsd: could not enable non-blocking i/o: %s", strerror(errno
)));
249 memcpy(&this->filter
, promiscfilter
, sizeof(promiscfilter
));
252 memcpy(&this->filter
, macfilter
, sizeof(macfilter
));
254 (macaddr
[2] & 0xff) << 24 |
255 (macaddr
[3] & 0xff) << 16 |
256 (macaddr
[4] & 0xff) << 8 |
259 (macaddr
[0] & 0xff) << 8 |
263 bp
.bf_insns
= &this->filter
[0];
264 if (ioctl(this->bpf_fd
, BIOCSETF
, &bp
) < 0) {
265 BX_PANIC(("eth_freebsd: could not set filter: %s", strerror(errno
)));
272 this->rx_timer_index
=
273 bx_pc_system
.register_timer(this, this->rx_timer_handler
, BX_BPF_POLL
,
274 1, 1, "eth_fbsd"); // continuous, active
279 #if BX_ETH_FBSD_LOGGING
280 // eventually Bryce wants ne2klog to dump in pcap format so that
281 // tcpdump -r FILE can read it and interpret packets.
282 ne2klog
= fopen ("ne2k.raw", "wb");
283 if (!ne2klog
) BX_PANIC (("open ne2k-tx.log failed"));
284 ne2klog_txt
= fopen ("ne2k.txt", "wb");
285 if (!ne2klog_txt
) BX_PANIC (("open ne2k-txdump.txt failed"));
286 fprintf (ne2klog_txt
, "null packetmover readable log file\n");
287 fprintf (ne2klog_txt
, "net IF = %s\n", netif
);
288 fprintf (ne2klog_txt
, "MAC address = ");
289 for (int i
=0; i
<6; i
++)
290 fprintf (ne2klog_txt
, "%02x%s", 0xff & macaddr
[i
], i
<5?":" : "");
291 fprintf (ne2klog_txt
, "\n--\n");
292 fflush (ne2klog_txt
);
296 // the output routine - called with pre-formatted ethernet frame.
298 bx_fbsd_pktmover_c::sendpkt(void *buf
, unsigned io_len
)
300 #if BX_ETH_FBSD_LOGGING
301 BX_DEBUG (("sendpkt length %u", io_len
));
302 // dump raw bytes to a file, eventually dump in pcap format so that
303 // tcpdump -r FILE can interpret them for us.
304 int n
= fwrite (buf
, io_len
, 1, ne2klog
);
305 if (n
!= 1) BX_ERROR (("fwrite to ne2klog failed", io_len
));
306 // dump packet in hex into an ascii log file
307 fprintf (ne2klog_txt
, "NE2K TX packet, length %u\n", io_len
);
308 Bit8u
*charbuf
= (Bit8u
*)buf
;
309 for (n
=0; n
<io_len
; n
++) {
310 if (((n
% 16) == 0) && n
>0)
311 fprintf (ne2klog_txt
, "\n");
312 fprintf (ne2klog_txt
, "%02x ", charbuf
[n
]);
314 fprintf (ne2klog_txt
, "\n--\n");
315 // flush log so that we see the packets as they arrive w/o buffering
317 fflush (ne2klog_txt
);
321 if (this->bpf_fd
!= -1)
322 status
= write(this->bpf_fd
, buf
, io_len
);
325 // The receive poll process
327 bx_fbsd_pktmover_c::rx_timer_handler(void *this_ptr
)
329 bx_fbsd_pktmover_c
*class_ptr
= (bx_fbsd_pktmover_c
*) this_ptr
;
331 class_ptr
->rx_timer();
336 bx_fbsd_pktmover_c::rx_timer(void)
339 unsigned char rxbuf
[BX_PACKET_BUFSIZE
];
340 struct bpf_hdr
*bhdr
;
341 struct bpf_stat bstat
;
342 static struct bpf_stat previous_bstat
;
344 #define phdr ((unsigned char*)bhdr)
346 bhdr
= (struct bpf_hdr
*) rxbuf
;
347 nbytes
= read(this->bpf_fd
, rxbuf
, sizeof(rxbuf
));
349 while (phdr
< (rxbuf
+ nbytes
)) {
350 if (ioctl(this->bpf_fd
, BIOCGSTATS
, &bstat
) < 0) {
351 BX_PANIC(("eth_freebsd: could not stat filter: %s", strerror(errno
)));
353 if (bstat
.bs_drop
> previous_bstat
.bs_drop
) {
354 BX_INFO (("eth_freebsd: %d packets dropped by the kernel.",
355 bstat
.bs_drop
- previous_bstat
.bs_drop
));
357 previous_bstat
= bstat
;
358 if (bhdr
->bh_caplen
< 20 || bhdr
->bh_caplen
> 1514) {
359 BX_ERROR(("eth_freebsd: received too weird packet length: %d", bhdr
->bh_caplen
));
362 // filter out packets sourced from this node
363 if (memcmp(bhdr
+ bhdr
->bh_hdrlen
+ 6, this->fbsd_macaddr
, 6)) {
364 (*rxh
)(rxarg
, phdr
+ bhdr
->bh_hdrlen
, bhdr
->bh_caplen
);
367 #if BX_ETH_FBSD_LOGGING
368 /// hey wait there is no receive data with a NULL ethernet, is there....
369 BX_DEBUG (("receive packet length %u", nbytes
));
370 // dump raw bytes to a file, eventually dump in pcap format so that
371 // tcpdump -r FILE can interpret them for us.
372 if (1 != fwrite (bhdr
, bhdr
->bh_caplen
, 1, ne2klog
)) {
373 BX_PANIC (("fwrite to ne2klog failed: %s", strerror(errno
)));
375 // dump packet in hex into an ascii log file
376 fprintf (this->ne2klog_txt
, "NE2K RX packet, length %u\n", bhdr
->bh_caplen
);
377 Bit8u
*charrxbuf
= (Bit8u
*)rxbuf
;
379 for (n
=0; n
<bhdr
->bh_caplen
; n
++) {
380 if (((n
% 16) == 0) && n
>0)
381 fprintf (this->ne2klog_txt
, "\n");
382 fprintf (this->ne2klog_txt
, "%02x ", phdr
[n
]);
384 fprintf (this->ne2klog_txt
, "\n--\n");
385 // flush log so that we see the packets as they arrive w/o buffering
386 fflush (this->ne2klog
);
387 fflush (this->ne2klog_txt
);
390 // Advance bhdr and phdr pointers to next packet
391 bhdr
= (struct bpf_hdr
*) ((char*) bhdr
+ BPF_WORDALIGN(bhdr
->bh_hdrlen
+ bhdr
->bh_caplen
));
395 #endif /* if BX_NETWORKING && defined(ETH_FBSD) */