/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
 * Copyright (c) 2009 University of Washington
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation;
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef TAP_BRIDGE_H
#define TAP_BRIDGE_H

#include <cstring>
#include "ns3/address.h"
#include "ns3/net-device.h"
#include "ns3/node.h"
#include "ns3/callback.h"
#include "ns3/packet.h"
#include "ns3/traced-callback.h"
#include "ns3/event-id.h"
#include "ns3/nstime.h"
#include "ns3/data-rate.h"
#include "ns3/ptr.h"
#include "ns3/mac48-address.h"
#include "ns3/unix-fd-reader.h"

namespace ns3 {

/**
 * \ingroup tap-bridge
 * Class to perform the actual reading from a socket
 */
class TapBridgeFdReader : public FdReader
{
private:
  FdReader::Data DoRead (void);
};

class Node;

/**
 * \ingroup tap-bridge
 * 
 * \brief A bridge to make it appear that a real host process is connected to 
 * an ns-3 net device.
 *
 * The Tap Bridge lives in a kind of a gray world somewhere between a
 * Linux host and an ns-3 bridge device.  From the Linux perspective,
 * this code appears as the user mode handler for a Tap net device.  That
 * is, when the Linux host writes to a /dev/tap device (that is either
 * manually or automatically created depending on basic operating mode 
 * -- more on this later), the write is redirected into the TapBridge that
 * lives in the ns-3 world; and from this perspective, becomes a read.
 * In other words, a Linux process writes a packet to a tap device and
 * this packet is redirected to an ns-3 process where it is received by
 * the TapBridge as a result of a read operation there.  The TapBridge
 * then sends the packet to the ns-3 net device to which it is bridged.
 * In the other direction, a packet received by an ns-3 net device is
 * bridged to the TapBridge (it appears via a callback from that net
 * device.  The TapBridge then takes that packet and writes it back to
 * the host using the Linux TAP mechanism.  This write to the device will
 * then appear to the Linux host as if a packet has arrived on its
 * device.
 * 
 * The upshot is that the Tap Bridge appears to bridge a tap device on a
 * Linux host in the "real world" to an ns-3 net device in the simulation
 * and make is appear that a ns-3 net device is actually installed in the
 * Linux host.  In order to do this on the ns-3 side, we need a "ghost
 * node" in the simulation to hold the bridged ns-3 net device and the
 * TapBridge.  This node should not actually do anything else in the
 * simulation since its job is simply to make the net device appear in
 * Linux.  This is not just arbitrary policy, it is because:
 *
 * - Bits sent to the Tap Bridge from higher layers in the ghost node (using
 *   the TapBridge Send() method) are completely ignored.  The Tap Bridge is 
 *   not, itself, connected to any network, neither in Linux nor in ns-3;
 * - The bridged ns-3 net device is has had its receive callback disconnected
 *   from the ns-3 node and reconnected to the Tap Bridge.  All data received 
 *   by a bridged device will be sent to the Linux host and will not be 
 *   received by the node.  From the perspective of the ghost node, you can 
 *   send over this device but you cannot ever receive.
 *
 * Of course, if you understand all of the issues you can take control of
 * your own destiny and do whatever you want -- we do not actively
 * prevent you from using the ghost node for anything you decide.  You
 * will be able to perform typical ns-3 operations on the ghost node if
 * you so desire.  The internet stack, for example, must be there and
 * functional on that node in order to participate in IP address
 * assignment and global routing.  However, as mentioned above,
 * interfaces talking any Tap Bridge or associated bridged net devices
 * will not work completely.  If you understand exactly what you are
 * doing, you can set up other interfaces and devices on the ghost node
 * and use them; or take advantage of the operational send side of the
 * bridged devices to create traffic generators.  We generally recommend
 * that you treat this node as a ghost of the Linux host and leave it to
 * itself, though.
 */
class TapBridge : public NetDevice
{
public:
  /**
   * \brief Get the type ID.
   * \return the object TypeId
   */
  static TypeId GetTypeId (void);

  /**
   * Enumeration of the operating modes supported in the class.
   *
   */
  enum Mode {
    ILLEGAL,         /**< mode not set */
    CONFIGURE_LOCAL, /**< ns-3 creates and configures tap device */
    USE_LOCAL,       /**< ns-3 uses a pre-created tap, without configuring it */
    USE_BRIDGE, /**< ns-3 uses a pre-created tap, and bridges to a bridging net device */
  };

  TapBridge ();
  virtual ~TapBridge ();

  /**
   * \brief Get the bridged net device.
   *
   * The bridged net device is the ns-3 device to which this bridge is connected,
   *
   * \returns the bridged net device.
   */
  Ptr<NetDevice> GetBridgedNetDevice (void);

