checkpoint tap development
authorCraig Dowell <craigdo@ee.washington.edu>
Tue, 27 Jan 2009 12:36:46 -0800
changeset 4163 8c48682b3d42
parent 4126 0ba0346d655b
child 4164 1f6ae48061a9
checkpoint tap development
examples/csma-tap-bridge.cc
examples/wscript
src/devices/tap-bridge/tap-bridge.cc
src/devices/tap-bridge/tap-bridge.h
src/devices/tap-bridge/tap-encode-decode.cc
src/devices/tap-bridge/tap-encode-decode.h
src/devices/tap-bridge/tap-sock-creator.cc
src/devices/tap-bridge/tap.h
src/devices/tap-bridge/waf
src/devices/tap-bridge/wscript
src/helper/tap-bridge-helper.cc
src/helper/tap-bridge-helper.h
src/helper/wscript
src/wscript
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/csma-tap-bridge.cc	Tue Jan 27 12:36:46 2009 -0800
@@ -0,0 +1,157 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * 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
+ */
+
+// Network topology
+//
+//  +----------+                           +----------+  
+//  | external |                           | external |  
+//  |  Linux   |                           |  Linux   |  
+//  |   Host   |                           |   Host   |  
+//  +----------+                           +----------+
+//       |           n0             n3           |
+//       |       +--------+     +--------+       |
+//       +-------|  tap   |     |  tap   |-------+
+//               | bridge | ... | bridge |
+//               +--------+     +--------+
+//               |  CSMA  |     |  CSMA  |
+//               +--------+     +--------+
+//                   |              |
+//                   |              |
+//                   |    n1  n2    |
+//                   |     |   |    |
+//                   ================
+//                          LAN
+
+#include <iostream>
+#include <fstream>
+
+#include "ns3/simulator-module.h"
+#include "ns3/node-module.h"
+#include "ns3/core-module.h"
+#include "ns3/helper-module.h"
+
+using namespace ns3;
+
+NS_LOG_COMPONENT_DEFINE ("CsmaTapBridgeExample");
+
+int 
+main (int argc, char *argv[])
+{
+  //
+  // Users may find it convenient to turn on explicit debugging
+  // for selected modules; the below lines suggest how to do this
+  //
+#if 0 
+  LogComponentEnable ("CsmaOneSubnetExample", LOG_LEVEL_INFO);
+#endif
+
+  //
+  // Make the random number generators generate reproducible results.
+  //
+  RandomVariable::UseGlobalSeed (1, 1, 2, 3, 5, 8);
+
+  //
+  // Allow the user to override any of the defaults and the above Bind() at
+  // run-time, via command-line arguments
+  //
+  CommandLine cmd;
+  cmd.Parse (argc, argv);
+
+  //
+  // Create the nodes required by the topology (shown above).
+  //
+  NS_LOG_INFO ("Create nodes.");
+  NodeContainer nodes;
+  nodes.Create (4);
+
+  //
+  // Create and install the network.
+  //
+  NS_LOG_INFO ("Build Topology");
+  CsmaHelper csma;
+  csma.SetChannelAttribute ("DataRate", DataRateValue (5000000));
+  csma.SetChannelAttribute ("Delay", TimeValue (MilliSeconds (2)));
+
+  NetDeviceContainer devices = csma.Install (nodes);
+
+  InternetStackHelper internet;
+  internet.Install (nodes);
+
+  //
+  // Add the tap bridges to nodes zero and one to enable external Linux 
+  // processes to talk to the CSMA devices.
+  //
+  TapBridgeHelper bridge;
+  NetDeviceContainer bridgeDevices;
+  bridgeDevices.Add (bridge.Install (nodes.Get (0), devices.Get (0)));
+  bridgeDevices.Add (bridge.Install (nodes.Get (3), devices.Get (3)));
+
+  //
+  // We've got the "hardware" in place.  Now add IP addresses.  We mjust not
+  // add IP addresses to the devices that we bridged using the TapBridgeHelper
+  // above.  The IP addresses are added to the bridge itself and are propagated
+  // to the tap device on the host.  We do need to add IP addresses to the CSMA
+  // devices that are attached to the nodes that are entirely contained within
+  // the simulation (not connected to any other external host).
+  //
+  NS_LOG_INFO ("Assign IP Addresses.");
+  NetDeviceContainer ndc;
+  ndc.Add (bridgeDevices.Get (0));
+  ndc.Add (devices.Get (1));
+  ndc.Add (devices.Get (2));
+  ndc.Add (bridgeDevices.Get (0));
+          
+  Ipv4AddressHelper ipv4;
+  ipv4.SetBase ("10.1.1.0", "255.255.255.0");
+  Ipv4InterfaceContainer interfaces = ipv4.Assign (ndc);
+
+#if 1
+  //
+  // Testing only -- send a packet from an internal node to an external node
+  //
+  uint32_t packetSize = 1024;
+  uint32_t maxPacketCount = 100;
+  Time interPacketInterval = Seconds (1.);
+  UdpEchoClientHelper client (interfaces.GetAddress (0), 9);
+  client.SetAttribute ("MaxPackets", UintegerValue (maxPacketCount));
+  client.SetAttribute ("Interval", TimeValue (interPacketInterval));
+  client.SetAttribute ("PacketSize", UintegerValue (packetSize));
+  ApplicationContainer apps = client.Install (nodes.Get (1));
+  apps.Start (Seconds (2.0));
+  apps.Stop (Seconds (10.0));
+#endif
+
+  //
+  // Configure tracing of all enqueue, dequeue, and NetDevice receive events.
+  //
+#if 0
+  NS_LOG_INFO ("Configure Tracing.");
+  std::ofstream ascii;
+  ascii.open ("csma-tap-bridge.tr");
+  CsmaHelper::EnableAsciiAll (ascii);
+  CsmaHelper::EnablePcapAll ("csma-tap-bridge");
+#endif
+
+  //
+  // Now, do the actual simulation.  Run for a few minutes to allow the user a chance
+  // to run some applications on the Linux hosts.
+  //
+  Simulator::Stop (Seconds (3. * 60.));
+  NS_LOG_INFO ("Run Simulation.");
+  Simulator::Run ();
+  Simulator::Destroy ();
+  NS_LOG_INFO ("Done.");
+}
--- a/examples/wscript	Sun Jan 25 21:57:24 2009 +0000
+++ b/examples/wscript	Tue Jan 27 12:36:46 2009 -0800
@@ -48,6 +48,10 @@
                                  ['csma', 'internet-stack'])
     obj.source = 'csma-one-subnet.cc'
 
