/* **********************************************************
 * 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/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"

#include "utils.h"
#include "global_tracking.h"

#define VNET_BRIDGE_HISTORY    8

typedef struct VNetBridge VNetBridge;

struct VNetBridge {
   struct notifier_block    notifier;	// must be first
   char                     name[8];
   struct device           *dev;
   struct sock             *sk;
   struct packet_type       pt;
   Bool                     promiscSaved;
   Bool                     savedPromisc;
   struct sk_buff          *history[VNET_BRIDGE_HISTORY];
   VNetPort                 port;
};


static int  VNetBridgeUp(VNetBridge *bridge);
static void VNetBridgeDown(VNetBridge *bridge);
static void VNetBridgeStartPromisc(VNetBridge *bridge);
static void VNetBridgeStopPromisc(VNetBridge *bridge);
static void VNetBridgeCheckPromisc(VNetBridge *bridge);

static int  VNetBridgeNotify(struct notifier_block *this, u_long msg,
			     void *data);
static int  VNetBridgeReceiveFromDev(struct sk_buff *skb, struct device *dev,
                                     struct packet_type *pt);

static void VNetBridgeFree(VNetJack *this);
static void VNetBridgeReceiveFromVNet(VNetJack *this, struct sk_buff *skb);
static Bool VNetBridgeCycleDetect(VNetJack *this, int generation);
static void VNetBridgePortsChanged(VNetJack *this);
static int  VNetBridgeIsBridged(VNetJack *this);

#ifdef KERNEL_2_1
static int VNetBridgeProcRead(char *page, char **start, off_t off,
                              int count, int *eof, void *data);
#endif


/*
 *----------------------------------------------------------------------
 *
 * VNetBridge_Create --
 *
 *      Create a bridge.
 * 
 * Results: 
 *      Errno. Also returns an allocated jack to connect to,
 *      NULL on error.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
VNetBridge_Create(char      *devName,
                  VNetPort **ret)
{
   VNetBridge *bridge = NULL;
   static unsigned id = 0;
   int retval = 0;

   *ret = NULL;
   
   /*
    * Its an error if device name is empty.
    */

   if (devName[0] == '\0') {
      retval = -EINVAL;
      goto out;
   }

   /*
    * Allocate bridge structure
    */

   bridge = kmalloc(sizeof *bridge, GFP_USER);
   if (bridge == NULL) {
      retval = -ENOMEM;
      goto out;
   }
   memset(bridge, 0, sizeof *bridge);
   memcpy(bridge->name, devName, sizeof bridge->name);

   /*
    * Set up notifier for network device state change
    */

   bridge->notifier.notifier_call = VNetBridgeNotify;
   bridge->notifier.priority = 0;
   register_netdevice_notifier(&bridge->notifier);

   /*
    * Try to bring it up
    */

   retval = VNetBridgeUp(bridge);
   if (retval == -ENODEV) {
      LOG(1, (KERN_DEBUG "bridge-%s: peer interface %s not found, "
	      "will wait for it to come up\n",
	      bridge->name, bridge->name));
      retval = 0;
   }
   if (retval != 0) {
      goto out;
   }


   /*
    * Initialize jack
    */

   bridge->port.id = id++;

   bridge->port.jack.peer = NULL;
   bridge->port.jack.numPorts = 1;
   sprintf(bridge->port.jack.name, "bridge%d", bridge->port.id);
   bridge->port.jack.private = bridge;
   bridge->port.jack.index = 0;
   bridge->port.jack.procEntry = NULL;
   bridge->port.jack.free = VNetBridgeFree;
   bridge->port.jack.rcv = VNetBridgeReceiveFromVNet;
   bridge->port.jack.cycleDetect = VNetBridgeCycleDetect;
   bridge->port.jack.portsChanged = VNetBridgePortsChanged;
   bridge->port.jack.isBridged = VNetBridgeIsBridged;
   
   /*
    * Make proc entry for this jack.
    */
   
   retval = VNetProc_MakeEntry(NULL, bridge->port.jack.name, S_IFREG,
                               &bridge->port.jack.procEntry);
   if (retval) {
      if (retval == -ENXIO) {
         bridge->port.jack.procEntry = NULL;
      } else {
         goto out;
      }
   } else {
#ifdef KERNEL_2_1
      bridge->port.jack.procEntry->read_proc = VNetBridgeProcRead;
      bridge->port.jack.procEntry->data = bridge;
#else
      bridge->port.jack.procEntry = NULL;
#endif
   }

   /*
    * Rest of fields.
    */
   
   bridge->port.flags = IFF_RUNNING;

   memset(bridge->port.paddr, 0, sizeof bridge->port.paddr);
   memset(bridge->port.ladrf, 0, sizeof bridge->port.ladrf);

   bridge->port.paddr[0] = VMX86_OUI0;
   bridge->port.paddr[1] = VMX86_OUI1;
   bridge->port.paddr[2] = VMX86_OUI2;

   bridge->port.fileOpRead = NULL;
   bridge->port.fileOpWrite = NULL;
   bridge->port.fileOpIoctl = NULL;
