Merge OpenFlow

This commit is contained in:
Josh Pelkey
2011-03-11 15:21:50 -05:00
parent aa879d1f15
commit 27080192c3
15 changed files with 4767 additions and 0 deletions

View File

@@ -0,0 +1,158 @@
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2011 Blake Hurd
*
* 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
*
* Author: Blake Hurd <naimorai@gmail.com>
*/
/**
* \ingroup devices
* \defgroup OpenFlowSwitchModel OpenFlow Switch Model
*
* \section OpenFlowSwitchModelOverview OpenFlow Switch Model Overview
*
* The ns-3 OpenFlowSwitch device models an OpenFlow-enabled switch. It is designed to
* express basic use of the OpenFlow protocol, with the maintaining of a virtual
* Flow Table and TCAM to provide OpenFlow-like results.
*
* The functionality comes down to the Controllers, which send messages to the
* switch that configure its flows, producing different effects. Controllers can
* be added by the user, under the ofi namespace extends ofi::Controller. To
* demonstrate this, a DropController, which creates flows for ignoring every single
* packet, and LearningController, which effectively makes the switch a more complicated
* BridgeNetDevice. A user versed in a standard OFSID, and/or OF protocol, can write
* virtual controllers to create switches of all kinds of types.
*
* \section OpenFlowSwitchModel OpenFlow Switch Model
*
* The OpenFlowSwitch device behaves somewhat according to the diagram setup as a classical OFSID
* switch, with a few modifications made for a proper simulation environment.
*
* Normal OF-enabled Switch
* -----------------------------------
* | Secure Channel | <--OF Protocol--> | Controller is external |
* | Hardware or Software Flow Table |
* -----------------------------------
*
* ns-3 OF-enabled Switch (module)
* -------------------------------------
* | m_controller->ReceiveFromSwitch() | <--OF Protocol--> | Controller is internal |
* | Software Flow Table, virtual TCAM |
* -------------------------------------
*
* In essence, there are two differences:
* # No SSL, Embedded Controller: Instead of a secure channel and connecting to an
* outside location for the Controller program/machine, we currently only allow a
* Controller extended from ofi::Controller, an extension of an ns3::Object. This
* means ns-3 programmers cannot model the SSL part of the interface or possibility
* of network failure. The connection to the OpenFlowSwitch is local and there aren't any
* reasons for the channel/connection to break down. <<This difference may be an
* option in the future. Using EmuNetDevices, it should be possible to engage an
* external Controller program/machine, and thus work with controllers designed
* outside of the ns-3 environment, that simply use the proper OF protocol when
* communicating messages to the switch through a tap device.>>
*
* # Virtual Flow Table, TCAM: Typical OF-enabled switches are implemented on a hardware
* TCAM. The OFSID we turn into a library includes a modelled software TCAM, that produces
* the same results as a hardware TCAM. We include an attribute FlowTableLookupDelay, which
* allows a simple delay of using the TCAM to be modelled. We don't endeavor to make this
* delay more complicated, based on the tasks we are running on the TCAM, that is a possible
* future improvement.
*
* \section OpenFlowSwitchNetDeviceModel OpenFlow Switch Net Device Model
*
* The OpenFlowSwitch network device is aimed to model an OpenFlow switch, with a TCAM and a connection
* to a controller program. With some tweaking, it can model every switch type, as per OpenFlow's
* extensibility. It outsources the complexity of the switch ports to NetDevices of the user's choosing.
* It should be noted that these NetDevices must behave like practical switch ports, i.e. a Mac Address
* is assigned, and nothing more. It also must support a SendFrom function so
* that the OpenFlowSwitch can forward across that port.
*
* The OpenFlowSwitchNetDevice provides following Attributes:
*
* - FlowTableLookUpDelay: This time gets run off the clock when making a lookup in our Flow Table.
* - Flags: OpenFlow specific configuration flags. They are defined in the ofp_config_flags enum. Choices include:
* OFPC_SEND_FLOW_EXP (OpenFlowSwitch notifies controller when a flow has expired),
* OFPC_FRAG_NORMAL (Match fragment against Flow table),
* OFPC_FRAG_DROP (Drop fragments),
* OFPC_FRAG_REASM (Reassemble only if OFPC_IP_REASM set, which is currently impossible,
* because switch implementation does not support IP reassembly)
* OFPC_FRAG_MASK (Mask Fragments)
* - FlowTableMissSendLength: When the packet doesn't match in our Flow Table, and we forward to the controller,
* this sets # of bytes forwarded (packet is not forwarded in its entirety, unless specified).
*
* \section OpenFlowSwitchModelSummary OpenFlow Switch Model Summary
*
* The ns-3 OpenFlowSwitch device models an OpenFlow-enabled switch. It is designed to
* express basic use of the OpenFlow protocol, with the maintaining of a virtual
* Flow Table and TCAM to provide OpenFlow-like results.
*
* The functionality comes down to the Controllers, which send messages to the
* switch that configure its flows, producing different effects. Controllers can
* be added by the user, under the ofi namespace extends ofi::Controller. To
* demonstrate this, a DropController, which creates flows for ignoring every single
* packet, and LearningController, which effectively makes the switch a more complicated
* BridgeNetDevice. A user versed in a standard OFSID, and/or OF protocol, can write
* virtual controllers to create switches of all kinds of types.
*
* In order to use the OpenFlowSwitch module, you must create and link the
* OFSID (OpenFlow Software Implementation Distribution) to ns-3.
* To do this:
*
* #1 Obtain the OFSID code. An ns-3 specific OFSID branch is provided
* to ensure operation with ns-3. The OFSID has several dependencies
* include libxml2, libdl, and the boost libraries. Use mercurial to
* download this branch and waf to build the library:
*
* $ hg clone http://code.nsnam.org/bhurd/openflow
* $ cd openflow
* $ ./waf configure
* $ ./waf build
*
* #2 Your OFSID is now built into a libopenflow.a library! To
* link to an ns-3 build with this switch module, run from the ns-3-dev
* (or whatever you have named your distribution):
*
* $ ./waf configure --with-openflow=path/to/openflow
*
* #3 Under "---- Summary of optional NS-3 features:", you should see
* "NS-3 OpenFlow Integration : enabled", indicating the library
* has been linked to ns-3. Run:
*
* $ ./waf build
*
* to build ns-3 and activate the OpenFlowSwitch module.
*
* Once set up, you have access to some tests:
*
* For a test suite for the switch module, run:
*
* $ ./test.py --suite=openflow
*
* For an example demonstrating its use in a simple learning controller/switch, run:
*
* $ ./waf --run openflow-switch
*
* To see it in detailed logging, run:
*
* $ ./waf --run "openflow-switch -v"
*
* If you have any questions, read the wiki <http://www.nsnam.org/wiki/index.php/GSOC2010OpenFlow>
* first, and consider posting to the ns-3 developers mailing list <http://www.isi.edu/nsnam/ns/ns-dev-list.html>,
* but feel free to reach me at <naimorai@gmail.com>
*/

View File