+    obj = bld.create_ns3_program('csma-tap-bridge',
+                                 ['csma', 'tap-bridge', 'internet-stack'])
+    obj.source = 'csma-tap-bridge.cc'
+
     obj = bld.create_ns3_program('csma-bridge',
                                  ['bridge', 'csma', 'internet-stack'])
     obj.source = 'csma-bridge.cc'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/devices/tap-bridge/tap-bridge.cc	Tue Jan 27 12:36:46 2009 -0800
@@ -0,0 +1,292 @@
+/* -*- 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
+ */
+
+#include "tap-bridge.h"
+#include "ns3/node.h"
+#include "ns3/channel.h"
+#include "ns3/packet.h"
+#include "ns3/log.h"
+#include "ns3/boolean.h"
+#include "ns3/simulator.h"
+
+NS_LOG_COMPONENT_DEFINE ("TapBridge");
+
+namespace ns3 {
+
+NS_OBJECT_ENSURE_REGISTERED (TapBridge);
+
+TypeId
+TapBridge::GetTypeId (void)
+{
+  static TypeId tid = TypeId ("ns3::TapBridge")
+    .SetParent<NetDevice> ()
+    .AddConstructor<TapBridge> ()
+    ;
+  return tid;
+}
+
+
+TapBridge::TapBridge ()
+  : m_node (0),
+    m_ifIndex (0)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+}
+
+TapBridge::~TapBridge()
+{
+  NS_LOG_FUNCTION_NOARGS ();
+}
+
+  void 
+TapBridge::DoDispose ()
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  NetDevice::DoDispose ();
+}
+
+void 
+TapBridge::SetBridgedDevice (Ptr<NetDevice> bridgedDevice)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+
+  NS_ASSERT_MSG (m_node != 0, "TapBridge::SetBridgedDevice:  Bridge not installed in a node");
+  NS_ASSERT_MSG (bridgedDevice != this, "TapBridge::SetBridgedDevice:  Cannot bridge to self");
+  NS_ASSERT_MSG (m_bridgedDevice == 0, "TapBridge::SetBridgedDevice:  Already bridged");
+  
+  if (!Mac48Address::IsMatchingType (bridgedDevice->GetAddress ()))
+    {
+      NS_FATAL_ERROR ("TapBridge::SetBridgedDevice: Device does not support eui 48 addresses: cannot be added to bridge.");
+    }
+  
+  if (!bridgedDevice->SupportsSendFrom ())
+    {
+      NS_FATAL_ERROR ("TapBridge::SetBridgedDevice: Device does not support SendFrom: cannot be added to bridge.");
+    }
+
+  //
+  // Tell the bridged device to forward its received packets here.
+  //
+  m_node->RegisterProtocolHandler (MakeCallback (&TapBridge::ReceiveFromBridgedDevice, this), 0, bridgedDevice, true);
+  m_bridgedDevice = bridgedDevice;
+}
+
+void
+TapBridge::ReceiveFromBridgedDevice (
+  Ptr<NetDevice> device, 
+  Ptr<const Packet> packet, 
+  uint16_t protocol,
+  Address const &src, 
+  Address const &dst, 
+  PacketType packetType)
+{
+  NS_LOG_FUNCTION (device << packet << protocol << src << dst << packetType);
+  NS_ASSERT_MSG (device == m_bridgedDevice, "TapBridge::SetBridgedDevice:  Received packet from unexpected device");
+  
+  NS_LOG_DEBUG ("Packet UID is " << packet->GetUid ());
+
+  Mac48Address src48 = Mac48Address::ConvertFrom (src);
+  Mac48Address dst48 = Mac48Address::ConvertFrom (dst);
+
+  //
+  // We have received a packet from the ns-3 net device that has been associated
+  // with this bridge.  We want to take these bits and send them off to the 
+  // Tap device on the Linux host.
+  //
+  NS_LOG_LOGIC ("TapBridge::ReceiveFromDevice: Not implemented");
+}
+
+void 
+TapBridge::SetName(const std::string name)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  m_name = name;
+}
+
+std::string 
+TapBridge::GetName(void) const
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return m_name;
+}
+
+void 
+TapBridge::SetIfIndex(const uint32_t index)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  m_ifIndex = index;
+}
+
+uint32_t 
+TapBridge::GetIfIndex(void) const
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return m_ifIndex;
+}
+
+Ptr<Channel> 
+TapBridge::GetChannel (void) const
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return 0;
+}
+
+Address 
+TapBridge::GetAddress (void) const
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return m_address;
+}
+
+bool 
+TapBridge::SetMtu (const uint16_t mtu)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  m_mtu = mtu;
+  return true;
+}
+
+uint16_t 
+TapBridge::GetMtu (void) const
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return m_mtu;
+}
+
+
+bool 
+TapBridge::IsLinkUp (void) const
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return true;
+}
+
+void 
+TapBridge::SetLinkChangeCallback (Callback<void> callback)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+}
+
+bool 
+TapBridge::IsBroadcast (void) const
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return true;
+}
+
+Address
+TapBridge::GetBroadcast (void) const
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return Mac48Address ("ff:ff:ff:ff:ff:ff");
+}
+
+bool
+TapBridge::IsMulticast (void) const
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return true;
+}
+
+Address
+TapBridge::GetMulticast (Ipv4Address multicastGroup) const
+{
+ NS_LOG_FUNCTION (this << multicastGroup);
+ Mac48Address multicast = Mac48Address::GetMulticast (multicastGroup);
+ return multicast;
+}
+
+bool 
+TapBridge::IsPointToPoint (void) const
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return false;
+}
+
+bool 
+TapBridge::IsBridge (void) const
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return true;
+}
+
+bool 
+TapBridge::Send (Ptr<Packet> packet, const Address& dst, uint16_t protocol)
+{
+  NS_LOG_FUNCTION (packet << dst << protocol);
+  NS_FATAL_ERROR ("TapBridge::Send: You may not call Send on a TapBridge directly");
+  return false;
+}
+
+bool 
+TapBridge::SendFrom (Ptr<Packet> packet, const Address& src, const Address& dst, uint16_t protocol)
+{
+  NS_LOG_FUNCTION (packet << src << dst << protocol);
+  NS_FATAL_ERROR ("TapBridge::Send: You may not call SendFrom on a TapBridge directly");
+  return false;
+}
+
+Ptr<Node> 
+TapBridge::GetNode (void) const
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return m_node;
+}
+
+void 
+TapBridge::SetNode (Ptr<Node> node)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  m_node = node;
+}
+
+bool 
+TapBridge::NeedsArp (void) const
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return true;
+}
+
+void 
+TapBridge::SetReceiveCallback (NetDevice::ReceiveCallback cb)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  m_rxCallback = cb;
+}
+
+void 
+TapBridge::SetPromiscReceiveCallback (NetDevice::PromiscReceiveCallback cb)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  m_promiscRxCallback = cb;
+}
+
+bool
+TapBridge::SupportsSendFrom () const
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return true;
+}
+
+Address TapBridge::GetMulticast (Ipv6Address addr) const
+{
+  NS_LOG_FUNCTION (this << addr);
+  return Mac48Address::GetMulticast (addr);
+}
+
+} // namespace ns3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/devices/tap-bridge/tap-bridge.h	Tue Jan 27 12:36:46 2009 -0800
@@ -0,0 +1,128 @@
+/* -*- 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 "ns3/net-device.h"
+#include "ns3/mac48-address.h"
+#include "ns3/nstime.h"
+
+namespace ns3 {
+
+class Node;
+
+/**
+ * \ingroup devices
+ * \defgroup tap-bridge TapBridge
+ * 
+ * \brief A bridge to make it appear that a host 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 the
+ * /dev/tapx device that we create for it, the write is redirected into the TapBridge
+ * and from that perspective, becomes a read.  The TapBridge then redirects the data
+ * written (by the Linux host) to the tap device on out the ns-3 net device to which
+ * we are bridged.  When a packet comes in from the ns-3 world to the ns-3 net device
+ * we are bridging, it appears via a callback from that net device.  Our job is to
+ * take those bits and write them back to the host using the user mode handler for
+ * /dev/tapx.  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.  In order to do this
+ * we need a "ghost node" in the simulation to hold the bridged ns-3 net device and 
+ * this Tap Bridge.  It won't actually do anything else in the simulation.  You will
+ * be able to perform typical ns-3 operations on that node, but they will have no
+ * effect other than to set up, tear down and configure the net devices and bridges
+ * mentioned above.
+ */
+
+/**
+ * \ingroup tap-bridge
+ * \brief A bridge to make it appear that a host is connected to an ns-3 net device.
+ */
+
+class TapBridge : public NetDevice
+{
+public:
+  static TypeId GetTypeId (void);
+
+  TapBridge ();
+  virtual ~TapBridge ();
+
+  /** \brief Set the device to bridge.
+   *
+   * This method tells the bridge which ns-3 net device it should use to connect
+   * the simulation side of the bridge.  
+   *
+   * \attention The ns-3 net device that is being set as the device must not 
+   * have an IP address.  This address is a property of the host Linux device.
+   */
+  void SetBridgedDevice (Ptr<NetDevice> bridgedDevice);
+
+  // inherited from NetDevice base class.
+  virtual void SetName(const std::string name);
+  virtual std::string GetName(void) const;
+  virtual void SetIfIndex(const uint32_t index);
+  virtual uint32_t GetIfIndex(void) const;
+  virtual Ptr<Channel> GetChannel (void) const;
+  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 SetLinkChangeCallback (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:
+  virtual void DoDispose (void);
+
+  void ReceiveFromBridgedDevice (Ptr<NetDevice> device, Ptr<const Packet> packet, uint16_t protocol,
+                                 Address const &src, Address const &dst, PacketType packetType);
+
+private:
+  NetDevice::ReceiveCallback m_rxCallback;
+  NetDevice::PromiscReceiveCallback m_promiscRxCallback;
+
+  Mac48Address m_address;
+  Ptr<Node> m_node;
+  std::string m_name;
+  uint32_t m_ifIndex;
+  uint16_t m_mtu;
+
+  Ptr<NetDevice> m_bridgedDevice;
+};
+
+} // namespace ns3
+
+#endif /* TAP_BRIDGE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/devices/tap-bridge/tap-encode-decode.cc	Tue Jan 27 12:36:46 2009 -0800
@@ -0,0 +1,110 @@
+/* -*- 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
+ */
+
+#include <string>
+#include <iostream>
+#include <iomanip>
+#include <sstream>
+
+namespace ns3 {
+
+/**
+ * \brief Convert a byte buffer to a string containing a hex representation
+ * of the buffer.  Make the string pretty by adding a colon (':') between
+ * the hex.
+ *
+ * \param buffer The input buffer to be converted.
+ * \param len The length of the input buffer.
+ * \returns A string containing a hex representation of the data in buffer.
+ */
+  std::string
+TapBufferToString (uint8_t *buffer, uint32_t len)
+{
+  std::ostringstream oss;
+  //
+  // Tell the stream to make hex characters, zero-filled
+  //
+  oss.setf (std::ios::hex, std::ios::basefield);
+  oss.fill('0');
+
+  //
+  // Loop through the buffer, separating the two-digit-wide hex bytes
+  // with a colon.
+  //
+  for (uint8_t i = 0; i < len; i++)
+    {
+      oss << ":" << std::setw (2) << (uint32_t)buffer[i];
+    }
+  return oss.str ();
+}
+
+/**
+ * \brief Convert string encoded by the inverse function (TapBufferToString)
+ * back into a byte buffer.
+ *
+ * \param s The input string.
+ * \param buffer The buffer to initialize with the converted bits.
+ * \param len The length of the data that is valid in the buffer.
+ * \returns True indicates a successful conversion.
+ */
+  bool
+TapStringToBuffer (std::string s, uint8_t *buffer, uint32_t *len)
+{
+  //
+  // If the string was made by our inverse function, the string length must
+  // be a multiple of three characters in length.  Use this fact to do a
+  // quick reasonableness test.
+  //
+  if ((s.length () % 3) != 0)
+    {
+      return false;
+    }
+
+  std::istringstream iss;
+  iss.str (s);
+
+  uint8_t n = 0;
+
+  while (iss.good ())
+    {
+      //
+      // The first character in the "triplet" we're working on is always the
+      // the ':' separator.  Read that into a char and make sure we're skipping
+      // what we think we're skipping.
+      //
+      char c;
+      iss.read (&c, 1);
+      if (c != ':')
+        {
+          return false;
+        }
+      
+      //
+      // And then read in the real bits and convert them.
+      //
+      uint32_t tmp;
+      iss >> std::hex >> tmp;
+      buffer[n] = tmp;
+      n++;
+    }
+
+  *len = n;
+  return true;
+}
+
+} // namespace ns3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/devices/tap-bridge/tap-encode-decode.h	Tue Jan 27 12:36:46 2009 -0800
@@ -0,0 +1,33 @@
+/* -*- 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_ENCODE_DECODE_H
+#define TAP_ENCODE_DECODE_H
+
+#include <string>
+
+namespace ns3 {
+
+  std::string TapBufferToString (uint8_t *buffer, uint32_t len);
+  bool TapStringToBuffer (std::string s, uint8_t *buffer, uint32_t *len);
+
+
+} // namespace ns3
+
+#endif // TAP_ENCODE_DECODE_H
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/devices/tap-bridge/tap-sock-creator.cc	Tue Jan 27 12:36:46 2009 -0800
@@ -0,0 +1,456 @@
+/* -*- 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
+ */
+
+#include <unistd.h>
+#include <stdint.h>
+#include <string>
+#include <iostream>
+#include <iomanip>
+#include <sstream>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <linux/un.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#include <linux/route.h>
+#include <netinet/in.h>
+
+#include "tap-encode-decode.h"
+
+#define TAP_MAGIC 95549
+
+static int gVerbose = 0;
+
+#define LOG(msg) \
+  if (gVerbose) \
+    { \
+      std::cout << __FUNCTION__ << "(): " << msg << std::endl;   \
+    }
+
+#define ABORT(msg, printErrno) \
+  std::cout << __FILE__ << ": fatal error at line " << __LINE__ << ": " << __FUNCTION__ << "(): " << msg << std::endl; \
+  if (printErrno) \
+    { \
+      std::cout << "    errno = " << errno << " (" << strerror (errno) << ")" << std::endl; \
+    } \
+  exit (-1); 
+
+#define ABORT_IF(cond, msg, printErrno) \
+  if (cond) \
+    { \
+      ABORT(msg, printErrno); \
+    }
+
+//
+// Thanks, Mathieu, for the beginning of these functions
+//
+#define ASCII_DOT (0x2e)
+#define ASCII_ZERO (0x30)
+#define ASCII_a (0x41)
+#define ASCII_z (0x5a)
+#define ASCII_A (0x61)
+#define ASCII_Z (0x7a)
+#define ASCII_COLON (0x3a)
+#define ASCII_ZERO (0x30)
+
+static char
+AsciiToLowCase (char c)
+{
+  if (c >= ASCII_a && c <= ASCII_z) {
+    return c;
+  } else if (c >= ASCII_A && c <= ASCII_Z) {
+    return c + (ASCII_a - ASCII_A);
+  } else {
+    return c;
+  }
+}
+
+static uint32_t 
+AsciiToIpv4 (const char *address)
+{
+  uint32_t host = 0;
+  while (true) {
+    uint8_t byte = 0;
+    while (*address != ASCII_DOT &&
+           *address != 0) {
+      byte *= 10;
+      byte += *address - ASCII_ZERO;
+      address++;
+    }
+    host <<= 8;
+    host |= byte;
+    if (*address == 0) {
+      break;
+    }
+    address++;
+  }
+  return host;
+}
+
+static void 
+AsciiToMac48 (const char *str, uint8_t addr[6])
+{
+  int i = 0;
+  while (*str != 0 && i < 6) 
+    {
+      uint8_t byte = 0;
+      while (*str != ASCII_COLON && *str != 0) 
+	{
+	  byte <<= 4;
+	  char low = AsciiToLowCase (*str);
+	  if (low >= ASCII_a) 
+	    {
+	      byte |= low - ASCII_a + 10;
+	    } 
+	  else 
+	    {
+	      byte |= low - ASCII_ZERO;
+	    }
+	  str++;
+	}
+      addr[i] = byte;
+      i++;
+      if (*str == 0) 
+	{
+	  break;
+	}
+      str++;
+    }
+}
+
+static void
+SetInetAddress (sockaddr *ad, uint32_t networkOrder)
+{
+  struct sockaddr_in *sin = (struct sockaddr_in*)ad;
+  sin->sin_family = AF_INET;
+  sin->sin_port = 0; // unused
+  sin->sin_addr.s_addr = htonl (networkOrder);
+}
+
+/**
+ * \brief Send the socket file descriptor we created back to the tap bridge, 
+ * which will read it as soon as we're done.
+ *
+ * \param path The socket address information from the Unix socket we use
+ * to send the created socket back to.
+ * \param fd The socket we're going to send. 
+ */
+  static void
+SendSocket (const char *path, int fd)
+{
+  //
+  // Open a Unix (local interprocess) socket to call back to the tap bridge
+  //
+  LOG ("Create Unix socket");
+  int sock = socket (PF_UNIX, SOCK_DGRAM, 0);
+  ABORT_IF (sock == -1, "Unable to open socket", 1);
+  
+  //
+  // We have this string called path, which is really a hex representation
+  // of the endpoint that the tap bridge created.  It used a forward encoding
+  // method (TapBufferToString) to take the sockaddr_un it made and passed 
+  // the resulting string to us.  So we need to take the inverse method
+  // (TapStringToBuffer) and build the same sockaddr_un over here.
+  //
+  socklen_t clientAddrLen;
+  struct sockaddr_un clientAddr;
+
+  LOG ("Decode address " << path);
+  bool rc = ns3::TapStringToBuffer (path, (uint8_t *)&clientAddr, &clientAddrLen);
+  ABORT_IF (rc == false, "Unable to decode path", 0);
+
+  LOG ("Connect");
+  int status = connect (sock, (struct sockaddr*)&clientAddr, clientAddrLen);
+  ABORT_IF (status == -1, "Unable to connect to tap bridge", 1);
+
+  LOG ("Connected");
+
+  //
+  // This is arcane enough that a few words are worthwhile to explain what's 
+  // going on here.
+  //
+  // The interesting information (the socket FD) is going to go back to the
+  // tap bridge as an integer of ancillary data.  Ancillary data is bits 
+  // that are not a part a socket payload (out-of-band data).  We're also 
+  // going to send one integer back.  It's just initialized to a magic number
+  // we use to make sure that the tap bridge is talking to the tap socket 
+  // creator and not some other creator process (emu, specifically)
+  //
+  // The struct iovec below is part of a scatter-gather list.  It describes a
+  // buffer.  In this case, it describes a buffer (an integer) containing the
+  // data that we're going to send back to the tap bridge (that magic number).
+  // 
+  struct iovec iov;
+  uint32_t magic = TAP_MAGIC;
+  iov.iov_base = &magic;
+  iov.iov_len = sizeof(magic);
+
+  //
+  // The CMSG macros you'll see below are used to create and access control 
+  // messages (which is another name for ancillary data).  The ancillary 
+  // data is made up of pairs of struct cmsghdr structures and associated
+  // data arrays.
+  // 
+  // First, we're going to allocate a buffer on the stack to contain our 
+  // data array (that contains the socket).  Sometimes you'll see this called
+  // an "ancillary element" but the msghdr uses the control message termimology
+  // so we call it "control."
+  //
+  size_t msg_size = sizeof(int);
+  char control[CMSG_SPACE(msg_size)];
+
+  //
+  // There is a msghdr that is used to minimize the number of parameters
+  // passed to sendmsg (which we will use to send our ancillary data).  This
+  // structure uses terminology corresponding to control messages, so you'll
+  // see msg_control, which is the pointer to the ancillary data and controllen
+  // which is the size of the ancillary data array.
+  //
+  // So, initialize the message header that describes our ancillary/control data
+  // and point it to the control message/ancillary data we just allocated space
+  // for.
+  //
+  struct msghdr msg;
+  msg.msg_name = 0;
+  msg.msg_namelen = 0;
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+  msg.msg_control = control;
+  msg.msg_controllen = sizeof (control);
+  msg.msg_flags = 0;
+
+  //
+  // A cmsghdr contains a length field that is the length of the header and
+  // the data.  It has a cmsg_level field corresponding to the originating 
+  // protocol.  This takes values which are legal levels for getsockopt and
+  // setsockopt (here SOL_SOCKET).  We're going to use the SCM_RIGHTS type of 
+  // cmsg, that indicates that the ancillary data array contains access rights 
+  // that we are sending back to the tap bridge.
+  //
+  // We have to put together the first (and only) cmsghdr that will describe
+  // the whole package we're sending.
+  //
+  struct cmsghdr *cmsg;
+  cmsg = CMSG_FIRSTHDR(&msg);
+  cmsg->cmsg_level = SOL_SOCKET;
+  cmsg->cmsg_type = SCM_RIGHTS;
+  cmsg->cmsg_len = CMSG_LEN(msg_size);
+  //
+  // We also have to update the controllen in case other stuff is actually
+  // in there we may not be aware of (due to macros).
+  //
+  msg.msg_controllen = cmsg->cmsg_len;
+
+  //
+  // Finally, we get a pointer to the start of the ancillary data array and
+  // put our file descriptor in.
+  //
+  int *fdptr = (int*) (CMSG_DATA(cmsg));
+  *fdptr = fd; // 
+
+  //
+  // Actually send the file descriptor back to the tap bridge.
+  //
+  ssize_t len = sendmsg(sock, &msg, 0);
+  ABORT_IF (len == -1, "Could not send socket back to tap bridge", 1);
+
+  LOG ("sendmsg complete");
+}
+
+  static int
+CreateTap (const char *dev, const char *gw, const char *ip, const char *netmask, const char *mac)
+{
+  //
+  // Creation and management of Tap devices is done via the tun device
+  //
+  int tap = open ("/dev/net/tun", O_RDWR);
+  ABORT_IF (tap == -1, "Could not open /dev/net/tun", true);
+
+  //
+  // Allocate a tap device, making sure that it will not send the tun_pi header.
+  // If we provide a null name to the ifr.ifr_name, we tell the kernel to pick
+  // a name for us (i.e., tapn where n = 0..255
+  //
+  struct ifreq ifr;
+  ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+  strcpy (ifr.ifr_name, dev);
+  int status = ioctl (tap, TUNSETIFF, (void *) &ifr);
+  ABORT_IF (status == -1, "Could not allocate tap device", true);
+
+  std::string tapDeviceName = (char *)ifr.ifr_name;
+  LOG ("Allocated TAP device " << tapDeviceName);
+
+  //
+  // Set the hardware (MAC) address of the new device
+  //
+  ifr.ifr_hwaddr.sa_family = 1; // this is ARPHRD_ETHER from if_arp.h
+  AsciiToMac48 (mac, (uint8_t*)ifr.ifr_hwaddr.sa_data);
+  status = ioctl (tap, SIOCSIFHWADDR, &ifr);
+  ABORT_IF (status == -1, "Could not set MAC address", true);
+  LOG ("Set device MAC address to " << mac);
+
+  int fd = socket (AF_INET, SOCK_DGRAM, 0);
+
+  //
+  // Bring the interface up.
+  //
+  status = ioctl (fd, SIOCGIFFLAGS, &ifr);
+  ABORT_IF (status == -1, "Could not get flags for interface", true);
+  ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
+  status = ioctl (fd, SIOCSIFFLAGS, &ifr);
+  ABORT_IF (status == -1, "Could not bring interface up", true);
+  LOG ("Device is up");
+
+  //
+  // Set the IP address of the new interface/device.
+  //
+  SetInetAddress (&ifr.ifr_addr, AsciiToIpv4 (ip));
+  status = ioctl (fd, SIOCSIFADDR, &ifr);
+  ABORT_IF (status == -1, "Could not set IP address", true);
+  LOG ("Set device IP address to " << ip);
+
+  //
+  // Set the net mask of the new interface/device
+  //
+  SetInetAddress (&ifr.ifr_netmask, AsciiToIpv4 (netmask));
+  status = ioctl (fd, SIOCSIFNETMASK, &ifr);
+  ABORT_IF (status == -1, "Could not set net mask", true);
+  LOG ("Set device Net Mask to " << netmask);
+
+  return tap;
+}
+
+  int 
+main (int argc, char *argv[])
+{
+  int c;
+  char *dev = NULL;
+  char *gw = NULL;
+  char *ip = NULL;
+  char *mac = NULL;
+  char *netmask = NULL;
+  char *path = NULL;
+
+  opterr = 0;
+
+  while ((c = getopt (argc, argv, "vd:g:i:m:n:p:")) != -1)
+    {
+      switch (c)
+        {
+        case 'v':
+          gVerbose = true;
+          break;
+        case 'd':
+          dev = optarg;     // name of the new tap device
+          break;
+        case 'g':
+          gw = optarg;      // gateway address for the new device
+          break;
+        case 'i':
+          ip = optarg;      // ip address of the new device
+          break;
+        case 'm':
+          mac = optarg;     // mac address of the new device
+          break;
+        case 'n':
+          netmask = optarg; // net mask for the new device
+          break;
+        case 'p':
+          path = optarg;    // path back to the tap bridge
+          break;
+        }
+    }
+
+  //
+  // We have got to be able to coordinate the name of the tap device we are
+  // going to create and or open with the device that an external Linux host
+  // will use.  THis name is given in dev
+  //
+  ABORT_IF (dev == NULL, "Device Name is a required argument", 0);
+  LOG ("Provided Device Name is \"" << dev << "\"");
+
+  //
+  // We have got to be able to provide a gateway to the external Linux host 
+  // so it can talk to the ns-3 network.  This ip address is provided in 
+  // gw.
+  //
+  ABORT_IF (gw == NULL, "Gateway Address is a required argument", 0);
+  LOG ("Provided Gateway Address is \"" << dev << "\"");
+
+  //
+  // We have got to be able to assign an IP address to the tap device we are
+  // allocating.  This address is allocated in the simulation and assigned to
+  // the tap bridge.  This address is given in ip.
+  //
+  ABORT_IF (ip == NULL, "IP Address is a required argument", 0);
+  LOG ("Provided IP Address is \"" << ip << "\"");
+
+  //
+  // We have got to be able to assign a Mac address to the tap device we are
+  // allocating.  This address is allocated in the simulation and assigned to
+  // the bridged device.  This allows packets addressed to the bridged device
+  // to appear in the Linux host as if they were received there.
+  //
+  ABORT_IF (mac == NULL, "MAC Address is a required argument", 0);
+  LOG ("Provided MAC Address is \"" << mac << "\"");
+
+  //
+  // We have got to be able to assign a net mask to the tap device we are
+  // allocating.  This mask is allocated in the simulation and given to
+  // the bridged device.  
+  //
+  ABORT_IF (netmask == NULL, "Net Mask is a required argument", 0);
+  LOG ("Provided Net Mask is \"" << netmask << "\"");
+
+  //
+  // This program is spawned by a tap bridge running in a simulation.  It
+  // wants to create a socket as described below.  We are going to do the
+  // work here since we're running suid root.  Once we create the socket,
+  // we have to send it back to the tap bridge.  We do that over a Unix
+  // (local interprocess) socket.  The tap bridge created a socket to 
+  // listen for our response on, and it is expected to have encoded the address
+  // information as a string and to have passed that string as an argument to
+  // us.  We see it here as the "path" string.  We can't do anything useful 
+  // unless we have that string.
+  //
+  ABORT_IF (path == NULL, "path is a required argument", 0);
+  LOG ("Provided path is \"" << path << "\"");
+
+  //
+  // The whole reason for all of the hoops we went through to call out to this
+  // program will pay off here.  We created this program to run as suid root
+  // in order to keep the main simulation program from having to be run with
+  // root privileges.  We need root privileges to be able to futz with the 
+  // Tap device underlying all of this.  So all of these hoops are to allow 
+  // us to exeucte the following code:
+  //
+  LOG ("Creating Tap");
+  int sock = CreateTap (dev, gw, ip, mac, netmask);
+  ABORT_IF (sock == -1, "main(): Unable to create tap socket", 1);
+
+  //
+  // Send the socket back to the tap net device so it can go about its business
+  //
+  SendSocket (path, sock);
+
+  return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/devices/tap-bridge/tap.h	Tue Jan 27 12:36:46 2009 -0800
@@ -0,0 +1,8 @@
+/**
+ * \ingroup devices
+ * \defgroup TapBridgeModel Tap Bridge Model
+ *
+ * \section TapBridgeModelOverview TapBridge Model Overview
+ *
+ * The tap bridge ...
+ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/devices/tap-bridge/waf	Tue Jan 27 12:36:46 2009 -0800
@@ -0,0 +1,1 @@
+exec "`dirname "$0"`"/../../../waf "$@"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/devices/tap-bridge/wscript	Tue Jan 27 12:36:46 2009 -0800
@@ -0,0 +1,41 @@
+## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+
+def configure(conf):
+    if conf.env['ENABLE_THREADING']:
+        conf.env['ENABLE_TAP'] = conf.check(header_name='linux/if_ether.h',
+                                            define_name='HAVE_IF_ETHER_H')
+        conf.report_optional_feature("TapBridge", "Tap Bridge",
+                                     conf.env['ENABLE_TAP'],
+                                     "<linux/if_ether.h> include not detected")
+    else:
+        conf.report_optional_feature("TapBridge", "Tap Bridge",
+                                     False,
+                                     "needs threading support which is not available")
+
+def build(bld):
+    module = bld.create_ns3_module('tap-bridge', ['node'])
+    module.source = [
+        ]
+    headers = bld.new_task_gen('ns3header')
+    headers.module = 'tap-bridge'
+    headers.source = [
+        'tap.h',
+        ]
+
+    env = bld.env_of_name('default')
+    if env['ENABLE_TAP']:
+        module.source.extend([
+                'tap-bridge.cc',
+                'tap-encode-decode.cc',
+                ])
+        headers.source.extend([
+                'tap-bridge.h',
+                ])
+
+        obj = bld.create_suid_program('tap-sock-creator')
+        obj.source = [
+            'tap-sock-creator.cc',
+            'tap-encode-decode.cc',
+            ]
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/helper/tap-bridge-helper.cc	Tue Jan 27 12:36:46 2009 -0800
@@ -0,0 +1,54 @@
+/* -*- 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
+ */
+
+#include "ns3/log.h"
+#include "ns3/node.h"
+#include "ns3/tap-bridge.h"
+#include "tap-bridge-helper.h"
+
+NS_LOG_COMPONENT_DEFINE ("TapBridgeHelper");
+
+namespace ns3 {
+
+TapBridgeHelper::TapBridgeHelper ()
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  m_deviceFactory.SetTypeId ("ns3::TapBridge");
+}
+
+void 
+TapBridgeHelper::SetDeviceAttribute (std::string n1, const AttributeValue &v1)
+{
+  NS_LOG_FUNCTION (n1 << &v1);
+  m_deviceFactory.Set (n1, v1);
+}
+
+  Ptr<NetDevice>
+TapBridgeHelper::Install (Ptr<Node> node, Ptr<NetDevice> nd)
+{
+  NS_LOG_FUNCTION (node << nd);
+  NS_LOG_LOGIC ("Install TapBridge on node " << node->GetId () << " bridging net device " << nd);
+
+  Ptr<TapBridge> bridge = m_deviceFactory.Create<TapBridge> ();
+  node->AddDevice (bridge);
+  bridge->SetBridgedDevice (nd);
+
+  return bridge;
+}
+
+} // namespace ns3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/helper/tap-bridge-helper.h	Tue Jan 27 12:36:46 2009 -0800
@@ -0,0 +1,44 @@
+/* -*- 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_HELPER_H
+#define TAP_BRIDGE_HELPER_H
+
+#include "net-device-container.h"
+#include "ns3/object-factory.h"
+#include <string>
+
+namespace ns3 {
+
+class Node;
+class AttributeValue;
+
+class TapBridgeHelper
+{
+public:
+  TapBridgeHelper ();
+  void SetDeviceAttribute (std::string n1, const AttributeValue &v1);
+  Ptr<NetDevice> Install (Ptr<Node> node, Ptr<NetDevice> nd);
+private:
+  ObjectFactory m_deviceFactory;
+};
+
+} // namespace ns3
+
+
+#endif /* TAP_BRIDGE_HELPER_H */
--- a/src/helper/wscript	Sun Jan 25 21:57:24 2009 +0000
+++ b/src/helper/wscript	Tue Jan 27 12:36:46 2009 -0800
@@ -21,6 +21,7 @@
         'ipv4-interface-container.cc',
         'udp-echo-helper.cc',
         'bridge-helper.cc',
+        'tap-bridge-helper.cc',
         'yans-wifi-helper.cc',
         'v4ping-helper.cc',
         ]
@@ -46,6 +47,7 @@
         'ipv4-interface-container.h',
         'udp-echo-helper.h',
         'bridge-helper.h',
+        'tap-bridge-helper.h',
         'yans-wifi-helper.h',
         'v4ping-helper.h',
         ]
--- a/src/wscript	Sun Jan 25 21:57:24 2009 +0000
+++ b/src/wscript	Tue Jan 27 12:36:46 2009 -0800
@@ -23,6 +23,7 @@
     'devices/csma',
     'devices/emu',
     'devices/bridge',
+    'devices/tap-bridge',
     'applications/onoff',
     'applications/packet-sink',
     'applications/udp-echo',
@@ -54,6 +55,7 @@
     conf.sub_config('core')
     conf.sub_config('simulator')
     conf.sub_config('devices/emu')
+    conf.sub_config('devices/tap-bridge')
     conf.sub_config('contrib')
     conf.sub_config('internet-stack')