#ifdef KERNEL_2_1
   bridge->port.fileOpPoll = NULL;
#else
   bridge->port.fileOpSelect = NULL;
#endif

   *ret = &bridge->port;
   
   LOG(1, (KERN_DEBUG "bridge-%s: attached\n", bridge->name));
   return 0;
   
out:
   if (bridge != NULL) {
      if (bridge->notifier.notifier_call != NULL) {
         unregister_netdevice_notifier(&bridge->notifier);
      }
      kfree(bridge);
   }
   return retval;
}


/*
 *----------------------------------------------------------------------
 *
 * VNetBridgeFree --
 *
 *      Disconnect the bridge, deallocate it.
 * 
 * Results: 
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
VNetBridgeFree(VNetJack *this)
{
   VNetBridge *bridge = (VNetBridge*)this->private;
   
   if (bridge->dev != NULL) {
      VNetBridgeDown(bridge);
   }

   unregister_netdevice_notifier(&bridge->notifier);

   if (this->procEntry) {
      VNetProc_RemoveEntry(this->procEntry, NULL);
   }

   LOG(1, (KERN_DEBUG "bridge-%s: detached\n", bridge->name));
   kfree(bridge);
}


/*
 *----------------------------------------------------------------------
 *
 * VNetBridgeReceiveFromVNet --
 *
 *      This jack is receiving a packet. Take appropriate action.
 * 
 * Results: 
 *      None.
 *
 * Side effects:
 *      Frees skb.
 *
 *----------------------------------------------------------------------
 */

void
VNetBridgeReceiveFromVNet(VNetJack        *this,
                          struct sk_buff  *skb)
{
   VNetBridge *bridge = (VNetBridge*)this->private;
   struct device *dev = bridge->dev;
   uint8 *dest = SKB_2_DESTMAC(skb);
   struct sk_buff *clone;
#ifdef PACIA_TRACE   
	char * fname = "VNetBridgeReceiveFromVNet";
	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 on bridge %s", fname, bridge->name);
	print_string (msg_buf);
#endif // VERBOSE_PACIA_TRACE
#endif

   LOG(3, (KERN_DEBUG "bridge-%s: transmit %d\n",
           bridge->name, (int) skb->len));

   if (!dev) {
      KFREE_SKB(skb, FREE_WRITE);
      return;
   }
   
// 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   

   /*
    * Check promiscuous bit periodically
    */

   VNetBridgeCheckPromisc(bridge);

#ifdef notdef
   // xxx;
   /*
    * We need to send the packet both up to the host and down
    * to the interface.
    * However, we ignore packets destined only for this hub.
    */

   for (i = 0; i < VNET_PORTS_PER_HUB; i++) {
      VNetPort *p = &port->hub->port[i];
      if (UP_AND_RUNNING(p->flags) && MAC_EQ(dest, p->paddr)) {
	 return;
      }
   }
#endif

