/* **********************************************************
 * Copyright (C) 1998-2000 VMware, Inc.
 * All Rights Reserved
 * **********************************************************/

#include "driver-config.h"

#ifdef KERNEL_2_1
#  define EXPORT_SYMTAB
#  ifndef __KERNEL__
#    define __KERNEL__
#  endif
#endif

#ifdef MODVERSIONS
#include <linux/modversions.h>
#endif

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/sched.h>
#include <linux/malloc.h>
#ifdef KERNEL_2_1
#include <linux/poll.h>
#include <asm/uaccess.h>
#endif

#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/mm.h>
#include <linux/skbuff.h>
#include <linux/if_ether.h>
#include <linux/sockios.h>
#include <net/sock.h>

#define __KERNEL_SYSCALLS__
#include <asm/io.h>

#include <linux/proc_fs.h>
#include <linux/file.h>

#include "vm_oui.h"
#include "vnet.h"
#include "vnetInt.h"

/* So we can print to an xterm instead of the console. */
#include "print_string.h"

/* Utility functions for PACIA_TRACE stuff. */
#include "utils.h"

#ifdef TOTAL_BYTES_TRACE
#include "global_tracking.h"
#endif // TOTAL_BYTES_TRACE

/*
 * Initialization and creation routines from other files.
 * Putting them here reduces the need for so many header files.
 */

extern void VNetHub_Init(void);

extern int VNetUserIf_Create(VNetPort **ret);
extern int VNetNetIf_Create(char *devName, VNetPort **ret);
extern int VNetBridge_Create(char *devName, VNetPort **ret);


/*
 *  Structure for cycle detection of host interfaces.
 */

typedef struct VNetInterface VNetInterface;

struct VNetInterface {
   char           name[8];
   int            myGeneration;
   VNetInterface *next;
};


/* this will let all multicast packets go through. */
uint8 AllMultiFilter[8] = { 0xff, 0xff, 0xff, 0xff,
                            0xff, 0xff, 0xff, 0xff };

/*
 *  Device driver interface.
 */

static int  VNetFileOpOpen(struct inode *inode, struct file *filp);
#ifdef KERNEL_2_1
static int  VNetFileOpClose(struct inode *inode, struct file *filp);
static unsigned int VNetFileOpPoll(struct file *filp, poll_table *wait);
static int  VNetFileOpRead(struct file *filp, char *buf, size_t count,
			   loff_t *ppos);
static int  VNetFileOpWrite(struct file *filp, const char *buf, size_t count,
			    loff_t *ppos);
#else
static void VNetFileOpClose(struct inode *inode, struct file *filp);
static int  VNetFileOpSelect(struct inode *inode, struct file *filp,
                             int sel_type, select_table *wait);
static int  VNetFileOpRead(struct inode *inode, struct file *filp,
                            char *buf, int count);
static int  VNetFileOpWrite(struct inode *inode, struct file *filp,
                             const char *buf, int count);
#endif
static int  VNetFileOpIoctl(struct inode *inode, struct file *filp,
                            unsigned int iocmd, unsigned long ioarg);

#ifdef KERNEL_2_1
static struct file_operations vnetFileOps = {
	NULL,              /* lseek */
	VNetFileOpRead,    /* read */
	VNetFileOpWrite,   /* write */
	NULL,              /* readdir */
	VNetFileOpPoll,    /* select */
	VNetFileOpIoctl,   /* ioctl */
	NULL,              /* mmap */
	VNetFileOpOpen,    /* open */
	NULL,		   /* flush */
	VNetFileOpClose,   /* release */
	NULL               /* fsync */
};
#else
static struct file_operations vnetFileOps = {
	NULL,              /* lseek */
	VNetFileOpRead,    /* read */
	VNetFileOpWrite,   /* write */
	NULL,              /* readdir */
	VNetFileOpSelect,  /* select */
	VNetFileOpIoctl,   /* ioctl */
	NULL,              /* mmap */
	VNetFileOpOpen,    /* open */
	VNetFileOpClose,   /* release */
	NULL               /* fsync */
};
#endif


/*
 * Utility functions
 */

static Bool VNetMulticastFilter(uint8 *destAddr, uint8 *ladrf);
static int  VNetMakeMACAddress(VNetPort *port, uint8 *paddr);

static int  VNetPrintPIDs(VNetPort *port, char *buf);
static Bool VNetProcessOwnsPort(struct task_struct *p, VNetPort *port);

VNetPort *vnetAllPorts;

static INLINE void
VNetAddPortToList(VNetPort *port)
{
   port->next = vnetAllPorts;
   vnetAllPorts = port;
}

static INLINE void
VNetRemovePortFromList(VNetPort *port)
{
   VNetPort **p;
   
   for (p = &vnetAllPorts; *p; p = &(*p)->next) {
      if (*p == port) {
         *p = port->next;
         break;
      }
   }
}

/*
 *  Ioctl callbacks
 */

#define MAX_IOCTL_CALLBACKS 16

typedef struct VNetIoctlCallback {
   Bool              allocated;
   int               num;
   VNetIoctlHandler  handler;
   void             *clientData;
} VNetIoctlCallback;

