checkpoint tap bridge
authorCraig Dowell <craigdo@ee.washington.edu>
Tue, 27 Jan 2009 20:26:34 -0800
changeset 4164 1f6ae48061a9
parent 4163 8c48682b3d42
child 4165 40789460dd8c
checkpoint tap bridge
src/devices/tap-bridge/tap-bridge.cc
src/devices/tap-bridge/tap-bridge.h
src/devices/tap-bridge/tap-creator.cc
src/devices/tap-bridge/tap-sock-creator.cc
src/devices/tap-bridge/wscript
src/wscript
wscript
--- a/src/devices/tap-bridge/tap-bridge.cc	Tue Jan 27 12:36:46 2009 -0800
+++ b/src/devices/tap-bridge/tap-bridge.cc	Tue Jan 27 20:26:34 2009 -0800
@@ -17,6 +17,8 @@
  */
 
 #include "tap-bridge.h"
+#include "tap-encode-decode.h"
+
 #include "ns3/node.h"
 #include "ns3/channel.h"
 #include "ns3/packet.h"
@@ -24,10 +26,26 @@
 #include "ns3/boolean.h"
 #include "ns3/simulator.h"
 
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/ioctl.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netpacket/packet.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <limits>
+#include <stdlib.h>
+
 NS_LOG_COMPONENT_DEFINE ("TapBridge");
 
 namespace ns3 {
 
+#define TAP_MAGIC 95549
+
 NS_OBJECT_ENSURE_REGISTERED (TapBridge);
 
 TypeId
@@ -40,10 +58,11 @@
   return tid;
 }
 
-
 TapBridge::TapBridge ()
   : m_node (0),
-    m_ifIndex (0)
+    m_ifIndex (0),
+    m_mtu (0),
+    m_sock (-1)
 {
   NS_LOG_FUNCTION_NOARGS ();
 }
@@ -60,6 +79,248 @@
   NetDevice::DoDispose ();
 }
 
