aboutsummaryrefslogtreecommitdiffstats
path: root/tools/ioemu/iodev/eth_fbsd.cc
diff options
context:
space:
mode:
Diffstat (limited to 'tools/ioemu/iodev/eth_fbsd.cc')
-rw-r--r--tools/ioemu/iodev/eth_fbsd.cc385
1 files changed, 385 insertions, 0 deletions
diff --git a/tools/ioemu/iodev/eth_fbsd.cc b/tools/ioemu/iodev/eth_fbsd.cc
new file mode 100644
index 0000000000..0c24b9b828
--- /dev/null
+++ b/tools/ioemu/iodev/eth_fbsd.cc
@@ -0,0 +1,385 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: eth_fbsd.cc,v 1.26 2002/11/20 19:06:22 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+// Peter Grehan (grehan@iprg.nokia.com) coded all of this
+// NE2000/ether stuff.
+
+// eth_fbsd.cc - A FreeBSD packet filter implementation of
+// an ethernet pktmover. There are some problems and limitations
+// with FreeBSD:
+// - the source address of the frame is overwritten by
+// the hosts's source address. This causes problems with
+// learning bridges - since they cannot determine where
+// BOCHS 'is', they broadcast the frame to all ports.
+// - packets cannot be sent from BOCHS to the host
+// - TCP performance seems to be abysmal; I think this is
+// a timing problem somewhere.
+// - I haven't handled the case where multiple frames arrive
+// in a single BPF read.
+//
+// The /dev/bpf* devices need to be set up with the appropriate
+// permissions for this to work.
+//
+// The config line in .bochsrc should look something like:
+//
+// ne2k: ioaddr=0x280, irq=9, mac=00:a:b:c:1:2, ethmod=fbsd, ethdev=fxp0
+//
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+#if BX_NE2K_SUPPORT && defined(ETH_FBSD)
+
+#define LOG_THIS bx_devices.pluginNE2kDevice->
+
+extern "C" {
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/bpf.h>
+#include <errno.h>
+};
+
+#define BX_BPF_POLL 1000 // Poll for a frame every 250 usecs
+
+#define BX_BPF_BUFSIZ 2048 // enough for an ether frame + bpf hdr
+
+#define BX_BPF_INSNSIZ 8 // number of bpf insns
+
+// template filter for a unicast mac address and all
+// multicast/broadcast frames
+static const struct bpf_insn macfilter[] = {
+ BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 2), // A <- P[2:4]
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0xaaaaaaaa, 0, 2), // if A != 0xaaaaaaa GOTO LABEL-1
+ BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 0), // A <- P[0:2]
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x0000aaaa, 2, 0), // if A == 0xaaaa GOTO ACCEPT
+ // LABEL-1
+ BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 0), // A <- P[0:1]
+ BPF_JUMP(BPF_JMP|BPF_JSET|BPF_K, 0x01, 0, 1), // if !(A & 1) GOTO LABEL-REJECT
+ // LABEL-ACCEPT
+ BPF_STMT(BPF_RET, 1514), // Accept packet
+ // LABEL-REJECT
+ BPF_STMT(BPF_RET, 0), // Reject packet
+};
+
+// template filter for all frames
+static const struct bpf_insn promiscfilter[] = {
+ BPF_STMT(BPF_RET, 1514)
+};
+
+//
+// Define the class. This is private to this module
+//
+class bx_fbsd_pktmover_c : public eth_pktmover_c {
+public:
+ bx_fbsd_pktmover_c(const char *netif,
+ const char *macaddr,
+ eth_rx_handler_t rxh,
+ void *rxarg);
+ void sendpkt(void *buf, unsigned io_len);
+
+private:
+ char *fbsd_macaddr[6];
+ int bpf_fd;
+ static void rx_timer_handler(void *);
+ void rx_timer(void);
+ int rx_timer_index;
+ struct bpf_insn filter[BX_BPF_INSNSIZ];
+ FILE *ne2klog, *ne2klog_txt;
+};
+
+
+//
+// Define the static class that registers the derived pktmover class,
+// and allocates one on request.
+//
+class bx_fbsd_locator_c : public eth_locator_c {
+public:
+ bx_fbsd_locator_c(void) : eth_locator_c("fbsd") {}
+protected:
+ eth_pktmover_c *allocate(const char *netif,
+ const char *macaddr,
+ eth_rx_handler_t rxh,
+ void *rxarg) {
+ return (new bx_fbsd_pktmover_c(netif, macaddr, rxh, rxarg));
+ }
+} bx_fbsd_match;
+
+
+//
+// Define the methods for the bx_fbsd_pktmover derived class
+//
+
+// the constructor
+//
+// Open a bpf file descriptor, and attempt to bind to
+// the specified netif (Replicates libpcap open code)
+//
+bx_fbsd_pktmover_c::bx_fbsd_pktmover_c(const char *netif,
+ const char *macaddr,
+ eth_rx_handler_t rxh,
+ void *rxarg)
+{
+ char device[sizeof "/dev/bpf000"];
+ int tmpfd;
+ int n = 0;
+ struct ifreq ifr;
+ struct bpf_version bv;
+ struct bpf_program bp;
+ u_int v;
+
+ memcpy(fbsd_macaddr, macaddr, 6);
+
+ do {
+ (void)sprintf(device, "/dev/bpf%d", n++);
+ this->bpf_fd = open(device, O_RDWR);
+ BX_DEBUG(("tried %s, returned %d (%s)",device,this->bpf_fd,strerror(errno)));
+ if(errno == EACCES)
+ break;
+ } while (this->bpf_fd == -1);
+
+ if (this->bpf_fd == -1) {
+ BX_PANIC(("eth_freebsd: could not open packet filter: %s", strerror(errno)));
+ return;
+ }
+
+ if (ioctl(this->bpf_fd, BIOCVERSION, (caddr_t)&bv) < 0) {
+ BX_PANIC(("eth_freebsd: could not retrieve bpf version"));
+ close(this->bpf_fd);
+ this->bpf_fd = -1;
+ return;
+ }
+ if (bv.bv_major != BPF_MAJOR_VERSION || bv.bv_minor < BPF_MINOR_VERSION) {
+ BX_PANIC(("eth_freebsd: bpf version mismatch between compilation and runtime"));
+ close(this->bpf_fd);
+ this->bpf_fd = -1;
+ return;
+ }
+
+ // Set buffer size
+ v = BX_BPF_BUFSIZ;
+ if (ioctl(this->bpf_fd, BIOCSBLEN, (caddr_t)&v) < 0) {
+ BX_PANIC(("eth_freebsd: could not set buffer size: %s", strerror(errno)));
+ close(this->bpf_fd);
+ this->bpf_fd = -1;
+ return;
+ }
+
+ (void)strncpy(ifr.ifr_name, netif, sizeof(ifr.ifr_name));
+ if (ioctl(this->bpf_fd, BIOCSETIF, (caddr_t)&ifr) < 0) {
+ BX_PANIC(("eth_freebsd: could not enable interface '%s': %s", netif, strerror(errno)));
+ close(this->bpf_fd);
+ this->bpf_fd == -1;
+ }
+
+ // Verify that the device is an ethernet.
+ if (ioctl(this->bpf_fd, BIOCGDLT, (caddr_t)&v) < 0) {
+ BX_PANIC(("eth_freebsd: could not retrieve datalink type: %s", strerror(errno)));
+ close(this->bpf_fd);
+ this->bpf_fd = -1;
+ return;
+ }
+ if (v != DLT_EN10MB) {
+ BX_PANIC(("eth_freebsd: incorrect datalink type %d, expected 10mb ethernet", v));
+ close(this->bpf_fd);
+ this->bpf_fd = -1;
+ return;
+ }
+
+ // Put the device into promisc mode. This could be optimised
+ // to filter on a MAC address, broadcast, and all-multi,
+ // but this will do for now.
+ //
+ if (ioctl(this->bpf_fd, BIOCPROMISC, NULL) < 0) {
+ BX_PANIC(("eth_freebsd: could not enable promisc mode: %s", strerror(errno)));
+ close(this->bpf_fd);
+ this->bpf_fd = -1;
+ return;
+ }
+
+ // Set up non-blocking i/o
+ v = 1;
+ if (ioctl(this->bpf_fd, FIONBIO, &v) < 0) {
+ BX_PANIC(("eth_freebsd: could not enable non-blocking i/o: %s", strerror(errno)));
+ close(this->bpf_fd);
+ this->bpf_fd = -1;
+ return;
+ }
+
+ // Install a filter
+#ifdef notdef
+ memcpy(&this->filter, promiscfilter, sizeof(promiscfilter));
+ bp.bf_len = 1;
+#else
+ memcpy(&this->filter, macfilter, sizeof(macfilter));
+ this->filter[1].k =
+ (macaddr[2] & 0xff) << 24 |
+ (macaddr[3] & 0xff) << 16 |
+ (macaddr[4] & 0xff) << 8 |
+ (macaddr[5] & 0xff);
+ this->filter[3].k =
+ (macaddr[0] & 0xff) << 8 |
+ (macaddr[1] & 0xff);
+ bp.bf_len = 8;
+#endif
+ bp.bf_insns = &this->filter[0];
+ if (ioctl(this->bpf_fd, BIOCSETF, &bp) < 0) {
+ BX_PANIC(("eth_freebsd: could not set filter: %s", strerror(errno)));
+ close(this->bpf_fd);
+ this->bpf_fd = -1;
+ return;
+ }
+
+ // Start the rx poll
+ this->rx_timer_index =
+ bx_pc_system.register_timer(this, this->rx_timer_handler, BX_BPF_POLL,
+ 1, 1, "eth_fbsd"); // continuous, active
+
+ this->rxh = rxh;
+ this->rxarg = rxarg;
+
+#if BX_ETH_FBSD_LOGGING
+ // eventually Bryce wants ne2klog to dump in pcap format so that
+ // tcpdump -r FILE can read it and interpret packets.
+ ne2klog = fopen ("ne2k.raw", "wb");
+ if (!ne2klog) BX_PANIC (("open ne2k-tx.log failed"));
+ ne2klog_txt = fopen ("ne2k.txt", "wb");
+ if (!ne2klog_txt) BX_PANIC (("open ne2k-txdump.txt failed"));
+ fprintf (ne2klog_txt, "null packetmover readable log file\n");
+ fprintf (ne2klog_txt, "net IF = %s\n", netif);
+ fprintf (ne2klog_txt, "MAC address = ");
+ for (int i=0; i<6; i++)
+ fprintf (ne2klog_txt, "%02x%s", 0xff & macaddr[i], i<5?":" : "");
+ fprintf (ne2klog_txt, "\n--\n");
+ fflush (ne2klog_txt);
+#endif
+}
+
+// the output routine - called with pre-formatted ethernet frame.
+void
+bx_fbsd_pktmover_c::sendpkt(void *buf, unsigned io_len)
+{
+#if BX_ETH_FBSD_LOGGING
+ BX_DEBUG (("sendpkt length %u", io_len));
+ // dump raw bytes to a file, eventually dump in pcap format so that
+ // tcpdump -r FILE can interpret them for us.
+ int n = fwrite (buf, io_len, 1, ne2klog);
+ if (n != 1) BX_ERROR (("fwrite to ne2klog failed", io_len));
+ // dump packet in hex into an ascii log file
+ fprintf (ne2klog_txt, "NE2K TX packet, length %u\n", io_len);
+ Bit8u *charbuf = (Bit8u *)buf;
+ for (n=0; n<io_len; n++) {
+ if (((n % 16) == 0) && n>0)
+ fprintf (ne2klog_txt, "\n");
+ fprintf (ne2klog_txt, "%02x ", charbuf[n]);
+ }
+ fprintf (ne2klog_txt, "\n--\n");
+ // flush log so that we see the packets as they arrive w/o buffering
+ fflush (ne2klog);
+ fflush (ne2klog_txt);
+#endif
+ int status;
+
+ if (this->bpf_fd != -1)
+ status = write(this->bpf_fd, buf, io_len);
+}
+
+// The receive poll process
+void
+bx_fbsd_pktmover_c::rx_timer_handler(void *this_ptr)
+{
+ bx_fbsd_pktmover_c *class_ptr = (bx_fbsd_pktmover_c *) this_ptr;
+
+ class_ptr->rx_timer();
+}
+
+
+void
+bx_fbsd_pktmover_c::rx_timer(void)
+{
+ int nbytes = 0;
+ unsigned char rxbuf[BX_BPF_BUFSIZ];
+ struct bpf_hdr *bhdr;
+ struct bpf_stat bstat;
+ static struct bpf_stat previous_bstat;
+ int counter = 10;
+#define phdr ((unsigned char*)bhdr)
+
+ bhdr = (struct bpf_hdr *) rxbuf;
+ nbytes = read(this->bpf_fd, rxbuf, sizeof(rxbuf));
+
+ while (phdr < (rxbuf + nbytes)) {
+ if (ioctl(this->bpf_fd, BIOCGSTATS, &bstat) < 0) {
+ BX_PANIC(("eth_freebsd: could not stat filter: %s", strerror(errno)));
+ }
+ if (bstat.bs_drop > previous_bstat.bs_drop) {
+ BX_INFO (("eth_freebsd: %d packets dropped by the kernel.",
+ bstat.bs_drop - previous_bstat.bs_drop));
+ }
+ previous_bstat = bstat;
+ if (bhdr->bh_caplen < 20 || bhdr->bh_caplen > 1514) {
+ BX_ERROR(("eth_freebsd: received too weird packet length: %d", bhdr->bh_caplen));
+ }
+
+ // filter out packets sourced from this node
+ if (memcmp(bhdr + bhdr->bh_hdrlen + 6, this->fbsd_macaddr, 6)) {
+ (*rxh)(rxarg, phdr + bhdr->bh_hdrlen, bhdr->bh_caplen);
+ }
+
+#if BX_ETH_FBSD_LOGGING
+ /// hey wait there is no receive data with a NULL ethernet, is there....
+ BX_DEBUG (("receive packet length %u", nbytes));
+ // dump raw bytes to a file, eventually dump in pcap format so that
+ // tcpdump -r FILE can interpret them for us.
+ if (1 != fwrite (bhdr, bhdr->bh_caplen, 1, ne2klog)) {
+ BX_PANIC (("fwrite to ne2klog failed: %s", strerror(errno)));
+ }
+ // dump packet in hex into an ascii log file
+ fprintf (this->ne2klog_txt, "NE2K RX packet, length %u\n", bhdr->bh_caplen);
+ Bit8u *charrxbuf = (Bit8u *)rxbuf;
+ int n;
+ for (n=0; n<bhdr->bh_caplen; n++) {
+ if (((n % 16) == 0) && n>0)
+ fprintf (this->ne2klog_txt, "\n");
+ fprintf (this->ne2klog_txt, "%02x ", phdr[n]);
+ }
+ fprintf (this->ne2klog_txt, "\n--\n");
+ // flush log so that we see the packets as they arrive w/o buffering
+ fflush (this->ne2klog);
+ fflush (this->ne2klog_txt);
+#endif
+
+ // Advance bhdr and phdr pointers to next packet
+ bhdr = (struct bpf_hdr*) ((char*) bhdr + BPF_WORDALIGN(bhdr->bh_hdrlen + bhdr->bh_caplen));
+ }
+}
+
+#endif /* if BX_NE2K_SUPPORT && defined(ETH_FBSD) */
+