VNetIoctlCallback vnetIoctlCallback[MAX_IOCTL_CALLBACKS];


/*
 *----------------------------------------------------------------------
 *
 * VNetRegister --
 *
 *      (debugging support) Should be the first function of this file
 *
 * Results:
 *      
 *      Registers the module. 
 *      /sbin/ksyms -a | grep VNetRegister will return the base
 *      address of that function as loaded in the kernel. 
 * 
 *      Since this is the first function of the kernel module,
 *      every other symbol can be computing by adding the base
 *      to the output of nm.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int VNetRegister(int);

#ifdef KERNEL_2_1
EXPORT_SYMBOL(VNetRegister);
EXPORT_SYMBOL(VNet_RegisterIoctlHandler);
EXPORT_SYMBOL(VNet_UnregisterIoctlHandler);
EXPORT_SYMBOL(VNetHub_Alloc);
EXPORT_SYMBOL(VNetConnect);
EXPORT_SYMBOL(VNetDisconnect);
EXPORT_SYMBOL(VNetSend);
EXPORT_SYMBOL(VNetProc_MakeEntry);
EXPORT_SYMBOL(VNetProc_RemoveEntry);
EXPORT_SYMBOL(VNetPrintJack);
#else
static struct symbol_table vnet_syms = {
#include <linux/symtab_begin.h>
   X(VNetRegister),
   X(VNet_RegisterIoctlHandler),
   X(VNet_UnregisterIoctlHandler),
   X(VNetHub_Alloc),
   X(VNetConnect),
   X(VNetDisconnect),
   X(VNetSend),
   X(VNetProc_MakeEntry),
   X(VNetProc_RemoveEntry),
   X(VNetPrintJack),
#include <linux/symtab_end.h>
};
#endif

int 
VNetRegister(int value)
{
   LOG(0, (KERN_WARNING "/dev/vmnet: VNetRegister called\n"));
   return 0;
}


/*
 *----------------------------------------------------------------------
 *
 * init_module --
 *
 *      linux module entry point. Called by /sbin/insmod command
 * 
 * Results: 
 *      registers a device driver for a major # that depends
 *      on the uid. Add yourself to that list.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int 
init_module(void)
{
   int retval;
   int i;
   
   VNetHub_Init();
   
   retval = register_chrdev(VNET_MAJOR_NUMBER, "vmnet", &vnetFileOps);
   if (retval) {
      LOG(0, (KERN_NOTICE "/dev/vmnet: could not register major device %d\n",
	      VNET_MAJOR_NUMBER));
      return -ENOENT;
   }

   retval = VNetProc_Init();
   if (retval) {
      LOG(0, (KERN_NOTICE "/dev/vmnet: could not register proc fs\n"));
      unregister_chrdev(VNET_MAJOR_NUMBER, "vmnet");
      return -ENOENT;
   }
   
#ifndef KERNEL_2_1      
   register_symtab(&vnet_syms);
#endif

   vnetAllPorts = NULL;
   
   for (i=0; i<MAX_IOCTL_CALLBACKS; i++) {
      vnetIoctlCallback[i].allocated = FALSE;
   }

#ifdef __VMNET_USE_FILE
{
	char msg_buf[80];
	char * fname = "init_module";
	sprintf (msg_buf, "%s: started\n", fname);
	print_string (msg_buf);
}
#endif // __VMNET_USE_FILE
#ifdef TOTAL_BYTES_TRACE
   //   initHistogram(&bytes_per_packet,        10000, 0, 10000, "Bytes per packet");
   //   initHistogram(&bytes_per_message,     1000000, 0, 1000000, "Bytes per message");
   //   initHistogram(&data_bytes_per_message, 1000000, 0, 1000000, "Data bytes per message");
   //   initHistogram(&data_packets_per_message,     10000, 0, 10000, "Data packets per message");
   //   initHistogram(&control_packets_per_message,  10000, 0, 10000, "Control packets per message");
#endif // TOTAL_BYTES_TRACE
   

   
   return 0;
}


/*
 *----------------------------------------------------------------------
 *
 * cleanup_module --
 *
 *      Called by /sbin/rmmod
 *
 * Results: 
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
cleanup_module(void)
{
   int retval;

   VNetProc_Cleanup();
      
   retval =  unregister_chrdev(VNET_MAJOR_NUMBER, "vmnet");
   if (retval != 0 ) {
      LOG(0, (KERN_WARNING "/dev/vmnet: could not unregister major device %d\n",
	      VNET_MAJOR_NUMBER));
   }

#ifdef TOTAL_BYTES_TRACE
{
	char msg_buf[80];
	char * fname = "cleanup_module";
	sprintf (msg_buf, "\n\nTotal bytes sent: %d", total_bytes_sent);
	print_string (msg_buf);
	sprintf (msg_buf, "Total data bytes sent: %d", total_data_bytes_sent);
	print_string (msg_buf);
	sprintf (msg_buf, "Total packets sent: %d", 
		 total_packets_sent);
	print_string (msg_buf);	
	sprintf (msg_buf, "Total control packets sent: %d", 
		 control_packets_sent);
	print_string (msg_buf);
	sprintf (msg_buf, "Total data packets sent: %d", 
		 total_packets_sent - control_packets_sent);
	print_string (msg_buf);


	//	printHistogramMergeZero(&bytes_per_packet);
	//	printHistogramMergeZero(&bytes_per_message);
	//	printHistogramMergeZero(&data_bytes_per_message);
	//	printHistogramMergeZero(&data_packets_per_message);
	//	printHistogramMergeZero(&control_packets_per_message);
	//	printHistogramMergeZero(&total_packets_per_message);

	//	printHistogram(&bytes_per_packet);
	//	printHistogram(&bytes_per_message);
	//	printHistogram(&data_bytes_per_message);
	//	printHistogram(&data_packets_per_message);
	//	printHistogram(&control_packets_per_message);
	//	printHistogram(&total_packets_per_message);
}	
#endif // TOTAL_BYTES_TRACE

   return 0;
}


/*
 *----------------------------------------------------------------------
 *
 * VNetIncrModCount --
 *
 *      Increment or decrement the module use count.
 *
 * Results: 
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
VNetIncrModCount(int delta)
{
   if (delta > 0) {
      MOD_INC_USE_COUNT;
   } else if (delta < 0) {
      MOD_DEC_USE_COUNT;
   }
}


/*
 *----------------------------------------------------------------------
 *
 * VNetFileOpOpen --
 *
 *      The virtual network's open file operation. Open a connection to
 *      this virtual network. (Like plugging a cable into this virtual
 *      hub.)
 *
 * Results: 
 *      Errno.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
VNetFileOpOpen(struct inode  *inode,
               struct file   *filp)
{
   VNetPort *port;
   VNetJack *hubJack;
   int hubNum;
   int retval;
   
   LOG(1, (KERN_DEBUG "/dev/vmnet: open called by PID %d (%s)\n",
           current->pid, current->comm));

   /*
    * Allocate port
    */
   
   retval = VNetUserIf_Create(&port);
   if (retval) {
      return -retval;
   }
   
   /*
    * Connect to hub.
    */
   
   hubNum = MINOR(inode->i_rdev);
   if ((hubNum < 0) || (hubNum >= VNET_NUM_HUBS)) {
      VNetFree(&port->jack);
      return -ENODEV;
   }

   hubJack = VNetHub_Alloc(hubNum);
   if (!hubJack) {
      VNetFree(&port->jack);
      return -EBUSY;
   }

   retval = VNetConnect(&port->jack, hubJack);
   if (retval) {
      VNetFree(&port->jack);
      VNetFree(hubJack);
      return retval;
   }
   
   VNetAddPortToList(port);
   
   /*
    * Store away jack in file pointer private field for later use.
    */
   
   filp->private_data = port;
   
   LOG(1, (KERN_DEBUG "/dev/vmnet: port on hub %d successfully opened\n", hubNum));
   
   MOD_INC_USE_COUNT;
   return 0;
}