   /*
    * Send down (imitate packet_sendmsg)
    *
    * Do this only if the packet is not addressed to the peer,
    * and the packet size is not too big.
    */

   DEV_LOCK_LIST();
   if (MAC_EQ(dest, dev->dev_addr) ||
       skb->len > dev->mtu + dev->hard_header_len) {
      DEV_UNLOCK_LIST();
   } else {
#     if 0 // XXX we should do header translation
      if ((dev->flags & IFF_SOFTHEADERS) != 0) {
	 if (skb->len > dev->mtu) {
	    clone = NULL;
	 } else {
	    clone = alloc_skb(skb->len + dev->hard_header_len, GFP_ATOMIC);
	 }
	 if (clone != NULL) {
	    skb_reserve(clone, dev->hard_header_len);
	    if (dev->hard_header != NULL) {
	       dev->hard_header(clone, dev, ETH_P_IP, NULL, NULL, skb->len);
	    }
	    memcpy(skb_put(clone, skb->len), skb->data, skb->len);
	 }
      }
#     endif
      clone = skb_clone(skb, GFP_ATOMIC);
      if (clone == NULL) {
	 DEV_UNLOCK_LIST();
      } else {
	 struct sock *sk = bridge->sk;
	 atomic_add(skb->truesize, &sk->wmem_alloc);
#	 ifndef KERNEL_2_1
	 clone->free = 1;
	 clone->arp = 1;
#	 endif
	 clone->sk = sk;
	 clone->protocol = ((struct ethhdr *)skb->data)->h_proto; // XXX
	 if ((dev->flags & IFF_UP) != 0) {
	    DEV_UNLOCK_LIST();
	    DEV_QUEUE_XMIT(clone, dev, 0); // pjh THIS SENDS THE PACKET
#ifdef PACIA_TRACE
		/*
		if (curr_dir == out
			&& curr_skb == skb)
		*/
		if (print)
		{
			unsigned int elapsed_us;
			do_gettimeofday(&now_tv);
	  		elapsed_us = abs (1000000*(now_tv.tv_sec - begin_time.tv_sec));
			elapsed_us += abs (now_tv.tv_usec - begin_time.tv_usec);
			sprintf (msg_buf, "Message 0x%x sent! took %u us",
					curr_skb, elapsed_us);
			print_string (msg_buf);
			curr_skb = NULL;
		}
#endif // PACIA_TRACE
	 } else {
	    DEV_UNLOCK_LIST();
	    KFREE_SKB(clone, FREE_WRITE);
	 }
      }
   }

   /*
    * Send up (imitate Ethernet receive)
    *
    * Do this if the packet is addressed to the peer (or is broadcast, etc.).
    *
    * This packet will get back to us, via VNetBridgeReceive.
    * We save it so we can recognize it (and its clones) again.
    *
    * netif_rx is an interrupt-time function.  Wrap it in cli, sti.
    */

   if (VNetPacketMatch(dest, dev->dev_addr, (uint8 *)&AllMultiFilter, dev->flags)) {
      clone = skb_clone(skb, GFP_ATOMIC);
      if (clone) {
	 unsigned long flags;
	 int i;
	 SKB_INCREF(clone);
	 // XXX need to lock history
	 for (i = 0; i < VNET_BRIDGE_HISTORY; i++) {
	    if (bridge->history[i] == NULL) {
	       bridge->history[i] = clone;
#	       if LOGLEVEL >= 3
	       {
		  int j;
		  int count = 0;
		  for (j = 0; j < VNET_BRIDGE_HISTORY; j++) {
		     if (bridge->history[j] != NULL) {
			count++;
		     }
		  }
		  LOG(3, (KERN_DEBUG "bridge-%s: host slot %d history %d\n",
			  bridge->name, i, count));
	       }
#	       endif
	       break;
	    }
	 }
	 if (i >= VNET_BRIDGE_HISTORY) {
	    LOG(1, (KERN_NOTICE "bridge-%s: history full\n",
		    bridge->name));
	    for (i = 0; i < VNET_BRIDGE_HISTORY; i++) {
	       struct sk_buff *s = bridge->history[i];
	       bridge->history[i] = NULL;
	       KFREE_SKB(s, FREE_WRITE);
	    }
	    bridge->history[0] = clone;
	 }
         
	 clone->dev = dev;
	 clone->protocol = eth_type_trans(clone, dev);
	 save_flags(flags);
	 cli();
	 netif_rx(clone);
#	 if LOGLEVEL >= 4
	 do_gettimeofday(&vnetTime);
#	 endif
	 restore_flags(flags);
      }
   }