  /**
   * \brief Set the ns-3 net device to bridge.
   *
   * This method tells the bridge which ns-3 net device it should use to connect
   * the simulation side of the bridge.
   *
   * \param bridgedDevice device to set
   *
   * \attention The ns-3 net device that is being set as the device must have an
   * an IP address assigned to it before the simulation is run.  This address 
   * will be used to set the hardware address of the host Linux device.
   */
  void SetBridgedNetDevice (Ptr<NetDevice> bridgedDevice);

  /**
   * \brief Set a start time for the device.
   *
   * The tap bridge consumes a non-trivial amount of time to start.  It starts
   * up in the context of a scheduled event to ensure that all configuration
   * has been completed before extracting the configuration (IP addresses, etc.)
   * In order to allow a more reasonable start-up sequence than a thundering 
   * herd of devices, the time at which each device starts is also configurable
   * bot via the Attribute system and via this call.
   *
   * \param tStart the start time
   */
  void Start (Time tStart);

  /**
   * Set a stop time for the device.
   *
   * @param tStop the stop time
   *
   * \see TapBridge::Start
   */
  void Stop (Time tStop);

  /**
   * Set the operating mode of this device.
   *
   * \param mode The operating mode of this device.
   */
  void SetMode (TapBridge::Mode mode);

  /**
   * Get the operating mode of this device.
   *
   * \returns The operating mode of this device.
   */
  TapBridge::Mode  GetMode (void);

  //
  // The following methods are inherited from NetDevice base class and are
  // documented there.
  //
  virtual void SetIfIndex (const uint32_t index);
  virtual uint32_t GetIfIndex (void) const;
  virtual Ptr<Channel> GetChannel (void) const;
  virtual void SetAddress (Address address);
  virtual Address GetAddress (void) const;
  virtual bool SetMtu (const uint16_t mtu);
  virtual uint16_t GetMtu (void) const;
  virtual bool IsLinkUp (void) const;
  virtual void AddLinkChangeCallback (Callback<void> callback);
  virtual bool IsBroadcast (void) const;
  virtual Address GetBroadcast (void) const;
  virtual bool IsMulticast (void) const;
  virtual Address GetMulticast (Ipv4Address multicastGroup) const;
  virtual bool IsPointToPoint (void) const;
  virtual bool IsBridge (void) const;
  virtual bool Send (Ptr<Packet> packet, const Address& dest, uint16_t protocolNumber);
  virtual bool SendFrom (Ptr<Packet> packet, const Address& source, const Address& dest, uint16_t protocolNumber);
  virtual Ptr<Node> GetNode (void) const;
  virtual void SetNode (Ptr<Node> node);
  virtual bool NeedsArp (void) const;
  virtual void SetReceiveCallback (NetDevice::ReceiveCallback cb);
  virtual void SetPromiscReceiveCallback (NetDevice::PromiscReceiveCallback cb);
  virtual bool SupportsSendFrom () const;
  virtual Address GetMulticast (Ipv6Address addr) const;

protected:
  /**
   * Call out to a separate process running as suid root in order to get our
   * tap device created.  We do this to avoid having the entire simulation 
   * running as root.  If this method returns, we'll have a socket waiting 
   * for us in m_sock that we can use to talk to the tap device.
   */
  virtual void DoDispose (void);

  /**
   * Receives a packet from a bridged Device
   * \param device the originating port
   * \param packet the received packet
   * \param protocol the packet protocol (e.g., Ethertype)
   * \param src the packet source
   * \param dst the packet destination
   * \param packetType the packet type (e.g., host, broadcast, etc.)
   * \returns true on success
   */
  bool ReceiveFromBridgedDevice (Ptr<NetDevice> device, Ptr<const Packet> packet, uint16_t protocol,
                                 Address const &src, Address const &dst, PacketType packetType);

  /**
   * Receives a packet from a bridged Device
   * \param device the originating port
   * \param packet the received packet
   * \param protocol the packet protocol (e.g., Ethertype)
   * \param src the packet source
   * \returns true on success
   */
  bool DiscardFromBridgedDevice (Ptr<NetDevice> device, Ptr<const Packet> packet, uint16_t protocol,
                                 Address const &src);

private:

  /**
   * Call out to a separate process running as suid root in order to get our
   * tap device created.  We do this to avoid having the entire simulation 
   * running as root.  If this method returns, we'll have a socket waiting 
   * for us in m_sock that we can use to talk to the tap device.
   */
  void CreateTap (void);

  /**
   * Spin up the device
   */
  void StartTapDevice (void);

  /**
   * Tear down the device
   */
  void StopTapDevice (void);

  /**
   * Callback to process packets that are read
   */
  void ReadCallback (uint8_t *buf, ssize_t len);

  /**
   * Forward a packet received from the tap device to the bridged ns-3 
   * device
   *
   * \param buf A character buffer containing the actual packet bits that were
   *            received from the host.
   * \param len The length of the buffer.
   */
  void ForwardToBridgedDevice (uint8_t *buf, ssize_t len);

