Merge tap branch
authorRaj Bhattacharjea <raj.b@gatech.edu>
Fri, 13 Feb 2009 00:10:21 -0500
changeset 4193 62e3ae1c4974
parent 4162 d8019fbcc7fe (current diff)
parent 4192 38b10e48f5b5 (diff)
child 4194 819e09dac21a
Merge tap branch
examples/wscript
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/tap-wifi-dumbbell.cc	Fri Feb 13 00:10:21 2009 -0500
@@ -0,0 +1,210 @@
+/* -*- 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 |                                                                  
+//  |  Linux   |                                                                  
+//  |   Host   |                                                                  
+//  |  "left"  |                                                                  
+//  +----------+                                                                  
+//       |           n0                n3                n4
+//       |       +--------+     +------------+     +------------+
+//       +-------|  tap   |     |            |     |            |
+//               | bridge | ... |            |     |            |
+//               +--------+     +------------+     +------------+
+//               |  Wifi  |     | Wifi | P2P |-----| P2P | CSMA |
+//               +--------+     +------+-----+     +-----+------+
+//                   |              |           ^           |
+//                 ((*))          ((*))         |           |         
+//                                          P2P 10.1.2      |         
+//                 ((*))          ((*))                     |    n5  n6   n7
+//                   |              |                       |     |   |    |
+//                  n1             n2                       ================
+//                     Wifi 10.1.1                           CSMA LAN 10.1.3
+//
+// The Wifi device on node zero is:  10.1.1.1
+// The Wifi device on node one is:   10.1.1.2
+// The Wifi device on node two is:   10.1.1.3
+// The Wifi device on node three is: 10.1.1.4
+// The P2P device on node three is:  10.1.2.1
+// The P2P device on node four is:   10.1.2.2
+// The CSMA device on node four is:  10.1.3.1
+// The CSMA device on node five is:  10.1.3.2
+// The CSMA device on node six is:   10.1.3.3
+// The CSMA device on node seven is: 10.1.3.4
+//
+// Some simple things to do:
+//
+// 1) Ping one of the simulated nodes on the left side of the topology.
+//
+//    ./waf --run tap-wifi-dumbbell&
+//    ping 10.1.1.3
+//
+// 2) Configure a route in the linux host and ping once of the nodes on the 
+//    right, across the point-to-point link.  You will see relatively large
+//    delays due to CBR background traffic on the point-to-point (see next
+//    item).
+//
+//    ./waf --run tap-wifi-dumbbell&
+//    sudo route add -net 10.1.3.0 netmask 255.255.255.0 dev left gw 10.1.1.2
+//    ping 10.1.3.4
+//
+//    Take a look at the pcap traces and note that the timing reflects the 
+//    addition of the significant delay and low bandwidth configured on the 
+//    point-to-point link along with the high traffic.
+//
+// 3) Fiddle with the background CBR traffic across the point-to-point 
+//    link and watch the ping timing change.  The OnOffApplication "DataRate"
+//    attribute defaults to 500kb/s and the "PacketSize" Attribute defaults
+//    to 512.  The point-to-point "DataRate" is set to 512kb/s in the script,
+//    so in the default case, the link is pretty full.  This should be 
+//    reflected in large delays seen by ping.  You can crank down the CBR 
+//    traffic data rate and watch the ping timing change dramatically.
+//
+//    ./waf --run "tap-wifi-dumbbell --ns3::OnOffApplication::DataRate=100kb/s"&
+//    sudo route add -net 10.1.3.0 netmask 255.255.255.0 dev left gw 10.1.1.2
+//    ping 10.1.3.4
+//
+
+#include <iostream>
+#include <fstream>
+
+#include "ns3/simulator-module.h"
+#include "ns3/node-module.h"
+#include "ns3/core-module.h"
+#include "ns3/wifi-module.h"
+#include "ns3/helper-module.h"
+#include "ns3/global-routing-module.h"
+
+using namespace ns3;
+
+NS_LOG_COMPONENT_DEFINE ("TapDumbbellExample");
+
+int 
+main (int argc, char *argv[])
+{
+  RandomVariable::UseGlobalSeed (1, 1, 2, 3, 5, 8);
+
+  CommandLine cmd;
+  cmd.Parse (argc, argv);
+
+  GlobalValue::Bind ("SimulatorImplementationType", StringValue ("ns3::RealtimeSimulatorImpl"));
+
+  Config::SetDefault ("ns3::Ipv4L3Protocol::CalcChecksum", BooleanValue (true)); 
+  Config::SetDefault ("ns3::Icmpv4L4Protocol::CalcChecksum", BooleanValue (true)); 
+  Config::SetDefault ("ns3::TcpL4Protocol::CalcChecksum", BooleanValue (true)); 
+  Config::SetDefault ("ns3::UdpL4Protocol::CalcChecksum", BooleanValue (true)); 
+
+  //
+  // The topology has a Wifi network of four nodes on the left side.  We'll make
+  // node zero the AP and have the other three will be the STAs.
+  //
+  NodeContainer nodesLeft;
+  nodesLeft.Create (4);
+
+  YansWifiPhyHelper wifiPhy = YansWifiPhyHelper::Default ();
+  YansWifiChannelHelper wifiChannel = YansWifiChannelHelper::Default ();
+  wifiPhy.SetChannel (wifiChannel.Create ());
+
+  Ssid ssid = Ssid ("left");
+  WifiHelper wifi = WifiHelper::Default ();
+  wifi.SetRemoteStationManager ("ns3::ArfWifiManager");
+
+  wifi.SetMac ("ns3::NqapWifiMac", 
+               "Ssid", SsidValue (ssid), 
+               "BeaconGeneration", BooleanValue (true), 
+               "BeaconInterval", TimeValue (Seconds (2.5)));
+  NetDeviceContainer devicesLeft = wifi.Install (wifiPhy, nodesLeft.Get (0));
+
+
+  wifi.SetMac ("ns3::NqstaWifiMac", 
+               "Ssid", SsidValue (ssid), 
+               "ActiveProbing", BooleanValue (false));
+  devicesLeft.Add (wifi.Install (wifiPhy, NodeContainer (nodesLeft.Get (1), nodesLeft.Get (2), nodesLeft.Get (3))));
+
+  MobilityHelper mobility;
+  mobility.Install (nodesLeft);
+
+  InternetStackHelper internetLeft;
+  internetLeft.Install (nodesLeft);
+
+  Ipv4AddressHelper ipv4Left;
+  ipv4Left.SetBase ("10.1.1.0", "255.255.255.0");
+  Ipv4InterfaceContainer interfacesLeft = ipv4Left.Assign (devicesLeft);
+
+  TapBridgeHelper bridgeLeft (interfacesLeft.GetAddress (1));
+  bridgeLeft.SetAttribute ("DeviceName", StringValue ("left"));
+  bridgeLeft.Install (nodesLeft.Get (0), devicesLeft.Get (0));
+
+  //
+  // Now, create the right side.
+  //
+  NodeContainer nodesRight;
+  nodesRight.Create (4);
+
+  CsmaHelper csmaRight;
+  csmaRight.SetChannelAttribute ("DataRate", DataRateValue (5000000));
+  csmaRight.SetChannelAttribute ("Delay", TimeValue (MilliSeconds (2)));
+
+  NetDeviceContainer devicesRight = csmaRight.Install (nodesRight);
+
+  InternetStackHelper internetRight;
+  internetRight.Install (nodesRight);
+
+  Ipv4AddressHelper ipv4Right;
+  ipv4Right.SetBase ("10.1.3.0", "255.255.255.0");
+  Ipv4InterfaceContainer interfacesRight = ipv4Right.Assign (devicesRight);
+
+  //
+  // Stick in the point-to-point line between the sides.
+  //
+  PointToPointHelper p2p;
+  p2p.SetDeviceAttribute ("DataRate", StringValue ("512kbps"));
+  p2p.SetChannelAttribute ("Delay", StringValue ("10ms"));
+
+  NodeContainer nodes = NodeContainer (nodesLeft.Get(3), nodesRight.Get (0));
+  NetDeviceContainer devices = p2p.Install (nodes);
+
+  Ipv4AddressHelper ipv4;
+  ipv4.SetBase ("10.1.2.0", "255.255.255.192");
+  Ipv4InterfaceContainer interfaces = ipv4.Assign (devices);
+
+  //
+  // Simulate some CBR traffic over the point-to-point link
+  //
+  uint16_t port = 9;   // Discard port (RFC 863)
+  OnOffHelper onoff ("ns3::UdpSocketFactory", InetSocketAddress (interfaces.GetAddress (1), port));
+  onoff.SetAttribute ("OnTime", RandomVariableValue (ConstantVariable (1)));
+  onoff.SetAttribute ("OffTime", RandomVariableValue (ConstantVariable (0)));
+
+  ApplicationContainer apps = onoff.Install (nodesLeft.Get (3));
+  apps.Start (Seconds (1.0));
+
+  // Create a packet sink to receive these packets
+  PacketSinkHelper sink ("ns3::UdpSocketFactory", InetSocketAddress (Ipv4Address::GetAny (), port));
+
+  apps = sink.Install (nodesRight.Get (0));
+  apps.Start (Seconds (1.0));
+
+  CsmaHelper::EnablePcapAll ("tap-dumbbell");
+  GlobalRouteManager::PopulateRoutingTables ();
+
+  Simulator::Stop (Seconds (60.));
+  Simulator::Run ();
+  Simulator::Destroy ();
+}
--- a/examples/wscript	Thu Feb 12 16:49:58 2009 -0500
+++ b/examples/wscript	Fri Feb 13 00:10:21 2009 -0500
@@ -145,3 +145,7 @@
                                      ['emu', 'internet-stack'])
         obj.source = 'emu-udp-echo.cc'
 
+    if env['ENABLE_TAP']:
+        obj = bld.create_ns3_program('tap-wifi-dumbbell',
+                                     ['wifi', 'csma', 'point-to-point', 'tap-bridge', 'internet-stack'])
+        obj.source = 'tap-wifi-dumbbell.cc'
--- a/src/devices/csma/csma-net-device.cc	Thu Feb 12 16:49:58 2009 -0500
+++ b/src/devices/csma/csma-net-device.cc	Fri Feb 13 00:10:21 2009 -0500
@@ -656,27 +656,22 @@
     }
   else
     {
+      uint16_t protocol;
       //
-      // variable <protocol> must be initialized to avoid a compiler warning in the RAW case that breaks the optimized build.
+      // If the length/type is less than 1500, it corresponds to a length 
+      // interpretation packet.  In this case, it is an 802.3 packet and 
+      // will also have an 802.2 LLC header.  If greater than 1500, we
+      // find the protocol number (Ethernet type) directly.
       //
-      uint16_t protocol = 0;
-
-      switch (m_encapMode)
+      if (header.GetLengthType () <= 1500)
         {
-        case DIX:
+          LlcSnapHeader llc;
+          packet->RemoveHeader (llc);
+          protocol = llc.GetType ();
+        }
+      else
+        {
           protocol = header.GetLengthType ();
-          break;
-        case LLC: 
-          {
-            LlcSnapHeader llc;
-            packet->RemoveHeader (llc);
-            protocol = llc.GetType ();
-          } 
-          break;
-        case ILLEGAL:
-        default:
-          NS_FATAL_ERROR ("CsmaNetDevice::Receive(): Unknown packet encapsulation mode");
-          break;
         }
 
       PacketType packetType;
--- a/src/devices/emu/wscript	Thu Feb 12 16:49:58 2009 -0500
+++ b/src/devices/emu/wscript	Fri Feb 13 00:10:21 2009 -0500
@@ -2,11 +2,11 @@
 
 def configure(conf):
     if conf.env['ENABLE_THREADING']:
-        conf.env['ENABLE_EMU'] = conf.check(header_name='linux/if_ether.h',
-                                            define_name='HAVE_IF_ETHER_H')
+        conf.env['ENABLE_EMU'] = conf.check(header_name='netpacket/packet.h',
+                                            define_name='HAVE_PACKET_H')
         conf.report_optional_feature("EmuNetDevice", "Emulated Net Device",
                                      conf.env['ENABLE_EMU'],
-                                     "<linux/if_ether.h> include not detected")
+                                     "<netpacket/packet.h> include not detected")
     else:
         conf.report_optional_feature("EmuNetDevice", "Emulated Net Device",
                                      False,
--- a/src/devices/point-to-point/ppp-header.cc	Thu Feb 12 16:49:58 2009 -0500
+++ b/src/devices/point-to-point/ppp-header.cc	Fri Feb 13 00:10:21 2009 -0500
@@ -17,6 +17,7 @@
  */
 
 #include <iostream>