/*
 *----------------------------------------------------------------------
 *
 * VNetFileOpClose --
 *
 *      The virtual network's close file operation. Unplugging the cable.
 *
 * Results: 
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

#ifdef KERNEL_2_1
static int
#else
static void
#endif
VNetFileOpClose(struct inode  *inode,
                struct file   *filp)
{
   VNetPort *port = (VNetPort*)filp->private_data;
   VNetJack *peer;
   
   if (!port) {
      LOG(1, (KERN_DEBUG "/dev/vmnet: bad file pointer on close\n"));
#ifdef KERNEL_2_1
      return -EBADF;
#else
      return;
#endif
   }

   peer = VNetDisconnect(&port->jack);

   VNetRemovePortFromList(port);

   VNetFree(&port->jack);
   VNetFree(peer);

   MOD_DEC_USE_COUNT;

#ifdef KERNEL_2_1
   return 0;
#endif
}


/*
 *----------------------------------------------------------------------
 *
 * VNetFileOpRead --
 *
 *      The virtual network's read file operation.
 *
 * Results: 
 *      On success the len of the packet received,
 *      else if no packet waiting and nonblocking 0,
 *      else -errno.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

#ifdef KERNEL_2_1
int 
VNetFileOpRead(struct file  *filp,
               char         *buf,
               size_t        count,
	       loff_t	    *ppos)
#else
int
VNetFileOpRead(struct inode *inode,
               struct file  *filp,
               char         *buf,
               int           count)
#endif
{
   VNetPort *port = (VNetPort*)filp->private_data;
   
   if (!port) {
      LOG(1, (KERN_DEBUG "/dev/vmnet: bad file pointer on read\n"));
      return -EBADF;
   }
   
   if (!port->fileOpRead) {
      return -EPERM;
   }
   return port->fileOpRead(port, filp, buf, count);
}


/*
 *----------------------------------------------------------------------
 *
 * VNetFileOpWrite --
 *
 *      The virtual network's write file operation.
 *
 * Results: 
 *      On success the count of bytes written else errno.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */
#ifdef KERNEL_2_1
int
VNetFileOpWrite(struct file   *filp,
                const char    *buf,
                size_t         count,
		loff_t	      *ppos)
#else
int
VNetFileOpWrite(struct inode  *inode,
                struct file   *filp,
                const char    *buf,
                int            count)
