All pastes #957391 Raw Edit

bridge.c

public text v1 · immutable
#957391 ·published 2008-03-25 22:57 UTC
rendered paste body
/* **********************************************************
 * Copyright 1998 VMware, Inc.  All rights reserved. -- VMware Confidential
 * **********************************************************/

#include "driver-config.h"

#define EXPORT_SYMTAB

#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/sched.h>
#ifdef KERNEL_2_2
#   include <linux/slab.h>
#else
#   include <linux/malloc.h>
#endif
#include <linux/poll.h>

#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/mm.h>
#include "compat_skbuff.h"
#include <linux/sockios.h>
#include "compat_sock.h"

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

#include <linux/proc_fs.h>
#include <linux/file.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <net/tcp.h>

#ifdef defined CONFIG_NET_RADIO || defined CONFIG_WLAN_80211
#   include <linux/wireless.h>
#endif
#include "vmnetInt.h"
#include "compat_spinlock.h"
#include "compat_netdevice.h"
#include "vnetInt.h"
#include "smac.h"

#define VNET_BRIDGE_HISTORY    48

/*
 * Bytes reserved before start of packet.  As Ethernet header has 14 bytes,
 * to get aligned IP header we must skip 2 bytes before packet.  Not that it
 * matters a lot for us, but using 2 is compatible with what newer 2.6.x
 * kernels do.
 */
#ifndef NET_IP_ALIGN
#define NET_IP_ALIGN	2
#endif

#if LOGLEVEL >= 4
static struct timeval vnetTime;
#endif

typedef struct VNetBridge VNetBridge;

struct VNetBridge {
   struct notifier_block    notifier;       // for device state changes
   char                     name[VNET_NAME_LEN]; // name of net device (e.g., "eth0")
   struct net_device       *dev;            // device structure for 'name'
   struct sock             *sk;             // socket associated with skb's
   struct packet_type       pt;             // used to add packet handler
   Bool                     enabledPromisc; // track if promisc enabled
   Bool                     warnPromisc;    // tracks if warning has been logged
   struct sk_buff          *history[VNET_BRIDGE_HISTORY];  // avoid duplicate packets
   spinlock_t		    historyLock;    // protects 'history'
   VNetPort                 port;           // connection to virtual hub
   Bool                     wirelessAdapter; // connected to wireless adapter?
   struct SMACState        *smac;           // device structure for wireless
#ifdef VMW_NETDEV_HAS_NET
   struct net_device       *internalDev;
#endif
};

typedef PacketStatus (* SMACINT SMACFunc)(struct SMACState *, SMACPackets *);

static int  VNetBridgeUp(VNetBridge *bridge, Bool rtnlLock);
static void VNetBridgeDown(VNetBridge *bridge, Bool rtnlLock);

static int  VNetBridgeNotify(struct notifier_block *this, u_long msg,
			     void *data);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) && \
    !defined(VMW_TL10S64_WORKAROUND)
static int VNetBridgeReceiveFromDev(struct sk_buff *skb,
                                    struct net_device *dev,
                                    struct packet_type *pt);
#else
static int VNetBridgeReceiveFromDev(struct sk_buff *skb,
                                    struct net_device *dev,
                                    struct packet_type *pt,
                                    struct net_device *real_dev);
#endif

static void VNetBridgeFree(VNetJack *this);
static void VNetBridgeReceiveFromVNet(VNetJack *this, struct sk_buff *skb);
static Bool VNetBridgeCycleDetect(VNetJack *this, int generation);
static Bool VNetBridgeIsDeviceWireless(struct net_device *dev);
static void VNetBridgePortsChanged(VNetJack *this);
static int  VNetBridgeIsBridged(VNetJack *this);
static int  VNetBridgeProcRead(char *page, char **start, off_t off,
                               int count, int *eof, void *data);


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

static void
VNetBridgeStartPromisc(VNetBridge *bridge,      // IN:
                       Bool rtnlLock)           // IN: Acquire RTNL lock
{
   struct net_device *dev = bridge->dev;

   /*
    * Disable wireless cards from going into promiscous mode because those
    * cards which do support RF monitoring would not be able to function
    * correctly i.e. they would not be able to send data packets.
    */
   if (rtnlLock) {
      rtnl_lock();
   }
   if (!bridge->enabledPromisc && !bridge->wirelessAdapter) {
      dev_set_promiscuity(dev, 1);
      bridge->enabledPromisc = TRUE;
      bridge->warnPromisc = FALSE;
      LOG(0, (KERN_NOTICE "bridge-%s: enabled promiscuous mode\n",
	      bridge->name));
   }
   if (rtnlLock) {
      rtnl_unlock();
   }
}


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