+#include "ns3/abort.h"
 #include "ns3/assert.h"
 #include "ns3/log.h"
 #include "ns3/header.h"
@@ -73,9 +74,8 @@
   uint32_t
 PppHeader::Deserialize (Buffer::Iterator start)
 {
-  uint16_t __attribute__((unused))data = start.ReadNtohU16 ();
-  NS_ASSERT_MSG (data == 0x0021, "MyHeader::Deserialize(): "
-    "expected protocol 0x0021");
+  uint16_t data = start.ReadNtohU16 ();
+  NS_ABORT_MSG_UNLESS (data == 0x0021, "MyHeader::Deserialize(): expected protocol 0x0021");
   return 2;
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/devices/tap-bridge/tap-bridge.cc	Fri Feb 13 00:10:21 2009 -0500
@@ -0,0 +1,976 @@
+/* -*- 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 "tap-encode-decode.h"
+
+#include "ns3/node.h"
+#include "ns3/channel.h"
+#include "ns3/packet.h"
+#include "ns3/ethernet-header.h"
+#include "ns3/llc-snap-header.h"
+#include "ns3/log.h"
+#include "ns3/abort.h"
+#include "ns3/boolean.h"
+#include "ns3/string.h"
+#include "ns3/ipv4.h"
+#include "ns3/simulator.h"
+#include "ns3/realtime-simulator-impl.h"
+#include "ns3/system-thread.h"
+
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <errno.h>
+#include <limits>
+#include <stdlib.h>
+
+//
+// Sometimes having a tap-creator is actually more trouble than solution.  In 
+// these cases you can uncomment the define of TAP_CREATOR below and the 
+// simulation will just use a device you have preconfigured.  This is useful
+// if you are running in an environment where you have got to run as root,
+// such as ORBIT or CORE.
+//
+//   sudo tunctl -t tap0
+//   sudo ifconfig tap0 hw ether 00:00:00:00:00:01
+//   sudo ifconfig tap0 10.1.1.1 netmask 255.255.255.0 up
+//
+
+// #define NO_CREATOR
+
+#ifdef NO_CREATOR
+#include <fcntl.h>
+#include <net/if.h>
+#include <linux/if_tun.h>
+#include <sys/ioctl.h>
+#endif
+
+NS_LOG_COMPONENT_DEFINE ("TapBridge");
+
+namespace ns3 {
+
+#define TAP_MAGIC 95549
+
+NS_OBJECT_ENSURE_REGISTERED (TapBridge);
+
+TypeId
+TapBridge::GetTypeId (void)
+{
+  static TypeId tid = TypeId ("ns3::TapBridge")
+    .SetParent<NetDevice> ()
+    .AddConstructor<TapBridge> ()
+    .AddAttribute ("DeviceName", 
+                   "The name of the tap device to create.",
+                   StringValue (""),
+                   MakeStringAccessor (&TapBridge::m_tapDeviceName),
+                   MakeStringChecker ())
+    .AddAttribute ("Gateway", 
+                   "The IP address of the default gateway to assign to the tap device.",
+                   Ipv4AddressValue ("255.255.255.255"),
+                   MakeIpv4AddressAccessor (&TapBridge::m_tapGateway),
+                   MakeIpv4AddressChecker ())
+    .AddAttribute ("IpAddress", 
+                   "The IP address to assign to the tap device.",
+                   Ipv4AddressValue ("255.255.255.255"),
+                   MakeIpv4AddressAccessor (&TapBridge::m_tapIp),
+                   MakeIpv4AddressChecker ())
+    .AddAttribute ("MacAddress", 
+                   "The MAC address to assign to the tap device.",
+                   Mac48AddressValue (Mac48Address ("ff:ff:ff:ff:ff:ff")),
+                   MakeMac48AddressAccessor (&TapBridge::m_tapMac),
+                   MakeMac48AddressChecker ())
+    .AddAttribute ("Netmask", 
+                   "The network mask to assign to the tap device.",
+                   Ipv4MaskValue ("255.255.255.255"),
+                   MakeIpv4MaskAccessor (&TapBridge::m_tapNetmask),
+                   MakeIpv4MaskChecker ())
+    .AddAttribute ("Start", 
+                   "The simulation time at which to spin up the tap device read thread.",
+                   TimeValue (Seconds (0.)),
+                   MakeTimeAccessor (&TapBridge::m_tStart),
+                   MakeTimeChecker ())
+    .AddAttribute ("Stop", 
+                   "The simulation time at which to tear down the tap device read thread.",
+                   TimeValue (Seconds (0.)),
+                   MakeTimeAccessor (&TapBridge::m_tStop),
+                   MakeTimeChecker ())
+    ;
+  return tid;
+}
+
+TapBridge::TapBridge ()
+: m_node (0),
+  m_ifIndex (0),
+  m_mtu (0),
+  m_sock (-1),
+  m_startEvent (),
+  m_stopEvent (),
+  m_readThread (0)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  Start (m_tStart);
+}
+
+TapBridge::~TapBridge()
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  m_bridgedDevice = 0;
+}
+
+  void 
+TapBridge::DoDispose ()
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  NetDevice::DoDispose ();
+}
+
+void
+TapBridge::Start (Time tStart)
+{
+  NS_LOG_FUNCTION (tStart);
+
+  //
+  // Cancel any pending start event and schedule a new one at some relative time in the future.
+  //
+  Simulator::Cancel (m_startEvent);
+  m_startEvent = Simulator::Schedule (tStart, &TapBridge::StartTapDevice, this);
+}
+
+  void
+TapBridge::Stop (Time tStop)
+{
+  NS_LOG_FUNCTION (tStop);
+  //
+  // Cancel any pending stop event and schedule a new one at some relative time in the future.
+  //
+  Simulator::Cancel (m_stopEvent);
+  m_startEvent = Simulator::Schedule (tStop, &TapBridge::StopTapDevice, this);
+}
+
+  void
+TapBridge::StartTapDevice (void)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+
+  NS_ABORT_MSG_IF (m_sock != -1, "TapBridge::StartTapDevice(): Tap is already started");
+
+  //
+  // Spin up the tap bridge and start receiving packets.
+  //
+  NS_LOG_LOGIC ("Creating tap device");
+
+  //
+  // Call out to a separate process running as suid root in order to get the 
+  // tap device allocated and set up.  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 newly created 
+  // tap device.
+  //
+  CreateTap ();
+
+  //
+  // Now spin up a read thread to read packets from the tap device.
+  //
+  NS_ABORT_MSG_IF (m_readThread != 0,"TapBridge::StartTapDevice(): Receive thread is already running");
+  NS_LOG_LOGIC ("Spinning up read thread");
+
+  m_readThread = Create<SystemThread> (MakeCallback (&TapBridge::ReadThread, this));
+  m_readThread->Start ();
+}
+
+void
+TapBridge::StopTapDevice (void)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+
+  close (m_sock);
+  m_sock = -1;
+
+  NS_ASSERT_MSG (m_readThread != 0, "TapBridge::StopTapDevice(): Receive thread is not running");
+
+  NS_LOG_LOGIC ("Joining read thread");
+  m_readThread->Join ();
+  m_readThread = 0;
+}
+
+void
+TapBridge::CreateTap (void)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+
+#ifdef NO_CREATOR
+  //
+  // In come cases, can you say FreeBSD, the tap-creator just gets in the way.
+  // in this case, just define NO_CREATOR, manually set up your tap device and
+  // just open and use it.
+  //
+  //
+  // Creation and management of Tap devices is done via the tun device
+  //
+  m_sock = open ("/dev/net/tun", O_RDWR);
+  NS_ABORT_MSG_IF (m_sock == -1, "TapBridge::CreateTap(): could not open /dev/net/tun: " << strerror (errno));
+
+  //
+  // 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, m_tapDeviceName.c_str ());
+  int status = ioctl (m_sock, TUNSETIFF, (void *) &ifr);
+  NS_ABORT_MSG_IF (status == -1, "TapBridge::CreateTap(): could not open device " << m_tapDeviceName << 
+                   ": " << strerror (errno));
+
+#else // use the tap-creator
+
+  //
+  // We want to create a tap device on the host.  Unfortunately for us
+  // you have to have root privileges to do that.  Instead of running the 
+  // entire simulation as root, we decided to make a small program who's whole
+  // reason for being is to run as suid root and do what it takes to create the
+  // tap.  We're going to fork and exec that program soon, but we need to have 
+  // a socket to talk to it with.  So we create a local interprocess (Unix) 
+  // socket for that purpose.
+  //
+  int sock = socket (PF_UNIX, SOCK_DGRAM, 0);
+  NS_ABORT_MSG_IF (sock == -1, "TapBridge::CreateTap(): Unix socket creation error, errno = " << strerror (errno));
+
+  //
+  // Bind to that socket and let the kernel allocate an endpoint
+  //
+  struct sockaddr_un un;
+  memset (&un, 0, sizeof (un));
+  un.sun_family = AF_UNIX;
+  int status = bind (sock, (struct sockaddr*)&un, sizeof (sa_family_t));
+  NS_ABORT_MSG_IF (status == -1, "TapBridge::CreateTap(): Could not bind(): errno = " << strerror (errno));
+  NS_LOG_INFO ("Created Unix socket");
+  NS_LOG_INFO ("sun_family = " << un.sun_family);
+  NS_LOG_INFO ("sun_path = " << un.sun_path);
+
+  //
+  // We have a socket here, but we want to get it there -- to the program we're
+  // going to exec.  What we'll do is to do a getsockname and then encode the
+  // resulting address information as a string, and then send the string to the
+  // program as an argument.  So we need to get the sock name.
+  //
+  socklen_t len = sizeof (un);
+  status = getsockname (sock, (struct sockaddr*)&un, &len);
+  NS_ABORT_MSG_IF (status == -1, "TapBridge::CreateTap(): Could not getsockname(): errno = " << strerror (errno));
+
+  //
+  // Now encode that socket name (family and path) as a string of hex digits
+  //
+  std::string path = TapBufferToString((uint8_t *)&un, len);
+  NS_LOG_INFO ("Encoded Unix socket as \"" << path << "\"");
+  //
+  // Fork and exec the process to create our socket.  If we're us (the parent)
+  // we wait for the child (the creator) to complete and read the  socket it created using the ancillary data mechanism.
+  //
+  pid_t pid = ::fork ();
+  if (pid == 0)
+    {
+      NS_LOG_DEBUG ("Child process");
+
+      //
+      // build a command line argument from the encoded endpoint string that 
+      // the socket creation process will use to figure out how to respond to
+      // the (now) parent process.  We're going to have to give this program
+      // quite a bit of information.
+      //
+      // -d<device-name> The name of the tap device we want to create;
+      // -g<gateway-address> The IP address to use as the default gateway;
+      // -i<IP-address> The IP address to assign to the new tap device;
+      // -m<MAC-address> The MAC-48 address to assign to the new tap device;
+      // -n<network-mask> The network mask to assign to the new tap device;
+      // -p<path> the path to the unix socket described above.
+      //
+      // Example tap-creator -dnewdev -g1.2.3.2 -i1.2.3.1 -m08:00:2e:00:01:23 -n255.255.255.0 -pblah
+      //
+      // We want to get as much of this stuff automagically as possible.
+      //
+      // <IP-address> is the IP address we are going to set in the newly 
+      // created Tap device on the Linux host.  At the point in the simulation
+      // where devices are coming up, we should have all of our IP addresses
+      // assigned.  That means that we can find the IP address to assign to 
+      // the new Tap device from the IP address associated with the bridged
+      // net device.
+      //
+      Ptr<NetDevice> nd = GetBridgedNetDevice ();
+      Ptr<Node> n = nd->GetNode ();
+      Ptr<Ipv4> ipv4 = n->GetObject<Ipv4> ();
+      uint32_t index = ipv4->FindInterfaceForDevice (nd);
+      Ipv4Address ipv4Address = ipv4->GetAddress (index);
+
+      //
+      // The net mask is sitting right there next to the ipv4 address.
+      //
+      Ipv4Mask ipv4Mask = ipv4->GetNetworkMask (index);
+
+      //
+      // The MAC address should also already be assigned and waiting for us in
+      // the bridged net device.
+      //
+      Address address = nd->GetAddress ();
+      Mac48Address mac48Address = Mac48Address::ConvertFrom (address);
+
+      //
+      // The device-name is something we may want the system to make up in 
+      // every case.  We also rely on it being configured via an Attribute 
+      // through the helper.  By default, it is set to the empty string 
+      // which tells the system to make up a device name such as "tap123".
+      //
+      std::ostringstream ossDeviceName;
+      ossDeviceName << "-d" << m_tapDeviceName;
+
+      //
+      // The gateway-address is something we can't derive, so we rely on it
+      // being configured via an Attribute through the helper.
+      //
+      std::ostringstream ossGateway;
+      ossGateway << "-g" << m_tapGateway;
+
+      //
+      // For flexibility, we do allow a client to override any of the values
+      // above via attributes, so only use our found values if the Attribute
+      // is not equal to its default value (empty string or broadcast address). 
+      //
+      std::ostringstream ossIp;
+      if (m_tapIp.IsBroadcast ())
+        {
+          ossIp << "-i" << ipv4Address;
+        }
+      else
+        {
+          ossIp << "-i" << m_tapIp;
+        }
+
+      std::ostringstream ossMac;
+      if (m_tapMac.IsBroadcast ())
+        {
+          ossMac << "-m" << mac48Address;
+        }
+      else
+        {
+          ossMac << "-m" << m_tapMac;
+        }
+
+      std::ostringstream ossNetmask;
+      if (m_tapNetmask.IsEqual (Ipv4Mask::GetOnes ()))
+        {
+          ossNetmask << "-n" << ipv4Mask;
+        }
+      else
+        {
+          ossNetmask << "-n" << m_tapNetmask;
+        }
+
+      std::ostringstream ossPath;
+      ossPath << "-p" << path;
+      //
+      // Execute the socket creation process image.
+      //
+      status = ::execl (FindCreator ("tap-creator").c_str (), 
+                        FindCreator ("tap-creator").c_str (), // argv[0] (filename)
+                        ossDeviceName.str ().c_str (),        // argv[1] (-d<device name>)
+                        ossGateway.str ().c_str (),           // argv[2] (-g<gateway>)
+                        ossIp.str ().c_str (),                // argv[3] (-i<IP address>)
+                        ossMac.str ().c_str (),               // argv[4] (-m<MAC address>)
+                        ossNetmask.str ().c_str (),           // argv[5] (-n<net mask>)
+                        ossPath.str ().c_str (),              // argv[6] (-p<path>)
+                        (char *)NULL);
+
+      //
+      // If the execl successfully completes, it never returns.  If it returns it failed or the OS is
+      // broken.  In either case, we bail.
+      //
+      NS_FATAL_ERROR ("TapBridge::CreateTap(): Back from execl(), errno = " << ::strerror (errno));
+    }
+  else
+    {
+      NS_LOG_DEBUG ("Parent process");
+      //
+      // We're the process running the emu net device.  We need to wait for the
+      // socket creator process to finish its job.
+      //
+      int st;
+      pid_t waited = waitpid (pid, &st, 0);
+      NS_ABORT_MSG_IF (waited == -1, "TapBridge::CreateTap(): waitpid() fails, errno = " << strerror (errno));
+      NS_ASSERT_MSG (pid == waited, "TapBridge::CreateTap(): pid mismatch");
+
+      //
+      // Check to see if the socket creator exited normally and then take a 
+      // look at the exit code.  If it bailed, so should we.  If it didn't
+      // even exit normally, we bail too.
+      //
+      if (WIFEXITED (st))
+	{
+          int exitStatus = WEXITSTATUS (st);
+          NS_ABORT_MSG_IF (exitStatus != 0, 
+                           "TapBridge::CreateTap(): socket creator exited normally with status " << exitStatus);
+	}
+      else 
+	{
+          NS_FATAL_ERROR ("TapBridge::CreateTap(): socket creator exited abnormally");
+	}
+
+      //
+      // At this point, the socket creator has run successfully and should 
+      // have created our tap device, initialized it with the information we
+      // passed and sent it back to the socket address we provided.  A socket
+      // (fd) we can use to talk to this tap device should be waiting on the 
+      // Unix socket we set up to receive information back from the creator
+      // program.  We've got to do a bunch of grunt work to get at it, though.
+      //
+      // The struct iovec below is part of a scatter-gather list.  It describes a
+      // buffer.  In this case, it describes a buffer (an integer) that will
+      // get the data that comes back from the socket creator process.  It will
+      // be a magic number that we use as a consistency/sanity check.
+      // 
+      struct iovec iov;
+      uint32_t 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 receive 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 recvmsg (which we will use to receive 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 the ancillary/control
+      // data we expect to receive and point it to buffer.
+      //
+      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;
+
+      //
+      // Now we can actually receive the interesting bits from the tap
+      // creator process.
+      //
+      ssize_t bytesRead = recvmsg (sock, &msg, 0);
+      NS_ABORT_MSG_IF (bytesRead != sizeof(int), "TapBridge::CreateTap(): Wrong byte count from socket creator");
+
+      //
+      // There may be a number of message headers/ancillary data arrays coming in.
+      // Let's look for the one with a type SCM_RIGHTS which indicates it' the
+      // one we're interested in.
+      //
+      struct cmsghdr *cmsg;
+      for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) 
+	{
+	  if (cmsg->cmsg_level == SOL_SOCKET &&
+	      cmsg->cmsg_type == SCM_RIGHTS)
+	    {
+              //
+              // This is the type of message we want.  Check to see if the magic 
+              // number is correct and then pull out the socket we care about if
+              // it matches
+              //
+              if (magic == TAP_MAGIC)
+                {
+                  NS_LOG_INFO ("Got SCM_RIGHTS with correct magic " << magic);
+                  int *rawSocket = (int*)CMSG_DATA (cmsg);
+                  NS_LOG_INFO ("Got the socket from the socket creator = " << *rawSocket);
+                  m_sock = *rawSocket;
+                  return;
+                }
+              else
+                {
+                  NS_LOG_INFO ("Got SCM_RIGHTS, but with bad magic " << magic);                  
+                }
+	    }
+	}
+      NS_FATAL_ERROR ("Did not get the raw socket from the socket creator");
+    }
+#endif // use the tap-creator
+}
+
+std::string
+TapBridge::FindCreator (std::string creatorName)
+{
+  NS_LOG_FUNCTION (creatorName);
+
+  std::list<std::string> locations;
+
+  // in repo
+  locations.push_back ("./build/optimized/src/devices/tap-bridge/");
+  locations.push_back ("./build/debug/src/devices/tap-bridge/");
+
+  // in src
+  locations.push_back ("../build/optimized/src/devices/tap-bridge/");
+  locations.push_back ("../build/debug/src/devices/tap-bridge/");
+
+  // in src/devices
+  locations.push_back ("../../build/optimized/src/devices/tap-bridge/");
+  locations.push_back ("../../build/debug/src/devices/tap-bridge/");
+
+  // in src/devices/tap-bridge
+  locations.push_back ("../../../build/optimized/src/devices/tap-bridge/");
+  locations.push_back ("../../../build/debug/src/devices/tap-bridge/");
+
+  for (std::list<std::string>::const_iterator i = locations.begin (); i != locations.end (); ++i)
+    {
+      struct stat st;
+
+      if (::stat ((*i + creatorName).c_str (), &st) == 0)
+	{
+          NS_LOG_INFO ("Found Creator " << *i + creatorName);                  
+	  return *i + creatorName;
+	}
+    }
+
+  NS_FATAL_ERROR ("TapBridge::FindCreator(): Couldn't find creator");
+  return ""; // quiet compiler
+}
+
+void
+TapBridge::ReadThread (void)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+
+  //
+  // It's important to remember that we're in a completely different thread than the simulator is running in.  We
+  // need to synchronize with that other thread to get the packet up into ns-3.  What we will need to do is to schedule 
+  // a method to deal with the packet using the multithreaded simulator we are most certainly running.  However, I just 
+  // said it -- we are talking about two threads here, so it is very, very dangerous to do any kind of reference couning
+  // on a shared object.  Just don't do it.  So what we're going to do is to allocate a buffer on the heap and pass that
+  // buffer into the ns-3 context thread where it will create the packet.
+  //
+  int32_t len = -1;
+
+  for (;;) 
+    {
+      uint32_t bufferSize = 65536;
+      uint8_t *buf = (uint8_t *)malloc (bufferSize);
+      NS_ABORT_MSG_IF (buf == 0, "TapBridge::ReadThread(): malloc packet buffer failed");
+      NS_LOG_LOGIC ("Calling read on tap device socket fd");
+      len = read (m_sock, buf, bufferSize);
+
+      if (len == -1)
+        {
+          free (buf);
+          buf = 0;
+          return;
+        }
+
+      NS_LOG_INFO ("TapBridge::ReadThread(): Received packet");
+      NS_LOG_INFO ("TapBridge::ReadThread(): Scheduling handler");
+      DynamicCast<RealtimeSimulatorImpl> (Simulator::GetImplementation ())->ScheduleRealtimeNow (
+        MakeEvent (&TapBridge::ForwardToBridgedDevice, this, buf, len));
+      buf = 0;
+    }
+}
+
+void
+TapBridge::ForwardToBridgedDevice (uint8_t *buf, uint32_t len)
+{
+  NS_LOG_FUNCTION (buf << len);
+
+  //
+  // Create a packet out of the buffer we received and free that buffer.
+  //
+  Ptr<Packet> packet = Create<Packet> (reinterpret_cast<const uint8_t *> (buf), len);
+  free (buf);
+  buf = 0;
+
+  //
+  // Make sure the packet we received is reasonable enough for the rest of the 
+  // system to handle and get it ready to be injected directly into an ns-3
+  // device.  What should come back is a packet with the Ethernet header 
+  // (and possibly an LLC header as well) stripped off.
+  //
+  Address src, dst;
+  uint16_t type;
+
+  NS_LOG_LOGIC ("Received packet from tap device");
+
+  Ptr<Packet> p = Filter (packet, &src, &dst, &type);
+  if (p == 0)
+    {
+      NS_LOG_LOGIC ("Discarding packet as unfit for ns-3 consumption");
+      return;
+    }
+
+  NS_LOG_LOGIC ("Pkt source is " << src);
+  NS_LOG_LOGIC ("Pkt destination is " << dst);
+  NS_LOG_LOGIC ("Pkt LengthType is " << type);
+
+  NS_LOG_LOGIC ("Forwarding packet");
+  m_bridgedDevice->Send (packet, dst, type);
+}
+
+Ptr<Packet>
+TapBridge::Filter (Ptr<Packet> p, Address *src, Address *dst, uint16_t *type)
+{
+  NS_LOG_FUNCTION (p);
+  uint32_t pktSize;
+
+  //
+  // We have a candidate packet for injection into ns-3.  We expect that since
+  // it came over a socket that provides Ethernet packets, it sould be big 
+  // enough to hold an EthernetHeader.  If it can't, we signify the packet 
+  // should be filtered out by returning 0.
+  //
+  pktSize = p->GetSize ();
+  EthernetHeader header (false);
+  if (pktSize < header.GetSerializedSize ())
+    {
+      return 0;
+    }
+
+  p->RemoveHeader (header);
+
+  NS_LOG_LOGIC ("Pkt source is " << header.GetSource ());
+  NS_LOG_LOGIC ("Pkt destination is " << header.GetDestination ());
+  NS_LOG_LOGIC ("Pkt LengthType is " << header.GetLengthType ());
+
+  //
+  // If the length/type is less than 1500, it corresponds to a length 
+  // interpretation packet.  In this case, it is an 802.3 packet and 
+  // will also have an 802.2 LLC header.  If greater than 1500, we
+  // find the protocol number (Ethernet type) directly.
+  //
+  if (header.GetLengthType () <= 1500)
+    {
+      *src = header.GetSource ();
+      *dst = header.GetDestination ();
+
+      pktSize = p->GetSize ();
+      LlcSnapHeader llc;
+      if (pktSize < llc.GetSerializedSize ())
+        {
+          return 0;
+        }
+
+        p->RemoveHeader (llc);
+        *type = llc.GetType ();
+    }
+  else
+    {
+      *src = header.GetSource ();
+      *dst = header.GetDestination ();
+      *type = header.GetLengthType ();
+    }
+
+  //
+  // What we give back is a packet without the Ethernet header (nor the 
+  // possible llc/snap header) on it.  We think it is ready to send on
+  // out the bridged net device.
+  //
+  return p;
+}
+
+Ptr<NetDevice>
+TapBridge::GetBridgedNetDevice (void)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return m_bridgedDevice;
+}
+
+void 
+TapBridge::SetBridgedNetDevice (Ptr<NetDevice> bridgedDevice)
+{
+  NS_LOG_FUNCTION (bridgedDevice);
+
+  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.  We use the 
+  // promiscuous mode hook to get both the source and destination addresses.
+  //
+  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 from = Mac48Address::ConvertFrom (src);
+  Mac48Address to = Mac48Address::ConvertFrom (dst);
+
+  //
+  // We hooked the promiscuous mode protocol handler so we could get the 
+  // destination address of the actual packet.  This means we will be getting
+  // PACKET_OTHERHOST packets (not broadcast, not multicast, not unicast to 
+  // this device, but to some other address).  We don't want to forward those
+  // PACKET_OTHERHOST packets so just ignore them
+  //
+  if (packetType == PACKET_OTHERHOST)
+    {
+      return;
+    }
+
+  //
+  // 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.  Once we do this, the bits in the packet will
+  // percolate up through the stack on the Linux host.
+  //
+  // The ns-3 net device that is the source of these bits has removed the MAC 
+  // header, so we have to put one back on.
+  //
+  Ptr<Packet> p = packet->Copy ();
+
+  EthernetHeader header = EthernetHeader (false);
+  header.SetSource (from);
+  header.SetDestination (to);
+  header.SetLengthType (protocol);
+  p->AddHeader (header);
+
+  NS_LOG_LOGIC ("Writing packet to Linux host");
+  NS_LOG_LOGIC ("Pkt source is " << header.GetSource ());
+  NS_LOG_LOGIC ("Pkt destination is " << header.GetDestination ());
+  NS_LOG_LOGIC ("Pkt LengthType is " << header.GetLengthType ());
+  NS_LOG_LOGIC ("Pkt size is " << p->GetSize ());
+
+  uint32_t bytesWritten = write (m_sock, p->PeekData (), p->GetSize ());
+  NS_ABORT_MSG_IF (bytesWritten != p->GetSize (), "TapBridge::ReceiveFromBridgedDevice(): Write error.");
+}
+
+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 ();
+  //
+  // Returning false from IsBridge in a device called TapBridge may seem odd
+  // at first glance, but this test is for a device that bridges ns-3 devices
+  // together.  The Tap bridge doesn't do that -- it bridges an ns-3 device to
+  // a Linux device.  This is a completely different story.
+  // 
+  return false;
+}
+
+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	Fri Feb 13 00:10:21 2009 -0500
@@ -0,0 +1,410 @@
+/* -*- 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 <string.h>
+#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/system-thread.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/tap 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.  This node will not be able to actually do anything else in the 
+ * simulation with respect to the Tap Bridge and its bridged net device.  This is 
+ * because:
+ *
+ * - Bits sent to the Tap Bridge using its Send() method are completely ignored.  
+ *   The Tap Bridge is not, itself, connected to any network.
+ * - 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.  You can send but you cannot ever receive.
+ * 
+ * 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, 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; but we 
+ * generally recommend that you treat this node as a ghost of the Linux host and 
+ * leave it alone.
+ */
+
+/**
+ * \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 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.  
+   *
+   * \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);
+
+  //
+  // The following methods are inherited from NetDevice base class and are
+  // documented there.
+  //
+  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:
+  /**
+   * \internal
+   *
+   * 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);
+
+  void ReceiveFromBridgedDevice (Ptr<NetDevice> device, Ptr<const Packet> packet, uint16_t protocol,
+                                 Address const &src, Address const &dst, PacketType packetType);
+private:
+
+  /**
+   * \internal
+   *
+   * 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);
+
+  /**
+   * \internal
+   *
+   * Figure out where the tap creation program lives on the system.
+   *
+   * \param creatorName The name of the program used to create the Tap.
+   * \returns A path name to use when you want to create a Tap.
+   */
+  std::string FindCreator (std::string creatorName);
+
+  /**
+   * \internal
+   *
+   * Spin up the device
+   */
+  void StartTapDevice (void);
+
+  /**
+   * \internal
+   *
+   * Tear down the device
+   */
+  void StopTapDevice (void);
+
+  /**
+   * \internal
+   *
+   * Loop to read and process packets
+   */
+  void ReadThread (void);
+
+  /*
+   * \internal
+   *
+   * Forward a packet received from the tap device to the bridged ns-3 
+   * device
+   *
+   * \param buf A character buffer containing the actaul packet bits that were
+   *            received from the host.
+   * \param buf The length of the buffer.
+   */
+  void ForwardToBridgedDevice (uint8_t *buf, uint32_t len);
+
+  /**
+   * \internal
+   *
+   * 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).
+   */   
+  Ptr<Packet> Filter (Ptr<Packet> packet, Address *src, Address *dst, uint16_t *type);
+
+  /**
+   * \internal
+   *
+   * 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;
+
+  /**
+   * \internal
+   *
+   * 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;
+
+  /**
+   * \internal
+   *
+   * Pointer to the (ghost) Node to which we are connected.
+   */
+  Ptr<Node> m_node;
+
+  /**
+   * \internal
+   *
+   * A possible name for the (ghost) Node to which we are connected.
+   */
+  std::string m_name;
+
+  /**
+   * \internal
+   *
+   * The ns-3 interface index of this TapBridge net device.
+   */
+  uint32_t m_ifIndex;
+
+  /**
+   * \internal
+   *
+   * The common mtu to use for the net devices
+   */
+  uint16_t m_mtu;
+
+  /**
+   * \internal
+   *
+   * The socket (actually interpreted as fd) to use to talk to the Tap device on
+   * the real internet host.
+   */
+  int32_t m_sock;
+
+  /**
+   * \internal
+   *
+   * 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;
+
+  /**
+   * \internal
+   *
+   * 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;
+
+  /**
+   * \internal
+   *
+   * Used to identify the ns-3 read thread used to do blocking reads on the 
+   * socket (fd) corresponding to the host device.
+   */
+  Ptr<SystemThread> m_readThread;
+
+  /**
+   * \internal
+   *
+   * 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.  The MAC address assigned to the internet
+   * host actually comes from the bridged (N.B. the "ed") device and not from 
+   * the bridge device.
+   */
+  Mac48Address m_address;
+
+  /**
+   * \internal
+   *
+   * Time to start spinning up the device
+   */
+  Time m_tStart;
+
+  /**
+   * \internal
+   *
+   * Time to start tearing down the device
+   */
+  Time m_tStop;
+
+  /**
+   * \internal
+   *
+   * 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;
+
+  /**
+   * \internal
+   *
+   * The IP address to use as the device default gateway on the host.
+   */
+  Ipv4Address m_tapGateway;
+
+  /**
+   * \internal
+   *
+   * The IP address to use as the device IP on the host.
+   */
+  Ipv4Address m_tapIp;
+  /**
+   * \internal
+   *
+   * The MAC address to use as the hardware address on the host.
+   */
+  Mac48Address m_tapMac;
+
+  /**
+   * \internal
+   *
+   * The network mask to assign to the device created on the host.
+   */
+  Ipv4Mask m_tapNetmask;
+
+  /**
+   * \internal
+   *
+   * The ns-3 net device to which we are bridging.
+   */
+  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-creator.cc	Fri Feb 13 00:10:21 2009 -0500
@@ -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 <string.h> // for strerror
+#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>
+#if 0
+#include <linux/un.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#include <linux/route.h>
+#else
+#include <sys/un.h>
+#include <net/if.h>
+#include <linux/if_tun.h>
+#include <net/route.h>
+#endif
+#include <netinet/in.h>
+
+#include "tap-encode-decode.h"
+
+#define TAP_MAGIC 95549
+
+static int gVerbose = 0; // Set to true to turn on logging messages.
+
+#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); \
+    }
+
+//
+// Lots of the following helper code taken from corresponding functions in src/node.
+//
+#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);
+}
+
+  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 *mac, const char *netmask)
+{
+  //
+  // 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 = (char *)"";
+  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 '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;
+        case 'v':
+          gVerbose = true;
+          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.  If this name is provided we use it.  If not we let the system
+  // create the device for us.  This name is given in dev
+  //
+  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 \"" << gw << "\"");
+
+  //
+  // 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-encode-decode.cc	Fri Feb 13 00:10:21 2009 -0500
@@ -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	Fri Feb 13 00:10:21 2009 -0500
@@ -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.h	Fri Feb 13 00:10:21 2009 -0500
@@ -0,0 +1,68 @@
+/**
+ * \ingroup devices
+ * \defgroup TapBridgeModel Tap Bridge Model
+ *
+ * \section TapBridgeModelOverview TapBridge Model Overview
+ *
+ * The Tap Bridge is designed to integrate "real" internet hosts (or more 
+ * precisely, hosts that support Tun/Tap devices) into ns-3 simulations.  The 
+ * goal is to make it appear to the host host node in question that it has an
+ * ns-3 net device as a local device.  The concept of a "real host" is a bit
+ * slippery the "real host" may actually be virtualized using readily avialable
+ * technologies such as VMware or OpenVZ.
+ *
+ * Since we are, in essence, connecting the inputs and outputs of an ns-3 net 
+ * device to the inputs and outputs of a Linux Tap net device, we call this 
+ * arrangement a Tap Bridge.
+ *
+ * The TapBridge appears to the Linux host computer as a network device just
+ * like any arbitrary "eth0" or "ath0" might appear.  The creation and
+ * configuration of the device is done by the ns-3 simulation, however.  You 
+ * should not expect to be able to configure a net device via, for example,
+ * wlanconfig.  The IP addresses, MAC addresses, gateway, etc., for the given
+ * Tap device are also set up by the ns-3 simulation.  If you change the
+ * or manipulate the configuration manually, you will almost certainly break
+ * the simulation.
+ *
+ * The TapBridge appears to an ns-3 simulation as a channel-less net device.
+ * This device, however, must _not_ have an IP address associated with it.  
+ * Be aware that this is the inverse situation of an ns-3 BridgeNetDevice
+ * which demands that its bridge ports not have IP addresses, but allows the
+ * bridge to have an IP address.  
+ *
+ * The host computer will appear in a simulation as a "ghost" node that contains
+ * pairs of net devices and Tap bridges that represent the host devices.  From
+ * the perspective of a simulation, the only difference between a ghost node and
+ * another node will be the presence of the TapBridge devices that connect to
+ * the hosts.  Configuration of address information and the ns-3 devices is
+ * not changed in any way.  A TapBridge will pick up the addressing info from
+ * the ns-3 net device to which it is connected (its "bridged" net device) and
+ * use that information to configure the device on the real host.
+ *
+ * The end result of this is a situation where one can, for example, use the
+ * standard ping utility on a real host to ping a simulated ns-3 net device.  If
+ * correct routes are added to the internet host, the routing systems in ns-3
+ * will enable correct routing of the packets across simulated ns-3 networks.
+ * For an example of this, see the example program, tap-dumbbell.cc in the 
+ * ns-3 distribution.
+ *
+ * \section TapBridgeChannelModel Tap Bridge Channel Model
+ *
+ * There is no channel model associated with the Tap Bridge.  In fact, the
+ * intention is make it appear that the real internet host is connected to 
+ * the channel of the bridged net device.
+ *
+ * \section TapBridgeTracingModel Tap Bridge Tracing Model
+ *
+ * Unlike most ns-3 devices, the TapBridge does not provide any standard trace
+ * sources.  This is because the bridge is an intermediary that is essentially
+ * one function call away from the bridged device.  We expect that the trace
+ * hooks in the bridged device will be sufficient for most users,
+ *
+ * \section TapBridgeUsage Using the Tap Bridge
+ *
+ * We expect that most users will interact with the TapBridge device through
+ * the TapBridgeHelper.  Users of other helper classes, such as CSMA or Wifi,
+ * should be comfortable with the idioms used.
+ */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/devices/tap-bridge/waf	Fri Feb 13 00:10:21 2009 -0500
@@ -0,0 +1,1 @@
+exec "`dirname "$0"`"/../../../waf "$@"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/devices/tap-bridge/wscript	Fri Feb 13 00:10:21 2009 -0500
@@ -0,0 +1,43 @@
+## -*- 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_tun.h',
+                                            define_name='HAVE_IF_TUN_H')
+        conf.report_optional_feature("TapBridge", "Tap Bridge",
+                                     conf.env['ENABLE_TAP'],
+                                     "<linux/if_tun.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',
+                ])
+
+
+        if not env['PLATFORM'].startswith('freebsd'):
+            obj = bld.create_suid_program('tap-creator')
+            obj.source = [
+                'tap-creator.cc',
+                'tap-encode-decode.cc',
+                ]
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/helper/tap-bridge-helper.cc	Fri Feb 13 00:10:21 2009 -0500
@@ -0,0 +1,55 @@
+/* -*- 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 (Ipv4Address gateway)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  m_deviceFactory.SetTypeId ("ns3::TapBridge");
+  SetAttribute ("Gateway", Ipv4AddressValue (gateway));
+}
+
+void 
+TapBridgeHelper::SetAttribute (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->SetBridgedNetDevice (nd);
+
+  return bridge;
+}
+
+} // namespace ns3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/helper/tap-bridge-helper.h	Fri Feb 13 00:10:21 2009 -0500
@@ -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 (Ipv4Address gateway);
+  void SetAttribute (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	Thu Feb 12 16:49:58 2009 -0500
+++ b/src/helper/wscript	Fri Feb 13 00:10:21 2009 -0500
@@ -58,4 +58,11 @@
         headers.source.extend([
                 'emu-helper.h',
                 ])
+    if env['ENABLE_TAP']:
+        helper.source.extend([
+                'tap-bridge-helper.cc',
+                ])
+        headers.source.extend([
+                'tap-bridge-helper.h',
+                ])
 
--- a/src/internet-stack/arp-header.cc	Thu Feb 12 16:49:58 2009 -0500
+++ b/src/internet-stack/arp-header.cc	Fri Feb 13 00:10:21 2009 -0500
@@ -143,18 +143,32 @@
   WriteTo (i, m_macDest);
   WriteTo (i, m_ipv4Dest);
 }
+
 uint32_t
 ArpHeader::Deserialize (Buffer::Iterator start)
 {
   Buffer::Iterator i = start;
-  i.Next (2+2);
-  uint32_t hardwareAddressLen = i.ReadU8 ();
-  i.Next (1);
-  m_type = i.ReadNtohU16 ();
-  ReadFrom (i, m_macSource, hardwareAddressLen);
-  ReadFrom (i, m_ipv4Source);
-  ReadFrom (i, m_macDest, hardwareAddressLen);
-  ReadFrom (i, m_ipv4Dest);
+  i.Next (2);                                    // Skip HRD
+  uint32_t protocolType = i.ReadNtohU16 ();      // Read PRO
+  uint32_t hardwareAddressLen = i.ReadU8 ();     // Read HLN
+  uint32_t protocolAddressLen = i.ReadU8 ();     // Read PLN
+
+  //
+  // It is implicit here that we have a protocol type of 0x800 (IP).
+  // It is also implicit here that we are using Ipv4 (PLN == 4).
+  // If this isn't the case, we need to return an error since we don't want to 
+  // be too fragile if we get connected to real networks.
+  //
+  if (protocolType != 0x800 || protocolAddressLen != 4)
+    {
+      return 0;
+    }
+
+  m_type = i.ReadNtohU16 ();                     // Read OP
+  ReadFrom (i, m_macSource, hardwareAddressLen); // Read SHA (size HLN)
+  ReadFrom (i, m_ipv4Source);                    // Read SPA (size PLN == 4)
+  ReadFrom (i, m_macDest, hardwareAddressLen);   // Read THA (size HLN)
+  ReadFrom (i, m_ipv4Dest);                      // Read TPA (size PLN == 4)
   return GetSerializedSize ();
 }
 
--- a/src/internet-stack/arp-l3-protocol.cc	Thu Feb 12 16:49:58 2009 -0500
+++ b/src/internet-stack/arp-l3-protocol.cc	Fri Feb 13 00:10:21 2009 -0500
@@ -124,9 +124,24 @@
 
   Ptr<Packet> packet = p->Copy ();
 
+  NS_LOG_LOGIC ("ARP: received packet of size "<< packet->GetSize ());
+
   Ptr<ArpCache> cache = FindCache (device);
+
+  // 
+  // If we're connected to a real world network, then some of the fields sizes 
+  // in an ARP packet can vary in ways not seen in simulations.  We need to be
+  // able to detect ARP packets with headers we don't recongnize and not process
+  // them instead of crashing.  The ArpHeader will return 0 if it can't deal
+  // with the received header.
+  //
   ArpHeader arp;
-  packet->RemoveHeader (arp);
+  uint32_t size = packet->RemoveHeader (arp);
+  if (size == 0)
+    {
+      NS_LOG_LOGIC ("ARP: Cannot remove ARP header");
+      return;
+    }
   
   NS_LOG_LOGIC ("ARP: received "<< (arp.IsRequest ()? "request" : "reply") <<
             " node="<<m_node->GetId ()<<", got request from " <<
--- a/src/node/ipv4-address-generator.cc	Thu Feb 12 16:49:58 2009 -0500
+++ b/src/node/ipv4-address-generator.cc	Fri Feb 13 00:10:21 2009 -0500
@@ -17,6 +17,7 @@
  */
 
 #include <list>