#endif
{
   VNetPort *port = (VNetPort*)filp->private_data;

   if (!port) {
      LOG(1, (KERN_DEBUG "/dev/vmnet: bad file pointer on write\n"));
      return -EBADF;
   }
   
   if (!port->fileOpWrite) {
      return -EPERM;
   }
   
   return port->fileOpWrite(port, filp, buf, count);
}


/*
 *----------------------------------------------------------------------
 *
 * VNetFileOpSelect --
 *
 *      The virtual network's file select operation.
 *
 * Results: 
 *      Return 1 if success, else sleep and return 0.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */
#ifdef KERNEL_2_1

static unsigned int
VNetFileOpPoll(struct file *filp,
	       poll_table  *wait)
{
   VNetPort *port = (VNetPort*)filp->private_data;

   if (!port) {
      LOG(1, (KERN_DEBUG "/dev/vmnet: bad file pointer on poll\n"));
      return -EBADF;
   }
   
   if (!port->fileOpPoll) {
      return -EPERM;
   }
   
   return port->fileOpPoll(port, filp, wait);
}

#else

static int
VNetFileOpSelect(struct inode  *inode,
                 struct file   *filp,
                 int            sel_type,
                 select_table  *wait)
{
   VNetPort *port = (VNetPort*)filp->private_data;

   if (!port) {
      LOG(1, (KERN_DEBUG "/dev/vmnet: bad file pointer on select\n"));
      return -EBADF;
   }
   
   if (!port->fileOpSelect) {
      return -EPERM;
   }
   
   return port->fileOpSelect(port, filp, sel_type, wait);
}

#endif