+void
+TapBridge::CreateTap (void)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  //
+  // 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);
+  if (sock == -1)
+    {
+      NS_FATAL_ERROR ("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));
+  if (status == -1)
+    {
+      NS_FATAL_ERROR ("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);
+  if (status == -1)
+    {
+      NS_FATAL_ERROR ("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 and we use program arguments to do so.
+      //
+      // -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-sock-creator -dnewdev -g1.2.3.2 -i1.2.3.1 -m08:00:2e:00:01:23 -n255.255.255.0 -pblah
+      //
+      std::ostringstream oss;
+      oss << "-d" << m_tapDeviceName << " -g" << m_tapGateway << " -i" << m_tapIp << " -m" << m_tapMac
+          << " -n" << m_tapNetmask << " -p" << path;
+      NS_LOG_INFO ("creator arguments set to \"" << oss.str () << "\"");
+
+      //
+      // Execute the socket creation process image.
+      //
+      status = ::execl (FindCreator ().c_str (), "tap-sock-creator", oss.str ().c_str (), (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);
+      if (waited == -1)
+	{
+	  NS_FATAL_ERROR ("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);
+          if (exitStatus != 0)
+            {
+              NS_FATAL_ERROR ("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);
+      if (bytesRead != sizeof(int))
+	{
+          NS_FATAL_ERROR ("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");
+    }
+}
+
+std::string
+TapBridge::FindCreator (void)
+{
+  struct stat st;
+  std::string debug = "./build/debug/src/devices/tap-bridge/tap-sock-creator";
+  std::string optimized = "./build/optimized/src/devices/tap-bridge/tap-sock-creator";
+
+  if (::stat (debug.c_str (), &st) == 0)
+    {
+      return debug;
+    }
+
+  if (::stat (optimized.c_str (), &st) == 0)
+    {
+      return optimized;
+    }
+
+  NS_FATAL_ERROR ("TapBridge::FindCreator(): Couldn't find creator");
+  return ""; // quiet compiler
+}
+
 void 
 TapBridge::SetBridgedDevice (Ptr<NetDevice> bridgedDevice)
 {
--- a/src/devices/tap-bridge/tap-bridge.h	Tue Jan 27 12:36:46 2009 -0800
+++ b/src/devices/tap-bridge/tap-bridge.h	Tue Jan 27 20:26:34 2009 -0800
@@ -111,6 +111,20 @@
                                  Address const &src, Address const &dst, PacketType packetType);
 
 private:
+
+  /**
+   * Call out to a separate process running as suid root in order to get our
+   * tap device created.  We do this to avoid having the entire simulation 
+   * running as root.  If this method returns, we'll have a socket waiting 
+   * for us in m_sock that we can use to talk to the tap device.
+   */
+  void CreateTap (void);
+
+  /**
+   * Figure out where the tap creation program lives on the system.
+   */
+  std::string FindCreator (void);
+
   NetDevice::ReceiveCallback m_rxCallback;
   NetDevice::PromiscReceiveCallback m_promiscRxCallback;
 
@@ -120,6 +134,14 @@
   uint32_t m_ifIndex;
   uint16_t m_mtu;
 
+  int32_t m_sock;
+
+  std::string m_tapDeviceName;
+  std::string m_tapGateway;
+  std::string m_tapIp;
+  std::string m_tapMac;
+  std::string m_tapNetmask;
+
   Ptr<NetDevice> m_bridgedDevice;
 };
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/devices/tap-bridge/tap-creator.cc	Tue Jan 27 20:26:34 2009 -0800
@@ -0,0 +1,464 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2009 University of Washington
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <unistd.h>
+#include <stdint.h>
+#include <string>
+#include <iostream>
+#include <iomanip>
+#include <sstream>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <linux/un.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#include <linux/route.h>
+#include <netinet/in.h>
+
+#include "tap-encode-decode.h"
+
+#define TAP_MAGIC 95549
+
+static int gVerbose = 0;
+
+#define LOG(msg) \
+  if (gVerbose) \
+    { \
+      std::cout << __FUNCTION__ << "(): " << msg << std::endl;   \
+    }
+
+#define ABORT(msg, printErrno) \
+  std::cout << __FILE__ << ": fatal error at line " << __LINE__ << ": " << __FUNCTION__ << "(): " << msg << std::endl; \
+  if (printErrno) \
+    { \
+      std::cout << "    errno = " << errno << " (" << strerror (errno) << ")" << std::endl; \
+    } \
+  exit (-1); 
+
+#define ABORT_IF(cond, msg, printErrno) \
+  if (cond) \
+    { \
+      ABORT(msg, printErrno); \
+    }
+
+//
+// Thanks, Mathieu, for the beginning of these functions
+//
+#define ASCII_DOT (0x2e)
+#define ASCII_ZERO (0x30)
+#define ASCII_a (0x41)
+#define ASCII_z (0x5a)
+#define ASCII_A (0x61)
+#define ASCII_Z (0x7a)
+#define ASCII_COLON (0x3a)
+#define ASCII_ZERO (0x30)
+
+static char
+AsciiToLowCase (char c)
+{
+  if (c >= ASCII_a && c <= ASCII_z) {
+    return c;
+  } else if (c >= ASCII_A && c <= ASCII_Z) {
+    return c + (ASCII_a - ASCII_A);
+  } else {
+    return c;
+  }
+}
+
+static uint32_t 
+AsciiToIpv4 (const char *address)
+{
+  uint32_t host = 0;
+  while (true) {
+    uint8_t byte = 0;
+    while (*address != ASCII_DOT &&
+           *address != 0) {
+      byte *= 10;
+      byte += *address - ASCII_ZERO;
+      address++;
+    }
+    host <<= 8;
+    host |= byte;
+    if (*address == 0) {
+      break;
+    }
+    address++;
+  }
+  return host;
+}
+
+static void 
+AsciiToMac48 (const char *str, uint8_t addr[6])
+{
+  int i = 0;
+  while (*str != 0 && i < 6) 
+    {
+      uint8_t byte = 0;
+      while (*str != ASCII_COLON && *str != 0) 
+	{
+	  byte <<= 4;
+	  char low = AsciiToLowCase (*str);
+	  if (low >= ASCII_a) 
+	    {
+	      byte |= low - ASCII_a + 10;
+	    } 
+	  else 
+	    {
+	      byte |= low - ASCII_ZERO;
+	    }
+	  str++;
+	}
+      addr[i] = byte;
+      i++;
+      if (*str == 0) 
+	{
+	  break;
+	}
+      str++;
+    }
+}
+
+static void
+SetInetAddress (sockaddr *ad, uint32_t networkOrder)
+{
+  struct sockaddr_in *sin = (struct sockaddr_in*)ad;
+  sin->sin_family = AF_INET;
+  sin->sin_port = 0; // unused
+  sin->sin_addr.s_addr = htonl (networkOrder);
+}
+
+/**
+ * \brief Send the socket file descriptor we created back to the tap bridge, 
+ * which will read it as soon as we're done.
+ *
+ * \param path The socket address information from the Unix socket we use
+ * to send the created socket back to.
+ * \param fd The socket we're going to send. 
+ */
+  static void
+SendSocket (const char *path, int fd)
+{
+  //
+  // Open a Unix (local interprocess) socket to call back to the tap bridge
+  //
+  LOG ("Create Unix socket");
+  int sock = socket (PF_UNIX, SOCK_DGRAM, 0);
+  ABORT_IF (sock == -1, "Unable to open socket", 1);
+  
+  //
+  // We have this string called path, which is really a hex representation
+  // of the endpoint that the tap bridge created.  It used a forward encoding
+  // method (TapBufferToString) to take the sockaddr_un it made and passed 
+  // the resulting string to us.  So we need to take the inverse method
+  // (TapStringToBuffer) and build the same sockaddr_un over here.
+  //
+  socklen_t clientAddrLen;
+  struct sockaddr_un clientAddr;
+
+  LOG ("Decode address " << path);
+  bool rc = ns3::TapStringToBuffer (path, (uint8_t *)&clientAddr, &clientAddrLen);
+  ABORT_IF (rc == false, "Unable to decode path", 0);
+
+  LOG ("Connect");
+  int status = connect (sock, (struct sockaddr*)&clientAddr, clientAddrLen);
+  ABORT_IF (status == -1, "Unable to connect to tap bridge", 1);
+
+  LOG ("Connected");
+
+  //
+  // This is arcane enough that a few words are worthwhile to explain what's 
+  // going on here.
+  //
+  // The interesting information (the socket FD) is going to go back to the
+  // tap bridge as an integer of ancillary data.  Ancillary data is bits 
+  // that are not a part a socket payload (out-of-band data).  We're also 
+  // going to send one integer back.  It's just initialized to a magic number
+  // we use to make sure that the tap bridge is talking to the tap socket 
+  // creator and not some other creator process (emu, specifically)
+  //
+  // The struct iovec below is part of a scatter-gather list.  It describes a
+  // buffer.  In this case, it describes a buffer (an integer) containing the
+  // data that we're going to send back to the tap bridge (that magic number).
+  // 
+  struct iovec iov;
+  uint32_t magic = TAP_MAGIC;
+  iov.iov_base = &magic;
+  iov.iov_len = sizeof(magic);
+
+  //
+  // The CMSG macros you'll see below are used to create and access control 
+  // messages (which is another name for ancillary data).  The ancillary 
+  // data is made up of pairs of struct cmsghdr structures and associated
+  // data arrays.
+  // 
+  // First, we're going to allocate a buffer on the stack to contain our 
+  // data array (that contains the socket).  Sometimes you'll see this called
+  // an "ancillary element" but the msghdr uses the control message termimology
+  // so we call it "control."
+  //
+  size_t msg_size = sizeof(int);
+  char control[CMSG_SPACE(msg_size)];
+
+  //
+  // There is a msghdr that is used to minimize the number of parameters
+  // passed to sendmsg (which we will use to send our ancillary data).  This
+  // structure uses terminology corresponding to control messages, so you'll
+  // see msg_control, which is the pointer to the ancillary data and controllen
+  // which is the size of the ancillary data array.
+  //
+  // So, initialize the message header that describes our ancillary/control data
+  // and point it to the control message/ancillary data we just allocated space
+  // for.
+  //
+  struct msghdr msg;
+  msg.msg_name = 0;
+  msg.msg_namelen = 0;
+  msg.msg_iov = &iov;
+  msg.msg_iovlen = 1;
+  msg.msg_control = control;
+  msg.msg_controllen = sizeof (control);
+  msg.msg_flags = 0;
+
+  //
+  // A cmsghdr contains a length field that is the length of the header and
+  // the data.  It has a cmsg_level field corresponding to the originating 
+  // protocol.  This takes values which are legal levels for getsockopt and
+  // setsockopt (here SOL_SOCKET).  We're going to use the SCM_RIGHTS type of 
+  // cmsg, that indicates that the ancillary data array contains access rights 
+  // that we are sending back to the tap bridge.
+  //
+  // We have to put together the first (and only) cmsghdr that will describe
+  // the whole package we're sending.
+  //
+  struct cmsghdr *cmsg;
+  cmsg = CMSG_FIRSTHDR(&msg);
+  cmsg->cmsg_level = SOL_SOCKET;
+  cmsg->cmsg_type = SCM_RIGHTS;
+  cmsg->cmsg_len = CMSG_LEN(msg_size);
+  //
+  // We also have to update the controllen in case other stuff is actually
+  // in there we may not be aware of (due to macros).
+  //
+  msg.msg_controllen = cmsg->cmsg_len;
+
+  //
+  // Finally, we get a pointer to the start of the ancillary data array and
+  // put our file descriptor in.
+  //
+  int *fdptr = (int*) (CMSG_DATA(cmsg));
+  *fdptr = fd; // 
+
+  //
+  // Actually send the file descriptor back to the tap bridge.
+  //
+  ssize_t len = sendmsg(sock, &msg, 0);
+  ABORT_IF (len == -1, "Could not send socket back to tap bridge", 1);
+
+  LOG ("sendmsg complete");
+}
+
+  static int
+CreateTap (const char *dev, const char *gw, const char *ip, const char *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 'v':
+          gVerbose = true;
+          break;
+        case 'd':
+          dev = optarg;     // name of the new tap device
+          break;
+        case 'g':
+          gw = optarg;      // gateway address for the new device
+          break;
+        case 'i':
+          ip = optarg;      // ip address of the new device
+          break;
+        case 'm':
+          mac = optarg;     // mac address of the new device
+          break;
+        case 'n':
+          netmask = optarg; // net mask for the new device
+          break;
+        case 'p':
+          path = optarg;    // path back to the tap bridge
+          break;
+        }
+    }
+
+  //
+  // We have got to be able to coordinate the name of the tap device we are
+  // going to create and or open with the device that an external Linux host
+  // will use.  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);
+
+#if 1
+  for (;;)
+    {
+      LOG ("z");
+      sleep (1);
+    }
+#endif
+
+  //
+  // Send the socket back to the tap net device so it can go about its business
+  //
+  SendSocket (path, sock);
+
+  return 0;
+}
--- a/src/devices/tap-bridge/tap-sock-creator.cc	Tue Jan 27 12:36:46 2009 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,456 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/*
- * Copyright (c) 2009 University of Washington
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation;
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include <unistd.h>
-#include <stdint.h>
-#include <string>
-#include <iostream>
-#include <iomanip>
-#include <sstream>
-#include <stdlib.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <linux/un.h>
-#include <linux/if.h>
-#include <linux/if_tun.h>
-#include <linux/route.h>
-#include <netinet/in.h>
-
-#include "tap-encode-decode.h"
-
-#define TAP_MAGIC 95549
-
-static int gVerbose = 0;
-
-#define LOG(msg) \
-  if (gVerbose) \
-    { \
-      std::cout << __FUNCTION__ << "(): " << msg << std::endl;   \
-    }
-
-#define ABORT(msg, printErrno) \
-  std::cout << __FILE__ << ": fatal error at line " << __LINE__ << ": " << __FUNCTION__ << "(): " << msg << std::endl; \
-  if (printErrno) \
-    { \
-      std::cout << "    errno = " << errno << " (" << strerror (errno) << ")" << std::endl; \
-    } \
-  exit (-1); 
-
-#define ABORT_IF(cond, msg, printErrno) \
-  if (cond) \
-    { \
-      ABORT(msg, printErrno); \
-    }
-
-//
-// Thanks, Mathieu, for the beginning of these functions
-//
-#define ASCII_DOT (0x2e)
-#define ASCII_ZERO (0x30)
-#define ASCII_a (0x41)
-#define ASCII_z (0x5a)
-#define ASCII_A (0x61)
-#define ASCII_Z (0x7a)
-#define ASCII_COLON (0x3a)
-#define ASCII_ZERO (0x30)
-
-static char
-AsciiToLowCase (char c)
-{
-  if (c >= ASCII_a && c <= ASCII_z) {
-    return c;
-  } else if (c >= ASCII_A && c <= ASCII_Z) {
-    return c + (ASCII_a - ASCII_A);
-  } else {
-    return c;
-  }
-}
-
-static uint32_t 
-AsciiToIpv4 (const char *address)
-{
-  uint32_t host = 0;
-  while (true) {
-    uint8_t byte = 0;
-    while (*address != ASCII_DOT &&
-           *address != 0) {
-      byte *= 10;
-      byte += *address - ASCII_ZERO;
-      address++;
-    }
-    host <<= 8;
-    host |= byte;
-    if (*address == 0) {
-      break;
-    }
-    address++;
-  }
-  return host;
-}
-
-static void 
-AsciiToMac48 (const char *str, uint8_t addr[6])
-{
-  int i = 0;
-  while (*str != 0 && i < 6) 
-    {
-      uint8_t byte = 0;
-      while (*str != ASCII_COLON && *str != 0) 
-	{
-	  byte <<= 4;
-	  char low = AsciiToLowCase (*str);
-	  if (low >= ASCII_a) 
-	    {
-	      byte |= low - ASCII_a + 10;
-	    } 
-	  else 
-	    {
-	      byte |= low - ASCII_ZERO;
-	    }
-	  str++;
-	}
-      addr[i] = byte;
-      i++;
-      if (*str == 0) 
-	{
-	  break;
-	}
-      str++;
-    }
-}
-
-static void
-SetInetAddress (sockaddr *ad, uint32_t networkOrder)
-{
-  struct sockaddr_in *sin = (struct sockaddr_in*)ad;
-  sin->sin_family = AF_INET;
-  sin->sin_port = 0; // unused
-  sin->sin_addr.s_addr = htonl (networkOrder);
-}
-
-/**
- * \brief Send the socket file descriptor we created back to the tap bridge, 
- * which will read it as soon as we're done.
- *
- * \param path The socket address information from the Unix socket we use
- * to send the created socket back to.
- * \param fd The socket we're going to send. 
- */
-  static void
-SendSocket (const char *path, int fd)
-{
-  //
-  // Open a Unix (local interprocess) socket to call back to the tap bridge
-  //
-  LOG ("Create Unix socket");
-  int sock = socket (PF_UNIX, SOCK_DGRAM, 0);
-  ABORT_IF (sock == -1, "Unable to open socket", 1);
-  
-  //
-  // We have this string called path, which is really a hex representation
-  // of the endpoint that the tap bridge created.  It used a forward encoding
-  // method (TapBufferToString) to take the sockaddr_un it made and passed 
-  // the resulting string to us.  So we need to take the inverse method
-  // (TapStringToBuffer) and build the same sockaddr_un over here.
-  //
-  socklen_t clientAddrLen;
-  struct sockaddr_un clientAddr;
-
-  LOG ("Decode address " << path);
-  bool rc = ns3::TapStringToBuffer (path, (uint8_t *)&clientAddr, &clientAddrLen);
-  ABORT_IF (rc == false, "Unable to decode path", 0);
-
-  LOG ("Connect");
-  int status = connect (sock, (struct sockaddr*)&clientAddr, clientAddrLen);
-  ABORT_IF (status == -1, "Unable to connect to tap bridge", 1);
-
-  LOG ("Connected");
-
-  //
-  // This is arcane enough that a few words are worthwhile to explain what's 
-  // going on here.
-  //
-  // The interesting information (the socket FD) is going to go back to the
-  // tap bridge as an integer of ancillary data.  Ancillary data is bits 
-  // that are not a part a socket payload (out-of-band data).  We're also 
-  // going to send one integer back.  It's just initialized to a magic number
-  // we use to make sure that the tap bridge is talking to the tap socket 
-  // creator and not some other creator process (emu, specifically)
-  //
-  // The struct iovec below is part of a scatter-gather list.  It describes a
-  // buffer.  In this case, it describes a buffer (an integer) containing the
-  // data that we're going to send back to the tap bridge (that magic number).
-  // 
-  struct iovec iov;
-  uint32_t magic = TAP_MAGIC;
-  iov.iov_base = &magic;
-  iov.iov_len = sizeof(magic);
-
-  //
-  // The CMSG macros you'll see below are used to create and access control 
-  // messages (which is another name for ancillary data).  The ancillary 
-  // data is made up of pairs of struct cmsghdr structures and associated
-  // data arrays.
-  // 
-  // First, we're going to allocate a buffer on the stack to contain our 
-  // data array (that contains the socket).  Sometimes you'll see this called
-  // an "ancillary element" but the msghdr uses the control message termimology
-  // so we call it "control."
-  //
-  size_t msg_size = sizeof(int);
-  char control[CMSG_SPACE(msg_size)];
-
-  //
-  // There is a msghdr that is used to minimize the number of parameters
-  // passed to sendmsg (which we will use to send our ancillary data).  This
-  // structure uses terminology corresponding to control messages, so you'll
-  // see msg_control, which is the pointer to the ancillary data and controllen
-  // which is the size of the ancillary data array.
-  //
-  // So, initialize the message header that describes our ancillary/control data
-  // and point it to the control message/ancillary data we just allocated space
-  // for.
-  //
-  struct msghdr msg;
-  msg.msg_name = 0;
-  msg.msg_namelen = 0;
-  msg.msg_iov = &iov;
-  msg.msg_iovlen = 1;
-  msg.msg_control = control;
-  msg.msg_controllen = sizeof (control);
-  msg.msg_flags = 0;
-
-  //
-  // A cmsghdr contains a length field that is the length of the header and
-  // the data.  It has a cmsg_level field corresponding to the originating 
-  // protocol.  This takes values which are legal levels for getsockopt and
-  // setsockopt (here SOL_SOCKET).  We're going to use the SCM_RIGHTS type of 
-  // cmsg, that indicates that the ancillary data array contains access rights 
-  // that we are sending back to the tap bridge.
-  //
-  // We have to put together the first (and only) cmsghdr that will describe
-  // the whole package we're sending.
-  //
-  struct cmsghdr *cmsg;
-  cmsg = CMSG_FIRSTHDR(&msg);
-  cmsg->cmsg_level = SOL_SOCKET;
-  cmsg->cmsg_type = SCM_RIGHTS;
-  cmsg->cmsg_len = CMSG_LEN(msg_size);
-  //
-  // We also have to update the controllen in case other stuff is actually
-  // in there we may not be aware of (due to macros).
-  //
-  msg.msg_controllen = cmsg->cmsg_len;
-
-  //
-  // Finally, we get a pointer to the start of the ancillary data array and
-  // put our file descriptor in.
-  //
-  int *fdptr = (int*) (CMSG_DATA(cmsg));
-  *fdptr = fd; // 
-
-  //
-  // Actually send the file descriptor back to the tap bridge.
-  //
-  ssize_t len = sendmsg(sock, &msg, 0);
-  ABORT_IF (len == -1, "Could not send socket back to tap bridge", 1);
-
-  LOG ("sendmsg complete");
-}
-
-  static int
-CreateTap (const char *dev, const char *gw, const char *ip, const char *netmask, const char *mac)
-{
-  //
-  // Creation and management of Tap devices is done via the tun device
-  //
-  int tap = open ("/dev/net/tun", O_RDWR);
-  ABORT_IF (tap == -1, "Could not open /dev/net/tun", true);
-
-  //
-  // Allocate a tap device, making sure that it will not send the tun_pi header.
-  // If we provide a null name to the ifr.ifr_name, we tell the kernel to pick
-  // a name for us (i.e., tapn where n = 0..255
-  //
-  struct ifreq ifr;
-  ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
-  strcpy (ifr.ifr_name, dev);
-  int status = ioctl (tap, TUNSETIFF, (void *) &ifr);
-  ABORT_IF (status == -1, "Could not allocate tap device", true);
-
-  std::string tapDeviceName = (char *)ifr.ifr_name;
-  LOG ("Allocated TAP device " << tapDeviceName);
-
-  //
-  // Set the hardware (MAC) address of the new device
-  //
-  ifr.ifr_hwaddr.sa_family = 1; // this is ARPHRD_ETHER from if_arp.h
-  AsciiToMac48 (mac, (uint8_t*)ifr.ifr_hwaddr.sa_data);
-  status = ioctl (tap, SIOCSIFHWADDR, &ifr);
-  ABORT_IF (status == -1, "Could not set MAC address", true);
-  LOG ("Set device MAC address to " << mac);
-
-  int fd = socket (AF_INET, SOCK_DGRAM, 0);
-
-  //
-  // Bring the interface up.
-  //
-  status = ioctl (fd, SIOCGIFFLAGS, &ifr);
-  ABORT_IF (status == -1, "Could not get flags for interface", true);
-  ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
-  status = ioctl (fd, SIOCSIFFLAGS, &ifr);
-  ABORT_IF (status == -1, "Could not bring interface up", true);
-  LOG ("Device is up");
-
-  //
-  // Set the IP address of the new interface/device.
-  //
-  SetInetAddress (&ifr.ifr_addr, AsciiToIpv4 (ip));
-  status = ioctl (fd, SIOCSIFADDR, &ifr);
-  ABORT_IF (status == -1, "Could not set IP address", true);
-  LOG ("Set device IP address to " << ip);
-
-  //
-  // Set the net mask of the new interface/device
-  //
-  SetInetAddress (&ifr.ifr_netmask, AsciiToIpv4 (netmask));
-  status = ioctl (fd, SIOCSIFNETMASK, &ifr);
-  ABORT_IF (status == -1, "Could not set net mask", true);
-  LOG ("Set device Net Mask to " << netmask);
-
-  return tap;
-}
-
-  int 
-main (int argc, char *argv[])
-{
-  int c;
-  char *dev = NULL;
-  char *gw = NULL;
-  char *ip = NULL;
-  char *mac = NULL;
-  char *netmask = NULL;
-  char *path = NULL;
-
-  opterr = 0;
-
-  while ((c = getopt (argc, argv, "vd:g:i:m:n:p:")) != -1)
-    {
-      switch (c)
-        {
-        case 'v':
-          gVerbose = true;
-          break;
-        case 'd':
-          dev = optarg;     // name of the new tap device
-          break;
-        case 'g':
-          gw = optarg;      // gateway address for the new device
-          break;
-        case 'i':
-          ip = optarg;      // ip address of the new device
-          break;
-        case 'm':
-          mac = optarg;     // mac address of the new device
-          break;
-        case 'n':
-          netmask = optarg; // net mask for the new device
-          break;
-        case 'p':
-          path = optarg;    // path back to the tap bridge
-          break;
-        }
-    }
-
-  //
-  // We have got to be able to coordinate the name of the tap device we are
-  // going to create and or open with the device that an external Linux host
-  // will use.  THis name is given in dev
-  //
-  ABORT_IF (dev == NULL, "Device Name is a required argument", 0);
-  LOG ("Provided Device Name is \"" << dev << "\"");
-
-  //
-  // We have got to be able to provide a gateway to the external Linux host 
-  // so it can talk to the ns-3 network.  This ip address is provided in 
-  // gw.
-  //
-  ABORT_IF (gw == NULL, "Gateway Address is a required argument", 0);
-  LOG ("Provided Gateway Address is \"" << dev << "\"");
-
-  //
-  // We have got to be able to assign an IP address to the tap device we are
-  // allocating.  This address is allocated in the simulation and assigned to
-  // the tap bridge.  This address is given in ip.
-  //
-  ABORT_IF (ip == NULL, "IP Address is a required argument", 0);
-  LOG ("Provided IP Address is \"" << ip << "\"");
-
-  //
-  // We have got to be able to assign a Mac address to the tap device we are
-  // allocating.  This address is allocated in the simulation and assigned to
-  // the bridged device.  This allows packets addressed to the bridged device
-  // to appear in the Linux host as if they were received there.
-  //
-  ABORT_IF (mac == NULL, "MAC Address is a required argument", 0);
-  LOG ("Provided MAC Address is \"" << mac << "\"");
-
-  //
-  // We have got to be able to assign a net mask to the tap device we are
-  // allocating.  This mask is allocated in the simulation and given to
-  // the bridged device.  
-  //
-  ABORT_IF (netmask == NULL, "Net Mask is a required argument", 0);
-  LOG ("Provided Net Mask is \"" << netmask << "\"");
-
-  //
-  // This program is spawned by a tap bridge running in a simulation.  It
-  // wants to create a socket as described below.  We are going to do the
-  // work here since we're running suid root.  Once we create the socket,
-  // we have to send it back to the tap bridge.  We do that over a Unix
-  // (local interprocess) socket.  The tap bridge created a socket to 
-  // listen for our response on, and it is expected to have encoded the address
-  // information as a string and to have passed that string as an argument to
-  // us.  We see it here as the "path" string.  We can't do anything useful 
-  // unless we have that string.
-  //
-  ABORT_IF (path == NULL, "path is a required argument", 0);
-  LOG ("Provided path is \"" << path << "\"");
-
-  //
-  // The whole reason for all of the hoops we went through to call out to this
-  // program will pay off here.  We created this program to run as suid root
-  // in order to keep the main simulation program from having to be run with
-  // root privileges.  We need root privileges to be able to futz with the 
-  // Tap device underlying all of this.  So all of these hoops are to allow 
-  // us to exeucte the following code:
-  //
-  LOG ("Creating Tap");
-  int sock = CreateTap (dev, gw, ip, mac, netmask);
-  ABORT_IF (sock == -1, "main(): Unable to create tap socket", 1);
-
-  //
-  // Send the socket back to the tap net device so it can go about its business
-  //
-  SendSocket (path, sock);
-
-  return 0;
-}
--- a/src/devices/tap-bridge/wscript	Tue Jan 27 12:36:46 2009 -0800
+++ b/src/devices/tap-bridge/wscript	Tue Jan 27 20:26:34 2009 -0800
@@ -32,9 +32,9 @@
                 'tap-bridge.h',
                 ])
 
-        obj = bld.create_suid_program('tap-sock-creator')
+        obj = bld.create_suid_program('tap-creator')
         obj.source = [
-            'tap-sock-creator.cc',
+            'tap-creator.cc',
             'tap-encode-decode.cc',
             ]
 
--- a/src/wscript	Tue Jan 27 12:36:46 2009 -0800
+++ b/src/wscript	Tue Jan 27 20:26:34 2009 -0800
@@ -45,12 +45,11 @@
                          " --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')
--- a/wscript	Tue Jan 27 12:36:46 2009 -0800
+++ b/wscript	Tue Jan 27 20:26:34 2009 -0800
@@ -133,7 +133,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 +147,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,
@@ -280,7 +279,7 @@
     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):
@@ -305,6 +304,7 @@
     program.module_deps = list()
     program.name = name
     program.target = name
+
     if bld.env['SUDO'] and Options.options.enable_sudo:
         SuidBuildTask(bld, program)
     return program