+#include "ns3/abort.h"
 #include "ns3/assert.h"
 #include "ns3/log.h"
 #include "ns3/simulation-singleton.h"
@@ -137,17 +138,14 @@
 // We're going to be playing with the actual bits in the network and mask so
 // pull them out into ints.
 //
-  uint32_t maskBits __attribute__((unused)) = mask.Get ();
+  uint32_t maskBits = mask.Get ();
   uint32_t netBits = net.Get ();
   uint32_t addrBits = addr.Get ();
 //
 // Some quick reasonableness testing.
 //
-  NS_ASSERT_MSG((netBits & ~maskBits) == 0,
-    "Ipv4AddressGeneratorImpl::Init (): Inconsistent network and mask");
-
-  NS_ASSERT_MSG((addrBits & maskBits) == 0,
-    "Ipv4AddressGeneratorImpl::Init (): Inconsistent address and mask");
+  NS_ABORT_MSG_UNLESS ((netBits & ~maskBits) == 0, "Ipv4AddressGeneratorImpl::Init (): Inconsistent network and mask");
+  NS_ABORT_MSG_UNLESS ((addrBits & maskBits) == 0, "Ipv4AddressGeneratorImpl::Init (): Inconsistent address and mask");
 
 //
 // Convert the network mask into an index into the network number table.
