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