static void
VNetBridgeStopPromisc(VNetBridge *bridge,       // IN:
                      Bool rtnlLock)            // IN: Acquire RTNL lock
{
   struct net_device *dev = bridge->dev;

   if (rtnlLock) {
      rtnl_lock();
   }
   if (bridge->enabledPromisc && !bridge->wirelessAdapter) {
      dev_set_promiscuity(dev, -1);
      bridge->enabledPromisc = FALSE;
      LOG(0, (KERN_NOTICE "bridge-%s: disabled promiscuous mode\n",
	      bridge->name));
   }
   if (rtnlLock) {
      rtnl_unlock();
   }
}


/*
 *----------------------------------------------------------------------
 *
 * VNetBridgeCheckPromisc --
 *
 *      Make sure IFF_PROMISC on the peer interface is set.
 *
 *      This can be called periodically.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Hopefully enables promiscuous mode again if it should have been enabled.
 *
 *----------------------------------------------------------------------
 */

static INLINE_SINGLE_CALLER void
VNetBridgeCheckPromisc(VNetBridge *bridge)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
   if (bridge->enabledPromisc && !bridge->wirelessAdapter) {
      struct net_device *dev = bridge->dev;
      Bool devPromisc = (dev->flags & IFF_PROMISC) != 0;

      if (!devPromisc) {
         if (!bridge->warnPromisc) {
            bridge->warnPromisc = TRUE;
            LOG(0, (KERN_NOTICE "bridge-%s: someone disabled promiscuous mode\n"
	            "Your Ethernet driver is not compatible with VMware's bridged networking.\n",
                    bridge->name));
         }
         rtnl_lock();
         dev_set_promiscuity(dev, 0);
         rtnl_unlock();
      }
   }
#endif
}


#ifdef VMW_NETDEV_HAS_NET
/*
 *----------------------------------------------------------------------
 *
 * VNetBridgeInternalSetup --
 *
 *      Setup callback for our bridge internal device.  Nothing to do,
 *      generic code sets up everything we expect from device.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
VNetBridgeInternalSetup(struct net_device *net)
{
   /* Do nothing. */
}
#endif


