add emu-udp-echo example
authorCraig Dowell <craigdo@ee.washington.edu>
Thu, 30 Oct 2008 10:00:41 -0700
changeset 3831 16c2970a0344
parent 3830 8862b9be62bb
child 3832 a457408d0bb6
add emu-udp-echo example
examples/emu-udp-echo.cc
examples/wscript
src/devices/emu/emu-net-device.cc
src/devices/emu/emu-sock-creator.cc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/emu-udp-echo.cc	Thu Oct 30 10:00:41 2008 -0700
@@ -0,0 +1,156 @@
+/* -*- 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
+//
+// Normally, the use case for emulated net devices is in collections of
+// small simulations that connect to the outside world through specific 
+// interfaces.  For example, one could construct a number of virtual
+// machines and connect them via a host-only network.  To use the emulated
+// net device, you would need to set all of the host-only interfaces in
+// promiscuous mode and provide an appropriate device name (search for "eth1"
+// below).  One could also use the emulated net device in a testbed situation
+// where the host on which the simulation is running has a specific interface
+// of interested.  You would also need to set this specific interface into
+// promiscuous mode and provide an appropriate device name.
+//
+// This philosophy carries over to this simple example.
+//
+// We don't assume any special configuration and all of the ns-3 emulated net
+// devices will actually talk to the same underlying OS device.  We rely on 
+// the fact that the OS will deliver copies of our packets to the other ns-3
+// net devices since we operate in promiscuous mode.  
+//
+// Packets will be sent out over the device, but we use MAC spoofing.  The
+// MAC addresses will be generated using the Organizationally Unique Identifier
+// (OUI) 00:00:00 as a base.  This vendor code is not assigned to any 
+// organization and so should not conflict with any real hardware.  We'll use 
+// the first n of these addresses, where n is the number of nodes, in this
+// simualtion.  It is up to you to determine that using these MAC addresses is
+// okay on your network and won't conflict with anything else (including another
+// simulation using emu devices) on your network.  Once you have made this 
+// determination, you need to put the interface you chose into promiscuous mode.
+// We don't do it for you since you need to think about it first.
+//
+// This simulation uses the real-time simulator and so will consume ten seconds
+// of real time.
+//
+// By default, we create the following topology
+//
+//       n0    n1   n2   n3
+//       |     |    |    |
+//       -----------------
+//             "eth1"
+//
+// - UDP flows from n0 to n1 and back
+// - DropTail queues 
+// - Tracing of queues and packet receptions to file "udp-echo.tr"
+// - pcap tracing on all devices
+//
+
+#include <fstream>
+#include "ns3/core-module.h"
+#include "ns3/simulator-module.h"
+#include "ns3/helper-module.h"
+
+using namespace ns3;
+
+NS_LOG_COMPONENT_DEFINE ("EmulatedUdpEchoExample");
+
+int 
+main (int argc, char *argv[])
+{
+  std::string deviceName ("eth1");
+  uint32_t nNodes = 4;
+
+  //
+  // Allow the user to override any of the defaults at run-time, via command-line
+  // arguments
+  //
+  CommandLine cmd;
+  cmd.AddValue("deviceName", "device name", deviceName);
+  cmd.AddValue("nNodes", "number of nodes to create (>= 2)", nNodes);
+  cmd.Parse (argc, argv);
+
+  GlobalValue::Bind ("SimulatorImplementationType", 
+    StringValue ("ns3::RealtimeSimulatorImpl"));
+
+  //
+  // need at least two nodes
+  //
+  nNodes = nNodes < 2 ? 2 : nNodes;
+
+  //
+  // Explicitly create the nodes required by the topology (shown above).
+  //
+  NS_LOG_INFO ("Create nodes.");
+  NodeContainer n;
+  n.Create (nNodes);
+
+  InternetStackHelper internet;
+  internet.Install (n);
+
+  //
+  // Explicitly create the channels required by the topology (shown above).
+  //
+  NS_LOG_INFO ("Create channels.");
+  EmuHelper emu;
+  emu.SetAttribute ("DeviceName", StringValue (deviceName));
+  NetDeviceContainer d = emu.Install (n);
+
+  //
+  // We've got the "hardware" in place.  Now we need to add IP addresses.
+  //
+  Ipv4AddressHelper ipv4;
+  NS_LOG_INFO ("Assign IP Addresses.");
+  ipv4.SetBase ("10.1.1.0", "255.255.255.0");
+  Ipv4InterfaceContainer i = ipv4.Assign (d);
+
+  //
+  // Create a UdpEchoServer application on node one.
+  //
+  NS_LOG_INFO ("Create Applications.");
+  UdpEchoServerHelper server (9);
+  ApplicationContainer apps = server.Install (n.Get(1));
+  apps.Start (Seconds (1.0));
+  apps.Stop (Seconds (10.0));
+
+  //
+  // Create a UdpEchoClient application to send UDP datagrams from node zero to node one.
+  //
+  uint32_t packetSize = 1024;
+  uint32_t maxPacketCount = 1;
+  Time interPacketInterval = Seconds (1.);
+  UdpEchoClientHelper client (i.GetAddress (1), 9);
+  client.SetAttribute ("MaxPackets", UintegerValue (maxPacketCount));
+  client.SetAttribute ("Interval", TimeValue (interPacketInterval));
+  client.SetAttribute ("PacketSize", UintegerValue (packetSize));
+  apps = client.Install (n.Get (0));
+  apps.Start (Seconds (2.0));
+  apps.Stop (Seconds (10.0));
+
+  std::ofstream ascii;
+  ascii.open ("emu-udp-echo.tr");
+  EmuHelper::EnablePcapAll ("emu-udp-echo");
+
+  //
+  // Now, do the actual simulation.
+  //
+  NS_LOG_INFO ("Run Simulation.");
+  Simulator::Run ();
+  Simulator::Destroy ();
+  NS_LOG_INFO ("Done.");
+}
--- a/examples/wscript	Wed Oct 29 22:39:36 2008 -0700
+++ b/examples/wscript	Thu Oct 30 10:00:41 2008 -0700
@@ -32,6 +32,10 @@
                                  ['csma', 'internet-stack'])
     obj.source = 'udp-echo.cc'
 
+    obj = bld.create_ns3_program('emu-udp-echo',
+                                 ['emu', 'internet-stack'])
+    obj.source = 'emu-udp-echo.cc'
+
     obj = bld.create_ns3_program('realtime-udp-echo',
                                  ['csma', 'internet-stack'])
     obj.source = 'realtime-udp-echo.cc'
--- a/src/devices/emu/emu-net-device.cc	Wed Oct 29 22:39:36 2008 -0700
+++ b/src/devices/emu/emu-net-device.cc	Thu Oct 30 10:00:41 2008 -0700
@@ -52,6 +52,8 @@
 
 NS_OBJECT_ENSURE_REGISTERED (EmuNetDevice);
 
+#define EMU_MAGIC 65867
+
 TypeId 
 EmuNetDevice::GetTypeId (void)
 {
@@ -192,6 +194,25 @@
   rc = bind (m_sock, (struct sockaddr *)&ll, sizeof (ll));
   NS_ASSERT_MSG (rc != -1, "EmuNetDevice::StartDevice(): Can't bind to specified interface");
 
+  rc = ioctl(m_sock, SIOCGIFFLAGS, &ifr);
+  NS_ASSERT_MSG (rc != -1, "EmuNetDevice::StartDevice(): Can't get interface flags");
+  
+  //
+  // This device only works if the underlying interface is up in promiscuous 
+  // mode.  We could have turned it on in the socket creator, but the situation
+  // is that we expect these devices to be used in conjunction with virtual 
+  // machines with connected host-only (simulated) networks, or in a testbed.
+  // There is a lot of setup and configuration happening outside of this one 
+  // issue, and we expect that configuration to include choosing a valid
+  // interface (e.g, "ath1"), ensuring that the device supports promiscuous 
+  // mode, and placing it in promiscuous mode.  We just make sure of the
+  // end result.
+  //
+  if ((ifr.ifr_flags & IFF_PROMISC) == 0)
+    {
+      NS_FATAL_ERROR ("EmuNetDevice::StartDevice(): " << m_deviceName << " is not in promiscuous mode");
+    }
+
   //
   // Now spin up a read thread to read packets.
   //
@@ -236,6 +257,10 @@
       NS_FATAL_ERROR ("EmuNetDevice::CreateSocket(): 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
@@ -250,10 +275,10 @@
     }
 
   //
-  // Now encode that socket name (endpoint information) as a string
+  // Now encode that socket name (family and path) as a string of hex digits
   //
-  std::string endpoint = EmuBufferToString((uint8_t *)&un, len);
-
+  std::string path = EmuBufferToString((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 socket creator) to complete and read the 
@@ -270,7 +295,8 @@
       // the (now) parent process.
       //
       std::ostringstream oss;
-      oss << "-p " << endpoint;
+      oss << "-v -p" << path;
+      NS_LOG_INFO ("Parameters set to \"" << oss.str () << "\"");
 
       //
       // Execute the socket creation process image.
@@ -386,10 +412,23 @@
 	  if (cmsg->cmsg_level == SOL_SOCKET &&
 	      cmsg->cmsg_type == SCM_RIGHTS)
 	    {
-	      int *rawSocket = (int*)CMSG_DATA (cmsg);
-	      NS_LOG_INFO ("Got the socket from the socket creator = " << *rawSocket);
-              m_sock = *rawSocket;
-              return;
+              //
+              // 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 == EMU_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");
--- a/src/devices/emu/emu-sock-creator.cc	Wed Oct 29 22:39:36 2008 -0700
+++ b/src/devices/emu/emu-sock-creator.cc	Thu Oct 30 10:00:41 2008 -0700
@@ -42,22 +42,21 @@
 #define LOG(msg) \
   if (gVerbose) \
     { \
-      std::cout << msg << std::endl; \
+      std::cout << __FUNCTION__ << "(): " << msg << std::endl;   \
     }
 
-#define ABORT(msg) \
-  std::cout << __FILE__ << __FUNCTION__ <<  ": fatal error at line " << __LINE__ << ": " << msg << std::endl; \
-  exit (-1);
+#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) \
     { \
-      std::cout << __FILE__ << __FUNCTION__ <<  ": fatal error at line " << __LINE__ << ": " << msg << std::endl; \
-      if (printErrno) \
-        { \
-          std::cout << "  errno = " << errno << "(" << strerror (errno) << ")" << std::endl; \
-        } \
-      exit (-1); \
+      ABORT(msg, printErrno); \
     }
 
 /**
@@ -75,9 +74,9 @@
   // Open a Unix (local interprocess) socket to call back to the emu net
   // device.
   //
-  LOG ("SendSocket(): Create Unix socket");
+  LOG ("Create Unix socket");
   int sock = socket (PF_UNIX, SOCK_DGRAM, 0);
-  ABORT_IF (sock == -1, "SendSocket(): Unable to open socket", 1);
+  ABORT_IF (sock == -1, "Unable to open socket", 1);
   
   //
   // We have this string called path, which is really a hex representation
@@ -89,15 +88,15 @@
   socklen_t clientAddrLen;
   struct sockaddr_un clientAddr;
 
-  LOG ("SendSocket(): Decode address");
+  LOG ("Decode address " << path);
   bool rc = ns3::EmuStringToBuffer (path, (uint8_t *)&clientAddr, &clientAddrLen);
-  ABORT_IF (rc == false, "SendSocket(): Unable to decode path", 0);
+  ABORT_IF (rc == false, "Unable to decode path", 0);
 
-  LOG ("SendSocket(): Connect");
+  LOG ("Connect");
   int status = connect (sock, (struct sockaddr*)&clientAddr, clientAddrLen);
-  ABORT_IF (status != -1, "SendSocket(): Unable to connect to emu device", 1);
+  ABORT_IF (status == -1, "Unable to connect to emu device", 1);
 
-  LOG ("SendSocket(): Connected");
+  LOG ("Connected");
 
   //
   // This is arcane enough that a few words are worthwhile to explain what's 
@@ -189,7 +188,7 @@
   ssize_t len = sendmsg(sock, &msg, 0);
   ABORT_IF (len == -1, "Could not send socket back to emu net device", 1);
 
-  LOG ("SendSocket(): sendmsg complete");
+  LOG ("sendmsg complete");
 }
 
   int 
@@ -225,7 +224,7 @@
   // 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
@@ -234,6 +233,7 @@
   // though.  So all of these hoops are to allow us to exeucte the following
   // single line of code:
   //
+  LOG ("Creating raw socket");
   int sock = socket (PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
   ABORT_IF (sock == -1, "CreateSocket(): Unable to open raw socket", 1);