aboutsummaryrefslogtreecommitdiffstats
path: root/tools/ioemu/iodev/eth_linux.cc
diff options
context:
space:
mode:
Diffstat (limited to 'tools/ioemu/iodev/eth_linux.cc')
-rw-r--r--tools/ioemu/iodev/eth_linux.cc286
1 files changed, 286 insertions, 0 deletions
diff --git a/tools/ioemu/iodev/eth_linux.cc b/tools/ioemu/iodev/eth_linux.cc
new file mode 100644
index 0000000000..ec5f1fee73
--- /dev/null
+++ b/tools/ioemu/iodev/eth_linux.cc
@@ -0,0 +1,286 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: eth_linux.cc,v 1.14 2003/02/16 19:35:57 vruppert 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_linux.cc - A Linux socket filter adaptation of the FreeBSD BPF driver
+// <splite@purdue.edu> 21 June 2001
+//
+// Problems and limitations:
+// - packets cannot be sent from BOCHS to the host
+// - Linux kernel sometimes gets network watchdog timeouts under emulation
+// - author doesn't know C++
+//
+// The config line in .bochsrc should look something like:
+//
+// ne2k: ioaddr=0x280, irq=10, mac=00:a:b:c:1:2, ethmod=linux, ethdev=eth0
+//
+
+// 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_LINUX)
+#define LOG_THIS bx_devices.pluginNE2kDevice->
+
+extern "C" {
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netpacket/packet.h>
+#include <netinet/in.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <linux/types.h>
+#include <linux/filter.h>
+};
+
+#define BX_PACKET_POLL 1000 // Poll for a frame every 1000 usecs
+
+#define BX_PACKET_BUFSIZ 2048 // Enough for an ether frame
+
+// template filter for a unicast mac address and all
+// multicast/broadcast frames
+static const struct sock_filter macfilter[] = {
+ BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 2),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0xaaaaaaaa, 0, 2),
+ BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 0),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x0000aaaa, 2, 0),
+ BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 0),
+ BPF_JUMP(BPF_JMP|BPF_JSET|BPF_K, 0x01, 0, 1),
+ BPF_STMT(BPF_RET, 1514),
+ BPF_STMT(BPF_RET, 0),
+};
+#define BX_LSF_ICNT 8 // number of lsf instructions in macfilter
+
+#if 0
+// template filter for all frames
+static const struct sock_filter promiscfilter[] = {
+ BPF_STMT(BPF_RET, 1514)
+};
+#endif
+
+//
+// Define the class. This is private to this module
+//
+class bx_linux_pktmover_c : public eth_pktmover_c {
+public:
+ bx_linux_pktmover_c(const char *netif,
+ const char *macaddr,
+ eth_rx_handler_t rxh,
+ void *rxarg);
+ void sendpkt(void *buf, unsigned io_len);
+
+private:
+ unsigned char *linux_macaddr[6];
+ int fd;
+ int ifindex;
+ static void rx_timer_handler(void *);
+ void rx_timer(void);
+ int rx_timer_index;
+ struct sock_filter filter[BX_LSF_ICNT];
+};
+
+
+//
+// Define the static class that registers the derived pktmover class,
+// and allocates one on request.
+//
+class bx_linux_locator_c : public eth_locator_c {
+public:
+ bx_linux_locator_c(void) : eth_locator_c("linux") {}
+protected:
+ eth_pktmover_c *allocate(const char *netif,
+ const char *macaddr,
+ eth_rx_handler_t rxh,
+ void *rxarg) {
+ return (new bx_linux_pktmover_c(netif, macaddr, rxh, rxarg));
+ }
+} bx_linux_match;
+
+
+//
+// Define the methods for the bx_linux_pktmover derived class
+//
+
+// the constructor
+//
+bx_linux_pktmover_c::bx_linux_pktmover_c(const char *netif,
+ const char *macaddr,
+ eth_rx_handler_t rxh,
+ void *rxarg)
+{
+ struct sockaddr_ll sll;
+ struct packet_mreq mr;
+ struct ifreq ifr;
+ struct sock_fprog fp;
+
+ memcpy(linux_macaddr, macaddr, 6);
+
+ // Open packet socket
+ //
+ if ((this->fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) == -1) {
+ if (errno == EACCES)
+ BX_PANIC(("eth_linux: must be root or have CAP_NET_RAW capability to open socket"));
+ else
+ BX_PANIC(("eth_linux: could not open socket: %s", strerror(errno)));
+ this->fd = -1;
+ return;
+ }
+
+ // Translate interface name to index
+ //
+ memset(&ifr, 0, sizeof(ifr));
+ strcpy(ifr.ifr_name, netif);
+ if (ioctl(this->fd, SIOCGIFINDEX, &ifr) == -1) {
+ BX_PANIC(("eth_linux: could not get index for interface '%s'\n", netif));
+ close(fd);
+ this->fd = -1;
+ return;
+ }
+ this->ifindex = ifr.ifr_ifindex;
+
+
+ // Bind to given interface
+ //
+ memset(&sll, 0, sizeof(sll));
+ sll.sll_family = AF_PACKET;
+ sll.sll_ifindex = this->ifindex;
+ if (bind(fd, (struct sockaddr *)&sll, (socklen_t)sizeof(sll)) == -1) {
+ BX_PANIC(("eth_linux: could not bind to interface '%s': %s\n", netif, strerror(errno)));
+ close(fd);
+ this->fd = -1;
+ return;
+ }
+
+ // Put the device into promisc mode.
+ //
+ memset(&mr, 0, sizeof(mr));
+ mr.mr_ifindex = this->ifindex;
+ mr.mr_type = PACKET_MR_PROMISC;
+ if (setsockopt(this->fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, (void *)&mr, (socklen_t)sizeof(mr)) == -1) {
+ BX_PANIC(("eth_linux: could not enable promisc mode: %s\n", strerror(errno)));
+ close(this->fd);
+ this->fd = -1;
+ return;
+ }
+
+ // Set up non-blocking i/o
+ if (fcntl(this->fd, F_SETFL, O_NONBLOCK) == -1) {
+ BX_PANIC(("eth_linux: could not set non-blocking i/o on socket"));
+ close(this->fd);
+ this->fd = -1;
+ return;
+ }
+
+ // Install a filter
+#ifdef notdef
+ memcpy(&this->filter, promiscfilter, sizeof(promiscfilter));
+ fp.len = 1;
+#endif
+ 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);
+ fp.len = BX_LSF_ICNT;
+ fp.filter = this->filter;
+ BX_INFO(("eth_linux: fp.len=%d fp.filter=%x", fp.len, (unsigned) fp.filter));
+ if (setsockopt(this->fd, SOL_SOCKET, SO_ATTACH_FILTER, &fp, sizeof(fp)) < 0) {
+ BX_PANIC(("eth_linux: could not set socket filter: %s", strerror(errno)));
+ close(this->fd);
+ this->fd = -1;
+ return;
+ }
+
+ // Start the rx poll
+ this->rx_timer_index =
+ bx_pc_system.register_timer(this, this->rx_timer_handler, BX_PACKET_POLL,
+ 1, 1, "eth_linux"); // continuous, active
+
+ this->rxh = rxh;
+ this->rxarg = rxarg;
+ BX_INFO(("eth_linux: enabled NE2K emulation on interface %s", netif));
+}
+
+// the output routine - called with pre-formatted ethernet frame.
+void
+bx_linux_pktmover_c::sendpkt(void *buf, unsigned io_len)
+{
+ int status;
+
+ if (this->fd != -1) {
+ status = write(this->fd, buf, io_len);
+ if (status == -1)
+ BX_INFO(("eth_linux: write failed: %s", strerror(errno)));
+ }
+}
+
+// The receive poll process
+void
+bx_linux_pktmover_c::rx_timer_handler(void *this_ptr)
+{
+ bx_linux_pktmover_c *class_ptr = (bx_linux_pktmover_c *) this_ptr;
+
+ class_ptr->rx_timer();
+}
+
+void
+bx_linux_pktmover_c::rx_timer(void)
+{
+ int nbytes = 0;
+ Bit8u rxbuf[BX_PACKET_BUFSIZ];
+ struct sockaddr_ll sll;
+ socklen_t fromlen;
+//static unsigned char bcast_addr[6] = {0xff,0xff,0xff,0xff,0xff,0xff};
+
+ if (this->fd == -1)
+ return;
+
+ fromlen = sizeof(sll);
+ nbytes = recvfrom(this->fd, rxbuf, sizeof(rxbuf), 0, (struct sockaddr *)&sll, &fromlen);
+
+ if (nbytes == -1) {
+ if (errno != EAGAIN)
+ BX_INFO(("eth_linux: error receiving packet: %s\n", strerror(errno)));
+ return;
+ }
+
+ // this should be done with LSF someday
+ // filter out packets sourced by us
+ if (memcmp(sll.sll_addr, this->linux_macaddr, 6) == 0)
+ return;
+ // let through broadcast, multicast, and our mac address
+// if ((memcmp(rxbuf, bcast_addr, 6) == 0) || (memcmp(rxbuf, this->linux_macaddr, 6) == 0) || rxbuf[0] & 0x01) {
+ BX_DEBUG(("eth_linux: got packet: %d bytes, dst=%x:%x:%x:%x:%x:%x, src=%x:%x:%x:%x:%x:%x\n", nbytes, rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3], rxbuf[4], rxbuf[5], rxbuf[6], rxbuf[7], rxbuf[8], rxbuf[9], rxbuf[10], rxbuf[11]));
+ (*rxh)(rxarg, rxbuf, nbytes);
+// }
+}
+#endif /* if BX_NE2K_SUPPORT && defined ETH_LINUX */