/*
 *----------------------------------------------------------------------
 *
 * VNetBridgeDevCompatible --
 *
 *      Check whether bridge and network device are compatible.
 *
 * Results:
 *      Non-zero if device is good enough for bridge.  Zero otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static INLINE_SINGLE_CALLER int
VNetBridgeDevCompatible(VNetBridge *bridge,      // IN: Bridge
                        struct net_device *net)  // IN: Network device
{
#ifdef VMW_NETDEV_HAS_NET
   if (net->nd_net != bridge->internalDev->nd_net) {
      return 0;
   }
#endif
   return strcmp(net->name, bridge->name) == 0;
}


/*
 *----------------------------------------------------------------------
 *
 * VNetBridge_Create --
 *
 *      Create a bridge.  Allocates/initializes struct, registers
 *      with kernel for device state changes, connects to virtual
 *      hub, initializes port/jack, and creates a proc entry.
 *
 * Results:
 *      Errno. Also returns an allocated jack to connect to,
 *      NULL on error.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
VNetBridge_Create(const char *devName, // IN:  name of device (e.g., "eth0")
                  VNetPort **ret)      // OUT: port to virtual hub
{
   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);
   spin_lock_init(&bridge->historyLock);
   memcpy(bridge->name, devName, sizeof bridge->name);
   NULL_TERMINATE_STRING(bridge->name);

#ifdef VMW_NETDEV_HAS_NET
   bridge->internalDev = compat_alloc_netdev(0, "vmnetX", VNetBridgeInternalSetup);
   if (!bridge->internalDev) {
      retval = -ENOMEM;
      goto out;
   }
#endif

   /*
    * 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, TRUE);
   if (retval == -ENODEV) {
      LOG(1, (KERN_DEBUG "bridge-%s: peer interface %s not found, "
	      "will wait for it to come up\n",
	      bridge->name, devName));
      retval = 0;
   }
   if (retval != 0) {
      goto out;
   }

   /*
    * Initialize jack
    */

   bridge->port.id = id++;
   bridge->port.next = NULL;

   bridge->port.jack.peer = NULL;
   bridge->port.jack.numPorts = 1;
   VNetSnprintf(bridge->port.jack.name, sizeof bridge->port.jack.name,
		"bridge%u", 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 {
      bridge->port.jack.procEntry->read_proc = VNetBridgeProcRead;
      bridge->port.jack.procEntry->data = bridge;
   }

   /*
    * 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_STATIC_OUI0;
   bridge->port.paddr[1] = VMX86_STATIC_OUI1;
   bridge->port.paddr[2] = VMX86_STATIC_OUI2;

   bridge->port.fileOpRead = NULL;
   bridge->port.fileOpWrite = NULL;
   bridge->port.fileOpIoctl = NULL;
   bridge->port.fileOpPoll = NULL;

   *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);
      }
#ifdef VMW_NETDEV_HAS_NET
      if (bridge->internalDev) {
         compat_free_netdev(bridge->internalDev);
      }
#endif
      kfree(bridge);
   }
   return retval;
}


/*
 *----------------------------------------------------------------------
 *
 * VNetBridgeFree --
 *
 *      Disconnect the bridge, unregister from device state
 *      notifications, remove proc entry, and deallocate struct.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
VNetBridgeFree(VNetJack *this) // IN: jack to free
{
   VNetBridge *bridge = (VNetBridge*)this->private;

   if (bridge->dev != NULL) {
      VNetBridgeDown(bridge, TRUE);
   }

   unregister_netdevice_notifier(&bridge->notifier);

#ifdef VMW_NETDEV_HAS_NET
   if (bridge->internalDev) {
      compat_free_netdev(bridge->internalDev);
   }
#endif

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

   if (bridge->smac){
      SMAC_CleanupState(&(bridge->smac));
   }

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


/*
 *----------------------------------------------------------------------
 *
 * VNetCallSMACFunc --
 *
 *      Wrapper for SMAC functions.
 *
 * Results:
 *      Packet Status.
 *
 * Side effects:
 *      The skb buffer is freed if not succesfull otherwise it points to
 *      the clone.
 *
 *----------------------------------------------------------------------
 */

PacketStatus
VNetCallSMACFunc(struct SMACState *state, // IN: pointer to state
                 struct sk_buff **skb,    // IN/OUT: packet to process
                 void *startOfData,       // IN: points to start of data
                 SMACFunc func)           // IN: function to be called
{
   SMACPackets packets = { {0} };
   PacketStatus status;

   packets.orig.skb = *skb;
   packets.orig.startOfData = startOfData;

   status = func(state, &packets);
   if (status != PacketStatusForwardPacket) {
      dev_kfree_skb(*skb);
      return status;
   }

   if (packets.clone.skb) {
      dev_kfree_skb(*skb);
      *skb = packets.clone.skb;
   }
   return status;
}


/*
 *----------------------------------------------------------------------
 *
 * VNetBridgeReceiveFromVNet --
 *
 *      This jack is receiving a packet from a vnet.  This function
 *      sends down (i.e., out on the host net device) if the packet
 *      isn't destined for the host, and it sends up (i.e.,
 *      simulates a receive for the host) if the packet
 *      satisfies the host's packet filter.
 *
 *      When the function sends up it keeps a reference to the
 *      packet in a history list so that we can avoid handing
 *      a VM a copy of its own packet.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Frees skb.  Checks if host device is still using
 *      promiscuous mode.
 *
 *----------------------------------------------------------------------
 */

void
VNetBridgeReceiveFromVNet(VNetJack        *this, // IN: jack
                          struct sk_buff  *skb)  // IN: pkt to receive
{
   VNetBridge *bridge = (VNetBridge*)this->private;
   struct net_device *dev = bridge->dev;
   uint8 dest[ETH_ALEN];
   struct sk_buff *clone;

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

   if (!dev) {
      dev_kfree_skb(skb);
      return;
   }

   /*
    * skb might be freed by wireless code, so need to keep
    * a local copy of the MAC rather than a pointer to it.
    */

   memcpy(dest, SKB_2_DESTMAC(skb), ETH_ALEN);

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

   /*
    * Wireless processing
    */

   if (bridge->smac) {
      if (VNetCallSMACFunc(bridge->smac, &skb, skb->data,
                           SMAC_CheckPacketToHost) !=
          PacketStatusForwardPacket) {
         LOG(4, (KERN_NOTICE "bridge-%s: packet dropped .\n",
                 bridge->name));
	 return;
      }
   }

   /*
    * 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 + 4) {
      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 = dev_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->sk_wmem_alloc);
	 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);
	 } else {
	    dev_unlock_list();
	    dev_kfree_skb(clone);
	 }
      }
   }

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

   if (VNetPacketMatch(dest, dev->dev_addr, allMultiFilter, dev->flags)) {
      clone = skb_clone(skb, GFP_ATOMIC);
      if (clone) {
	 unsigned long flags;
	 int i;

	 atomic_inc(&clone->users);

	 clone->dev = dev;
	 clone->protocol = eth_type_trans(clone, dev);
	 spin_lock_irqsave(&bridge->historyLock, flags);
	 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];

	       /*
		* We special case 0 to avoid races with another thread on
		* another cpu wanting to use the 0 entry. This could happen
		* when we release the lock to free the former entry.
		* See bug 11231 for details.
		*/
	       if (i == 0) {
		  bridge->history[0] = clone;
	       } else {
		  bridge->history[i] = NULL;
	       }
	       if (s) {
	       	  spin_unlock_irqrestore(&bridge->historyLock, flags);
		  dev_kfree_skb(s);
		  spin_lock_irqsave(&bridge->historyLock, flags);
	       }
	    }
	 }
         spin_unlock_irqrestore(&bridge->historyLock, flags);

         /*
          * We used to cli() before calling netif_rx() here. It was probably
          * unneeded (as we never did it in netif.c, and the code worked). In
          * any case, now that we are using netif_rx_ni(), we should certainly
          * not do it, or netif_rx_ni() will deadlock on the cli() lock --hpreg
          */

	 netif_rx_ni(clone);
