add emu-udp-echo example
authorCraig Dowell <craigdo@ee.washington.edu>
Thu Oct 30 10:00:41 2008 -0700 (15 months ago)
changeset 383116c2970a0344
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
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/examples/emu-udp-echo.cc	Thu Oct 30 10:00:41 2008 -0700
     1.3 @@ -0,0 +1,156 @@
     1.4 +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
     1.5 +/*
     1.6 + * This program is free software; you can redistribute it and/or modify
     1.7 + * it under the terms of the GNU General Public License version 2 as
     1.8 + * published by the Free Software Foundation;
     1.9 + *
    1.10 + * This program is distributed in the hope that it will be useful,
    1.11 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    1.13 + * GNU General Public License for more details.
    1.14 + *
    1.15 + * You should have received a copy of the GNU General Public License
    1.16 + * along with this program; if not, write to the Free Software
    1.17 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    1.18 + */
    1.19 +
    1.20 +// Network topology
    1.21 +//
    1.22 +// Normally, the use case for emulated net devices is in collections of
    1.23 +// small simulations that connect to the outside world through specific 
    1.24 +// interfaces.  For example, one could construct a number of virtual
    1.25 +// machines and connect them via a host-only network.  To use the emulated
    1.26 +// net device, you would need to set all of the host-only interfaces in
    1.27 +// promiscuous mode and provide an appropriate device name (search for "eth1"
    1.28 +// below).  One could also use the emulated net device in a testbed situation
    1.29 +// where the host on which the simulation is running has a specific interface
    1.30 +// of interested.  You would also need to set this specific interface into
    1.31 +// promiscuous mode and provide an appropriate device name.
    1.32 +//
    1.33 +// This philosophy carries over to this simple example.
    1.34 +//
    1.35 +// We don't assume any special configuration and all of the ns-3 emulated net
    1.36 +// devices will actually talk to the same underlying OS device.  We rely on 
    1.37 +// the fact that the OS will deliver copies of our packets to the other ns-3
    1.38 +// net devices since we operate in promiscuous mode.  
    1.39 +//
    1.40 +// Packets will be sent out over the device, but we use MAC spoofing.  The
    1.41 +// MAC addresses will be generated using the Organizationally Unique Identifier
    1.42 +// (OUI) 00:00:00 as a base.  This vendor code is not assigned to any 
    1.43 +// organization and so should not conflict with any real hardware.  We'll use 
    1.44 +// the first n of these addresses, where n is the number of nodes, in this
    1.45 +// simualtion.  It is up to you to determine that using these MAC addresses is
    1.46 +// okay on your network and won't conflict with anything else (including another
    1.47 +// simulation using emu devices) on your network.  Once you have made this 
    1.48 +// determination, you need to put the interface you chose into promiscuous mode.
    1.49 +// We don't do it for you since you need to think about it first.
    1.50 +//
    1.51 +// This simulation uses the real-time simulator and so will consume ten seconds
    1.52 +// of real time.
    1.53 +//
    1.54 +// By default, we create the following topology
    1.55 +//
    1.56 +//       n0    n1   n2   n3
    1.57 +//       |     |    |    |
    1.58 +//       -----------------
    1.59 +//             "eth1"
    1.60 +//
    1.61 +// - UDP flows from n0 to n1 and back
    1.62 +// - DropTail queues 
    1.63 +// - Tracing of queues and packet receptions to file "udp-echo.tr"
    1.64 +// - pcap tracing on all devices
    1.65 +//
    1.66 +
    1.67 +#include <fstream>
    1.68 +#include "ns3/core-module.h"
    1.69 +#include "ns3/simulator-module.h"
    1.70 +#include "ns3/helper-module.h"
    1.71 +
    1.72 +using namespace ns3;
    1.73 +
    1.74 +NS_LOG_COMPONENT_DEFINE ("EmulatedUdpEchoExample");
    1.75 +
    1.76 +int 
    1.77 +main (int argc, char *argv[])
    1.78 +{
    1.79 +  std::string deviceName ("eth1");
    1.80 +  uint32_t nNodes = 4;
    1.81 +
    1.82 +  //
    1.83 +  // Allow the user to override any of the defaults at run-time, via command-line
    1.84 +  // arguments
    1.85 +  //
    1.86 +  CommandLine cmd;
    1.87 +  cmd.AddValue("deviceName", "device name", deviceName);
    1.88 +  cmd.AddValue("nNodes", "number of nodes to create (>= 2)", nNodes);
    1.89 +  cmd.Parse (argc, argv);
    1.90 +
    1.91 +  GlobalValue::Bind ("SimulatorImplementationType", 
    1.92 +    StringValue ("ns3::RealtimeSimulatorImpl"));
    1.93 +
    1.94 +  //
    1.95 +  // need at least two nodes
    1.96 +  //
    1.97 +  nNodes = nNodes < 2 ? 2 : nNodes;
    1.98 +
    1.99 +  //
   1.100 +  // Explicitly create the nodes required by the topology (shown above).
   1.101 +  //
   1.102 +  NS_LOG_INFO ("Create nodes.");
   1.103 +  NodeContainer n;
   1.104 +  n.Create (nNodes);
   1.105 +
   1.106 +  InternetStackHelper internet;
   1.107 +  internet.Install (n);
   1.108 +
   1.109 +  //
   1.110 +  // Explicitly create the channels required by the topology (shown above).
   1.111 +  //
   1.112 +  NS_LOG_INFO ("Create channels.");
   1.113 +  EmuHelper emu;
   1.114 +  emu.SetAttribute ("DeviceName", StringValue (deviceName));
   1.115 +  NetDeviceContainer d = emu.Install (n);
   1.116 +
   1.117 +  //
   1.118 +  // We've got the "hardware" in place.  Now we need to add IP addresses.
   1.119 +  //
   1.120 +  Ipv4AddressHelper ipv4;
   1.121 +  NS_LOG_INFO ("Assign IP Addresses.");
   1.122 +  ipv4.SetBase ("10.1.1.0", "255.255.255.0");
   1.123 +  Ipv4InterfaceContainer i = ipv4.Assign (d);
   1.124 +
   1.125 +  //
   1.126 +  // Create a UdpEchoServer application on node one.
   1.127 +  //
   1.128 +  NS_LOG_INFO ("Create Applications.");
   1.129 +  UdpEchoServerHelper server (9);
   1.130 +  ApplicationContainer apps = server.Install (n.Get(1));
   1.131 +  apps.Start (Seconds (1.0));
   1.132 +  apps.Stop (Seconds (10.0));
   1.133 +
   1.134 +  //
   1.135 +  // Create a UdpEchoClient application to send UDP datagrams from node zero to node one.
   1.136 +  //
   1.137 +  uint32_t packetSize = 1024;
   1.138 +  uint32_t maxPacketCount = 1;
   1.139 +  Time interPacketInterval = Seconds (1.);
   1.140 +  UdpEchoClientHelper client (i.GetAddress (1), 9);
   1.141 +  client.SetAttribute ("MaxPackets", UintegerValue (maxPacketCount));
   1.142 +  client.SetAttribute ("Interval", TimeValue (interPacketInterval));
   1.143 +  client.SetAttribute ("PacketSize", UintegerValue (packetSize));
   1.144 +  apps = client.Install (n.Get (0));
   1.145 +  apps.Start (Seconds (2.0));
   1.146 +  apps.Stop (Seconds (10.0));
   1.147 +
   1.148 +  std::ofstream ascii;
   1.149 +  ascii.open ("emu-udp-echo.tr");
   1.150 +  EmuHelper::EnablePcapAll ("emu-udp-echo");
   1.151 +
   1.152 +  //
   1.153 +  // Now, do the actual simulation.
   1.154 +  //
   1.155 +  NS_LOG_INFO ("Run Simulation.");
   1.156 +  Simulator::Run ();
   1.157 +  Simulator::Destroy ();
   1.158 +  NS_LOG_INFO ("Done.");
   1.159 +}
     2.1 --- a/examples/wscript	Wed Oct 29 22:39:36 2008 -0700
     2.2 +++ b/examples/wscript	Thu Oct 30 10:00:41 2008 -0700
     2.3 @@ -32,6 +32,10 @@
     2.4                                   ['csma', 'internet-stack'])
     2.5      obj.source = 'udp-echo.cc'
     2.6  
     2.7 +    obj = bld.create_ns3_program('emu-udp-echo',
     2.8 +                                 ['emu', 'internet-stack'])
     2.9 +    obj.source = 'emu-udp-echo.cc'
    2.10 +
    2.11      obj = bld.create_ns3_program('realtime-udp-echo',
    2.12                                   ['csma', 'internet-stack'])
    2.13      obj.source = 'realtime-udp-echo.cc'
     3.1 --- a/src/devices/emu/emu-net-device.cc	Wed Oct 29 22:39:36 2008 -0700
     3.2 +++ b/src/devices/emu/emu-net-device.cc	Thu Oct 30 10:00:41 2008 -0700
     3.3 @@ -52,6 +52,8 @@
     3.4  
     3.5  NS_OBJECT_ENSURE_REGISTERED (EmuNetDevice);
     3.6  
     3.7 +#define EMU_MAGIC 65867
     3.8 +
     3.9  TypeId 
    3.10  EmuNetDevice::GetTypeId (void)
    3.11  {
    3.12 @@ -192,6 +194,25 @@
    3.13    rc = bind (m_sock, (struct sockaddr *)&ll, sizeof (ll));
    3.14    NS_ASSERT_MSG (rc != -1, "EmuNetDevice::StartDevice(): Can't bind to specified interface");
    3.15  
    3.16 +  rc = ioctl(m_sock, SIOCGIFFLAGS, &ifr);
    3.17 +  NS_ASSERT_MSG (rc != -1, "EmuNetDevice::StartDevice(): Can't get interface flags");
    3.18 +  
    3.19 +  //
    3.20 +  // This device only works if the underlying interface is up in promiscuous 
    3.21 +  // mode.  We could have turned it on in the socket creator, but the situation
    3.22 +  // is that we expect these devices to be used in conjunction with virtual 
    3.23 +  // machines with connected host-only (simulated) networks, or in a testbed.
    3.24 +  // There is a lot of setup and configuration happening outside of this one 
    3.25 +  // issue, and we expect that configuration to include choosing a valid
    3.26 +  // interface (e.g, "ath1"), ensuring that the device supports promiscuous 
    3.27 +  // mode, and placing it in promiscuous mode.  We just make sure of the
    3.28 +  // end result.
    3.29 +  //
    3.30 +  if ((ifr.ifr_flags & IFF_PROMISC) == 0)
    3.31 +    {
    3.32 +      NS_FATAL_ERROR ("EmuNetDevice::StartDevice(): " << m_deviceName << " is not in promiscuous mode");
    3.33 +    }
    3.34 +
    3.35    //
    3.36    // Now spin up a read thread to read packets.
    3.37    //
    3.38 @@ -236,6 +257,10 @@
    3.39        NS_FATAL_ERROR ("EmuNetDevice::CreateSocket(): Could not bind(): errno = " << strerror (errno));
    3.40      }
    3.41  
    3.42 +  NS_LOG_INFO ("Created Unix socket");
    3.43 +  NS_LOG_INFO ("sun_family = " << un.sun_family);
    3.44 +  NS_LOG_INFO ("sun_path = " << un.sun_path);
    3.45 +
    3.46    //
    3.47    // We have a socket here, but we want to get it there -- to the program we're
    3.48    // going to exec.  What we'll do is to do a getsockname and then encode the
    3.49 @@ -250,10 +275,10 @@
    3.50      }
    3.51  
    3.52    //
    3.53 -  // Now encode that socket name (endpoint information) as a string
    3.54 +  // Now encode that socket name (family and path) as a string of hex digits
    3.55    //
    3.56 -  std::string endpoint = EmuBufferToString((uint8_t *)&un, len);
    3.57 -
    3.58 +  std::string path = EmuBufferToString((uint8_t *)&un, len);
    3.59 +  NS_LOG_INFO ("Encoded Unix socket as \"" << path << "\"");
    3.60    //
    3.61    // Fork and exec the process to create our socket.  If we're us (the parent)
    3.62    // we wait for the child (the socket creator) to complete and read the 
    3.63 @@ -270,7 +295,8 @@
    3.64        // the (now) parent process.
    3.65        //
    3.66        std::ostringstream oss;
    3.67 -      oss << "-p " << endpoint;
    3.68 +      oss << "-v -p" << path;
    3.69 +      NS_LOG_INFO ("Parameters set to \"" << oss.str () << "\"");
    3.70  
    3.71        //
    3.72        // Execute the socket creation process image.
    3.73 @@ -386,10 +412,23 @@
    3.74  	  if (cmsg->cmsg_level == SOL_SOCKET &&
    3.75  	      cmsg->cmsg_type == SCM_RIGHTS)
    3.76  	    {
    3.77 -	      int *rawSocket = (int*)CMSG_DATA (cmsg);
    3.78 -	      NS_LOG_INFO ("Got the socket from the socket creator = " << *rawSocket);
    3.79 -              m_sock = *rawSocket;
    3.80 -              return;
    3.81 +              //
    3.82 +              // This is the type of message we want.  Check to see if the magic 
    3.83 +              // number is correct and then pull out the socket we care about if
    3.84 +              // it matches
    3.85 +              //
    3.86 +              if (magic == EMU_MAGIC)
    3.87 +                {
    3.88 +                  NS_LOG_INFO ("Got SCM_RIGHTS with correct magic " << magic);
    3.89 +                  int *rawSocket = (int*)CMSG_DATA (cmsg);
    3.90 +                  NS_LOG_INFO ("Got the socket from the socket creator = " << *rawSocket);
    3.91 +                  m_sock = *rawSocket;
    3.92 +                  return;
    3.93 +                }
    3.94 +              else
    3.95 +                {
    3.96 +                  NS_LOG_INFO ("Got SCM_RIGHTS, but with bad magic " << magic);                  
    3.97 +                }
    3.98  	    }
    3.99  	}
   3.100        NS_FATAL_ERROR ("Did not get the raw socket from the socket creator");
     4.1 --- a/src/devices/emu/emu-sock-creator.cc	Wed Oct 29 22:39:36 2008 -0700
     4.2 +++ b/src/devices/emu/emu-sock-creator.cc	Thu Oct 30 10:00:41 2008 -0700
     4.3 @@ -42,22 +42,21 @@
     4.4  #define LOG(msg) \
     4.5    if (gVerbose) \
     4.6      { \
     4.7 -      std::cout << msg << std::endl; \
     4.8 +      std::cout << __FUNCTION__ << "(): " << msg << std::endl;   \
     4.9      }
    4.10  
    4.11 -#define ABORT(msg) \
    4.12 -  std::cout << __FILE__ << __FUNCTION__ <<  ": fatal error at line " << __LINE__ << ": " << msg << std::endl; \
    4.13 -  exit (-1);
    4.14 +#define ABORT(msg, printErrno) \
    4.15 +  std::cout << __FILE__ << ": fatal error at line " << __LINE__ << ": " << __FUNCTION__ << "(): " << msg << std::endl; \
    4.16 +  if (printErrno) \
    4.17 +    { \
    4.18 +      std::cout << "    errno = " << errno << " (" << strerror (errno) << ")" << std::endl; \
    4.19 +    } \
    4.20 +  exit (-1); 
    4.21  
    4.22  #define ABORT_IF(cond, msg, printErrno) \
    4.23    if (cond) \
    4.24      { \
    4.25 -      std::cout << __FILE__ << __FUNCTION__ <<  ": fatal error at line " << __LINE__ << ": " << msg << std::endl; \
    4.26 -      if (printErrno) \
    4.27 -        { \
    4.28 -          std::cout << "  errno = " << errno << "(" << strerror (errno) << ")" << std::endl; \
    4.29 -        } \
    4.30 -      exit (-1); \
    4.31 +      ABORT(msg, printErrno); \
    4.32      }
    4.33  
    4.34  /**
    4.35 @@ -75,9 +74,9 @@
    4.36    // Open a Unix (local interprocess) socket to call back to the emu net
    4.37    // device.
    4.38    //
    4.39 -  LOG ("SendSocket(): Create Unix socket");
    4.40 +  LOG ("Create Unix socket");
    4.41    int sock = socket (PF_UNIX, SOCK_DGRAM, 0);
    4.42 -  ABORT_IF (sock == -1, "SendSocket(): Unable to open socket", 1);
    4.43 +  ABORT_IF (sock == -1, "Unable to open socket", 1);
    4.44    
    4.45    //
    4.46    // We have this string called path, which is really a hex representation
    4.47 @@ -89,15 +88,15 @@
    4.48    socklen_t clientAddrLen;
    4.49    struct sockaddr_un clientAddr;
    4.50  
    4.51 -  LOG ("SendSocket(): Decode address");
    4.52 +  LOG ("Decode address " << path);
    4.53    bool rc = ns3::EmuStringToBuffer (path, (uint8_t *)&clientAddr, &clientAddrLen);
    4.54 -  ABORT_IF (rc == false, "SendSocket(): Unable to decode path", 0);
    4.55 +  ABORT_IF (rc == false, "Unable to decode path", 0);
    4.56  
    4.57 -  LOG ("SendSocket(): Connect");
    4.58 +  LOG ("Connect");
    4.59    int status = connect (sock, (struct sockaddr*)&clientAddr, clientAddrLen);
    4.60 -  ABORT_IF (status != -1, "SendSocket(): Unable to connect to emu device", 1);
    4.61 +  ABORT_IF (status == -1, "Unable to connect to emu device", 1);
    4.62  
    4.63 -  LOG ("SendSocket(): Connected");
    4.64 +  LOG ("Connected");
    4.65  
    4.66    //
    4.67    // This is arcane enough that a few words are worthwhile to explain what's 
    4.68 @@ -189,7 +188,7 @@
    4.69    ssize_t len = sendmsg(sock, &msg, 0);
    4.70    ABORT_IF (len == -1, "Could not send socket back to emu net device", 1);
    4.71  
    4.72 -  LOG ("SendSocket(): sendmsg complete");
    4.73 +  LOG ("sendmsg complete");
    4.74  }
    4.75  
    4.76    int 
    4.77 @@ -225,7 +224,7 @@
    4.78    // unless we have that string.
    4.79    //
    4.80    ABORT_IF (path == NULL, "path is a required argument", 0);
    4.81 -
    4.82 +  LOG ("Provided path is \"" << path << "\"");
    4.83    //
    4.84    // The whole reason for all of the hoops we went through to call out to this
    4.85    // program will pay off here.  We created this program to run as suid root
    4.86 @@ -234,6 +233,7 @@
    4.87    // though.  So all of these hoops are to allow us to exeucte the following
    4.88    // single line of code:
    4.89    //
    4.90 +  LOG ("Creating raw socket");
    4.91    int sock = socket (PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    4.92    ABORT_IF (sock == -1, "CreateSocket(): Unable to open raw socket", 1);
    4.93