   // xxx;
   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
}


/*
 *----------------------------------------------------------------------
 *
 * VNetBridgeCycleDetect --
 *
 *      Cycle detection algorithm.
 * 
 * Results: 
 *      TRUE if a cycle was detected, FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

Bool
VNetBridgeCycleDetect(VNetJack *this,
                      int       generation)
{
   VNetBridge *bridge = (VNetBridge*)this->private; 
   return VNetCycleDetectIf(bridge->name, generation);
}


/*
 *----------------------------------------------------------------------
 *
 * VNetBridgePortsChanged --
 *
 *      The number of ports connected to this jack has change, react
 *      accordingly.
 * 
 * Results: 
 *      None.
 *
 * Side effects:
 *      Promiscuous mode may be started or stopped.
 *
 *----------------------------------------------------------------------
 */

void
VNetBridgePortsChanged(VNetJack *this)
{
   VNetBridge *bridge = (VNetBridge*)this->private;
   if (bridge->dev) {
      if (VNetGetAttachedPorts(this)) {
         VNetBridgeStartPromisc(bridge);
      } else {
         VNetBridgeStopPromisc(bridge);
      }
   }
}


/*
 *----------------------------------------------------------------------
 *
 * VNetBridgeIsBridged --
 *
 *      Are we bridged, well duh.
 * 
 * Results: 
 *      0 - not bridged
 *      1 - we are bridged but the interface is not up
 *      2 - we are bridged and the interface is up
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
VNetBridgeIsBridged(VNetJack *this)
{
   VNetBridge *bridge = (VNetBridge*)this->private;
   if (bridge->dev) {
      return 2;
   } else {
      return 1;
   }
}


/*
 *----------------------------------------------------------------------
 *
 * VNetBridgeUp --
 *
 *      Bring a bridge up
 *
 * Results: 
 *      errno.
 *
 * Side effects:
 *      Bridging may be brought up with a peer interface.
 *
 *----------------------------------------------------------------------
 */

int
VNetBridgeUp(VNetBridge *bridge)
{
   int retval = 0;

   if (bridge->dev != NULL) {
      LOG(0, (KERN_NOTICE "bridge-%s: already up\n", bridge->name));
      goto out;
   }

   /*
    * Get peer device structure
    */

   DEV_LOCK_LIST();
   bridge->dev = DEV_GET(bridge->name);
   LOG(2, (KERN_DEBUG "bridge-%s: got dev 0x%08lx\n",
	   bridge->name, (uint32) bridge->dev));
   if (bridge->dev == NULL) {
      DEV_UNLOCK_LIST();
      retval = -ENODEV;
      goto out;
   }

   /*
    * At a minimum, the header size should be the same as ours.
    *
    * XXX we should either do header translation or ensure this
    * is an Ethernet.
    */

   if (bridge->dev->hard_header_len != ETH_HLEN) {
      LOG(1, (KERN_DEBUG "bridge-%s: can't bridge with %s, bad header length %d\n",
	      bridge->name, bridge->name, bridge->dev->hard_header_len));
      DEV_UNLOCK_LIST();
      retval = -EINVAL;
      goto out;
   }

   /*
    * Get a socket to play with
    *
    * We set the dead field so we don't get a call back from kfree_skb().
    * (The alternative is to support the callback.)
    */

   bridge->sk = SK_ALLOC(GFP_USER);
   if (bridge->sk == NULL) {
      DEV_UNLOCK_LIST();
      retval = -ENOMEM;
      goto out;
   }
   bridge->sk->dead = 1;
   *(VNetBridge**)&bridge->sk->protinfo = bridge;

   /*
    * Link up with the peer device
    */

   bridge->pt.func = VNetBridgeReceiveFromDev;
   bridge->pt.type = htons(ETH_P_ALL);
   bridge->pt.data = bridge->sk;
   bridge->pt.dev = bridge->dev;
   bridge->promiscSaved = FALSE;
   dev_add_pack(&bridge->pt);

   /*
    * Put in promiscuous mode if need be.
    */

   if (VNetGetAttachedPorts(&bridge->port.jack)) {
      VNetBridgeStartPromisc(bridge);
   }
   
   /*
    * Finish up
    */

   DEV_UNLOCK_LIST();
   LOG(1, (KERN_DEBUG "bridge-%s: up\n", bridge->name));

   /*
    * Return
    */

out:
   if (retval != 0) {
      if (bridge->sk != NULL) {
	 sk_free(bridge->sk);
	 bridge->sk = NULL;
      }
      bridge->dev = NULL;
   }
   return retval;
}