#	 if LOGLEVEL >= 4
	 do_gettimeofday(&vnetTime);
#	 endif
      }
   }

   // xxx;
   dev_kfree_skb(skb);
}


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

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


/*
 *----------------------------------------------------------------------
 *
 * VNetBridgePortsChanged --
 *
 *      The number of ports connected to this jack has change, react
 *      accordingly by starting/stopping promiscuous mode based on
 *      whether any peers exist.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Promiscuous mode may be started or stopped.
 *
 *----------------------------------------------------------------------
 */

void
VNetBridgePortsChanged(VNetJack *this) // IN: jack
{
   VNetBridge *bridge = (VNetBridge*)this->private;
   if (bridge->dev) {
      if (VNetGetAttachedPorts(this)) {
         VNetBridgeStartPromisc(bridge, TRUE);
      } else {
         VNetBridgeStopPromisc(bridge, TRUE);
      }
   }
}


/*
 *----------------------------------------------------------------------
 *
 * VNetBridgeIsBridged --
 *
 *      Reports if the bridged interface is up or down.
 *
 * Results:
 *      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) // IN: jack
{
   VNetBridge *bridge = (VNetBridge*)this->private;
   if (bridge->dev) {
      return 2;
   } else {
      return 1;
   }
}

/*
 *----------------------------------------------------------------------
 *
 * VNetBridgeIsDeviceWireless --
 *
 *      Check if the device is a wireless adapter, depending on the version
 *      of the wireless extension present in the kernel.
 *
 * Results:
 *      TRUE if the device is wireless, FALSE otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static Bool
VNetBridgeIsDeviceWireless(struct net_device *dev) //IN: sock
{
#ifdef CONFIG_WIRELESS_EXT
   return dev->wireless_handlers != NULL;
#elif !defined CONFIG_NET_RADIO && !defined CONFIG_WLAN_80211
   return FALSE;
#elif WIRELESS_EXT > 19
   return dev->wireless_handlers != NULL;
#elif WIRELESS_EXT > 12
   return dev->wireless_handlers != NULL || dev->get_wireless_stats != NULL;
#else
   return dev->get_wireless_stats != NULL;
#endif
}

/*
 *----------------------------------------------------------------------
 *
 * VNetBridgeUp --
 *
 *      Bring a bridge up.  Gets peer's device structure, verifies
 *      that interface is up, checks the header length,
 *      allocates a socket, adds a packet handler to the network
 *      stack, and then places the peer's device in promiscuous
 *      mode.
 *
 * Results:
 *      errno.
 *
 * Side effects:
 *      Bridging may be brought up with a peer interface.
 *
 *----------------------------------------------------------------------
 */