/*
 *----------------------------------------------------------------------
 *
 * VNetFileOpIoctl --
 *
 *      The virtual network's ioctl file operation. This is used for
 *      setup of the connection. Currently supported commands are
 *      (taken from sockios.h):
 *
 *      SIOCGIFADDR - get ethernet address          - ioarg OUT: 6 bytes
 *      SIOCSIFADDR - set ethernet address          - ioarg IN:  6 bytes
 *      SIOCGIFFLAGS - get flags                    - ioarg OUT  4 bytes
 *      SIOCSIFFLAGS - set flags                    - ioarg IN:  4 bytes
 *      SIOCGIFBR - get bridging status             - ioarg OUT: 4 bytes
 *      SIOCSIFBR - set bridge peer interface       - ioarg IN:  8 bytes
 *
 *      Private ioctl calls, taken from device-private ioctl space 
 *      in sockios.h, and defined in includes/vm_oui.h:
 *
 *      SIOCSKEEP (0x89F0) - set dealloc flag       - ioarg IN:  1 byte
 *      SIOCGKEEP (0x89F1) - get dealloc flag       - ioarg OUT: 1 byte
 *         By default all hubs (and their associated interfaces) are 
 *         deallocated from memory when their last open port is closed.  
 *         Setting this true "keeps" the hub and thus we will not 
 *         deallocate it until the module is unloaded.
 *
 *      SIOCSLADRF (0x89F2) - set logical address filter (for 
 *         filtering multicast packets)             - ioarg IN:  8 bytes
 *
 *
 *      Supported flags are (taken from if.h):
 *
 *      IFF_UP - ready to receive packets             - OFF by default
 *      IFF_BROADCAST - receive broadcast packets     - OFF by default
 *      IFF_DEBUG - turn on debugging                 - OFF by default
 *      IFF_PROMISC - promiscuous mode                - OFF by default
 *      IFF_MULTICAST - receive multicast packets     - OFF by default
 *      IFF_ALLMULTI - receive all multicast packets 
 *            (like IFF_PROMISC but with multicast)   - OFF by default
 *
 * Results: 
 *      On success 0 else errno.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
VNetFileOpIoctl(struct inode   *inode,
                struct file    *filp,
                unsigned int    iocmd,
                unsigned long   ioarg)
{
   VNetPort *port = (VNetPort*)filp->private_data;
   VNetJack *peer;
   VNetPort *new;
   char name[8];
   int retval, retval2;
   uint8 paddr[6];
   int flags = 0;
   int i;
   VNet_SetMacAddrIOCTL macAddr;
   VNetPort *p;
   
   if (!port) {
      LOG(1, (KERN_DEBUG "/dev/vmnet: bad file pointer on ioctl\n"));
      return -EBADF;
   }

   // sprintf(vnetHub[hubNum]->devName, "vmnet%d", hubNum);

   switch (iocmd) {
   case SIOCSIFBR:
   case SIOCPORT:   
   case SIOCBRIDGE:
   case SIOCNETIF:
      if (iocmd == SIOCPORT) {
         retval = VNetUserIf_Create(&new);
      } else {
         retval = verify_area(VERIFY_READ, (void *)ioarg, sizeof name);
         if (retval != 0) {
            return retval;
         }
         
         memcpy_fromfs(name, (void *)ioarg, sizeof name);
         name[sizeof name - 1] = 0;

         if (iocmd == SIOCNETIF) {
            retval = VNetNetIf_Create(name, &new);
         } else {
#ifdef KERNEL_2_1
            if (!capable(CAP_NET_RAW)) {
               return -EACCES;
            }
#else
            if (!suser()) {
               return -EACCES;
            }            
#endif
   
            retval = VNetBridge_Create(name, &new);
         }
      }
      if (retval) {
         return retval;
      }

      /*
       *  OK this is tricky. Try and connect the new port to the old
       *  ports peer. However we need to save enough info so that we can
       *  reconnect with the old port if a cycle is detected.
       */
      
      peer = VNetDisconnect(&port->jack);

      retval = VNetConnect(&new->jack, peer);
      if (retval) {
         VNetFree(&new->jack);
         retval2 = VNetConnect(&port->jack, peer);
         if (retval2) {
            // assert xxx redo this
            LOG(1, (KERN_NOTICE "/dev/vmnet: cycle on connect failure\n"));
            return -EBADF;
         }
         return retval;
      }

      VNetAddPortToList(new);
      VNetRemovePortFromList(port);
      VNetFree(&port->jack);

      filp->private_data = new;
      break;

   case SIOCGIFBR:
      retval = verify_area(VERIFY_WRITE, (void *)ioarg, sizeof flags);
      if (retval) {
         return retval;
      }
      flags = VNetIsBridged(&port->jack);
      put_user(flags, (int *)ioarg);      
      break;
      
   case SIOCGIFADDR:
      retval = verify_area(VERIFY_WRITE, (void*)ioarg, ETH_ALEN);
      if (retval) {
         return retval;
      }
      memcpy_tofs((void*)ioarg, port->paddr, ETH_ALEN);
      break;
      
   case SIOCSIFADDR:
      retval = verify_area(VERIFY_READ, (void*)ioarg, ETH_ALEN);
      if (retval) {
         return retval;
      }
      memcpy_fromfs(paddr, (void*)ioarg, ETH_ALEN);      
      return VNetMakeMACAddress(port, paddr);
      break;
      
   case SIOCSLADRF:
      memcpy_fromfs(port->ladrf, (void*)ioarg, 8);
      break;

   case SIOCGIFFLAGS:
      retval = verify_area(VERIFY_WRITE, (void*)ioarg, 4);
      if (retval) {
         return retval;
      }
      put_user(port->flags, (uint32*)ioarg);
      break;
      
   case SIOCSIFFLAGS:
      port->flags = ((ioarg
                      & (IFF_UP|IFF_BROADCAST|IFF_DEBUG
                         |IFF_PROMISC|IFF_MULTICAST|IFF_ALLMULTI))
                     | IFF_RUNNING);
      /* XXX process queued packets? */
      break;

   case SIOCSETMACADDR:
      retval = verify_area(VERIFY_READ, (void*)ioarg, sizeof macAddr);
      if (retval) {
         return retval;
      }
      memcpy_fromfs(&macAddr, (void*)ioarg, sizeof macAddr);      
      
      switch (macAddr.version) {
      case 1:
         if (macAddr.flags & VNET_SETMACADDRF_UNIQUE) {
            for (p=vnetAllPorts; p; p=p->next) {
               if ((p != port) && MAC_EQ(p->paddr, macAddr.addr)) {
                  return -EBUSY;
               }
            }
         }
         memcpy(port->paddr, macAddr.addr, ETH_ALEN);
         break;
      default:
         return -EINVAL;
         break;
      }
      break;
      
   default:
      for (i=0; i<MAX_IOCTL_CALLBACKS; i++) {
         if (vnetIoctlCallback[i].allocated
             && (vnetIoctlCallback[i].num == iocmd)) {
            return (*vnetIoctlCallback[i].handler)(vnetIoctlCallback[i].clientData,
                                                   port, filp, iocmd, ioarg);
         }
      }
      if (!port->fileOpIoctl) {
         return -ENOIOCTLCMD;
      }      
      return port->fileOpIoctl(port, filp, iocmd, ioarg);
      break;
   }
   
   return 0;
}


