checkpoint tap development

This commit is contained in:
Craig Dowell
2009-01-27 12:36:46 -08:00
parent a77eb54bcc
commit bcec13606f
14 changed files with 1332 additions and 0 deletions

157
examples/csma-tap-bridge.cc Normal file
View File

@@ -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.");
}

View File

@@ -48,6 +48,10 @@ def build(bld):
['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'

View File

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

View File

@@ -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 */

View File

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

View File

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

View File

@@ -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;
}

View File

@@ -0,0 +1,8 @@
/**
* \ingroup devices
* \defgroup TapBridgeModel Tap Bridge Model
*
* \section TapBridgeModelOverview TapBridge Model Overview
*
* The tap bridge ...
*/

1
src/devices/tap-bridge/waf vendored Executable file
View File

@@ -0,0 +1 @@
exec "`dirname "$0"`"/../../../waf "$@"

View File

@@ -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',
]

View File

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

View File

@@ -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 */

View File

@@ -21,6 +21,7 @@ def build(bld):
'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 @@ def build(bld):
'ipv4-interface-container.h',
'udp-echo-helper.h',
'bridge-helper.h',
'tap-bridge-helper.h',
'yans-wifi-helper.h',
'v4ping-helper.h',
]

View File

@@ -23,6 +23,7 @@ all_modules = (
'devices/csma',
'devices/emu',
'devices/bridge',
'devices/tap-bridge',
'applications/onoff',
'applications/packet-sink',
'applications/udp-echo',
@@ -54,6 +55,7 @@ def configure(conf):
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')