  /**
   * The host we are bridged to is in the evil real world.  Do some sanity
   * checking on a received packet to make sure it isn't too evil for our
   * poor naive virginal simulator to handle.
   *
   * \param packet The packet we received from the host, and which we need 
   *               to check.
   * \param src    A pointer to the data structure that will get the source
   *               MAC address of the packet (extracted from the packet Ethernet
   *               header).
   * \param dst    A pointer to the data structure that will get the destination
   *               MAC address of the packet (extracted from the packet Ethernet 
   *               header).
   * \param type   A pointer to the variable that will get the packet type from 
   *               either the Ethernet header in the case of type interpretation
   *               (DIX framing) or from the 802.2 LLC header in the case of 
   *               length interpretation (802.3 framing).
   * \returns the packet, or null if the packet has been filtered.
   */
  Ptr<Packet> Filter (Ptr<Packet> packet, Address *src, Address *dst, uint16_t *type);

  /**
   * Notifies that the link is up and ready.
   */
  void NotifyLinkUp (void);

  /**
   * Callback used to hook the standard packet receive callback of the TapBridge
   * ns-3 net device.  This is never called, and therefore no packets will ever
   * be received forwarded up the IP stack on the ghost node through this device.
   */
  NetDevice::ReceiveCallback m_rxCallback;

  /**
   * Callback used to hook the promiscuous packet receive callback of the TapBridge
   * ns-3 net device.  This is never called, and therefore no packets will ever
   * be received forwarded up the IP stack on the ghost node through this device.
   *
   * Note that we intercept the similar callback in the bridged device in order to
   * do the actual bridging between the bridged ns-3 net device and the Tap device
   * on the host.
   */
  NetDevice::PromiscReceiveCallback m_promiscRxCallback;

  /**
   * Pointer to the (ghost) Node to which we are connected.
   */
  Ptr<Node> m_node;


  /**
   * The ns-3 interface index of this TapBridge net device.
   */
  uint32_t m_ifIndex;

  /**
   * The common mtu to use for the net devices
   */
  uint16_t m_mtu;

  /**
   * The socket (actually interpreted as fd) to use to talk to the Tap device on
   * the real internet host.
   */
  int m_sock;

  /**
   * The ID of the ns-3 event used to schedule the start up of the underlying
   * host Tap device and ns-3 read thread.
   */
  EventId m_startEvent;

  /**
   * The ID of the ns-3 event used to schedule the tear down of the underlying
   * host Tap device and ns-3 read thread.
   */
  EventId m_stopEvent;

  /**
   * Includes the ns-3 read thread used to do blocking reads on the fd
   * corresponding to the host device.
   */
  Ptr<TapBridgeFdReader> m_fdReader;

  /**
   * The operating mode of the bridge.  Tells basically who creates and
   * configures the underlying network tap.
   */
  Mode m_mode;

  /**
   * The (unused) MAC address of the TapBridge net device.  Since the TapBridge
   * is implemented as a ns-3 net device, it is required to implement certain
   * functionality.  In this case, the TapBridge is automatically assigned a
   * MAC address, but it is not used.
   */
  Mac48Address m_address;

  /**
   * Time to start spinning up the device
   */
  Time m_tStart;

  /**
   * Time to start tearing down the device
   */
  Time m_tStop;

  /**
   * The name of the device to create on the host.  If the device name is the
   * empty string, we allow the host kernel to choose a name.
   */
  std::string m_tapDeviceName;

  /**
   * The IP address to use as the device default gateway on the host.
   */
  Ipv4Address m_tapGateway;

  /**
   * The IP address to use as the device IP on the host.
   */
  Ipv4Address m_tapIp;
  /**
   * The MAC address to use as the hardware address on the host; only used
   * in UseLocal mode.  This value comes from the MAC
   * address assigned to the bridged ns-3 net device and matches the MAC 
   * address of the underlying network TAP which we configured to have the 
   * same value.
   */
  Mac48Address m_tapMac;

  /**
   * The network mask to assign to the device created on the host.
   */
  Ipv4Mask m_tapNetmask;

  /**
   * The ns-3 net device to which we are bridging.
   */
  Ptr<NetDevice> m_bridgedDevice;
  /**
   * Whether the MAC address of the underlying ns-3 device has already been
   * rewritten is stored in this variable (for UseLocal/UseBridge mode only).
   */
  bool m_ns3AddressRewritten;

  /**
   * A 64K buffer to hold packet data while it is being sent.
   */
  uint8_t *m_packetBuffer;

  /**
   * a copy of the node id so the read thread doesn't have to GetNode() in
   * in order to find the node ID.  Thread unsafe reference counting in 
   * multithreaded apps is not a good thing.
   */
  uint32_t m_nodeId;

  /**
   * Flag indicating whether or not the link is up.  In this case,
   * whether or not ns-3 is connected to the underlying TAP device
   * with a file descriptor.
   */
  bool m_linkUp;

  /**
   * Callbacks to fire if the link changes state (up or down).
   */
  TracedCallback<> m_linkChangeCallbacks;
};

} // namespace ns3

#endif /* TAP_BRIDGE_H */