/*
 *----------------------------------------------------------------------
 *
 * VNet_RegisterIoctlHandler --
 *
 *      Register a callback to happen on certain ioctls. Allows other
 *      modules to hook into us. For now I only allow 1 callback per
 *      ioctl number, but that isn't really necessary. 
 *
 * Results: 
 *      errno.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
VNet_RegisterIoctlHandler(int               ioctlNum,
                          VNetIoctlHandler  handler,
                          void             *clientData)
{
   int index = -1;
   int i;

   if (!handler) {
      return -EFAULT;
   }

   for (i=0; i<MAX_IOCTL_CALLBACKS; i++) {
      if (!vnetIoctlCallback[i].allocated) {
         index = i;
      } else if (vnetIoctlCallback[i].num == ioctlNum) {
         return -EBUSY;
      }
   }

   if (index == -1) {
      return -EBUSY;
   }

   vnetIoctlCallback[index].allocated = TRUE;
   vnetIoctlCallback[index].num = ioctlNum;
   vnetIoctlCallback[index].handler = handler;
   vnetIoctlCallback[index].clientData = clientData;

   return 0;
}


/*
 *----------------------------------------------------------------------
 *
 * VNet_UnregisterIoctlHandler --
 *
 *      Unregister an ioctl callback.
 *
 * Results: 
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
VNet_UnregisterIoctlHandler(int ioctlNum)
{
   int i;

   for (i=0; i<MAX_IOCTL_CALLBACKS; i++) {
      if (vnetIoctlCallback[i].allocated
          && vnetIoctlCallback[i].num == ioctlNum) {
         vnetIoctlCallback[i].allocated = FALSE;
      }
   }
}


/*
 *----------------------------------------------------------------------
 *
 * VNetPacketMatch --
 *
 *      Determines whether the packet should be given to the interface.
 *
 * Results: 
 *      TRUE if the pasket is OK for this interface, FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

Bool
VNetPacketMatch(uint8   *destAddr,
                uint8   *ifAddr,
		uint8   *ladrf,
                uint32   flags)
{
   static uint8 broadcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
   return ((flags & IFF_PROMISC)
           || MAC_EQ(destAddr, ifAddr)
           || ((flags & IFF_BROADCAST) && MAC_EQ(destAddr, broadcast))
           || ((destAddr[0] & 0x1) && 
	       (flags & IFF_ALLMULTI || // allow all multi
		     (flags & IFF_MULTICAST && 
		      VNetMulticastFilter(destAddr, ladrf)))));
}

/*
 *----------------------------------------------------------------------
 *
 * VNetMulticastFilter --
 *
 *      Utility function that filters multicast packets according
 *      to a 64-bit logical address filter (like the one on the
 *      lance chipset).  AllMultiFilter lets all packets through.
 *
 *      We generate a hash value from the destination MAC address
 *      and see if it's in our filter.  Broadcast packets have
 *      already OK'd by PacketMatch, so we don't have to worry
 *      about that.
 *
 *      (This is in the green AMD "Ethernet Controllers" book, 
 *      page 1-53.)
 *
 * Results:
 *      TRUE if packet is in filter, FALSE if not.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

#define CRC_POLYNOMIAL_BE 0x04c11db7UL  /* Ethernet CRC, big endian */

Bool
VNetMulticastFilter(uint8 *destAddr,
		    uint8 *ladrf)
{
   uint16 hashcode;
   int32 crc, poly = CRC_POLYNOMIAL_BE;
   int j, bit, byte;
   
   crc = 0xffffffff;                  /* init CRC for each address */
   for (byte=0; byte<6; byte++) {     /* for each address byte */
      /* process each address bit */ 
      for (bit = *destAddr++,j=0;j<8;j++, bit>>=1) {
	 crc = (crc << 1) ^ ((((crc<0?1:0) ^ bit) & 0x01) ? poly : 0);
      }
   }
   hashcode = (crc & 1);              /* hashcode is 6 LSb of CRC ... */
   for (j=0; j<5; j++) {                /* ... in reverse order. */
      hashcode = (hashcode << 1) | ((crc>>=1) & 1);
   }                                      
   
   byte = hashcode >> 3;              /* bit[3-5] -> byte in filter */
   bit = 1 << (hashcode & 0x07);      /* bit[0-2] -> bit in byte */
   if (ladrf[byte] & bit) {
      return TRUE;
   } else {
      return FALSE;
   }
}


/*
 *----------------------------------------------------------------------
 *
 * VNetMakeMACAddress --
 *
 *      Set the ports address to the given one making it unique.
 *
 * Results: 
 *      errno.
 *
 * Side effects:
 *      The address is changed.
 *
 *----------------------------------------------------------------------
 */

#ifdef i386

static inline unsigned long long GET_TSC(void)
{
    unsigned long long tim;
   __asm__ volatile (".byte 0x0f,0x31"
           :"=A" (tim));
    return tim;
}

#endif 

int
VNetMakeMACAddress(VNetPort *port,
                   uint8    *paddr)
{
   Bool ipmacs[VNET_NUM_IPBASED_MACS];
   Bool conflict;
   VNetPort *p;
   uint32 r;
   int i;
   
   if (paddr[0] != VMX86_OUI0
       || paddr[1] != VMX86_OUI1
       || paddr[2] != VMX86_OUI2) {
      return -EINVAL;
   }

   switch (paddr[3] & VMX86_MAC_PREFIX) {
   case VMX86_MAC_RANDOM:
      /* Randomize bits 21-0 and make them unique on this machine. */
      while (1) {
         conflict = FALSE;
         
         for (p=vnetAllPorts; p; p=p->next) {
            if ((p != port) && MAC_EQ(p->paddr, paddr)) {
               conflict = TRUE;
               break;
            }
         }

         if (!conflict) {
            break;
         }

         r = GET_TSC();
         paddr[3] = VMX86_MAC_RANDOM | (r >> 16 & ~VMX86_MAC_PREFIX);
         paddr[4] = r >> 8;
         paddr[5] = r;
      }
      break;
   case VMX86_MAC_IPBASED:
      /* Randomize bits 21-16 and make them unique on this machine. */
      memset(ipmacs, 0, sizeof(ipmacs));
      for (p=vnetAllPorts; p; p=p->next) {
         if (p != port) {
            int val = (p->paddr[3] & 0x3F);
            ipmacs[val] = 1;
         }
      }
      for (i = 1; i < VNET_NUM_IPBASED_MACS; i++) {
         if (!ipmacs[i]) {
            LOG(0, (KERN_INFO "/dev/vmnet: assigning IP-based address %d.\n", i));
            paddr[3] = VMX86_MAC_IPBASED | i;
            break;
         }
      }
      if (i == VNET_NUM_IPBASED_MACS) {
         LOG(0, (KERN_NOTICE "/dev/vmnet: out of IP-based MAC addresses.\n"));
         return -EBUSY;
      }
      break;
   case VMX86_MAC_FIXED:
      /* use as is */
      break;
   default:
      return -EINVAL;
      break;
   }

   memcpy(port->paddr, paddr, ETH_ALEN);
   return 0;
}