@@ -158,10 +156,7 @@
   uint32_t index = MaskToIndex (mask);
 
   m_netTable[index].network = netBits >> m_netTable[index].shift;
-
-  NS_ASSERT_MSG (addrBits <= m_netTable[index].addrMax,
-    "Ipv4AddressGeneratorImpl::Init(): Address overflow");
-
+  NS_ABORT_MSG_UNLESS (addrBits <= m_netTable[index].addrMax, "Ipv4AddressGeneratorImpl::Init(): Address overflow");
   m_netTable[index].addr = addrBits;
   return;
 }
@@ -205,9 +200,7 @@
   uint32_t index = MaskToIndex (mask);
   uint32_t addrBits = addr.Get ();
 
-  NS_ASSERT_MSG (addrBits <= m_netTable[index].addrMax,
-    "Ipv4AddressGeneratorImpl::InitAddress(): Address overflow");
-
+  NS_ABORT_MSG_UNLESS (addrBits <= m_netTable[index].addrMax, "Ipv4AddressGeneratorImpl::InitAddress(): Address overflow");
   m_netTable[index].addr = addrBits;
 }
 
@@ -237,8 +230,8 @@
 //
   uint32_t index = MaskToIndex (mask);
 
-  NS_ASSERT_MSG (m_netTable[index].addr <= m_netTable[index].addrMax,
-    "Ipv4AddressGeneratorImpl::NextAddress(): Address overflow");
+  NS_ABORT_MSG_UNLESS (m_netTable[index].addr <= m_netTable[index].addrMax,
+                       "Ipv4AddressGeneratorImpl::NextAddress(): Address overflow");
 
   Ipv4Address addr = Ipv4Address (
     (m_netTable[index].network << m_netTable[index].shift) |
@@ -260,8 +253,7 @@
 
   uint32_t addr = address.Get ();
 
-  NS_ASSERT_MSG (addr, "Ipv4AddressGeneratorImpl::Add(): "
-    "Allocating the broadcast address is not a good idea"); 
+  NS_ABORT_MSG_UNLESS (addr, "Ipv4AddressGeneratorImpl::Add(): Allocating the broadcast address is not a good idea"); 
  
   std::list<Entry>::iterator i;
 
@@ -275,12 +267,10 @@
 //
       if (addr >= (*i).addrLow && addr <= (*i).addrHigh)
         {
-          NS_LOG_LOGIC ("Ipv4AddressGeneratorImpl::Add(): "
-            "Address Collision: " << Ipv4Address (addr)); 
+          NS_LOG_LOGIC ("Ipv4AddressGeneratorImpl::Add(): Address Collision: " << Ipv4Address (addr)); 
           if (!m_test) 
             {
-              NS_ASSERT_MSG (0, "Ipv4AddressGeneratorImpl::Add(): "
-                "Address Collision: " << Ipv4Address (addr)); 
+              NS_FATAL_ERROR ("Ipv4AddressGeneratorImpl::Add(): Address Collision: " << Ipv4Address (addr));
             }
           return false;
        }
@@ -313,9 +303,7 @@
                     "Address Collision: " << Ipv4Address (addr)); 
                   if (!m_test)
                     {
-                      NS_ASSERT_MSG (0, 
-                        "Ipv4AddressGeneratorImpl::Add(): "
-                     "Address Collision: " << Ipv4Address (addr));
+                      NS_FATAL_ERROR ("Ipv4AddressGeneratorImpl::Add(): Address Collision: " << Ipv4Address (addr));
                     }
                   return false;
                 }