/*
 *----------------------------------------------------------------------
 *
 * VNetBridgeDown --
 *
 *      Bring a bridge down
 *
 * Results: 
 *      None.
 *
 * Side effects:
 *      Bridging is brought down.
 *
 *----------------------------------------------------------------------
 */

void
VNetBridgeDown(VNetBridge *bridge)
{
   if (bridge->dev == NULL) {
      LOG(0, (KERN_NOTICE "bridge-%s: already down\n", bridge->name));
      return;
   }

   VNetBridgeStopPromisc(bridge);
   bridge->dev = NULL;
   dev_remove_pack(&bridge->pt);
   sk_free(bridge->sk);
   bridge->sk = NULL;
   LOG(1, (KERN_DEBUG "bridge-%s: down\n", bridge->name));
}


/*
 *----------------------------------------------------------------------
 *
 * VNetBridgeNotify --
 *
 *      Callback on peer device state change
 *
 * Results: 
 *      NOTIFY_DONE
 *
 * Side effects:
 *      Bridging is disabled or enabled as the case may be.
 *
 *----------------------------------------------------------------------
 */

int
VNetBridgeNotify(struct notifier_block *this, u_long msg, void *data)
{
   VNetBridge *bridge = (VNetBridge *) this;
   struct device *dev = (struct device *) data;

   LOG(2, (KERN_DEBUG "bridge-%s: notify 0x%lx (%s), dev 0x%p (%s)\n",
	   bridge->name,
	   msg,
	   msg == NETDEV_DOWN ? "NETDEV_DOWN" :
	   msg == NETDEV_UP ? "NETDEV_UP" : "???",
	   dev, dev->name));

   switch (msg) {
   case NETDEV_DOWN:
      if (dev == bridge->dev) {
	 LOG(1, (KERN_DEBUG "bridge-%s: lost peer %s\n", bridge->name, bridge->name));
	 VNetBridgeDown(bridge);
      }
      break;

   case NETDEV_UP:
      if (bridge->dev == NULL && strcmp(dev->name, bridge->name) == 0) {
	 int errno;
	 LOG(1, (KERN_DEBUG "bridge-%s: found peer %s\n", bridge->name, bridge->name));
	 errno = VNetBridgeUp(bridge);
	 switch (-errno) {
	 case 0:
	    break;
	 case ENODEV:
	    LOG(0, (KERN_WARNING "bridge-%s: peer interface %s not found",
		    bridge->name, bridge->name));
	    break;
	 case EINVAL:
	    LOG(0, (KERN_WARNING "bridge-%s: peer %s is not a valid Ethernet interface\n",
		    bridge->name, bridge->name));
	    break;
	 case EBUSY:
	    LOG(0, (KERN_WARNING "bridge-%s: already bridged with %s\n",
		    bridge->name, bridge->name));
	    break;
	 case EISCONN:
	    LOG(0, (KERN_WARNING "bridge-%s: already attached as a network interface, "
		    "can't bridge with %s\n",
		    bridge->name, bridge->name));
	    break;
	 case ENOMEM:
	    LOG(0, (KERN_WARNING "bridge-%s: failed to allocate memory\n",
		    bridge->name));
	    break;
	 default:
	    LOG(0, (KERN_WARNING "bridge-%s: failed to bring up bridge %s (error %d)\n",
		    bridge->name, bridge->name, -errno));
	 }
      }
      break;
   }

   return NOTIFY_DONE;
}