@@ -0,0 +1,215 @@
/* -*- 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
//
// n0 n1
// | |
// ----------
// | Switch |
// ----------
// | |
// n2 n3
//
//
// - CBR/UDP flows from n0 to n1 and from n3 to n0
// - DropTail queues
// - Tracing of queues and packet receptions to file "openflow-switch.tr"
// - If order of adding nodes and netdevices is kept:
// n0 = 00:00:00;00:00:01, n1 = 00:00:00:00:00:03, n3 = 00:00:00:00:00:07
// and port number corresponds to node number, so port 0 is connected to n0, for example.
#include <iostream>
#include <fstream>
#include "ns3/core-module.h"
#include "ns3/network-module.h"
#include "ns3/csma-module.h"
#include "ns3/internet-module.h"
#include "ns3/applications-module.h"
#include "ns3/openflow-module.h"
#include "ns3/log.h"
using namespace ns3;
NS_LOG_COMPONENT_DEFINE ("OpenFlowCsmaSwitchExample");
bool verbose = false;
bool use_drop = false;
ns3::Time timeout = ns3::Seconds (0);
bool
SetVerbose (std::string value)
{
verbose = true;
return true;
}
bool
SetDrop (std::string value)
{
use_drop = true;
return true;
}
bool
SetTimeout (std::string value)
{
try {
timeout = ns3::Seconds (atof (value.c_str ()));
return true;
}
catch (...) { return false; }
return false;
}
int
main (int argc, char *argv[])
{
#ifdef NS3_OPENFLOW
//
// Allow the user to override any of the defaults and the above Bind() at
// run-time, via command-line arguments
//
CommandLine cmd;
cmd.AddValue ("v", "Verbose (turns on logging).", MakeCallback (&SetVerbose));
cmd.AddValue ("verbose", "Verbose (turns on logging).", MakeCallback (&SetVerbose));
cmd.AddValue ("d", "Use Drop Controller (Learning if not specified).", MakeCallback (&SetDrop));
cmd.AddValue ("drop", "Use Drop Controller (Learning if not specified).", MakeCallback (&SetDrop));
cmd.AddValue ("t", "Learning Controller Timeout (has no effect if drop controller is specified).", MakeCallback( &SetTimeout));
cmd.AddValue ("timeout", "Learning Controller Timeout (has no effect if drop controller is specified).", MakeCallback( &SetTimeout));
cmd.Parse (argc, argv);
if (verbose)
{
LogComponentEnable ("OpenFlowCsmaSwitchExample", LOG_LEVEL_INFO);
LogComponentEnable ("OpenFlowInterface", LOG_LEVEL_INFO);
LogComponentEnable ("OpenFlowSwitchNetDevice", LOG_LEVEL_INFO);
}
//
// Explicitly create the nodes required by the topology (shown above).
//
NS_LOG_INFO ("Create nodes.");
NodeContainer terminals;
terminals.Create (4);
NodeContainer csmaSwitch;
csmaSwitch.Create (1);
NS_LOG_INFO ("Build Topology");
CsmaHelper csma;
csma.SetChannelAttribute ("DataRate", DataRateValue (5000000));
csma.SetChannelAttribute ("Delay", TimeValue (MilliSeconds (2)));
// Create the csma links, from each terminal to the switch
NetDeviceContainer terminalDevices;
NetDeviceContainer switchDevices;
for (int i = 0; i < 4; i++)
{
NetDeviceContainer link = csma.Install (NodeContainer (terminals.Get (i), csmaSwitch));
terminalDevices.Add (link.Get (0));
switchDevices.Add (link.Get (1));
}
// Create the switch netdevice, which will do the packet switching
Ptr<Node> switchNode = csmaSwitch.Get (0);
OpenFlowSwitchHelper swtch;
if (use_drop)
{
Ptr<ns3::ofi::DropController> controller = CreateObject<ns3::ofi::DropController> ();
swtch.Install (switchNode, switchDevices, controller);
}
else
{
Ptr<ns3::ofi::LearningController> controller = CreateObject<ns3::ofi::LearningController> ();
if (!timeout.IsZero ()) controller->SetAttribute ("ExpirationTime", TimeValue (timeout));
swtch.Install (switchNode, switchDevices, controller);
}
// Add internet stack to the terminals
InternetStackHelper internet;
internet.Install (terminals);
// We've got the "hardware" in place. Now we need to add IP addresses.
NS_LOG_INFO ("Assign IP Addresses.");
Ipv4AddressHelper ipv4;
ipv4.SetBase ("10.1.1.0", "255.255.255.0");
ipv4.Assign (terminalDevices);
// Create an OnOff application to send UDP datagrams from n0 to n1.
NS_LOG_INFO ("Create Applications.");
uint16_t port = 9; // Discard port (RFC 863)
OnOffHelper onoff ("ns3::UdpSocketFactory",
Address (InetSocketAddress (Ipv4Address ("10.1.1.2"), port)));
onoff.SetAttribute ("OnTime", RandomVariableValue (ConstantVariable (1)));
onoff.SetAttribute ("OffTime", RandomVariableValue (ConstantVariable (0)));
ApplicationContainer app = onoff.Install (terminals.Get (0));
// Start the application
app.Start (Seconds (1.0));
app.Stop (Seconds (10.0));
// Create an optional packet sink to receive these packets
PacketSinkHelper sink ("ns3::UdpSocketFactory",
Address (InetSocketAddress (Ipv4Address::GetAny (), port)));
app = sink.Install (terminals.Get (1));
app.Start (Seconds (0.0));
//
// Create a similar flow from n3 to n0, starting at time 1.1 seconds
//
onoff.SetAttribute ("Remote",
AddressValue (InetSocketAddress (Ipv4Address ("10.1.1.1"), port)));
app = onoff.Install (terminals.Get (3));
app.Start (Seconds (1.1));
app.Stop (Seconds (10.0));
app = sink.Install (terminals.Get (0));
app.Start (Seconds (0.0));
NS_LOG_INFO ("Configure Tracing.");
//
// Configure tracing of all enqueue, dequeue, and NetDevice receive events.
// Trace output will be sent to the file "openflow-switch.tr"
//
AsciiTraceHelper ascii;
csma.EnableAsciiAll (ascii.CreateFileStream ("openflow-switch.tr"));
//
// Also configure some tcpdump traces; each interface will be traced.
// The output files will be named:
// openflow-switch-<nodeId>-<interfaceId>.pcap
// and can be read by the "tcpdump -r" command (use "-tt" option to
// display timestamps correctly)
//
csma.EnablePcapAll ("openflow-switch", false);
//
// Now, do the actual simulation.
//
NS_LOG_INFO ("Run Simulation.");
Simulator::Run ();
Simulator::Destroy ();
NS_LOG_INFO ("Done.");
#else
NS_LOG_INFO ("NS-3 OpenFlow is not enabled. Cannot run simulation.");
#endif // NS3_OPENFLOW
}

View File

@@ -0,0 +1,6 @@
## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
def build(bld):
obj = bld.create_ns3_program('openflow-switch',
['openflow', 'csma', 'internet', 'applications'])
obj.source = 'openflow-switch.cc'

View File

@@ -0,0 +1,97 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2011 Blake Hurd
*
* 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
*
* Author: Blake Hurd <naimorai@gmail.com>
*/
#ifdef NS3_OPENFLOW
#include "openflow-switch-helper.h"
#include "ns3/log.h"
#include "ns3/openflow-switch-net-device.h"
#include "ns3/openflow-interface.h"
#include "ns3/node.h"
#include "ns3/names.h"
NS_LOG_COMPONENT_DEFINE ("OpenFlowSwitchHelper");
namespace ns3 {
OpenFlowSwitchHelper::OpenFlowSwitchHelper ()
{
NS_LOG_FUNCTION_NOARGS ();
m_deviceFactory.SetTypeId ("ns3::OpenFlowSwitchNetDevice");
}
void
OpenFlowSwitchHelper::SetDeviceAttribute (std::string n1, const AttributeValue &v1)
{
NS_LOG_FUNCTION_NOARGS ();
m_deviceFactory.Set (n1, v1);
}
NetDeviceContainer
OpenFlowSwitchHelper::Install (Ptr<Node> node, NetDeviceContainer c, Ptr<ns3::ofi::Controller> controller)
{
NS_LOG_FUNCTION_NOARGS ();
NS_LOG_INFO ("**** Install switch device on node " << node->GetId ());
NetDeviceContainer devs;
Ptr<OpenFlowSwitchNetDevice> dev = m_deviceFactory.Create<OpenFlowSwitchNetDevice> ();
devs.Add (dev);
node->AddDevice (dev);
NS_LOG_INFO ("**** Set up Controller");
dev->SetController (controller);
for (NetDeviceContainer::Iterator i = c.Begin (); i != c.End (); ++i)
{
NS_LOG_INFO ("**** Add SwitchPort " << *i);
dev->AddSwitchPort (*i);
}
return devs;
}
NetDeviceContainer
OpenFlowSwitchHelper::Install (Ptr<Node> node, NetDeviceContainer c)
{
NS_LOG_FUNCTION_NOARGS ();
NS_LOG_INFO ("**** Install switch device on node " << node->GetId ());
NetDeviceContainer devs;
Ptr<OpenFlowSwitchNetDevice> dev = m_deviceFactory.Create<OpenFlowSwitchNetDevice> ();
devs.Add (dev);
node->AddDevice (dev);
for (NetDeviceContainer::Iterator i = c.Begin (); i != c.End (); ++i)
{
NS_LOG_INFO ("**** Add SwitchPort " << *i);
dev->AddSwitchPort (*i);
}
return devs;
}
NetDeviceContainer
OpenFlowSwitchHelper::Install (std::string nodeName, NetDeviceContainer c)
{
NS_LOG_FUNCTION_NOARGS ();
Ptr<Node> node = Names::Find<Node> (nodeName);
return Install (node, c);
}
} // namespace ns3
#endif // NS3_OPENFLOW

View File