/*
 *----------------------------------------------------------------------
 *
 * VNetConnect --
 *
 *      Connect 2 jacks.
 *
 * Results: 
 *      errno.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
VNetConnect(VNetJack *jack1,
            VNetJack *jack2)
{
   static int vnetGeneration = 0;
   Bool foundCycle;
   
   vnetGeneration++;

   foundCycle = VNetCycleDetect(jack1, vnetGeneration);
   if (foundCycle) {
      return -EDEADLK;
   }
   
   foundCycle = VNetCycleDetect(jack2, vnetGeneration);
   if (foundCycle) {
      return -EDEADLK;
   }
   
   jack1->peer = jack2;
   jack2->peer = jack1;
   
   if (jack2->numPorts) {
      VNetPortsChanged(jack1);
   }

   if (jack1->numPorts) {
      VNetPortsChanged(jack2);
   }

   return 0;
}


/*
 *----------------------------------------------------------------------
 *
 * VNetDisconnect --
 *
 *      Disconnect 2 jacks.
 *
 * Results: 
 *      Return the peer jack.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

VNetJack *
VNetDisconnect(VNetJack *jack)
{
   VNetJack *peer;

   peer = jack->peer;

   jack->peer = NULL;
   peer->peer = NULL;

   if (peer->numPorts) {
      VNetPortsChanged(jack);
   }

   if (jack->numPorts) {
      VNetPortsChanged(peer);
   }

   return peer;
}


/*
 *----------------------------------------------------------------------
 *
 * VNetCycleDetectIf --
 *
 *      Perform the cycle detect alogorithm for this generation on a
 *      specific interface. This could be a bridged interface, host
 *      interface or both.
 *
 * Results: 
 *      TRUE if a cycle was detected, FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

Bool
VNetCycleDetectIf(char *name,
                  int   generation)
{
   static VNetInterface *vnetInterfaces = 0;
   VNetInterface *p;

   for (p=vnetInterfaces; p; p=p->next) {
      if (!strcmp(name, p->name)) {
         if (p->myGeneration == generation) {
            return TRUE;
         } else {
            p->myGeneration = generation;
            return FALSE;
         }
      }
   }
   
   p = (VNetInterface*)kmalloc(sizeof(VNetInterface), GFP_USER);
   if (!p) {
      // assert
      return TRUE;
   }
   
   memcpy(p->name, name, sizeof p->name);
   p->myGeneration = generation;
   p->next = vnetInterfaces;
   vnetInterfaces = p;

   return FALSE;
}


/*
 *----------------------------------------------------------------------
 *
 * VNetSend --
 *
 *      Send a packet through this jack. Note, the packet goes to the
 *      jacks peer.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      The skb is no longer owned by us.
 *
 *----------------------------------------------------------------------
 */

void
VNetSend(VNetJack        *jack,
         struct sk_buff  *skb)
{
#ifdef PACIA_TRACE   
	char * fname = "VNetSend";
	char msg_buf[256];
	struct timeval tv;
	struct timeval now_tv;
	int print = 0; // whether or not to print information
	do_gettimeofday(&tv);

#ifdef VERBOSE_PACIA_TRACE
	sprintf (msg_buf, "%s called", fname);
	print_string (msg_buf);
#endif // VERBOSE_PACIA_TRACE
#endif

   if (jack && jack->peer && jack->peer->rcv) {
// XXX 736 stuff
#ifdef PACIA_TRACE   
	{
		__u16 src = 0, dst = 0;
		int retval;
	
		/* Set print to 1 iff this is a packet from the server to
		 * the client. */
		retval = getTcpPorts (skb, &src, &dst);
		if (retval != 0
			&& (src == DEFAULT_SERVER_PORT
				|| src == DEFAULT_CLIENT_PORT)
			&& (dst == DEFAULT_CLIENT_PORT
				|| dst == DEFAULT_SERVER_PORT)
			)
		{
			print = 1;
		}
		if (print != 0)
		{
			sprintf (msg_buf, "%s skb:", fname);
			print_string (msg_buf);

			// print the skb
			print_skb (skb);
		}
	}
#endif // PACIA_TRACE   
      jack->peer->rcv(jack->peer, skb);
   } else {
      DEV_KFREE_SKB(skb, FREE_WRITE);
   }
#ifdef PACIA_TRACE   
	if (print != 0)
	{
		unsigned int foo_us;
		do_gettimeofday(&now_tv);
	  	foo_us = abs (1000000*(now_tv.tv_sec  - tv.tv_sec));
		foo_us += abs (now_tv.tv_usec - tv.tv_usec);
		sprintf (msg_buf, "%s: call took %u us", fname, foo_us);
		print_string (msg_buf);
	}
#endif
}