@@ -374,10 +362,7 @@
       if (maskBits & 1)
         {
           uint32_t index = N_BITS - i;
-
-          NS_ASSERT_MSG (index > 0 && index < N_BITS,
-            "Ipv4AddressGenerator::MaskToIndex(): Illegal Mask");
-
+          NS_ABORT_MSG_UNLESS (index > 0 && index < N_BITS, "Ipv4AddressGenerator::MaskToIndex(): Illegal Mask");
           return index;
         }
       maskBits >>= 1;
--- a/src/wscript	Thu Feb 12 16:49:58 2009 -0500
+++ b/src/wscript	Fri Feb 13 00:10:21 2009 -0500
@@ -23,6 +23,7 @@
     'devices/csma',
     'devices/emu',
     'devices/bridge',
+    'devices/tap-bridge',
     'applications/onoff',
     'applications/packet-sink',
     'applications/udp-echo',
@@ -44,16 +45,16 @@
                          " --run and --shell; moreover, only works in some"
                          " specific platforms, such as Linux and Solaris)"),
                    action="store_true", dest='enable_rpath', default=False)
-
+    
     opt.add_option('--enable-modules',
                    help=("Build only these modules (and dependencies)"),
                    dest='enable_modules')
 
-
 def configure(conf):
     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')
 