/*
 *----------------------------------------------------------------------
 *
 * VNetBridgeReceiveFromDev --
 *
 *      Receive a packet from a bridged peer device
 *
 *      This is called from the bottom half.  Must be careful.
 *
 * Results: 
 *      errno.
 *
 * Side effects:
 *      A packet may be sent to the vnet.
 *
 *----------------------------------------------------------------------
 */
/* pjh
 * The kernel calls this when a packet destined for the vmnet
 * comes in. It has to do with struct packet_type.
 */

int
VNetBridgeReceiveFromDev(struct sk_buff *skb,
                         struct device *dev,
                         struct packet_type *pt)
{
   VNetBridge *bridge = *(VNetBridge**)&((struct sock *)pt->data)->protinfo;
   int i;
#ifdef PACIA_TRACE   
	char * fname = "VNetBridgeReceiveFromDev";
	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 on bridge %s", fname, bridge->name);
	print_string (msg_buf);
#endif // VERBOSE_PACIA_TRACE
#endif

   if (bridge->dev == NULL) {
      LOG(3, (KERN_DEBUG "bridge-%s: received %d closed\n",
	      bridge->name, (int) skb->len));
      DEV_KFREE_SKB(skb, FREE_READ);
      return -EIO;	// value is ignored anyway
   }

   // XXX need to lock history
   for (i = 0; i < VNET_BRIDGE_HISTORY; i++) {
      struct sk_buff *s = bridge->history[i];
      if (s != NULL &&
	  (s == skb || SKB_IS_CLONE_OF(skb, s))) {
	 bridge->history[i] = NULL;
	 KFREE_SKB(s, FREE_WRITE);
	 LOG(3, (KERN_DEBUG "bridge-%s: receive %d self %d\n",
		 bridge->name, (int) skb->len, i));
	 // FREE_WRITE because we did the allocation, it's not used anyway
	 DEV_KFREE_SKB(skb, FREE_WRITE);
	 return 0;
      }
   }

#  if LOGLEVEL >= 4
   {
      struct timeval now;
      do_gettimeofday(&now);
      LOG(3, (KERN_DEBUG "bridge-%s: time %d.%06d\n",
	      bridge->name,
	      now.tv_sec - vnetTime.tv_sec, now.tv_usec - vnetTime.tv_usec));
   }
#  endif

#ifdef KERNEL_2_3_15
   skb = skb_share_check(skb, GFP_ATOMIC);
   if (!skb) return 0;
#endif

   skb_push(skb, skb->data - skb->mac.raw);
   LOG(3, (KERN_DEBUG "bridge-%s: receive %d\n",
	   bridge->name, (int) skb->len));

// 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;

			/* set up the stuff to track the time it takes for this
			 * message to get through vmnet */
			begin_time.tv_sec = tv.tv_sec;
			begin_time.tv_usec = tv.tv_usec;
	  		curr_dir = in;
			curr_skb = skb;
		}
		if (print != 0)
		{
			sprintf (msg_buf, "%s skb:", fname);
			print_string (msg_buf);

			// print the skb
			print_skb (skb);
		}
	}
#endif // PACIA_TRACE   

   VNetSend(&bridge->port.jack, skb);
   
#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

   return 0;
}


/*
 *----------------------------------------------------------------------
 *
 * VNetBridgeStartPromisc --
 *
 *      Set IFF_PROMISC on the peer interface
 *
 *      XXX need locking?
 *
 * Results: 
 *      None.
 *
 * Side effects:
 *      The peer interface IFF_PROMISC flag may be changed.
 *
 *----------------------------------------------------------------------
 */