/*
 *----------------------------------------------------------------------
 *
 * VNetPrintJack --
 *
 *      Print info about the jack to a buffer.
 *
 * Results:
 *      Length of the write.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
VNetPrintJack(VNetJack  *jack,
              char      *buf)
{
   int len = 0;
   
   if (!jack->peer) {
      len += sprintf(buf+len, "connected not ");
   } else {
      len += sprintf(buf+len, "connected %s ", jack->peer->name);
   }

   return len;
}
              

/*
 *----------------------------------------------------------------------
 *
 * VNetPrintPort --
 *
 *      Print info about the port to a buffer.
 *
 * Results:
 *      Length of the write.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
VNetPrintPort(VNetPort  *port,
              char      *buf)
{
   int len = 0;

   len += VNetPrintJack(&port->jack, buf+len);

   len += VNetPrintPIDs(port, buf+len);

   len += sprintf(buf+len, "mac %02x:%02x:%02x:%02x:%02x:%02x ",
                  port->paddr[0], port->paddr[1], port->paddr[2], 
                  port->paddr[3], port->paddr[4], port->paddr[5]); 
   
   len += sprintf(buf+len, "ladrf %02x:%02x:%02x:%02x:%02x:%02x ",
                  port->ladrf[0], port->ladrf[1], port->ladrf[2], 
                  port->ladrf[3], port->ladrf[4], port->ladrf[5]); 
   
   len += sprintf(buf+len, "flags IFF_RUNNING");
   
   if (port->flags & IFF_UP) {
      len += sprintf(buf+len, ",IFF_UP");      
   }

   if (port->flags & IFF_BROADCAST) {
      len += sprintf(buf+len, ",IFF_BROADCAST");      
   }

   if (port->flags & IFF_DEBUG) {
      len += sprintf(buf+len, ",IFF_DEBUG");      
   }

   if (port->flags & IFF_PROMISC) {
      len += sprintf(buf+len, ",IFF_PROMISC");      
   }

   if (port->flags & IFF_MULTICAST) {
      len += sprintf(buf+len, ",IFF_MULTICAST");      
   }

   if (port->flags & IFF_ALLMULTI) {
      len += sprintf(buf+len, ",IFF_ALLMULTI");      
   }

   len += sprintf(buf+len, " ");
   
   return len;
}
              

/*
 *----------------------------------------------------------------------
 *
 * VNetPrintPIDs --
 *
 *      Return a list of PID that have this port open.
 *
 * Results: 
 *      The number of characters of the list, and a string with a
 *      comma separated list of PIDs.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
VNetPrintPIDs(VNetPort *port,  // IN: the file in question
              char     *buf)   // OUT: the buffer to fill in
{
   struct task_struct *p;
   int len = 0;
   int seen = 0;
   
   len += sprintf(buf+len, "pids ");

   for (p = current->next_task; p != current; p = p->next_task) {
      if (VNetProcessOwnsPort(p, port)) {
         len += sprintf(buf+len, "%d", p->pid);
         if (seen) {
            len += sprintf(buf+len, ",");
         } else {
            seen = 1;
         }
      }
   }  

   if (!seen) {
      len += sprintf(buf+len, "{}");
   }

   len += sprintf(buf+len, " ");
   
   return len;
}

/*
 *----------------------------------------------------------------------
 *
 * VNetProcessOwnsPort --
 *
 *      Return whether a process has the port open.
 *
 * Results: 
 *      TRUE if the process has the file open, FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

#ifdef KERNEL_2_1

Bool
VNetProcessOwnsPort(struct task_struct  *p,
                    VNetPort            *port)
{
   struct file *file;
   int fd;
   int maj;
   
   if (!p->files) {
      return FALSE;
   }
   
   for (fd = 0; fd < p->files->max_fds; fd++) {
#ifdef KERNEL_2_3_99
      file = fcheck_files(p->files, fd);
#else
      file = fcheck_task(p, fd);
#endif
      if (file && file->f_dentry) {
         maj = MAJOR(file->f_dentry->d_inode->i_rdev);
         if ((maj == VNET_MAJOR_NUMBER) && (file->private_data == port)) {
            return TRUE;
         }
      }
   }

   return FALSE;
}

#else

Bool
VNetProcessOwnsPort(struct task_struct  *p,
                    VNetPort            *port)
{
   struct file *file;
   int fd;
   int maj;
   
   if (!p->files) {
      return FALSE;
   }
   
   for (fd = 0; fd < NR_OPEN; fd++) {
      file = p->files->fd[fd];
      if (p->files->fd[fd] && p->files->fd[fd]->f_inode) {
         maj = MAJOR(p->files->fd[fd]->f_inode->i_rdev); 
         if ((maj == VNET_MAJOR_NUMBER) && (file->private_data == port)) {
            return TRUE;
         }
      }      
   }

   return FALSE;
}

#endif