@@ -0,0 +1,105 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2011 Blake Hurd
*
* 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
*
* Author: Blake Hurd <naimorai@gmail.com>
*/
#ifdef NS3_OPENFLOW
#ifndef OPENFLOW_SWITCH_HELPER_H
#define OPENFLOW_SWITCH_HELPER_H
#include "ns3/openflow-interface.h"
#include "ns3/net-device-container.h"
#include "ns3/object-factory.h"
#include <string>
namespace ns3 {
class Node;
class AttributeValue;
class Controller;
/**
* \brief Add capability to switch multiple LAN segments (IEEE 802.1D bridging)
*/
class OpenFlowSwitchHelper
{
public:
/*
* Construct a OpenFlowSwitchHelper
*/
OpenFlowSwitchHelper ();
/**
* Set an attribute on each ns3::OpenFlowSwitchNetDevice created by
* OpenFlowSwitchHelper::Install
*
* \param n1 the name of the attribute to set
* \param v1 the value of the attribute to set
*/
void
SetDeviceAttribute (std::string n1, const AttributeValue &v1);
/**
* This method creates an ns3::OpenFlowSwitchNetDevice with the attributes
* configured by OpenFlowSwitchHelper::SetDeviceAttribute, adds the device
* to the node, attaches the given NetDevices as ports of the
* switch, and sets up a controller connection using the provided
* Controller.
*
* \param node The node to install the device in
* \param c Container of NetDevices to add as switch ports
* \param controller The controller connection.
* \returns A container holding the added net device.
*/
NetDeviceContainer
Install (Ptr<Node> node, NetDeviceContainer c, Ptr<ns3::ofi::Controller> controller);
/**
* This method creates an ns3::OpenFlowSwitchNetDevice with the attributes
* configured by OpenFlowSwitchHelper::SetDeviceAttribute, adds the device
* to the node, and attaches the given NetDevices as ports of the
* switch.
*
* \param node The node to install the device in
* \param c Container of NetDevices to add as switch ports
* \returns A container holding the added net device.
*/
NetDeviceContainer
Install (Ptr<Node> node, NetDeviceContainer c);
/**
* This method creates an ns3::OpenFlowSwitchNetDevice with the attributes
* configured by OpenFlowSwitchHelper::SetDeviceAttribute, adds the device
* to the node, and attaches the given NetDevices as ports of the
* switch.
*
* \param nodeName The name of the node to install the device in
* \param c Container of NetDevices to add as switch ports
* \returns A container holding the added net device.
*/
NetDeviceContainer
Install (std::string nodeName, NetDeviceContainer c);
private:
ObjectFactory m_deviceFactory;
};
} // namespace ns3
#endif // NS3_OPENFLOW
#endif /* OPENFLOW_SWITCH_HELPER_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,567 @@
/* -*- 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
*
* Author: Blake Hurd <naimorai@gmail.com>
*/
#ifndef OPENFLOW_INTERFACE_H
#define OPENFLOW_INTERFACE_H
#ifdef NS3_OPENFLOW
#include <assert.h>
#include <errno.h>
// Include OFSI code
#include "ns3/simulator.h"
#include "ns3/log.h"
#include "ns3/net-device.h"
#include "ns3/packet.h"
#include "ns3/address.h"
#include "ns3/nstime.h"
#include "ns3/mac48-address.h"
#include <set>
#include <map>
#include <limits>
// Include main header and Vendor Extension files
#include "openflow.h"
#include "nicira-ext.h"
#include "ericsson-ext.h"
extern "C"
{
// Inexplicably, the OpenFlow implementation uses these two reserved words as member names.
#define private _private
#define delete _delete
#define list List
// Include OFSI Library files
#include "csum.h"
#include "poll-loop.h"
#include "rconn.h"
#include "stp.h"
#include "vconn.h"
#include "xtoxll.h"
// Include OFSI Switch files
#include "chain.h"
#include "table.h"
#include "datapath.h" // The functions below are defined in datapath.c
uint32_t save_buffer (ofpbuf *);
ofpbuf * retrieve_buffer (uint32_t id);
void discard_buffer (uint32_t id);
#include "dp_act.h" // The functions below are defined in dp_act.c
void set_vlan_vid (ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah);
void set_vlan_pcp (ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah);
void strip_vlan (ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah);
void set_dl_addr (ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah);
void set_nw_addr (ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah);
void set_tp_port (ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah);
void set_mpls_label (ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah);
void set_mpls_exp (ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah);
#include "pt_act.h" // The function below is defined in pt_act.c
void update_checksums (ofpbuf *buffer, const sw_flow_key *key, uint32_t old_word, uint32_t new_word);
#undef list
#undef private
#undef delete
}
// Capabilities supported by this implementation.
#define OFP_SUPPORTED_CAPABILITIES ( OFPC_FLOW_STATS \
| OFPC_TABLE_STATS \
| OFPC_PORT_STATS \
| OFPC_MULTI_PHY_TX \
| OFPC_VPORT_TABLE)
// Actions supported by this implementation.
#define OFP_SUPPORTED_ACTIONS ( (1 << OFPAT_OUTPUT) \
| (1 << OFPAT_SET_VLAN_VID) \
| (1 << OFPAT_SET_VLAN_PCP) \
| (1 << OFPAT_STRIP_VLAN) \
| (1 << OFPAT_SET_DL_SRC) \
| (1 << OFPAT_SET_DL_DST) \
| (1 << OFPAT_SET_NW_SRC) \
| (1 << OFPAT_SET_NW_DST) \
| (1 << OFPAT_SET_TP_SRC) \
| (1 << OFPAT_SET_TP_DST) \
| (1 << OFPAT_SET_MPLS_LABEL) \
| (1 << OFPAT_SET_MPLS_EXP) )
#define OFP_SUPPORTED_VPORT_TABLE_ACTIONS ( (1 << OFPPAT_OUTPUT) \
| (1 << OFPPAT_POP_MPLS) \
| (1 << OFPPAT_PUSH_MPLS) \
| (1 << OFPPAT_SET_MPLS_LABEL) \
| (1 << OFPPAT_SET_MPLS_EXP) ) \
namespace ns3 {
class OpenFlowSwitchNetDevice;
namespace ofi {
/**
* \brief Port and its metadata.
*
* We need to store port metadata, because OpenFlow dictates that there
* exists a type of request where the Controller asks for data about a
* port, or multiple ports. Otherwise, we'd refer to it via Ptr<NetDevice>
* everywhere.
*/
struct Port
{
Port () : config (0),
state (0),
netdev (0),
rx_packets (0),
tx_packets (0),
rx_bytes (0),
tx_bytes (0),
tx_dropped (0),
mpls_ttl0_dropped (0)
{
}
uint32_t config; ///< Some subset of OFPPC_* flags.
uint32_t state; ///< Some subset of OFPPS_* flags.
Ptr<NetDevice> netdev;
unsigned long long int rx_packets, tx_packets;
unsigned long long int rx_bytes, tx_bytes;
unsigned long long int tx_dropped;
unsigned long long int mpls_ttl0_dropped;
};
class Stats
{
public:
Stats (ofp_stats_types _type, size_t body_len);
/**
* \brief Prepares to dump some kind of statistics on the connected OpenFlowSwitchNetDevice.
*
* \param body Body member of the struct ofp_stats_request.
* \param body_len Length of the body member.
* \param state State information.
* \return 0 if successful, otherwise a negative error code.
*/
int DoInit (const void *body, int body_len, void **state);
/**
* \brief Appends statistics for OpenFlowSwitchNetDevice to 'buffer'.
*
* \param swtch The OpenFlowSwitchNetDevice this callback is associated with.
* \param state State information.
* \param buffer Buffer to append stats reply to.
* \return 1 if it should be called again later with another buffer, 0 if it is done, or a negative errno value on failure.
*/
int DoDump (Ptr<OpenFlowSwitchNetDevice> swtch, void *state, ofpbuf *buffer);
/**
* \brief Cleans any state created by the init or dump functions.
*
* May not be implemented if no cleanup is required.
*
* \param state State information to clear.
*/
void DoCleanup (void *state);
/**
* \brief State of the FlowStats request/reply.
*/
struct FlowStatsState
{
int table_idx;
sw_table_position position;
ofp_flow_stats_request rq;
time_t now;
ofpbuf *buffer;
};
/**
* \brief State of the PortStats request/reply.
*/
struct PortStatsState
{
uint32_t num_ports; ///< Number of ports in host byte order
uint32_t *ports; ///< Array of ports in network byte order
};
ofp_stats_types type;
private:
int DescStatsDump (void *state, ofpbuf *buffer);
int FlowStatsInit (const void *body, int body_len, void **state);
int (*FlowDumpCallback)(sw_flow *flow, void *state);
int FlowStatsDump (Ptr<OpenFlowSwitchNetDevice> dp, FlowStatsState *s, ofpbuf *buffer);
int AggregateStatsInit (const void *body, int body_len, void **state);
int (*AggregateDumpCallback)(sw_flow *flow, void *state);
int AggregateStatsDump (Ptr<OpenFlowSwitchNetDevice> dp, ofp_aggregate_stats_request *s, ofpbuf *buffer);
int TableStatsDump (Ptr<OpenFlowSwitchNetDevice> dp, void *state, ofpbuf *buffer);
int PortStatsInit (const void *body, int body_len, void **state);
int PortStatsDump (Ptr<OpenFlowSwitchNetDevice> dp, PortStatsState *s, ofpbuf *buffer);
int PortTableStatsDump (Ptr<OpenFlowSwitchNetDevice> dp, void *state, ofpbuf *buffer);
};
/**
* \brief Class for handling flow table actions.
*/
struct Action
{
/**
* \param type Type of Flow Table Action.
* \return true if the provided type is a type of flow table action.
*/
static bool IsValidType (ofp_action_type type);
/**
* \brief Validates the action on whether its data is valid or not.
*
* \param type Type of action to validate.
* \param len Length of the action data.
* \param key Matching key for the flow that is tied to this action.
* \param ah Action's data header.
* \return ACT_VALIDATION_OK if the action checks out, otherwise an error type.
*/
static uint16_t Validate (ofp_action_type type, size_t len, const sw_flow_key *key, const ofp_action_header *ah);
/**
* \brief Executes the action.
*
* \param type Type of action to execute.
* \param buffer Buffer of the Packet if it's needed for the action.
* \param key Matching key for the flow that is tied to this action.
* \param ah Action's data header.
*/
static void Execute (ofp_action_type type, ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah);
};
/**
* \brief Class for handling virtual port table actions.
*/
struct VPortAction
{
/**
* \param type Type of virtual port table Action.
* \return true if the provided type is a type of virtual port table action.
*/
static bool IsValidType (ofp_vport_action_type type);
/**
* \brief Validates the action on whether its data is valid or not.
*
* \param type Type of action to validate.
* \param len Length of the action data.
* \param ah Action's data header.
* \return ACT_VALIDATION_OK if the action checks out, otherwise an error type.
*/
static uint16_t Validate (ofp_vport_action_type type, size_t len, const ofp_action_header *ah);
/**
* \brief Executes the action.
*
* \param type Type of action to execute.
* \param buffer Buffer of the Packet if it's needed for the action.
* \param key Matching key for the flow that is tied to this action.
* \param ah Action's data header.
*/
static void Execute (ofp_vport_action_type type, ofpbuf *buffer, const sw_flow_key *key, const ofp_action_header *ah);
};
/**
* \brief Class for handling Ericsson Vendor-defined actions.
*/
struct EricssonAction
{
/**
* \param type Type of Ericsson Vendor-defined Action.
* \return true if the provided type is a type of Ericsson Vendor-defined action.
*/
static bool IsValidType (er_action_type type);
/**
* \brief Validates the action on whether its data is valid or not.
*
* \param type Type of action to validate.
* \param len Length of the action data.
* \return ACT_VALIDATION_OK if the action checks out, otherwise an error type.
*/
static uint16_t Validate (er_action_type type, size_t len);
/**
* \brief Executes the action.
*
* \param type Type of action to execute.
* \param buffer Buffer of the Packet if it's needed for the action.
* \param key Matching key for the flow that is tied to this action.
* \param ah Action's data header.
*/
static void Execute (er_action_type type, ofpbuf *buffer, const sw_flow_key *key, const er_action_header *ah);
};
/**
* \brief Callback for a stats dump request.
*/
struct StatsDumpCallback
{
bool done; ///< Whether we are done requesting stats.
ofp_stats_request *rq; ///< Current stats request.
Stats *s; ///< Handler of the stats request.
void *state; ///< Stats request state data.
Ptr<OpenFlowSwitchNetDevice> swtch; ///< The switch that we're requesting data from.
};
/**
* \brief Packet Metadata, allows us to track the packet's metadata as it passes through the switch.
*/
struct SwitchPacketMetadata
{
Ptr<Packet> packet; ///< The Packet itself.
ofpbuf* buffer; ///< The OpenFlow buffer as created from the Packet, with its data and headers.
uint16_t protocolNumber; ///< Protocol type of the Packet when the Packet is received
Address src; ///< Source Address of the Packet when the Packet is received
Address dst; ///< Destination Address of the Packet when the Packet is received.
};
/**
* \brief An interface for a Controller of OpenFlowSwitchNetDevices
*
* Follows the OpenFlow specification for a controller.
*/
class Controller : public Object
{
public:
static TypeId GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::ofi::Controller")
.SetParent<Object> ()
.AddConstructor<Controller> ()
;
return tid;
}
virtual ~Controller ()
{
m_switches.clear ();
}
/**
* Adds a switch to the controller.
*
* \param swtch The switch to register.
*/
virtual void AddSwitch (Ptr<OpenFlowSwitchNetDevice> swtch);
/**
* A switch calls this method to pass a message on to the Controller.
*
* \param swtch The switch the message was received from.
* \param buffer The message.
*/
virtual void ReceiveFromSwitch (Ptr<OpenFlowSwitchNetDevice> swtch, ofpbuf* buffer)
{
}
/**
* \brief Starts a callback-based, reliable, possibly multi-message reply to a request made by the controller.
*
* If an incoming request needs to have a reliable reply that might
* require multiple messages, it can use StartDump() to set up
* a callback that will be called as buffer space for replies.
*
* A stats request made by the controller is processed by the switch,
* the switch then calls this method to tell the controller to start
* asking for information. By default (it can be overridden), the
* controller stops all work to run through the callback. ReceiveFromSwitch
* must be defined appropriately to handle the status reply messages
* generated by the switch, or otherwise the status reply messages will be sent
* and discarded.
*
* \param cb The callback data.
*/
void StartDump (StatsDumpCallback* cb);
protected:
/**
* \internal
*
* However the controller is implemented, this method is to
* be used to pass a message on to a switch.
*
* \param swtch The switch to receive the message.
* \param msg The message to send.
* \param length The length of the message.
*/
virtual void SendToSwitch (Ptr<OpenFlowSwitchNetDevice> swtch, void * msg, size_t length);
/**
* \internal
*
* Construct flow data from a matching key to build a flow
* entry for adding, modifying, or deleting a flow.
*
* \param key The matching key data; used to create a flow that matches the packet.
* \param buffer_id The OpenFlow Buffer ID; used to run the actions on the packet if we add or modify the flow.
* \param command Whether to add, modify, or delete this flow.
* \param acts List of actions to execute.
* \param actions_len Length of the actions buffer.
* \param idle_timeout Flow expires if left inactive for this amount of time (specify OFP_FLOW_PERMANENT to disable feature).
* \param hard_timeout Flow expires after this amount of time (specify OFP_FLOW_PERMANENT to disable feature).
* \return Flow data that when passed to SetFlow will add, modify, or delete a flow it defines.
*/
ofp_flow_mod* BuildFlow (sw_flow_key key, uint32_t buffer_id, uint16_t command, void* acts, size_t actions_len, int idle_timeout, int hard_timeout);
/**
* \internal
*
* Get the packet type on the buffer, which can then be used
* to determine how to handle the buffer.
*
* \param buffer The packet in OpenFlow buffer format.
* \return The packet type, as defined in the ofp_type struct.
*/
uint8_t GetPacketType (ofpbuf* buffer);
typedef std::set<Ptr<OpenFlowSwitchNetDevice> > Switches_t;
Switches_t m_switches; ///< The collection of switches registered to this controller.
};
/**
* Demonstration of a Drop controller. When a connected switch
* passes it a packet the switch doesn't recognize, the controller
* configures the switch to make a flow that drops alike packets.
*/
class DropController : public Controller
{
public:
void ReceiveFromSwitch (Ptr<OpenFlowSwitchNetDevice> swtch, ofpbuf* buffer);
};
/**
* Demonstration of a Learning controller. When a connected switch
* passes it a packet the switch doesn't recognize, the controller
* delves into its learned states and figures out if we know what
* port the packet is supposed to go to, flooding if unknown, and
* adjusts the switch's flow table accordingly.
*/
class LearningController : public Controller
{
public:
static TypeId GetTypeId (void);
virtual ~LearningController ()
{
m_learnState.clear ();
}
void ReceiveFromSwitch (Ptr<OpenFlowSwitchNetDevice> swtch, ofpbuf* buffer);
protected:
struct LearnedState
{
uint32_t port; ///< Learned port.
};
Time m_expirationTime; ///< Time it takes for learned MAC state entry/created flow to expire.
typedef std::map<Mac48Address, LearnedState> LearnState_t;
LearnState_t m_learnState; ///< Learned state data.
};
/**
* \brief Executes a list of flow table actions.
*
* \param swtch OpenFlowSwitchNetDevice these actions are being executed on.
* \param packet_uid Packet UID; used to fetch the packet and its metadata.
* \param buffer The Packet OpenFlow buffer.
* \param key The matching key for the flow tied to this list of actions.
* \param actions A buffer of actions.
* \param actions_len Length of actions buffer.
* \param ignore_no_fwd If true, during port forwarding actions, ports that are set to not forward are forced to forward.
*/
void ExecuteActions (Ptr<OpenFlowSwitchNetDevice> swtch, uint64_t packet_uid, ofpbuf* buffer, sw_flow_key *key, const ofp_action_header *actions, size_t actions_len, int ignore_no_fwd);
/**
* \brief Validates a list of flow table actions.
*
* \param key The matching key for the flow tied to this list of actions.
* \param actions A buffer of actions.
* \param actions_len Length of actions buffer.
* \return If the action list validates, ACT_VALIDATION_OK is returned. Otherwise, a code for the OFPET_BAD_ACTION error type is returned.
*/
uint16_t ValidateActions (const sw_flow_key *key, const ofp_action_header *actions, size_t actions_len);
/**
* \brief Executes a list of virtual port table entry actions.
*
* \param swtch OpenFlowSwitchNetDevice these actions are being executed on.
* \param packet_uid Packet UID; used to fetch the packet and its metadata.
* \param buffer The Packet OpenFlow buffer.
* \param key The matching key for the flow tied to this list of actions.
* \param actions A buffer of actions.
* \param actions_len Length of actions buffer.
*/
void ExecuteVPortActions (Ptr<OpenFlowSwitchNetDevice> swtch, uint64_t packet_uid, ofpbuf* buffer, sw_flow_key *key, const ofp_action_header *actions, size_t actions_len);
/**
* \brief Validates a list of virtual port table entry actions.
*
* \param actions A buffer of actions.
* \param actions_len Length of actions buffer.
* \return If the action list validates, ACT_VALIDATION_OK is returned. Otherwise, a code for the OFPET_BAD_ACTION error type is returned.
*/
uint16_t ValidateVPortActions (const ofp_action_header *actions, size_t actions_len);
/**
* \brief Executes a vendor-defined action.
*
* \param buffer The Packet OpenFlow buffer.
* \param key The matching key for the flow tied to this list of actions.
* \param ah Header of the action.
*/
void ExecuteVendor (ofpbuf *buffer, const sw_flow_key *key, const ofp_action_header *ah);
/**
* \brief Validates a vendor-defined action.
*
* \param key The matching key for the flow tied to this list of actions.
* \param ah Header of the action.
* \param len Length of the action.
* \return If the action list validates, ACT_VALIDATION_OK is returned. Otherwise, a code for the OFPET_BAD_ACTION error type is returned.
*/
uint16_t ValidateVendor (const sw_flow_key *key, const ofp_action_header *ah, uint16_t len);
/*
* From datapath.c
* Buffers are identified to userspace by a 31-bit opaque ID. We divide the ID
* into a buffer number (low bits) and a cookie (high bits). The buffer number
* is an index into an array of buffers. The cookie distinguishes between
* different packets that have occupied a single buffer. Thus, the more
* buffers we have, the lower-quality the cookie...
*/
#define PKT_BUFFER_BITS 8
#define N_PKT_BUFFERS (1 << PKT_BUFFER_BITS)
#define PKT_BUFFER_MASK (N_PKT_BUFFERS - 1)
#define PKT_COOKIE_BITS (32 - PKT_BUFFER_BITS)
}
}
#endif // NS3_OPENFLOW
#endif /* OPENFLOW_INTERFACE_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,550 @@
/* -*- 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
*
* Author: Blake Hurd <naimorai@gmail.com>
*/
#ifdef NS3_OPENFLOW
#ifndef OPENFLOW_SWITCH_NET_DEVICE_H
#define OPENFLOW_SWITCH_NET_DEVICE_H
#include "ns3/simulator.h"
#include "ns3/log.h"
#include "ns3/mac48-address.h"
#include "ns3/ethernet-header.h"
#include "ns3/arp-header.h"
#include "ns3/tcp-header.h"
#include "ns3/udp-header.h"
#include "ns3/ipv4-l3-protocol.h"
#include "ns3/arp-l3-protocol.h"
#include "ns3/bridge-channel.h"
#include "ns3/node.h"
#include "ns3/enum.h"
#include "ns3/string.h"
#include "ns3/integer.h"
#include "ns3/uinteger.h"
#include <map>
#include <set>
#include "openflow-interface.h"
namespace ns3 {
/**
* \ingroup devices
* \defgroup openflow OpenFlow
*
* \brief A net device that switches multiple LAN segments via an OpenFlow-compatible flow table
*
* The OpenFlowSwitchNetDevice object aggregates multiple netdevices as ports
* and acts like a switch. It implements OpenFlow-compatibility,
* according to the OpenFlow Switch Specification v0.8.9
* <www.openflowswitch.org/documents/openflow-spec-v0.8.9.pdf>.
* It implements a flow table that all received packets are run through.
* It implements a connection to a controller via a subclass of the Controller class,
* which can send messages to manipulate the flow table, thereby manipulating
* how the OpenFlow switch behaves.
*
* There are two controllers available in the original package. DropController
* builds a flow for each received packet to drop all packets it matches (this
* demonstrates the flow table's basic implementation), and the LearningController
* implements a "learning switch" algorithm (see 802.1D), where incoming unicast
* frames from one port may occasionally be forwarded throughout all other ports,
* but usually they are forwarded only to a single correct output port.
*
* \attention The Spanning Tree Protocol part of 802.1D is not
* implemented. Therefore, you have to be careful not to create
* bridging loops, or else the network will collapse.
*
* \attention Each NetDevice used must only be assigned a Mac Address, adding it
* to an Ipv4 or Ipv6 layer will cause an error. It also must support a SendFrom
* call.
*/
/**
* \ingroup switch
* \brief A net device that switches multiple LAN segments via an OpenFlow-compatible flow table
*/
class OpenFlowSwitchNetDevice : public NetDevice
{
public:
static TypeId GetTypeId (void);
/**
* \name OpenFlowSwitchNetDevice Description Data
* \brief These four data describe the OpenFlowSwitchNetDevice as if it were a real OpenFlow switch.
*
* There is a type of stats request that OpenFlow switches are supposed
* to handle that returns the description of the OpenFlow switch. Currently
* manufactured by "The ns-3 team", software description is "Simulated
* OpenFlow Switch", and the other two are "N/A".
*/
//\{
static const char * GetManufacturerDescription ();
static const char * GetHardwareDescription ();
static const char * GetSoftwareDescription ();
static const char * GetSerialNumber ();
//\}
OpenFlowSwitchNetDevice ();
virtual ~OpenFlowSwitchNetDevice ();
/**
* \brief Set up the Switch's controller connection.
*
* \param c Pointer to a Controller.
*/
void SetController (Ptr<ofi::Controller> c);
/**
* \brief Add a 'port' to a switch device
*
* This method adds a new switch port to a OpenFlowSwitchNetDevice, so that
* the new switch port NetDevice becomes part of the switch and L2
* frames start being forwarded to/from this NetDevice.
*
* \attention The netdevice that is being added as switch port must
* _not_ have an IP address. In order to add IP connectivity to a
* bridging node you must enable IP on the OpenFlowSwitchNetDevice itself,
* never on its port netdevices.
*
* \param switchPort The port to add.
* \return 0 if everything's ok, otherwise an error number.
* \sa #EXFULL
*/
int AddSwitchPort (Ptr<NetDevice> switchPort);
/**
* \brief Add a virtual port to a switch device
*
* The Ericsson OFSID has the concept of virtual ports and virtual
* port tables. These are implemented in the OpenFlowSwitchNetDevice, but
* don't have an understood use [perhaps it may have to do with
* MPLS integration].
*
* \sa #EINVAL
*
* \param ovpm The data for adding a virtual port.
* \return 0 if everything's ok, otherwise an error number.
*/
int AddVPort (const ofp_vport_mod *ovpm);
/**
* \brief Stats callback is ready for a dump.
*
* Controllers have a callback system for status requests which calls this function.
*
* \param cb_ The callback data.
* \return 0 if everything's ok, otherwise an error number.
*/
int StatsDump (ofi::StatsDumpCallback *cb_);
/**
* \brief Stats callback is done.
*
* Controllers have a callback system for status requests which calls this function.
*
* \param cb_ The callback data.
*/
void StatsDone (ofi::StatsDumpCallback *cb_);
/**
* \brief Called from the OpenFlow Interface to output the Packet on either a Port or the Controller
*
* \param packet_uid Packet UID; used to fetch the packet and its metadata.
* \param in_port The index of the port the Packet was initially received on.
* \param max_len The maximum number of bytes the caller wants to be sent; a value of 0 indicates the entire packet should be sent. Used when outputting to controller.
* \param out_port The port we want to output on.
* \param ignore_no_fwd If true, Ports that are set to not forward are forced to forward.
*/
void DoOutput (uint32_t packet_uid, int in_port, size_t max_len, int out_port, bool ignore_no_fwd);
/**
* \brief The registered controller calls this method when sending a message to the switch.
*
* \param msg The message received from the controller.
* \param length Length of the message.
* \return 0 if everything's ok, otherwise an error number.
*/
int ForwardControlInput (const void *msg, size_t length);
/**
* \return The flow table chain.
*/
sw_chain* GetChain ();
/**
* \return Number of switch ports attached to this switch.
*/
uint32_t GetNSwitchPorts (void) const;
/**
* \param p The Port to get the index of.
* \return The index of the provided Port.
*/
int GetSwitchPortIndex (ofi::Port p);
/**
* \param n index of the Port.
* \return The Port.
*/
ofi::Port GetSwitchPort (uint32_t n) const;
/**
* \return The virtual port table.
*/
vport_table_t GetVPortTable ();
///\name From NetDevice
//\{
virtual void SetIfIndex (const uint32_t index);
virtual uint32_t GetIfIndex (void) const;
virtual Ptr<Channel> GetChannel (void) const;
virtual void SetAddress (Address address);
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 AddLinkChangeCallback (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);
/**
* \internal
*
* Called when a packet is received on one of the switch's ports.
*
* \param netdev The port the packet was received on.
* \param packet The Packet itself.
* \param protocol The protocol defining the Packet.
* \param src The source address of the Packet.
* \param dst The destination address of the Packet.
* \param PacketType Type of the packet.
*/
void ReceiveFromDevice (Ptr<NetDevice> netdev, Ptr<const Packet> packet, uint16_t protocol, const Address& src, const Address& dst, PacketType packetType);
/**
* \internal
*
* Takes a packet and generates an OpenFlow buffer from it, loading the packet data as well as its headers.
*
* \param packet The packet.
* \param src The source address.
* \param dst The destination address.
* \param mtu The Maximum Transmission Unit.
* \param protocol The protocol defining the packet.
* \return The OpenFlow Buffer created from the packet.
*/
ofpbuf * BufferFromPacket (Ptr<Packet> packet, Address src, Address dst, int mtu, uint16_t protocol);
private:
/**
* \internal
*
* Add a flow.
*
* \sa #ENOMEM, #ENOBUFS, #ESRCH
*
* \param ofm The flow data to add.
* \return 0 if everything's ok, otherwise an error number.
*/
int AddFlow (const ofp_flow_mod *ofm);
/**
* \internal
*
* Modify a flow.
*
* \param ofm The flow data to modify.
* \return 0 if everything's ok, otherwise an error number.
*/
int ModFlow (const ofp_flow_mod *ofm);
/**
* \internal
*
* Send packets out all the ports except the originating one
*
* \param packet_uid Packet UID; used to fetch the packet and its metadata.
* \param in_port The index of the port the Packet was initially received on. This port doesn't forward when flooding.
* \param flood If true, don't send out on the ports with flooding disabled.
* \return 0 if everything's ok, otherwise an error number.
*/
int OutputAll (uint32_t packet_uid, int in_port, bool flood);
/**
* \internal
*
* Sends a copy of the Packet over the provided output port
*
* \param packet_uid Packet UID; used to fetch the packet and its metadata.
*/
void OutputPacket (uint32_t packet_uid, int out_port);
/**
* \internal
*
* Seeks to send out a Packet over the provided output port. This is called generically
* when we may or may not know the specific port we're outputting on. There are many
* pre-set types of port options besides a Port that's hooked to our OpenFlowSwitchNetDevice.
* For example, it could be outputting as a flood, or seeking to output to the controller.
*
* \param packet_uid Packet UID; used to fetch the packet and its metadata.
* \param in_port The index of the port the Packet was initially received on.
* \param out_port The port we want to output on.
* \param ignore_no_fwd If true, Ports that are set to not forward are forced to forward.
*/
void OutputPort (uint32_t packet_uid, int in_port, int out_port, bool ignore_no_fwd);
/**
* \internal
*
* Sends a copy of the Packet to the controller. If the packet can be saved
* in an OpenFlow buffer, then only the first 'max_len' bytes of the packet
* are sent; otherwise, all of the packet is sent.
*
* \param packet_uid Packet UID; used to fetch the packet and its metadata.
* \param in_port The index of the port the Packet was initially received on.
* \param max_len The maximum number of bytes that the caller wants to be sent; a value of 0 indicates the entire packet should be sent.
* \param reason Why the packet is being sent.
*/
void OutputControl (uint32_t packet_uid, int in_port, size_t max_len, int reason);
/**
* \internal
*
* If an error message happened during the controller's request, send it to the controller.
*
* \param type The type of error.
* \param code The error code.
* \param data The faulty data that lead to the error.
* \param len The length of the faulty data.
*/
void SendErrorMsg (uint16_t type, uint16_t code, const void *data, size_t len);
/**
* \internal
*
* Send a reply about this OpenFlow switch's features to the controller.
*
* List of capabilities and actions to support are found in the specification
* <www.openflowswitch.org/documents/openflow-spec-v0.8.9.pdf>.
*
* Supported capabilities and actions are defined in the openflow interface.
* To recap, flow status, flow table status, port status, virtual port table
* status can all be requested. It can also transmit over multiple physical
* interfaces.
*
* It supports every action: outputting over a port, and all of the flow table
* manipulation actions: setting the 802.1q VLAN ID, the 802.1q priority,
* stripping the 802.1 header, setting the Ethernet source address and destination,
* setting the IP source address and destination, setting the TCP/UDP source address
* and destination, and setting the MPLS label and EXP bits.
*
* \attention Capabilities STP (Spanning Tree Protocol) and IP packet
* reassembly are not currently supported.
*
*/
void SendFeaturesReply ();
/**
* \internal
*
* Send a reply to the controller that a specific flow has expired.
*
* \param flow The flow that expired.
* \param reason The reason for sending this expiration notification.
*/
void SendFlowExpired (sw_flow *flow, enum ofp_flow_expired_reason reason);
/**
* \internal
*
* Send a reply about a Port's status to the controller.
*
* \param p The port to get status from.
* \param status The reason for sending this reply.
*/
void SendPortStatus (ofi::Port p, uint8_t status);
/**
* \internal
*
* Send a reply about this OpenFlow switch's virtual port table features to the controller.
*/
void SendVPortTableFeatures ();
/**
* \internal
*
* Send a message to the controller. This method is the key
* to communicating with the controller, it does the actual
* sending. The other Send methods call this one when they
* are ready to send a message.
*
* \param buffer Buffer of the message to send out.
* \return 0 if successful, otherwise an error number.
*/
int SendOpenflowBuffer (ofpbuf *buffer);
/**
* \internal
*
* Run the packet through the flow table. Looks up in the flow table for a match.
* If it doesn't match, it forwards the packet to the registered controller, if the flag is set.
*
* \param packet_uid Packet UID; used to fetch the packet and its metadata.
* \param port The port this packet was received over.
* \param send_to_controller If set, sends to the controller if the packet isn't matched.
*/
void RunThroughFlowTable (uint32_t packet_uid, int port, bool send_to_controller = true);
/**
* \internal
*
* Run the packet through the vport table. As with AddVPort,
* this doesn't have an understood use yet.
*
* \param packet_uid Packet UID; used to fetch the packet and its metadata.
* \param port The port this packet was received over.
* \param vport The virtual port this packet identifies itself by.
* \return 0 if everything's ok, otherwise an error number.
*/
int RunThroughVPortTable (uint32_t packet_uid, int port, uint32_t vport);
/**
* \internal
*
* Called by RunThroughFlowTable on a scheduled delay
* to account for the flow table lookup overhead.
*
* \param key Matching key to look up in the flow table.
* \param buffer Buffer of the packet received.
* \param packet_uid Packet UID; used to fetch the packet and its metadata.
* \param port The port the packet was received over.
* \param send_to_controller
*/
void FlowTableLookup (sw_flow_key key, ofpbuf* buffer, uint32_t packet_uid, int port, bool send_to_controller);
/**
* \internal
*
* Update the port status field of the switch port.
* A non-zero return value indicates some field has changed.
*
* \param p A reference to a Port; used to change its config and flag fields.
* \return true if the status of the Port is changed, false if unchanged (was already the right status).
*/
int UpdatePortStatus (ofi::Port& p);
/**
* \internal
*
* Fill out a description of the switch port.
*
* \param p The port to get the description from.
* \param desc A pointer to the description message; used to fill the description message with the data from the port.
*/
void FillPortDesc (ofi::Port p, ofp_phy_port *desc);
/**
* \internal
*
* Generates an OpenFlow reply message based on the type.
*
* \param openflow_len Length of the reply to make.
* \param type Type of reply message to make.
* \param bufferp Message buffer; used to make the reply.
* \return The OpenFlow reply message.
*/
void* MakeOpenflowReply (size_t openflow_len, uint8_t type, ofpbuf **bufferp);
/**
* \internal
* \name Receive Methods
*
* Actions to do when a specific OpenFlow message/packet is received
*
* \param msg The OpenFlow message received.
* \return 0 if everything's ok, otherwise an error number.
*/
//\{
int ReceiveFeaturesRequest (const void *msg);
int ReceiveGetConfigRequest (const void *msg);
int ReceiveSetConfig (const void *msg);
int ReceivePacketOut (const void *msg);
int ReceiveFlow (const void *msg);
int ReceivePortMod (const void *msg);
int ReceiveStatsRequest (const void *oh);
int ReceiveEchoRequest (const void *oh);
int ReceiveEchoReply (const void *oh);
int ReceiveVPortMod (const void *msg);
int ReceiveVPortTableFeaturesRequest (const void *msg);
//\}
/// Callbacks
NetDevice::ReceiveCallback m_rxCallback;
NetDevice::PromiscReceiveCallback m_promiscRxCallback;
Mac48Address m_address; ///< Address of this device.
Ptr<Node> m_node; ///< Node this device is installed on.
Ptr<BridgeChannel> m_channel; ///< Collection of port channels into the Switch Channel.
uint32_t m_ifIndex; ///< Interface Index
uint16_t m_mtu; ///< Maximum Transmission Unit
typedef std::map<uint32_t,ofi::SwitchPacketMetadata> PacketData_t;
PacketData_t m_packetData; ///< Packet data
typedef std::vector<ofi::Port> Ports_t;
Ports_t m_ports; ///< Switch's ports
Ptr<ofi::Controller> m_controller; ///< Connection to controller.
uint64_t m_id; ///< Unique identifier for this switch, needed for OpenFlow
Time m_lookupDelay; ///< Flow Table Lookup Delay [overhead].
Time m_lastExecute; ///< Last time the periodic execution occurred.
uint16_t m_flags; ///< Flags; configurable by the controller.
uint16_t m_missSendLen; ///< Flow Table Miss Send Length; configurable by the controller.
sw_chain *m_chain; ///< Flow Table; forwarding rules.
vport_table_t m_vportTable; ///< Virtual Port Table
};
} // namespace ns3
#endif /* OPENFLOW_SWITCH_NET_DEVICE_H */
#endif // NS3_OPENFLOW

