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