--- /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);