View File

@@ -0,0 +1,20 @@
#! /usr/bin/env python
## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
# A list of C++ examples to run in order to ensure that they remain
# buildable and runnable over time. Each tuple in the list contains
#
# (example_name, do_run, do_valgrind_run).
#
# See test.py for more information.
cpp_examples = [
("openflow-switch", "ENABLE_OPENFLOW == True", "True"),
]
# A list of Python examples to run in order to ensure that they remain
# runnable over time. Each tuple in the list contains
#
# (example_name, do_run).
#
# See test.py for more information.
python_examples = []

View File

@@ -0,0 +1,193 @@
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2011 Blake Hurd
*
* 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
*
* Author: Blake Hurd <naimorai@gmail.com>
*/
#ifdef NS3_OPENFLOW
// An essential include is test.h
#include "ns3/test.h"
#include "ns3/openflow-switch-net-device.h"
#include "ns3/openflow-interface.h"
// Do not put your test classes in namespace ns3. You may find it useful
// to use the using directive to access the ns3 namespace directly
using namespace ns3;
// This is an example TestCase.
class SwitchFlowTableTestCase : public TestCase
{
public:
SwitchFlowTableTestCase () : TestCase ("Switch test case")
{
m_chain = chain_create ();
}
virtual ~SwitchFlowTableTestCase ()
{
chain_destroy (m_chain);
}
private:
virtual void DoRun (void);
sw_chain* m_chain;
};
void
SwitchFlowTableTestCase::DoRun (void)
{
// Flow Table implementation is used by the OpenFlowSwitchNetDevice under the chain_ methods
// we should test its implementation to verify the flow table works.
// Initialization
time_init (); // OFSI requires this, otherwise we crash before we can do anything.
size_t actions_len = 0; // Flow is created with 0 actions.
int output_port = 0; // Flow will be modified later with an action to output on port 0.
Mac48Address dl_src ("00:00:00:00:00:00"), dl_dst ("00:00:00:00:00:01");
Ipv4Address nw_src ("192.168.1.1"), nw_dst ("192.168.1.2");
int tp_src = 5000, tp_dst = 80;
// Create an sw_flow_key; in actual usage this is generated from the received packet's headers.
sw_flow_key key;
key.wildcards = 0;
key.flow.in_port = htons (0);
key.flow.dl_vlan = htons (OFP_VLAN_NONE);
key.flow.dl_type = htons (ETH_TYPE_IP);
key.flow.nw_proto = htons (IP_TYPE_UDP);
key.flow.reserved = 0;
key.flow.mpls_label1 = htonl (MPLS_INVALID_LABEL);
key.flow.mpls_label2 = htonl (MPLS_INVALID_LABEL);
// Set Mac Addresses
dl_src.CopyTo (key.flow.dl_src);
dl_dst.CopyTo (key.flow.dl_dst);
// Set IP Addresses
key.flow.nw_src = htonl (nw_src.Get ());
key.flow.nw_dst = htonl (nw_dst.Get ());
// Set TCP/UDP Ports
key.flow.tp_src = htonl (tp_src);
key.flow.tp_dst = htonl (tp_dst);
// Create flow
ofp_flow_mod ofm;
ofm.header.version = OFP_VERSION;
ofm.header.type = OFPT_FLOW_MOD;
ofm.header.length = htons (sizeof (ofp_flow_mod) + actions_len);
ofm.command = htons (OFPFC_ADD);
ofm.idle_timeout = htons (OFP_FLOW_PERMANENT);
ofm.hard_timeout = htons (OFP_FLOW_PERMANENT);
ofm.buffer_id = htonl (-1);
ofm.priority = OFP_DEFAULT_PRIORITY;
ofm.match.wildcards = key.wildcards; // Wildcard fields
ofm.match.in_port = key.flow.in_port; // Input switch port
memcpy (ofm.match.dl_src, key.flow.dl_src, sizeof ofm.match.dl_src); // Ethernet source address.
memcpy (ofm.match.dl_dst, key.flow.dl_dst, sizeof ofm.match.dl_dst); // Ethernet destination address.
ofm.match.dl_vlan = key.flow.dl_vlan; // Input VLAN OFP_VLAN_NONE;
ofm.match.dl_type = key.flow.dl_type; // Ethernet frame type ETH_TYPE_IP;
ofm.match.nw_proto = key.flow.nw_proto; // IP Protocol
ofm.match.nw_src = key.flow.nw_src; // IP source address
ofm.match.nw_dst = key.flow.nw_dst; // IP destination address
ofm.match.tp_src = key.flow.tp_src; // TCP/UDP source port
ofm.match.tp_dst = key.flow.tp_dst; // TCP/UDP destination port
ofm.match.mpls_label1 = key.flow.mpls_label1; // Top of label stack
ofm.match.mpls_label2 = key.flow.mpls_label1; // Second label (if available)
// Build a sw_flow from the ofp_flow_mod
sw_flow *flow = flow_alloc (actions_len);
NS_TEST_ASSERT_MSG_NE (flow, 0, "Cannot allocate memory for the flow.");
flow_extract_match (&flow->key, &ofm.match);
// Fill out flow.
flow->priority = flow->key.wildcards ? ntohs (ofm.priority) : -1;
flow->idle_timeout = ntohs (ofm.idle_timeout);
flow->hard_timeout = ntohs (ofm.hard_timeout);
flow->used = flow->created = time_now ();
flow->sf_acts->actions_len = actions_len;
flow->byte_count = 0;
flow->packet_count = 0;
memcpy (flow->sf_acts->actions, ofm.actions, actions_len);
// Insert the flow into the Flow Table
NS_TEST_ASSERT_MSG_EQ (chain_insert (m_chain, flow), 0, "Flow table failed to insert Flow.");
// Use key to match the flow to verify we created it correctly.
NS_TEST_ASSERT_MSG_NE (chain_lookup (m_chain, &key), 0, "Key provided doesn't match to the flow that was created from it.");
// Modify key to make sure the flow doesn't match it.
dl_dst.CopyTo (key.flow.dl_src);
dl_src.CopyTo (key.flow.dl_dst);
key.flow.nw_src = htonl (nw_dst.Get ());
key.flow.nw_dst = htonl (nw_src.Get ());
key.flow.tp_src = htonl (tp_dst);
key.flow.tp_dst = htonl (tp_src);
NS_TEST_ASSERT_MSG_EQ (chain_lookup (m_chain, &key), 0, "Key provided shouldn't match the flow but it does.");
// Modify key back to matching the flow so we can test flow modification.
dl_dst.CopyTo (key.flow.dl_dst);
dl_src.CopyTo (key.flow.dl_src);
key.flow.nw_src = htonl (nw_src.Get ());
key.flow.nw_dst = htonl (nw_dst.Get ());
key.flow.tp_src = htonl (tp_src);
key.flow.tp_dst = htonl (tp_dst);
// Testing Flow Modification; chain_modify should return 1, for 1 flow modified.
// Create output-to-port action
ofp_action_output acts[1];
acts[0].type = htons (OFPAT_OUTPUT);
acts[0].len = htons (sizeof (ofp_action_output));
acts[0].port = output_port;
uint16_t priority = key.wildcards ? ntohs (ofm.priority) : -1;
NS_TEST_ASSERT_MSG_EQ (chain_modify (m_chain, &key, priority, false, (const ofp_action_header*)acts, sizeof (acts)), 1, "Flow table failed to modify Flow.");
// Testing Flow Deletion; chain_delete should return 1, for 1 flow deleted.
// Note: By providing chain_delete with output_port, the flow must have an action that outputs on that port in order to delete the flow.
// This is how we verify that our action was truly added via the flow modification.
NS_TEST_ASSERT_MSG_EQ (chain_delete (m_chain, &key, output_port, 0, 0), 1, "Flow table failed to delete Flow.");
NS_TEST_ASSERT_MSG_EQ (chain_lookup (m_chain, &key), 0, "Key provided shouldn't match the flow but it does.");
}
class SwitchTestSuite : public TestSuite
{
public:
SwitchTestSuite ();
};
SwitchTestSuite::SwitchTestSuite () : TestSuite ("openflow", UNIT)
{
AddTestCase (new SwitchFlowTableTestCase);
}
// Do not forget to allocate an instance of this TestSuite
SwitchTestSuite switchTestSuite;
#endif // NS3_OPENFLOW