--- a/wscript	Thu Feb 12 16:49:58 2009 -0500
+++ b/wscript	Fri Feb 13 00:10:21 2009 -0500
@@ -17,6 +17,7 @@
 ccroot.USE_TOP_LEVEL = True
 
 import Task
+import Constants
 import Utils
 import Build
 import Configure
@@ -133,7 +134,10 @@
                    help=('Run a shell with an environment suitably modified to run locally built programs'),
                    action="store_true", default=False,
                    dest='shell')
-
+    opt.add_option('--enable-sudo',
+                   help=('Use sudo to setup suid bits on ns3 executables.'),
+                   dest='enable_sudo', action='store_true',
+                   default=False)
     opt.add_option('--regression',
                    help=("Enable regression testing; only used for the 'check' target"),
                    default=False, dest='regression', action="store_true")
@@ -144,10 +148,6 @@
                    help=('For regression testing, only run/generate the indicated regression tests, '
                          'specified as a comma separated list of test names'),
                    dest='regression_tests', type="string")
-    opt.add_option('--enable-sudo',
-                   help=('Use sudo to setup suid bits on ns3 executables.'),
-                   dest='enable_sudo', action='store_true',
-                   default=False)
     opt.add_option('--with-regression-traces',
                    help=('Path to the regression reference traces directory'),
                    default=None,
@@ -240,6 +240,8 @@
         env.append_value('CXXDEFINES', 'NS3_ASSERT_ENABLE')
         env.append_value('CXXDEFINES', 'NS3_LOG_ENABLE')
 
+    env['PLATFORM'] = sys.platform
+
     if sys.platform == 'win32':
         if env['COMPILER_CXX'] == 'g++':
             env.append_value("LINKFLAGS", "-Wl,--enable-runtime-pseudo-reloc")
@@ -258,6 +260,18 @@
     # for suid bits
     conf.find_program('sudo', var='SUDO')
 
+    why_not_sudo = "because we like it"
+    if Options.options.enable_sudo and conf.env['SUDO']:
+        env['ENABLE_SUDO'] = True
+    else:
+        env['ENABLE_SUDO'] = False
+        if Options.options.enable_sudo:
+            why_not_sudo = "program sudo not found"
+        else:
+            why_not_sudo = "option --enable-sudo not selected"
+
+    conf.report_optional_feature("ENABLE_SUDO", "Use sudo to set suid bit", env['ENABLE_SUDO'], why_not_sudo)
+
     # we cannot pull regression traces without mercurial
     conf.find_program('hg', var='MERCURIAL')
 
@@ -276,28 +290,37 @@
 class SuidBuildTask(Task.TaskBase):
     """task that makes a binary Suid
     """
-    after = 'link'
+    after = 'cxx_link cc_link'
+    maxjobs = 1
     def __init__(self, bld, program):
         self.m_display = 'build-suid'
         self.__program = program
-        self.__env = bld.env ()
+        self.__env = bld.env.copy ()
         super(SuidBuildTask, self).__init__()
-
-    def run(self):
         try:
             program_obj = wutils.find_program(self.__program.target, self.__env)
         except ValueError, ex:
             raise Utils.WafError(str(ex))
+        program_node = program_obj.path.find_or_declare(ccroot.get_target_name(program_obj))
+        self.filename = program_node.abspath(self.__env)
 
-        program_node = program_obj.path.find_or_declare(ccroot.get_target_name(program_obj))
-        #try:
-        #    program_node = program_obj.path.find_or_declare(ccroot.get_target_name(program_obj))
-        #except AttributeError:
-        #    raise Utils.WafError("%s does not appear to be a program" % (self.__program.name,))
+
+    def run(self):
+        print >> sys.stderr, 'setting suid bit on executable ' + self.filename
+        if subprocess.Popen(['sudo', 'chown', 'root', self.filename]).wait():
+            return 1
+        if subprocess.Popen(['sudo', 'chmod', 'u+s', self.filename]).wait():
+            return 1
+        return 0
 
-        filename = program_node.abspath(self.__env)
-        os.system ('sudo chown root ' + filename)
-        os.system ('sudo chmod u+s ' + filename)
+    def runnable_status(self):
+        "RUN_ME SKIP_ME or ASK_LATER"
+        st = os.stat(self.filename)
+        if st.st_uid == 0:
+            return Constants.SKIP_ME
+        else:
+            return Constants.RUN_ME
+
 
 def create_suid_program(bld, name):
     program = bld.new_task_gen('cxx', 'program')
@@ -305,8 +328,10 @@
     program.module_deps = list()
     program.name = name
     program.target = name
-    if bld.env['SUDO'] and Options.options.enable_sudo:
+
+    if bld.env['ENABLE_SUDO']:
         SuidBuildTask(bld, program)
+
     return program
 
 def create_ns3_program(bld, name, dependencies=('simulator',)):