void
VNetBridgeStartPromisc(VNetBridge *bridge)
{
   struct device *dev = bridge->dev;
   Bool devPromisc = (dev->flags & IFF_PROMISC) != 0;

   if (!bridge->promiscSaved) {
      bridge->promiscSaved = TRUE;
      bridge->savedPromisc = devPromisc;
   }
   if (!devPromisc) {
      LOG(0, (KERN_NOTICE "bridge-%s: set IFF_PROMISC\n",
	      bridge->name));
      dev->flags |= IFF_PROMISC;
      if (dev->set_multicast_list != NULL) {
	 dev->set_multicast_list(dev);
      }
   }
}


/*
 *----------------------------------------------------------------------
 *
 * VNetBridgeStopPromisc --
 *
 *      Restore saved IFF_PROMISC on the peer interface
 *
 *      XXX need locking?
 *
 * Results: 
 *      None.
 *
 * Side effects:
 *      The peer interface IFF_PROMISC flag may be changed.
 *
 *----------------------------------------------------------------------
 */

void
VNetBridgeStopPromisc(VNetBridge *bridge)
{
   struct device *dev = bridge->dev;
   Bool devPromisc = (dev->flags & IFF_PROMISC) != 0;
   
   /*
    * If somebody else has cleared the bit on us, then that's
    * what they want, and we don't touch it.
    */
   
   if (devPromisc && bridge->promiscSaved && !bridge->savedPromisc) {
      LOG(0, (KERN_NOTICE "bridge-%s: clear IFF_PROMISC\n",
	      bridge->name));
      dev->flags &= ~IFF_PROMISC;
      if (dev->set_multicast_list != NULL) {
	 dev->set_multicast_list(dev);
      }
   }
   bridge->promiscSaved = FALSE;
}


/*
 *----------------------------------------------------------------------
 *
 * VNetBridgeCheckPromisc --
 *
 *      Make sure IFF_PROMISC on the peer interface is set
 *
 *      This can be called periodically.
 *
 *      XXX need locking?
 *
 * Results: 
 *      None.
 *
 * Side effects:
 *      The peer interface IFF_PROMISC flag may be changed.
 *
 *----------------------------------------------------------------------
 */

void
VNetBridgeCheckPromisc(VNetBridge *bridge)
{
   struct device *dev = bridge->dev;
   Bool devPromisc = (dev->flags & IFF_PROMISC) != 0;

   if (bridge->promiscSaved && !devPromisc) {
      LOG(0, (KERN_NOTICE "bridge-%s: lost IFF_PROMISC\n",
	      bridge->name));
      bridge->savedPromisc = devPromisc;
      dev->flags |= IFF_PROMISC;
      if (dev->set_multicast_list != NULL) {
	 dev->set_multicast_list(dev);
      }
   }
}


/*
 *----------------------------------------------------------------------
 *
 * VNetBridgeProcRead --
 *
 *      Callback for read operation on this bridge entry in vnets proc fs.
 *
 * Results: 
 *      Length of read operation.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */
#ifdef KERNEL_2_1
int
VNetBridgeProcRead(char    *page,   // IN/OUT: buffer to write into
                   char   **start,  // OUT: 0 if file < 4k, else offset into page
                   off_t    off,    // IN: offset of read into the file
                   int      count,  // IN: maximum number of bytes to read
                   int     *eof,    // OUT: TRUE if there is nothing more to read
                   void    *data)   // IN: client data - not used
{
   VNetBridge *bridge = (VNetBridge*)data; 
   int len = 0;

   if (!bridge) {
      return len;
   }
   
   len += VNetPrintPort(&bridge->port, page+len);

   len += sprintf(page+len, "dev %s ", bridge->name);

   len += sprintf(page+len, "\n");
   
   *start = 0;
   *eof   = 1;
   return len;
}
#endif