1
src/openflow/waf vendored Normal file
View File

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

131
src/openflow/wscript Normal file
View File

@@ -0,0 +1,131 @@
## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
import os
import Options
def set_options(opt):
opt.add_option('--with-openflow',
help=('Path to OFSID source for NS-3 OpenFlow Integration support'),
default='', dest='with_openflow')
def configure(conf):
conf.check_tool('boost')
conf.env['BOOST'] = conf.check_boost(lib = 'signals filesystem',
kind = 'STATIC_BOTH',
score_version = (-1000, 1000),
tag_minscore = 1000,)
if not conf.env['BOOST']:
conf.report_optional_feature("openflow", "NS-3 OpenFlow Integration", False,
"Required boost libraries not found")
return
if Options.options.with_openflow:
if os.path.isdir(Options.options.with_openflow):
conf.check_message("OpenFlow location", '', True, ("%s (given)" % Options.options.with_openflow))
conf.env['WITH_OPENFLOW'] = os.path.abspath(Options.options.with_openflow)
else:
openflow_dir = os.path.join('..','openflow')
if os.path.isdir(openflow_dir):
conf.check_message("OpenFlow location", '', True, ("%s (guessed)" % openflow_dir))
conf.env['WITH_OPENFLOW'] = os.path.abspath(openflow_dir)
del openflow_dir
if not conf.env['WITH_OPENFLOW']:
conf.check_message("OpenFlow location", '', False)
conf.report_optional_feature("openflow", "NS-3 OpenFlow Integration", False,
"OpenFlow not enabled (see option --with-openflow)")
return
test_code = '''
#include "openflow.h"
#include "nicira-ext.h"
#include "ericsson-ext.h"
extern "C"
{
#define private _private
#define delete _delete
#define list List
#include "csum.h"
#include "poll-loop.h"
#include "rconn.h"
#include "stp.h"
#include "vconn.h"
#include "xtoxll.h"
#include "chain.h"
#include "table.h"
#include "datapath.h" // The functions below are defined in datapath.c
uint32_t save_buffer (ofpbuf *);
ofpbuf * retrieve_buffer (uint32_t id);
void discard_buffer (uint32_t id);
#include "dp_act.h" // The functions below are defined in dp_act.c
void set_vlan_vid (ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah);
void set_vlan_pcp (ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah);
void strip_vlan (ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah);
void set_dl_addr (ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah);
void set_nw_addr (ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah);
void set_tp_port (ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah);
void set_mpls_label (ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah);
void set_mpls_exp (ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah);
#include "pt_act.h" // The function below is defined in pt_act.c
void update_checksums (ofpbuf *buffer, const sw_flow_key *key, uint32_t old_word, uint32_t new_word);
#undef list
#undef private
#undef delete
}
int main()
{
return 0;
}
'''
conf.env['DL'] = conf.check(mandatory=True, lib='dl', define_name='DL', uselib='DL')
conf.env['XML2'] = conf.check(mandatory=True, lib='xml2', define_name='XML2', uselib='XML2')
conf.env.append_value('NS3_MODULE_PATH',os.path.abspath(os.path.join(conf.env['WITH_OPENFLOW'],'build','default')))
conf.env['CPPPATH_OPENFLOW'] = [
os.path.abspath(os.path.join(conf.env['WITH_OPENFLOW'],'include')),
os.path.abspath(os.path.join(conf.env['WITH_OPENFLOW'],'include','openflow')),
os.path.abspath(os.path.join(conf.env['WITH_OPENFLOW'],'lib')),
os.path.abspath(os.path.join(conf.env['WITH_OPENFLOW'],'switch'))
]
conf.env['LIBPATH_OPENFLOW'] = [os.path.abspath(os.path.join(conf.env['WITH_OPENFLOW'],'build','default'))]
conf.env['OPENFLOW'] = conf.check(fragment=test_code, lib='openflow', uselib='OPENFLOW DL XML2')
conf.report_optional_feature("openflow", "NS-3 OpenFlow Integration",
conf.env['OPENFLOW'], "openflow library not found")
if conf.env['OPENFLOW']:
conf.env['ENABLE_OPENFLOW'] = True
conf.env.append_value('CXXDEFINES', 'NS3_OPENFLOW')
conf.env.append_value('CPPPATH', conf.env['CPPPATH_OPENFLOW'])
def build(bld):
# Build the Switch module
obj = bld.create_ns3_module('openflow', ['internet'])
obj.source = [
]
if bld.env['OPENFLOW'] and bld.env['DL'] and bld.env['XML2']:
obj.uselib = 'OPENFLOW DL XML2'
headers = bld.new_task_gen('ns3header')
headers.module = 'openflow'
headers.source = [
]
if bld.env['ENABLE_OPENFLOW']:
obj.source.append('model/openflow-interface.cc')
obj.source.append('model/openflow-switch-net-device.cc')
obj.source.append('helper/openflow-switch-helper.cc')
obj.source.append('test/openflow-switch-test-suite.cc')
headers.source.append('model/openflow-interface.h')
headers.source.append('model/openflow-switch-net-device.h')
headers.source.append('helper/openflow-switch-helper.h')
if bld.env['ENABLE_EXAMPLES'] and bld.env['ENABLE_OPENFLOW']:
bld.add_subdirs('examples')

View File

@@ -35,6 +35,7 @@ all_modules = (
'aodv',
'dsdv',
'click',
'openflow',
'mobility',
'wifi',
'netanim',
@@ -59,6 +60,7 @@ all_modules = (
def set_options(opt):
opt.sub_options('core')
opt.sub_options('click')
opt.sub_options('openflow')
opt.add_option('--enable-rpath',
help=("Link programs with rpath"
@@ -80,6 +82,7 @@ def configure(conf):
conf.sub_config('netanim')
conf.sub_config('test')
conf.sub_config('click')
conf.sub_config('openflow')
blddir = os.path.abspath(os.path.join(conf.blddir, conf.env.variant()))
conf.env.append_value('NS3_MODULE_PATH', blddir)

View File

@@ -51,12 +51,14 @@ interesting_config_items = [
"EXAMPLE_DIRECTORIES",
"ENABLE_PYTHON_BINDINGS",
"ENABLE_CLICK",
"ENABLE_OPENFLOW",
]
NSC_ENABLED = False
ENABLE_REAL_TIME = False
ENABLE_EXAMPLES = True
ENABLE_CLICK = False
ENABLE_OPENFLOW = False
EXAMPLE_DIRECTORIES = []
#