int
VNetBridgeUp(VNetBridge *bridge, // IN: bridge struct
             Bool rtnlLock)      // IN: acquire RTNL lock
{
   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);
   LOG(2, (KERN_DEBUG "bridge-%s: got dev %p\n",
	   bridge->name, bridge->dev));
   if (bridge->dev == NULL) {
      dev_unlock_list();
      retval = -ENODEV;
      goto out;
   }
   if (!(bridge->dev->flags & IFF_UP)) {
      LOG(2, (KERN_DEBUG "bridge-%s: interface %s is not up\n",
              bridge->name, bridge->dev->name));
      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 && bridge->dev->hard_header_len != ETH_HLEN + 4) {
      LOG(1, (KERN_DEBUG "bridge-%s: can't bridge with %s, bad header length %d\n", 
	      bridge->name, bridge->dev->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 dev_kfree_skb().
    * (The alternative is to support the callback.)
    */

   bridge->sk = compat_sk_alloc(bridge, GFP_ATOMIC);
   if (bridge->sk == NULL) {
      dev_unlock_list();
      retval = -ENOMEM;
      goto out;
   }
   SET_SK_DEAD(bridge->sk, 1);

   bridge->wirelessAdapter = VNetBridgeIsDeviceWireless(bridge->dev);

   /*
    * If it is a wireless adapter initialize smac struct.
    */

   if (bridge->wirelessAdapter) {

      LOG(1, (KERN_NOTICE "bridge-%s: is a Wireless Adapter\n", bridge->name));
      SMAC_InitState(&(bridge->smac));
      if (bridge->smac) {
         /*
          * Store the MAC address of the adapter
          */

         SMAC_SetMac(bridge->smac, bridge->dev->dev_addr);
      }
   }

   /*
    * Link up with the peer device by adding a
    * packet handler to the networking stack.
    */

   bridge->pt.func = VNetBridgeReceiveFromDev;
   bridge->pt.type = htons(ETH_P_ALL);
   bridge->pt.dev = bridge->dev;

   /*
    * TurboLinux10 uses 2.6.0-test5, which we do not support, so special case it,
    * 2.6.0 with tl_kernel_version_h is 2.6.0-pre5...
    */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) || \
   (LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 0) && defined(__tl_kernel_version_h__))
   bridge->pt.data = bridge->sk;
#else
   bridge->pt.af_packet_priv = bridge->sk;
#endif
   bridge->enabledPromisc = FALSE;
   bridge->warnPromisc = FALSE;
   dev_add_pack(&bridge->pt);
   dev_unlock_list();

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

   down(&vnetStructureSemaphore);
   if (VNetGetAttachedPorts(&bridge->port.jack)) {
      VNetBridgeStartPromisc(bridge, rtnlLock);
   }
   up(&vnetStructureSemaphore);

   /*
    * Finish up
    */

   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.  Stops promiscuous mode, removes the
 *      packet handler from the network stack, and frees the
 *      socket.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Bridging is brought down.
 *
 *----------------------------------------------------------------------
 */

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

   VNetBridgeStopPromisc(bridge, rtnlLock);
   if (bridge->smac){
      SMAC_SetMac(bridge->smac, NULL);
   }
   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.  The function brings
 *      the bridge up/down in response to changes in the peer device.
 *
 * Results:
 *      NOTIFY_DONE
 *
 * Side effects:
 *      Promiscuous mode is changed when bridge brought up/down.
 *
 *-----------------------------------------------------------------------------
 */

int
VNetBridgeNotify(struct notifier_block *this, // IN: callback data (bridge)
                 u_long msg,                  // IN: type of event
                 void *data)                  // IN: device pertaining to event
{
   VNetBridge *bridge = list_entry(this, VNetBridge, notifier);
   struct net_device *dev = (struct net_device *) data;

   switch (msg) {
   case NETDEV_UNREGISTER:
      LOG(2, (KERN_DEBUG "bridge-%s: interface %s is unregistering\n",
              bridge->name, dev->name));
      if (dev == bridge->dev) {
         /* This should never happen --hpreg */
         LOG(0, (KERN_WARNING "bridge-%s: interface %s unregistered without "
                 "going down! Disabling the bridge\n", bridge->name,
                 dev->name));
         VNetBridgeDown(bridge, FALSE);
      }
      break;

   case NETDEV_DOWN:
      LOG(2, (KERN_DEBUG "bridge-%s: interface %s is going down\n",
              bridge->name, dev->name));
      if (dev == bridge->dev) {
         LOG(1, (KERN_DEBUG "bridge-%s: disabling the bridge\n",
                 bridge->name));
         VNetBridgeDown(bridge, FALSE);
      }
      break;

   case NETDEV_UP:
      LOG(2, (KERN_DEBUG "bridge-%s: interface %s is going up\n",
              bridge->name, dev->name));
      if (bridge->dev == NULL && VNetBridgeDevCompatible(bridge, dev)) {
         int errno;

         LOG(1, (KERN_DEBUG "bridge-%s: enabling the bridge\n", bridge->name));
	 errno = VNetBridgeUp(bridge, FALSE);
	 switch (-errno) {
	 case 0:
	    break;

	 case ENODEV:
            LOG(0, (KERN_WARNING "bridge-%s: interface %s not found or not "
                    "up\n", bridge->name, dev->name));
	    break;

	 case EINVAL:
            LOG(0, (KERN_WARNING "bridge-%s: interface %s is not a valid "
                    "Ethernet interface\n", bridge->name, dev->name));
	    break;

	 case ENOMEM:
            LOG(0, (KERN_WARNING "bridge-%s: failed to allocate memory\n",
                    bridge->name));
	    break;

	 default:
            /* This should never happen --hpreg */
            LOG(0, (KERN_WARNING "bridge-%s: failed to enable the bridge to "
                    "interface %s (error %d)\n", bridge->name, dev->name,
                    -errno));
            break;
	 }
      }
      break;

   default:
      LOG(2, (KERN_DEBUG "bridge-%s: interface %s is sending notification "
              "0x%lx\n", bridge->name, dev->name, msg));
      break;
   }

   return NOTIFY_DONE;
}


/*
 *----------------------------------------------------------------------
 *
 * VNetBridgeComputeHeaderPos --
 *
 *      Compute correct position for UDP/TCP header.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      transport header pointer updated to point to the tcp/udp header.
 *
 *----------------------------------------------------------------------
 */

static INLINE_SINGLE_CALLER void
VNetBridgeComputeHeaderPos(struct sk_buff *skb) // IN: buffer to examine
{
   /* Maybe some kernel gets it right... */
   if (compat_skb_network_header_len(skb)) {
      return;
   }
   switch (be16_to_cpu(skb->protocol)) {
      case ETH_P_IP: {
            struct iphdr *ipHdr = compat_skb_ip_header(skb);

            compat_skb_set_transport_header(skb, compat_skb_network_offset(skb) +
                                                 ipHdr->ihl * 4);
         }
	 return;
      default:
         LOG(3, (KERN_DEBUG "Unknown EII protocol %04X: csum at %d\n",
		 be16_to_cpu(skb->protocol), compat_skb_csum_offset(skb)));
	 break;
   }
   return;
}


/*
 * We deal with three types of kernels:
 * New kernels: skb_shinfo() has gso_size member, and there is
 *              skb_gso_segment() helper to split GSO skb into flat ones.
 * Older kernels: skb_shinfo() has tso_size member, and there is
 *                no helper.
 * Oldest kernels: without any segmentation offload support.
 */
#if defined(NETIF_F_GSO) || LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
#define VNetBridgeIsGSO(skb) skb_shinfo(skb)->gso_size
#define VNetBridgeGSOSegment(skb) skb_gso_segment(skb, 0)
#elif defined(NETIF_F_TSO)
#define VNetBridgeIsGSO(skb) skb_shinfo(skb)->tso_size


/*
 *----------------------------------------------------------------------
 *
 * VNetBridgeGSOSegment --
 *
 *	Split a large TCP/IPv4 sk_buff into multiple sk_buffs of
 *	size skb_shinfo(skb)->tso_size
 *	Called from VNetBridgeSendLargePacket().
 *
 * Results:
 *	List of skbs created.
 *
 * Side effects:
 *	The incoming packet is split into multiple packets.
 *
 *----------------------------------------------------------------------
 */

static struct sk_buff *
VNetBridgeGSOSegment(struct sk_buff *skb)        // IN: packet to split
{
   struct sk_buff *segs = NULL;
   struct sk_buff **next = &segs;
   int bytesPerPacket, bytesLeft;
   int macHdrLen, ipHdrLen, tcpHdrLen, allHdrLen;
   int curByteOffset;
   uint16 ipID;
   uint32 seqNo;

   if (((struct ethhdr *)compat_skb_mac_header(skb))->h_proto != htons(ETH_P_IP)) {
      return ERR_PTR(-EPFNOSUPPORT);
   }

   if (compat_skb_ip_header(skb)->protocol != IPPROTO_TCP) {
      return ERR_PTR(-EPROTONOSUPPORT);
   }

   macHdrLen = compat_skb_network_header(skb) - compat_skb_mac_header(skb);
   ipHdrLen = compat_skb_ip_header(skb)->ihl << 2;
   tcpHdrLen = compat_skb_tcp_header(skb)->doff << 2;
   allHdrLen = macHdrLen + ipHdrLen + tcpHdrLen;

   ipID = ntohs(compat_skb_ip_header(skb)->id);
   seqNo = ntohl(compat_skb_tcp_header(skb)->seq);

   /* Host TCP stack populated this (MSS) for the host NIC driver */
   bytesPerPacket = skb_shinfo(skb)->tso_size;

   bytesLeft = skb->len - allHdrLen;
   curByteOffset = allHdrLen;

   while (bytesLeft) {
      struct sk_buff *newSkb;
      int payloadSize = (bytesLeft < bytesPerPacket) ? bytesLeft : bytesPerPacket;

      newSkb = dev_alloc_skb(payloadSize + allHdrLen + NET_IP_ALIGN);
      if (!newSkb) {
         while (segs) {
            newSkb = segs;
            segs = segs->next;
            newSkb->next = NULL;
            dev_kfree_skb(newSkb);
         }
         return ERR_PTR(-ENOMEM);
      }
      skb_reserve(newSkb, NET_IP_ALIGN);
      newSkb->dev = skb->dev;
      newSkb->protocol = skb->protocol;
      newSkb->pkt_type = skb->pkt_type;
      newSkb->ip_summed = VM_CHECKSUM_PARTIAL;

      /*
       * MAC+IP+TCP copy
       * This implies that ALL fields in the IP and TCP headers are copied from
       * the original skb. This is convenient: we'll only fix up fields that
       * need to be changed below
       */
      memcpy(skb_put(newSkb, allHdrLen), skb->data, allHdrLen);

      /* Fix up pointers to different layers */
      compat_skb_reset_mac_header(newSkb);
      compat_skb_set_network_header(newSkb, macHdrLen);
      compat_skb_set_transport_header(newSkb, macHdrLen + ipHdrLen);

      /* Payload copy */
      skb_copy_bits(skb, curByteOffset, compat_skb_tail_pointer(newSkb), payloadSize);
      skb_put(newSkb, payloadSize);

      curByteOffset+=payloadSize;
      bytesLeft -= payloadSize;

      /* Fix up IP hdr */
      compat_skb_ip_header(newSkb)->tot_len = htons(payloadSize + tcpHdrLen + ipHdrLen);
      compat_skb_ip_header(newSkb)->id = htons(ipID);
      compat_skb_ip_header(newSkb)->check = 0;
      /* Recompute new IP checksum */
      compat_skb_ip_header(newSkb)->check =
              ip_fast_csum(compat_skb_network_header(newSkb),
                           compat_skb_ip_header(newSkb)->ihl);

      /* Fix up TCP hdr */
      compat_skb_tcp_header(newSkb)->seq = htonl(seqNo);
      /* Clear FIN/PSH if not last packet */
      if (bytesLeft > 0) {
         compat_skb_tcp_header(newSkb)->fin = 0;
         compat_skb_tcp_header(newSkb)->psh = 0;
      }
      /* Recompute partial TCP checksum */
      compat_skb_tcp_header(newSkb)->check =
         ~csum_tcpudp_magic(compat_skb_ip_header(newSkb)->saddr,
                            compat_skb_ip_header(newSkb)->daddr,
                            payloadSize+tcpHdrLen, IPPROTO_TCP, 0);

      /* Offset of field */
      newSkb->csum = offsetof(struct tcphdr, check);

      /* Join packet to the list of segments */
      *next = newSkb;
      next = &newSkb->next;

      /* Bump up our counters */
      ipID++;
      seqNo += payloadSize;

   }
   return segs;
}
#else
#define VNetBridgeIsGSO(skb) (0)
#define VNetBridgeGSOSegment(skb) ERR_PTR(-ENOSYS)
#endif


/*
 *----------------------------------------------------------------------
 *
 * VNetBridgeSendLargePacket --
 *
 *      Split and send a large TCP/IPv4 sk_buff into multiple sk_buffs which
 *      fits on wire.  Called from VNetBridgeReceiveFromDev(), which is a
 *	protocol handler called from the bottom half, so steady as she
 *	goes...
 *
 *	skb passed in is deallocated by function.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      The incoming packet is split into multiple packets and sent to the
 *      vnet.
 *
 *----------------------------------------------------------------------
 */

void
VNetBridgeSendLargePacket(struct sk_buff *skb,        // IN: packet to split
                          VNetBridge *bridge)         // IN: bridge
{
   struct sk_buff *segs;

   segs = VNetBridgeGSOSegment(skb);
   dev_kfree_skb(skb);
   if (IS_ERR(segs)) {
      LOG(1, (KERN_DEBUG "bridge-%s: cannot segment packet: error %ld\n",
              bridge->name, PTR_ERR(segs)));
      return;
   }

   while (segs) {
      struct sk_buff *newSkb;

      newSkb = segs;
      segs = newSkb->next;
      newSkb->next = NULL;
      /* Send it along */
      skb = newSkb;
      VNetSend(&bridge->port.jack, newSkb);
   }
}


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

#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) && \
    !defined(VMW_TL10S64_WORKAROUND)
int
VNetBridgeReceiveFromDev(struct sk_buff *skb,         // IN: packet to receive
                         struct net_device *dev,      // IN: unused
                         struct packet_type *pt)      // IN: pt (pointer to bridge)
#else
int
VNetBridgeReceiveFromDev(struct sk_buff *skb,         // IN: packet to receive
                         struct net_device *dev,      // IN: unused
                         struct packet_type *pt,      // IN: pt (pointer to bridge)
                         struct net_device *real_dev) // IN: real device, unused
#endif
{
   VNetBridge *bridge = list_entry(pt, VNetBridge, pt);
   int i;
   unsigned long flags;

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

   /*
    * Check is this is a packet that we sent up to the host, and if
    * so then don't bother to receive the packet.
    */

   spin_lock_irqsave(&bridge->historyLock, flags);
   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;
	 spin_unlock_irqrestore(&bridge->historyLock, flags);
	 dev_kfree_skb(s);
	 LOG(3, (KERN_DEBUG "bridge-%s: receive %d self %d\n",
		 bridge->name, (int) skb->len, i));
	 dev_kfree_skb(skb);
	 return 0;
      }
   }
   spin_unlock_irqrestore(&bridge->historyLock, flags);

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

   if (bridge->smac) {
      if (VNetCallSMACFunc(bridge->smac, &skb, compat_skb_mac_header(skb),
                           SMAC_CheckPacketFromHost) !=
          PacketStatusForwardPacket) {
         LOG(4, (KERN_NOTICE "bridge-%s: packet dropped .\n",
                 bridge->name));
	 return 0;
      }
   }

#ifdef KERNEL_2_3_15
   skb = skb_share_check(skb, GFP_ATOMIC);
   if (!skb) {
      return 0;
   }
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 4)
   /*
    * Unbelievable... Caller sets h.raw = nh.raw before invoking us...
    */
   VNetBridgeComputeHeaderPos(skb);
#endif
#endif

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

   /*
    * If this is a large packet, chop chop chop (if supported)...
    */
   if (VNetBridgeIsGSO(skb)) {
      VNetBridgeSendLargePacket(skb, bridge);
   } else {
      VNetSend(&bridge->port.jack, skb);
   }

   return 0;
}


/*
 *----------------------------------------------------------------------
 *
 * VNetBridgeProcRead --
 *
 *      Callback for read operation on this bridge entry in vnets proc fs.
 *
 * Results:
 *      Length of read operation.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

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: (unused) offset of read into the file
                   int      count,  // IN: (unused) maximum number of bytes to read
                   int     *eof,    // OUT: TRUE if there is nothing more to read
                   void    *data)   // IN: client data - pointer to bridge
{
   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;
}