- added instructions how to update the online documentation
[bochs-mirror.git] / iodev / eth_fbsd.cc
blob3cd29f65d9375d7e3e58b1e7e6afdfa32599c191
1 /////////////////////////////////////////////////////////////////////////
2 // $Id: eth_fbsd.cc,v 1.35 2008/12/11 18:01:56 vruppert Exp $
3 /////////////////////////////////////////////////////////////////////////
4 //
5 // Copyright (C) 2001 MandrakeSoft S.A.
6 //
7 // MandrakeSoft S.A.
8 // 43, rue d'Aboukir
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
32 // with FreeBSD:
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.
54 #define BX_PLUGGABLE
56 #define NO_DEVICE_INCLUDES
57 #include "iodev.h"
59 #if BX_NETWORKING && defined(ETH_FBSD)
61 #include "eth.h"
63 #define LOG_THIS bx_devices.pluginNE2kDevice->
65 extern "C" {
66 #include <fcntl.h>
67 #include <sys/ioctl.h>
68 #include <sys/socket.h>
69 #include <net/if.h>
70 #include <net/bpf.h>
71 #include <errno.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
85 // LABEL-1
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
88 // LABEL-ACCEPT
89 BPF_STMT(BPF_RET, 1514), // Accept packet
90 // LABEL-REJECT
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 {
103 public:
104 bx_fbsd_pktmover_c(const char *netif,
105 const char *macaddr,
106 eth_rx_handler_t rxh,
107 void *rxarg, const char *script);
108 void sendpkt(void *buf, unsigned io_len);
110 private:
111 char *fbsd_macaddr[6];
112 int bpf_fd;
113 static void rx_timer_handler(void *);
114 void rx_timer(void);
115 int rx_timer_index;
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 {
126 public:
127 bx_fbsd_locator_c(void) : eth_locator_c("fbsd") {}
128 protected:
129 eth_pktmover_c *allocate(const char *netif,
130 const char *macaddr,
131 eth_rx_handler_t rxh,
132 void *rxarg, const char *script) {
133 return (new bx_fbsd_pktmover_c(netif, macaddr, rxh, rxarg, script));
135 } bx_fbsd_match;
139 // Define the methods for the bx_fbsd_pktmover derived class
142 // the constructor
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,
148 const char *macaddr,
149 eth_rx_handler_t rxh,
150 void *rxarg,
151 const char *script)
153 char device[sizeof "/dev/bpf000"];
154 int tmpfd;
155 int n = 0;
156 struct ifreq ifr;
157 struct bpf_version bv;
158 struct bpf_program bp;
159 u_int v;
161 memcpy(fbsd_macaddr, macaddr, 6);
163 do {
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)));
167 if(errno == EACCES)
168 break;
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)));
173 return;
176 if (ioctl(this->bpf_fd, BIOCVERSION, (caddr_t)&bv) < 0) {
177 BX_PANIC(("eth_freebsd: could not retrieve bpf version"));
178 close(this->bpf_fd);
179 this->bpf_fd = -1;
180 return;
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"));
184 close(this->bpf_fd);
185 this->bpf_fd = -1;
186 return;
189 // Set buffer size
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)));
193 close(this->bpf_fd);
194 this->bpf_fd = -1;
195 return;
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)));
201 close(this->bpf_fd);
202 this->bpf_fd == -1;
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)));
208 close(this->bpf_fd);
209 this->bpf_fd = -1;
210 return;
212 if (v != DLT_EN10MB) {
213 BX_PANIC(("eth_freebsd: incorrect datalink type %d, expected 10mb ethernet", v));
214 close(this->bpf_fd);
215 this->bpf_fd = -1;
216 return;
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)));
225 close(this->bpf_fd);
226 this->bpf_fd = -1;
227 return;
230 v = 1;
231 if (ioctl(this->bpf_fd, BIOCIMMEDIATE, &v) < 0) {
232 BX_PANIC(("eth_freebsd: could not enable immediate mode"));
233 close(this->bpf_fd);
234 this->bpf_fd = -1;
235 return;
238 // Set up non-blocking i/o
239 v = 1;
240 if (ioctl(this->bpf_fd, FIONBIO, &v) < 0) {
241 BX_PANIC(("eth_freebsd: could not enable non-blocking i/o: %s", strerror(errno)));
242 close(this->bpf_fd);
243 this->bpf_fd = -1;
244 return;
247 // Install a filter
248 #ifdef notdef
249 memcpy(&this->filter, promiscfilter, sizeof(promiscfilter));
250 bp.bf_len = 1;
251 #else
252 memcpy(&this->filter, macfilter, sizeof(macfilter));
253 this->filter[1].k =
254 (macaddr[2] & 0xff) << 24 |
255 (macaddr[3] & 0xff) << 16 |
256 (macaddr[4] & 0xff) << 8 |
257 (macaddr[5] & 0xff);
258 this->filter[3].k =
259 (macaddr[0] & 0xff) << 8 |
260 (macaddr[1] & 0xff);
261 bp.bf_len = 8;
262 #endif
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)));
266 close(this->bpf_fd);
267 this->bpf_fd = -1;
268 return;
271 // Start the rx poll
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
276 this->rxh = rxh;
277 this->rxarg = rxarg;
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);
293 #endif
296 // the output routine - called with pre-formatted ethernet frame.
297 void
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
316 fflush (ne2klog);
317 fflush (ne2klog_txt);
318 #endif
319 int status;
321 if (this->bpf_fd != -1)
322 status = write(this->bpf_fd, buf, io_len);
325 // The receive poll process
326 void
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();
335 void
336 bx_fbsd_pktmover_c::rx_timer(void)
338 int nbytes = 0;
339 unsigned char rxbuf[BX_PACKET_BUFSIZE];
340 struct bpf_hdr *bhdr;
341 struct bpf_stat bstat;
342 static struct bpf_stat previous_bstat;
343 int counter = 10;
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;
378 int n;
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);
388 #endif
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) */