/////////////////////////////////////////////////////////////////////////
// $Id: eth_linux.cc,v 1.6 2002/03/06 15:56:27 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_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
//

#include "bochs.h"
#include <Xtl.h>
#ifdef ETH_LINUX
#define LOG_THIS this->

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


/*
 * The instruction encondings.
 */
/* instruction classes */

#define BPF_CLASS(code) ((code) & 0x07)
#define		BPF_LD		0x00
#define		BPF_LDX		0x01
#define		BPF_ST		0x02
#define		BPF_STX		0x03
#define		BPF_ALU		0x04
#define		BPF_JMP		0x05
#define		BPF_RET		0x06
#define		BPF_MISC	0x07

/* ld/ldx fields */
#define BPF_SIZE(code)	((code) & 0x18)
#define		BPF_W		0x00
#define		BPF_H		0x08
#define		BPF_B		0x10
#define BPF_MODE(code)	((code) & 0xe0)
#define		BPF_IMM 	0x00
#define		BPF_ABS		0x20
#define		BPF_IND		0x40
#define		BPF_MEM		0x60
#define		BPF_LEN		0x80
#define		BPF_MSH		0xa0

/* alu/jmp fields */
#define BPF_OP(code)	((code) & 0xf0)
#define		BPF_ADD		0x00
#define		BPF_SUB		0x10
#define		BPF_MUL		0x20
#define		BPF_DIV		0x30
#define		BPF_OR		0x40
#define		BPF_AND		0x50
#define		BPF_LSH		0x60
#define		BPF_RSH		0x70
#define		BPF_NEG		0x80
#define		BPF_JA		0x00
#define		BPF_JEQ		0x10
#define		BPF_JGT		0x20
#define		BPF_JGE		0x30
#define		BPF_JSET	0x40
#define BPF_SRC(code)	((code) & 0x08)
#define		BPF_K		0x00
#define		BPF_X		0x08

/* ret - BPF_K and BPF_X also apply */
#define BPF_RVAL(code)	((code) & 0x18)
#define		BPF_A		0x10

/* misc */
#define BPF_MISCOP(code) ((code) & 0xf8)
#define		BPF_TAX		0x00
#define		BPF_TXA		0x80

#define BPF_STMT(code, k) { (unsigned short)(code), 0, 0, k }
#define BPF_JUMP(code, k, jt, jf) { (unsigned short)(code), jt, jf, k }

typedef	int bpf_int32;

/*
 * The instruction data structure.
 */
struct bpf_insn {
	unsigned short	code;
	unsigned char 	jt;
	unsigned char 	jf;
	bpf_int32 k;
};

struct bpf_program {
	unsigned int bf_len;
	struct bpf_insn *bf_insns;
};

struct bpf_hdr {
	struct timeval	bh_tstamp;	/* time stamp */
	unsigned int	bh_caplen;	/* length of captured portion */
	unsigned int	bh_datalen;	/* original length of packet */
	unsigned short	bh_hdrlen;	/* length of bpf header (this struct
					   plus alignment padding) */
};

#define sock_filter bpf_insn

// 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),
};

struct sockaddr_ll
{
        unsigned short  sll_family;             /* Always AF_PACKET */
        unsigned short  sll_protocol;   /* Physical layer protocol in network order */
        int             sll_ifindex;                    /* Interface number */
        unsigned short  sll_hatype;             /* Header type */
        unsigned char   sll_pkttype;    /* Packet type */
        unsigned char   sll_halen;              /* Length of address */
        unsigned char   sll_addr[8];    /* Physical layer address */
};
struct packet_mreq
{
        int             mr_ifindex;        /* interface index */
        unsigned short  mr_type;           /* mreq type as defined below */
        unsigned short  mr_alen;           /* address length */
        unsigned char   mr_address[8];     /* physical layer address */
};

typedef	char *	caddr_t;

struct ifreq {
#define IFHWADDRLEN     6
#define IFNAMSIZ        16
    union {
        char    ifrn_name[IFNAMSIZ];            /* if name, e.g. "en0" */
        char    ifrn_hwaddr[IFHWADDRLEN];
    } ifr_ifrn;
    union {
        struct  sockaddr ifru_addr;
        struct  sockaddr ifru_dstaddr;
        struct  sockaddr ifru_broadaddr;
        struct  sockaddr ifru_netmask;
        short   ifru_flags;
        int     ifru_metric;
        int     ifru_mtu;
        caddr_t ifru_data;
    } ifr_ifru;
};

struct sock_fprog /* Required for SO_ATTACH_FILTER. */
{
      unsigned short          len;  /* Number of filter blocks */
      struct sock_filter      *filter;
};


#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=%lx", fp.len, 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); // 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
