From 27080192c3fb9ca137c650dafdbf90558955b535 Mon Sep 17 00:00:00 2001 From: Josh Pelkey Date: Fri, 11 Mar 2011 15:21:50 -0500 Subject: [PATCH 1/3] Merge OpenFlow --- src/openflow/doc/openflow-switch.h | 158 ++ src/openflow/examples/openflow-switch.cc | 215 +++ src/openflow/examples/wscript | 6 + src/openflow/helper/openflow-switch-helper.cc | 97 + src/openflow/helper/openflow-switch-helper.h | 105 ++ src/openflow/model/openflow-interface.cc | 1135 ++++++++++++ src/openflow/model/openflow-interface.h | 567 ++++++ .../model/openflow-switch-net-device.cc | 1584 +++++++++++++++++ .../model/openflow-switch-net-device.h | 550 ++++++ src/openflow/test/examples-to-run.py | 20 + .../test/openflow-switch-test-suite.cc | 193 ++ src/openflow/waf | 1 + src/openflow/wscript | 131 ++ src/wscript | 3 + test.py | 2 + 15 files changed, 4767 insertions(+) create mode 100644 src/openflow/doc/openflow-switch.h create mode 100644 src/openflow/examples/openflow-switch.cc create mode 100644 src/openflow/examples/wscript create mode 100644 src/openflow/helper/openflow-switch-helper.cc create mode 100644 src/openflow/helper/openflow-switch-helper.h create mode 100644 src/openflow/model/openflow-interface.cc create mode 100644 src/openflow/model/openflow-interface.h create mode 100644 src/openflow/model/openflow-switch-net-device.cc create mode 100644 src/openflow/model/openflow-switch-net-device.h create mode 100644 src/openflow/test/examples-to-run.py create mode 100644 src/openflow/test/openflow-switch-test-suite.cc create mode 100644 src/openflow/waf create mode 100644 src/openflow/wscript diff --git a/src/openflow/doc/openflow-switch.h b/src/openflow/doc/openflow-switch.h new file mode 100644 index 000000000..a9217fd57 --- /dev/null +++ b/src/openflow/doc/openflow-switch.h @@ -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 + */ + +/** + * \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. <> + * + * # 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 + * first, and consider posting to the ns-3 developers mailing list , + * but feel free to reach me at + */ diff --git a/src/openflow/examples/openflow-switch.cc b/src/openflow/examples/openflow-switch.cc new file mode 100644 index 000000000..dca578f14 --- /dev/null +++ b/src/openflow/examples/openflow-switch.cc @@ -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 +#include + +#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 switchNode = csmaSwitch.Get (0); + OpenFlowSwitchHelper swtch; + + if (use_drop) + { + Ptr controller = CreateObject (); + swtch.Install (switchNode, switchDevices, controller); + } + else + { + Ptr controller = CreateObject (); + 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--.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 +} diff --git a/src/openflow/examples/wscript b/src/openflow/examples/wscript new file mode 100644 index 000000000..23bb252fe --- /dev/null +++ b/src/openflow/examples/wscript @@ -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' diff --git a/src/openflow/helper/openflow-switch-helper.cc b/src/openflow/helper/openflow-switch-helper.cc new file mode 100644 index 000000000..f78c34006 --- /dev/null +++ b/src/openflow/helper/openflow-switch-helper.cc @@ -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 + */ +#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, NetDeviceContainer c, Ptr controller) +{ + NS_LOG_FUNCTION_NOARGS (); + NS_LOG_INFO ("**** Install switch device on node " << node->GetId ()); + + NetDeviceContainer devs; + Ptr dev = m_deviceFactory.Create (); + 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, NetDeviceContainer c) +{ + NS_LOG_FUNCTION_NOARGS (); + NS_LOG_INFO ("**** Install switch device on node " << node->GetId ()); + + NetDeviceContainer devs; + Ptr dev = m_deviceFactory.Create (); + 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 = Names::Find (nodeName); + return Install (node, c); +} + +} // namespace ns3 + +#endif // NS3_OPENFLOW diff --git a/src/openflow/helper/openflow-switch-helper.h b/src/openflow/helper/openflow-switch-helper.h new file mode 100644 index 000000000..8b10c476c --- /dev/null +++ b/src/openflow/helper/openflow-switch-helper.h @@ -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 + */ +#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 + +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, NetDeviceContainer c, Ptr 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, 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 */ diff --git a/src/openflow/model/openflow-interface.cc b/src/openflow/model/openflow-interface.cc new file mode 100644 index 000000000..649e45154 --- /dev/null +++ b/src/openflow/model/openflow-interface.cc @@ -0,0 +1,1135 @@ +/* -*- 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 + */ +#ifdef NS3_OPENFLOW + +#include "openflow-interface.h" +#include "openflow-switch-net-device.h" + +namespace ns3 { + +namespace ofi { + +NS_LOG_COMPONENT_DEFINE ("OpenFlowInterface"); + +Stats::Stats (ofp_stats_types _type, size_t body_len) +{ + type = _type; + size_t min_body = 0, max_body = 0; + + switch (type) + { + case OFPST_DESC: + break; + case OFPST_FLOW: + min_body = max_body = sizeof(ofp_flow_stats_request); + break; + case OFPST_AGGREGATE: + min_body = max_body = sizeof(ofp_aggregate_stats_request); + break; + case OFPST_TABLE: + break; + case OFPST_PORT: + min_body = 0; + max_body = std::numeric_limits::max (); // Not sure about this one. This would guarantee that the body_len is always acceptable. + break; + case OFPST_PORT_TABLE: + break; + default: + NS_LOG_ERROR ("received stats request of unknown type " << type); + return;// -EINVAL; + } + + if ((min_body != 0 || max_body != 0) && (body_len < min_body || body_len > max_body)) + { + NS_LOG_ERROR ("stats request type " << type << " with bad body length " << body_len); + return;// -EINVAL; + } +} + +int +Stats::DoInit (const void *body, int body_len, void **state) +{ + switch (type) + { + case OFPST_DESC: + return 0; + case OFPST_FLOW: + return FlowStatsInit (body, body_len, state); + case OFPST_AGGREGATE: + return AggregateStatsInit (body, body_len, state); + case OFPST_TABLE: + return 0; + case OFPST_PORT: + return PortStatsInit (body, body_len, state); + case OFPST_PORT_TABLE: + return 0; + case OFPST_VENDOR: + return 0; + } + + return 0; +} + +int +Stats::DoDump (Ptr swtch, void *state, ofpbuf *buffer) +{ + switch (type) + { + case OFPST_DESC: + return DescStatsDump (state, buffer); + case OFPST_FLOW: + return FlowStatsDump (swtch, (FlowStatsState *)state, buffer); + case OFPST_AGGREGATE: + return AggregateStatsDump (swtch, (ofp_aggregate_stats_request *)state, buffer); + case OFPST_TABLE: + return TableStatsDump (swtch, state, buffer); + case OFPST_PORT: + return PortStatsDump (swtch, (PortStatsState *)state, buffer); + case OFPST_PORT_TABLE: + return PortTableStatsDump (swtch, state, buffer); + case OFPST_VENDOR: + return 0; + } + + return 0; +} + +void +Stats::DoCleanup (void *state) +{ + switch (type) + { + case OFPST_DESC: + break; + case OFPST_FLOW: + free ((FlowStatsState *)state); + break; + case OFPST_AGGREGATE: + free ((ofp_aggregate_stats_request *)state); + break; + case OFPST_TABLE: + break; + case OFPST_PORT: + free (((PortStatsState *)state)->ports); + free ((PortStatsState *)state); + break; + case OFPST_PORT_TABLE: + break; + case OFPST_VENDOR: + break; + } +} + +int +Stats::DescStatsDump (void *state, ofpbuf *buffer) +{ + ofp_desc_stats *ods = (ofp_desc_stats*)ofpbuf_put_zeros (buffer, sizeof *ods); + strncpy (ods->mfr_desc, OpenFlowSwitchNetDevice::GetManufacturerDescription (), sizeof ods->mfr_desc); + strncpy (ods->hw_desc, OpenFlowSwitchNetDevice::GetHardwareDescription (), sizeof ods->hw_desc); + strncpy (ods->sw_desc, OpenFlowSwitchNetDevice::GetSoftwareDescription (), sizeof ods->sw_desc); + strncpy (ods->serial_num, OpenFlowSwitchNetDevice::GetSerialNumber (), sizeof ods->serial_num); + return 0; +} + +#define MAX_FLOW_STATS_BYTES 4096 + +int +Stats::FlowStatsInit (const void *body, int body_len, void **state) +{ + const ofp_flow_stats_request *fsr = (ofp_flow_stats_request*)body; + FlowStatsState *s = (FlowStatsState*)xmalloc (sizeof *s); + + s->table_idx = fsr->table_id == 0xff ? 0 : fsr->table_id; + memset (&s->position, 0, sizeof s->position); + s->rq = *fsr; + *state = s; + return 0; +} + +int +Stats_FlowDumpCallback (sw_flow *flow, void* state) +{ + Stats::FlowStatsState *s = (Stats::FlowStatsState*)state; + + // Fill Flow Stats + ofp_flow_stats *ofs; + int length = sizeof *ofs + flow->sf_acts->actions_len; + ofs = (ofp_flow_stats*)ofpbuf_put_zeros (s->buffer, length); + ofs->length = htons (length); + ofs->table_id = s->table_idx; + ofs->match.wildcards = htonl (flow->key.wildcards); + ofs->match.in_port = flow->key.flow.in_port; + memcpy (ofs->match.dl_src, flow->key.flow.dl_src, ETH_ADDR_LEN); + memcpy (ofs->match.dl_dst, flow->key.flow.dl_dst, ETH_ADDR_LEN); + ofs->match.dl_vlan = flow->key.flow.dl_vlan; + ofs->match.dl_type = flow->key.flow.dl_type; + ofs->match.nw_src = flow->key.flow.nw_src; + ofs->match.nw_dst = flow->key.flow.nw_dst; + ofs->match.nw_proto = flow->key.flow.nw_proto; + ofs->match.tp_src = flow->key.flow.tp_src; + ofs->match.tp_dst = flow->key.flow.tp_dst; + ofs->duration = htonl (s->now - flow->created); + ofs->priority = htons (flow->priority); + ofs->idle_timeout = htons (flow->idle_timeout); + ofs->hard_timeout = htons (flow->hard_timeout); + ofs->packet_count = htonll (flow->packet_count); + ofs->byte_count = htonll (flow->byte_count); + memcpy (ofs->actions, flow->sf_acts->actions, flow->sf_acts->actions_len); + + return s->buffer->size >= MAX_FLOW_STATS_BYTES; +} + +int +Stats::FlowStatsDump (Ptr swtch, FlowStatsState* s, ofpbuf *buffer) +{ + sw_flow_key match_key; + + flow_extract_match (&match_key, &s->rq.match); + + s->buffer = buffer; + s->now = time_now (); + while (s->table_idx < swtch->GetChain ()->n_tables + && (s->rq.table_id == 0xff || s->rq.table_id == s->table_idx)) + { + sw_table *table = swtch->GetChain ()->tables[s->table_idx]; + + if (table->iterate (table, &match_key, s->rq.out_port, &s->position, Stats::FlowDumpCallback, s)) + { + break; + } + + s->table_idx++; + memset (&s->position, 0, sizeof s->position); + } + return s->buffer->size >= MAX_FLOW_STATS_BYTES; +} + +int +Stats::AggregateStatsInit (const void *body, int body_len, void **state) +{ + //ofp_aggregate_stats_request *s = (ofp_aggregate_stats_request*)body; + *state = (ofp_aggregate_stats_request*)body; + return 0; +} + +int +Stats_AggregateDumpCallback (sw_flow *flow, void *state) +{ + ofp_aggregate_stats_reply *s = (ofp_aggregate_stats_reply*)state; + s->packet_count += flow->packet_count; + s->byte_count += flow->byte_count; + s->flow_count++; + return 0; +} + +int +Stats::AggregateStatsDump (Ptr swtch, ofp_aggregate_stats_request *s, ofpbuf *buffer) +{ + ofp_aggregate_stats_request *rq = s; + ofp_aggregate_stats_reply *rpy = (ofp_aggregate_stats_reply*)ofpbuf_put_zeros (buffer, sizeof *rpy); + sw_flow_key match_key; + flow_extract_match (&match_key, &rq->match); + int table_idx = rq->table_id == 0xff ? 0 : rq->table_id; + + sw_table_position position; + memset (&position, 0, sizeof position); + + while (table_idx < swtch->GetChain ()->n_tables + && (rq->table_id == 0xff || rq->table_id == table_idx)) + { + sw_table *table = swtch->GetChain ()->tables[table_idx]; + int error = table->iterate (table, &match_key, rq->out_port, &position, Stats::AggregateDumpCallback, rpy); + if (error) + { + return error; + } + + table_idx++; + memset (&position, 0, sizeof position); + } + + rpy->packet_count = htonll (rpy->packet_count); + rpy->byte_count = htonll (rpy->byte_count); + rpy->flow_count = htonl (rpy->flow_count); + return 0; +} + +int +Stats::TableStatsDump (Ptr swtch, void *state, ofpbuf *buffer) +{ + sw_chain* ft = swtch->GetChain (); + for (int i = 0; i < ft->n_tables; i++) + { + ofp_table_stats *ots = (ofp_table_stats*)ofpbuf_put_zeros (buffer, sizeof *ots); + sw_table_stats stats; + ft->tables[i]->stats (ft->tables[i], &stats); + strncpy (ots->name, stats.name, sizeof ots->name); + ots->table_id = i; + ots->wildcards = htonl (stats.wildcards); + ots->max_entries = htonl (stats.max_flows); + ots->active_count = htonl (stats.n_flows); + ots->lookup_count = htonll (stats.n_lookup); + ots->matched_count = htonll (stats.n_matched); + } + return 0; +} + +// stats for the port table which is similar to stats for the flow tables +int +Stats::PortTableStatsDump (Ptr swtch, void *state, ofpbuf *buffer) +{ + ofp_vport_table_stats *opts = (ofp_vport_table_stats*)ofpbuf_put_zeros (buffer, sizeof *opts); + opts->max_vports = htonl (swtch->GetVPortTable ().max_vports); + opts->active_vports = htonl (swtch->GetVPortTable ().active_vports); + opts->lookup_count = htonll (swtch->GetVPortTable ().lookup_count); + opts->port_match_count = htonll (swtch->GetVPortTable ().port_match_count); + opts->chain_match_count = htonll (swtch->GetVPortTable ().chain_match_count); + + return 0; +} + +int +Stats::PortStatsInit (const void *body, int body_len, void **state) +{ + PortStatsState *s = (PortStatsState*)xmalloc (sizeof *s); + + // the body contains a list of port numbers + s->ports = (uint32_t*)xmalloc (body_len); + memcpy (s->ports, body, body_len); + s->num_ports = body_len / sizeof(uint32_t); + + *state = s; + return 0; +} + +int +Stats::PortStatsDump (Ptr swtch, PortStatsState *s, ofpbuf *buffer) +{ + ofp_port_stats *ops; + uint32_t port; + + // port stats are different depending on whether port is physical or virtual + for (size_t i = 0; i < s->num_ports; i++) + { + port = ntohl (s->ports[i]); + // physical port? + if (port <= OFPP_MAX) + { + Port p = swtch->GetSwitchPort (port); + + if (p.netdev == 0) + { + continue; + } + + ops = (ofp_port_stats*)ofpbuf_put_zeros (buffer, sizeof *ops); + ops->port_no = htonl (swtch->GetSwitchPortIndex (p)); + ops->rx_packets = htonll (p.rx_packets); + ops->tx_packets = htonll (p.tx_packets); + ops->rx_bytes = htonll (p.rx_bytes); + ops->tx_bytes = htonll (p.tx_bytes); + ops->rx_dropped = htonll (-1); + ops->tx_dropped = htonll (p.tx_dropped); + ops->rx_errors = htonll (-1); + ops->tx_errors = htonll (-1); + ops->rx_frame_err = htonll (-1); + ops->rx_over_err = htonll (-1); + ops->rx_crc_err = htonll (-1); + ops->collisions = htonll (-1); + ops->mpls_ttl0_dropped = htonll (p.mpls_ttl0_dropped); + ops++; + } + else if (port >= OFPP_VP_START && port <= OFPP_VP_END) // virtual port? + { + // lookup the virtual port + vport_table_t vt = swtch->GetVPortTable (); + vport_table_entry *vpe = vport_table_lookup (&vt, port); + if (vpe == 0) + { + NS_LOG_ERROR ("vport entry not found!"); + continue; + } + // only tx_packets and tx_bytes are really relevant for virtual ports + ops = (ofp_port_stats*)ofpbuf_put_zeros (buffer, sizeof *ops); + ops->port_no = htonl (vpe->vport); + ops->rx_packets = htonll (-1); + ops->tx_packets = htonll (vpe->packet_count); + ops->rx_bytes = htonll (-1); + ops->tx_bytes = htonll (vpe->byte_count); + ops->rx_dropped = htonll (-1); + ops->tx_dropped = htonll (-1); + ops->rx_errors = htonll (-1); + ops->tx_errors = htonll (-1); + ops->rx_frame_err = htonll (-1); + ops->rx_over_err = htonll (-1); + ops->rx_crc_err = htonll (-1); + ops->collisions = htonll (-1); + ops->mpls_ttl0_dropped = htonll (-1); + ops++; + } + } + return 0; +} + +bool +Action::IsValidType (ofp_action_type type) +{ + switch (type) + { + case OFPAT_OUTPUT: + case OFPAT_SET_VLAN_VID: + case OFPAT_SET_VLAN_PCP: + case OFPAT_STRIP_VLAN: + case OFPAT_SET_DL_SRC: + case OFPAT_SET_DL_DST: + case OFPAT_SET_NW_SRC: + case OFPAT_SET_NW_DST: + case OFPAT_SET_TP_SRC: + case OFPAT_SET_TP_DST: + case OFPAT_SET_MPLS_LABEL: + case OFPAT_SET_MPLS_EXP: + return true; + default: + return false; + } +} + +uint16_t +Action::Validate (ofp_action_type type, size_t len, const sw_flow_key *key, const ofp_action_header *ah) +{ + size_t size = 0; + + switch (type) + { + case OFPAT_OUTPUT: + { + if (len != sizeof(ofp_action_output)) + { + return OFPBAC_BAD_LEN; + } + + ofp_action_output *oa = (ofp_action_output *)ah; + + // To prevent loops, make sure there's no action to send to the OFP_TABLE virtual port. + + // port is now 32-bit + if (oa->port == OFPP_NONE || oa->port == key->flow.in_port) // htonl(OFPP_NONE); + { // if (oa->port == htons(OFPP_NONE) || oa->port == key->flow.in_port) + return OFPBAC_BAD_OUT_PORT; + } + + return ACT_VALIDATION_OK; + } + case OFPAT_SET_VLAN_VID: + size = sizeof(ofp_action_vlan_vid); + break; + case OFPAT_SET_VLAN_PCP: + size = sizeof(ofp_action_vlan_pcp); + break; + case OFPAT_STRIP_VLAN: + size = sizeof(ofp_action_header); + break; + case OFPAT_SET_DL_SRC: + case OFPAT_SET_DL_DST: + size = sizeof(ofp_action_dl_addr); + break; + case OFPAT_SET_NW_SRC: + case OFPAT_SET_NW_DST: + size = sizeof(ofp_action_nw_addr); + break; + case OFPAT_SET_TP_SRC: + case OFPAT_SET_TP_DST: + size = sizeof(ofp_action_tp_port); + break; + case OFPAT_SET_MPLS_LABEL: + size = sizeof(ofp_action_mpls_label); + break; + case OFPAT_SET_MPLS_EXP: + size = sizeof(ofp_action_mpls_exp); + break; + default: + break; + } + + if (len != size) + { + return OFPBAC_BAD_LEN; + } + return ACT_VALIDATION_OK; +} + +void +Action::Execute (ofp_action_type type, ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah) +{ + switch (type) + { + case OFPAT_OUTPUT: + break; + case OFPAT_SET_VLAN_VID: + set_vlan_vid (buffer, key, ah); + break; + case OFPAT_SET_VLAN_PCP: + set_vlan_pcp (buffer, key, ah); + break; + case OFPAT_STRIP_VLAN: + strip_vlan (buffer, key, ah); + break; + case OFPAT_SET_DL_SRC: + case OFPAT_SET_DL_DST: + set_dl_addr (buffer, key, ah); + break; + case OFPAT_SET_NW_SRC: + case OFPAT_SET_NW_DST: + set_nw_addr (buffer, key, ah); + break; + case OFPAT_SET_TP_SRC: + case OFPAT_SET_TP_DST: + set_tp_port (buffer, key, ah); + break; + case OFPAT_SET_MPLS_LABEL: + set_mpls_label (buffer, key, ah); + break; + case OFPAT_SET_MPLS_EXP: + set_mpls_exp (buffer, key, ah); + break; + default: + break; + } +} + +bool +VPortAction::IsValidType (ofp_vport_action_type type) +{ + switch (type) + { + case OFPPAT_POP_MPLS: + case OFPPAT_PUSH_MPLS: + case OFPPAT_SET_MPLS_LABEL: + case OFPPAT_SET_MPLS_EXP: + return true; + default: + return false; + } +} + +uint16_t +VPortAction::Validate (ofp_vport_action_type type, size_t len, const ofp_action_header *ah) +{ + size_t size = 0; + + switch (type) + { + case OFPPAT_POP_MPLS: + size = sizeof(ofp_vport_action_pop_mpls); + break; + case OFPPAT_PUSH_MPLS: + size = sizeof(ofp_vport_action_push_mpls); + break; + case OFPPAT_SET_MPLS_LABEL: + size = sizeof(ofp_vport_action_set_mpls_label); + break; + case OFPPAT_SET_MPLS_EXP: + size = sizeof(ofp_vport_action_set_mpls_exp); + break; + default: + break; + } + + if (len != size) + { + return OFPBAC_BAD_LEN; + } + return ACT_VALIDATION_OK; +} + +void +VPortAction::Execute (ofp_vport_action_type type, ofpbuf *buffer, const sw_flow_key *key, const ofp_action_header *ah) +{ + switch (type) + { + case OFPPAT_POP_MPLS: + { + ofp_vport_action_pop_mpls *opapm = (ofp_vport_action_pop_mpls *)ah; + pop_mpls_act (0, buffer, key, &opapm->apm); + break; + } + case OFPPAT_PUSH_MPLS: + { + ofp_vport_action_push_mpls *opapm = (ofp_vport_action_push_mpls *)ah; + push_mpls_act (0, buffer, key, &opapm->apm); + break; + } + case OFPPAT_SET_MPLS_LABEL: + { + ofp_vport_action_set_mpls_label *oparml = (ofp_vport_action_set_mpls_label *)ah; + set_mpls_label_act (buffer, key, oparml->label_out); + break; + } + case OFPPAT_SET_MPLS_EXP: + { + ofp_vport_action_set_mpls_exp *oparme = (ofp_vport_action_set_mpls_exp *)ah; + set_mpls_exp_act (buffer, key, oparme->exp); + break; + } + default: + break; + } +} + +bool +EricssonAction::IsValidType (er_action_type type) +{ + switch (type) + { + case ERXT_POP_MPLS: + case ERXT_PUSH_MPLS: + return true; + default: + return false; + } +} + +uint16_t +EricssonAction::Validate (er_action_type type, size_t len) +{ + size_t size = 0; + + switch (type) + { + case ERXT_POP_MPLS: + size = sizeof(er_action_pop_mpls); + break; + case ERXT_PUSH_MPLS: + size = sizeof(er_action_push_mpls); + break; + default: + break; + } + + if (len != size) + { + return OFPBAC_BAD_LEN; + } + return ACT_VALIDATION_OK; +} + +void +EricssonAction::Execute (er_action_type type, ofpbuf *buffer, const sw_flow_key *key, const er_action_header *ah) +{ + switch (type) + { + case ERXT_POP_MPLS: + { + er_action_pop_mpls *erapm = (er_action_pop_mpls *)ah; + pop_mpls_act (0, buffer, key, &erapm->apm); + break; + } + case ERXT_PUSH_MPLS: + { + er_action_push_mpls *erapm = (er_action_push_mpls *)ah; + push_mpls_act (0, buffer, key, &erapm->apm); + break; + } + default: + break; + } +} + +void +Controller::AddSwitch (Ptr swtch) +{ + if (m_switches.find (swtch) != m_switches.end ()) + { + NS_LOG_INFO ("This Controller has already registered this switch!"); + } + else + { + m_switches.insert (swtch); + } +} + +void +Controller::SendToSwitch (Ptr swtch, void * msg, size_t length) +{ + if (m_switches.find (swtch) == m_switches.end ()) + { + NS_LOG_ERROR ("Can't send to this switch, not registered to the Controller."); + return; + } + + swtch->ForwardControlInput (msg, length); +} + +ofp_flow_mod* +Controller::BuildFlow (sw_flow_key key, uint32_t buffer_id, uint16_t command, void* acts, size_t actions_len, int idle_timeout, int hard_timeout) +{ + ofp_flow_mod* ofm = (ofp_flow_mod*)malloc (sizeof(ofp_flow_mod) + actions_len); + ofm->header.version = OFP_VERSION; + ofm->header.type = OFPT_FLOW_MOD; + ofm->header.length = htons (sizeof(ofp_flow_mod) + actions_len); + ofm->command = htons (command); + ofm->idle_timeout = htons (idle_timeout); + ofm->hard_timeout = htons (hard_timeout); + ofm->buffer_id = htonl (buffer_id); + ofm->priority = OFP_DEFAULT_PRIORITY; + memcpy (ofm->actions,acts,actions_len); + + 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 htonl(MPLS_INVALID_LABEL); + ofm->match.mpls_label2 = key.flow.mpls_label1; // Second label (if available) htonl(MPLS_INVALID_LABEL); + + return ofm; +} + +uint8_t +Controller::GetPacketType (ofpbuf* buffer) +{ + ofp_header* hdr = (ofp_header*)ofpbuf_try_pull (buffer, sizeof (ofp_header)); + uint8_t type = hdr->type; + ofpbuf_push_uninit (buffer, sizeof (ofp_header)); + return type; +} + +void +Controller::StartDump (StatsDumpCallback* cb) +{ + if (cb != 0) + { + int error = 1; + while (error > 0) // Switch's StatsDump returns 1 if the reply isn't complete. + { + error = cb->swtch->StatsDump (cb); + } + + if (error != 0) // When the reply is complete, error will equal zero if there's no errors. + { + NS_LOG_WARN ("Dump Callback Error: " << strerror (-error)); + } + + // Clean up + cb->swtch->StatsDone (cb); + } +} + +void +DropController::ReceiveFromSwitch (Ptr swtch, ofpbuf* buffer) +{ + if (m_switches.find (swtch) == m_switches.end ()) + { + NS_LOG_ERROR ("Can't receive from this switch, not registered to the Controller."); + return; + } + + // We have received any packet at this point, so we pull the header to figure out what type of packet we're handling. + uint8_t type = GetPacketType(buffer); + + if (type == OFPT_PACKET_IN) // The switch didn't understand the packet it received, so it forwarded it to the controller. + { + ofp_packet_in * opi = (ofp_packet_in*)ofpbuf_try_pull (buffer, offsetof (ofp_packet_in, data)); + int port = ntohs (opi->in_port); + + // Create matching key. + sw_flow_key key; + key.wildcards = 0; + flow_extract (buffer, port != -1 ? port : OFPP_NONE, &key.flow); + + ofp_flow_mod* ofm = BuildFlow (key, opi->buffer_id, OFPFC_ADD, 0, 0, OFP_FLOW_PERMANENT, OFP_FLOW_PERMANENT); + SendToSwitch (swtch, ofm, ofm->header.length); + } +} + +TypeId LearningController::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::ofi::LearningController") + .SetParent (Controller::GetTypeId()) + .AddConstructor () + .AddAttribute ("ExpirationTime", + "Time it takes for learned MAC state entry/created flow to expire.", + TimeValue (Seconds (0)), + MakeTimeAccessor (&LearningController::m_expirationTime), + MakeTimeChecker ()) + ; + return tid; +} + +void +LearningController::ReceiveFromSwitch (Ptr swtch, ofpbuf* buffer) +{ + if (m_switches.find (swtch) == m_switches.end ()) + { + NS_LOG_ERROR ("Can't receive from this switch, not registered to the Controller."); + return; + } + + // We have received any packet at this point, so we pull the header to figure out what type of packet we're handling. + uint8_t type = GetPacketType(buffer); + + if (type == OFPT_PACKET_IN) // The switch didn't understand the packet it received, so it forwarded it to the controller. + { + ofp_packet_in * opi = (ofp_packet_in*)ofpbuf_try_pull (buffer, offsetof (ofp_packet_in, data)); + int port = ntohs (opi->in_port); + + // Create matching key. + sw_flow_key key; + key.wildcards = 0; + flow_extract (buffer, port != -1 ? port : OFPP_NONE, &key.flow); + + uint16_t out_port = OFPP_FLOOD; + uint16_t in_port = ntohs (key.flow.in_port); + + // If the destination address is learned to a specific port, find it. + Mac48Address dst_addr; + dst_addr.CopyFrom (key.flow.dl_dst); + if (!dst_addr.IsBroadcast ()) + { + LearnState_t::iterator st = m_learnState.find (dst_addr); + if (st != m_learnState.end ()) + { + out_port = st->second.port; + } + else + { + NS_LOG_INFO ("Setting to flood; don't know yet what port " << dst_addr << " is connected to"); + } + } + else + { + NS_LOG_INFO ("Setting to flood; this packet is a broadcast"); + } + + // Create output-to-port action + ofp_action_output x[1]; + x[0].type = htons (OFPAT_OUTPUT); + x[0].len = htons (sizeof(ofp_action_output)); + x[0].port = out_port; + + // Create a new flow that outputs matched packets to a learned port, OFPP_FLOOD if there's no learned port. + ofp_flow_mod* ofm = BuildFlow (key, opi->buffer_id, OFPFC_ADD, x, sizeof(x), OFP_FLOW_PERMANENT, m_expirationTime.IsZero ()?OFP_FLOW_PERMANENT:m_expirationTime.GetSeconds ()); + SendToSwitch (swtch, ofm, ofm->header.length); + + // We can learn a specific port for the source address for future use. + Mac48Address src_addr; + src_addr.CopyFrom (key.flow.dl_src); + LearnState_t::iterator st = m_learnState.find (src_addr); + if (st == m_learnState.end ()) // We haven't learned our source MAC yet. + { + LearnedState ls; + ls.port = in_port; + m_learnState.insert (std::make_pair (src_addr,ls)); + NS_LOG_INFO ("Learned that " << src_addr << " can be found over port " << in_port); + + // Learn src_addr goes to a certain port. + ofp_action_output x2[1]; + x2[0].type = htons (OFPAT_OUTPUT); + x2[0].len = htons (sizeof(ofp_action_output)); + x2[0].port = in_port; + + // Switch MAC Addresses and ports to the flow we're modifying + src_addr.CopyTo (key.flow.dl_dst); + dst_addr.CopyTo (key.flow.dl_src); + key.flow.in_port = out_port; + ofp_flow_mod* ofm2 = BuildFlow (key, -1, OFPFC_MODIFY, x2, sizeof(x2), OFP_FLOW_PERMANENT, m_expirationTime.IsZero ()?OFP_FLOW_PERMANENT:m_expirationTime.GetSeconds ()); + SendToSwitch (swtch, ofm2, ofm2->header.length); + } + } +} + +void +ExecuteActions (Ptr swtch, uint64_t packet_uid, ofpbuf* buffer, sw_flow_key *key, const ofp_action_header *actions, size_t actions_len, int ignore_no_fwd) +{ + NS_LOG_FUNCTION_NOARGS (); + /* Every output action needs a separate clone of 'buffer', but the common + * case is just a single output action, so that doing a clone and then + * freeing the original buffer is wasteful. So the following code is + * slightly obscure just to avoid that. */ + int prev_port; + size_t max_len = 0; // Initialze to make compiler happy + uint16_t in_port = key->flow.in_port; // ntohs(key->flow.in_port); + uint8_t *p = (uint8_t *)actions; + + prev_port = -1; + + if (actions_len == 0) + { + NS_LOG_INFO ("No actions set to this flow. Dropping packet."); + return; + } + + /* The action list was already validated, so we can be a bit looser + * in our sanity-checking. */ + while (actions_len > 0) + { + ofp_action_header *ah = (ofp_action_header *)p; + size_t len = htons (ah->len); + + if (prev_port != -1) + { + swtch->DoOutput (packet_uid, in_port, max_len, prev_port, ignore_no_fwd); + prev_port = -1; + } + + if (ah->type == htons (OFPAT_OUTPUT)) + { + ofp_action_output *oa = (ofp_action_output *)p; + + // port is now 32-bits + prev_port = oa->port; // ntohl(oa->port); + // prev_port = ntohs(oa->port); + max_len = ntohs (oa->max_len); + } + else + { + uint16_t type = ntohs (ah->type); + if (Action::IsValidType ((ofp_action_type)type)) // Execute a built-in OpenFlow action against 'buffer'. + { + Action::Execute ((ofp_action_type)type, buffer, key, ah); + } + else if (type == OFPAT_VENDOR) + { + ExecuteVendor (buffer, key, ah); + } + } + + p += len; + actions_len -= len; + } + + if (prev_port != -1) + { + swtch->DoOutput (packet_uid, in_port, max_len, prev_port, ignore_no_fwd); + } +} + +uint16_t +ValidateActions (const sw_flow_key *key, const ofp_action_header *actions, size_t actions_len) +{ + uint8_t *p = (uint8_t *)actions; + int err; + + while (actions_len >= sizeof(ofp_action_header)) + { + ofp_action_header *ah = (ofp_action_header *)p; + size_t len = ntohs (ah->len); + uint16_t type; + + /* Make there's enough remaining data for the specified length + * and that the action length is a multiple of 64 bits. */ + if ((actions_len < len) || (len % 8) != 0) + { + return OFPBAC_BAD_LEN; + } + + type = ntohs (ah->type); + if (Action::IsValidType ((ofp_action_type)type)) // Validate built-in OpenFlow actions. + { + err = Action::Validate ((ofp_action_type)type, len, key, ah); + if (err != ACT_VALIDATION_OK) + { + return err; + } + } + else if (type == OFPAT_VENDOR) + { + err = ValidateVendor (key, ah, len); + if (err != ACT_VALIDATION_OK) + { + return err; + } + } + else + { + return OFPBAC_BAD_TYPE; + } + + p += len; + actions_len -= len; + } + + // Check if there's any trailing garbage. + if (actions_len != 0) + { + return OFPBAC_BAD_LEN; + } + + return ACT_VALIDATION_OK; +} + +void +ExecuteVPortActions (Ptr swtch, uint64_t packet_uid, ofpbuf* buffer, sw_flow_key *key, const ofp_action_header *actions, size_t actions_len) +{ + /* Every output action needs a separate clone of 'buffer', but the common + * case is just a single output action, so that doing a clone and then + * freeing the original buffer is wasteful. So the following code is + * slightly obscure just to avoid that. */ + int prev_port; + size_t max_len = 0; // Initialize to make compiler happy + uint16_t in_port = ntohs (key->flow.in_port); + uint8_t *p = (uint8_t *)actions; + uint16_t type; + ofp_action_output *oa; + + prev_port = -1; + /* The action list was already validated, so we can be a bit looser + * in our sanity-checking. */ + while (actions_len > 0) + { + ofp_action_header *ah = (ofp_action_header *)p; + size_t len = htons (ah->len); + if (prev_port != -1) + { + swtch->DoOutput (packet_uid, in_port, max_len, prev_port, false); + prev_port = -1; + } + + if (ah->type == htons (OFPAT_OUTPUT)) + { + oa = (ofp_action_output *)p; + prev_port = ntohl (oa->port); + max_len = ntohs (oa->max_len); + } + else + { + type = ah->type; // ntohs(ah->type); + VPortAction::Execute ((ofp_vport_action_type)type, buffer, key, ah); + } + + p += len; + actions_len -= len; + } + + if (prev_port != -1) + { + swtch->DoOutput (packet_uid, in_port, max_len, prev_port, false); + } +} + +uint16_t +ValidateVPortActions (const ofp_action_header *actions, size_t actions_len) +{ + uint8_t *p = (uint8_t *)actions; + int err; + + while (actions_len >= sizeof(ofp_action_header)) + { + ofp_action_header *ah = (ofp_action_header *)p; + size_t len = ntohs (ah->len); + uint16_t type; + + /* Make there's enough remaining data for the specified length + * and that the action length is a multiple of 64 bits. */ + if ((actions_len < len) || (len % 8) != 0) + { + return OFPBAC_BAD_LEN; + } + + type = ntohs (ah->type); + if (VPortAction::IsValidType ((ofp_vport_action_type)type)) // Validate "built-in" OpenFlow port table actions. + { + err = VPortAction::Validate ((ofp_vport_action_type)type, len, ah); + if (err != ACT_VALIDATION_OK) + { + return err; + } + } + else + { + return OFPBAC_BAD_TYPE; + } + + p += len; + actions_len -= len; + } + + // Check if there's any trailing garbage. + if (actions_len != 0) + { + return OFPBAC_BAD_LEN; + } + + return ACT_VALIDATION_OK; +} + +void +ExecuteVendor (ofpbuf *buffer, const sw_flow_key *key, const ofp_action_header *ah) +{ + ofp_action_vendor_header *avh = (ofp_action_vendor_header *)ah; + + switch (ntohl (avh->vendor)) + { + case NX_VENDOR_ID: + // Nothing to execute yet. + break; + case ER_VENDOR_ID: + { + const er_action_header *erah = (const er_action_header *)avh; + EricssonAction::Execute ((er_action_type)ntohs (erah->subtype), buffer, key, erah); + break; + } + default: + // This should not be possible due to prior validation. + NS_LOG_INFO ("attempt to execute action with unknown vendor: " << ntohl (avh->vendor)); + break; + } +} + +uint16_t +ValidateVendor (const sw_flow_key *key, const ofp_action_header *ah, uint16_t len) +{ + ofp_action_vendor_header *avh; + int ret = ACT_VALIDATION_OK; + + if (len < sizeof(ofp_action_vendor_header)) + { + return OFPBAC_BAD_LEN; + } + + avh = (ofp_action_vendor_header *)ah; + + switch (ntohl (avh->vendor)) + { + case NX_VENDOR_ID: // Validate Nicara OpenFlow actions. + ret = OFPBAC_BAD_VENDOR_TYPE; // Nothing to validate yet. + break; + case ER_VENDOR_ID: // Validate Ericsson OpenFlow actions. + { + const er_action_header *erah = (const er_action_header *)avh; + ret = EricssonAction::Validate ((er_action_type)ntohs (erah->subtype), len); + break; + } + default: + return OFPBAC_BAD_VENDOR; + } + + return ret; +} + +} + +} + +#endif // NS3_OPENFLOW diff --git a/src/openflow/model/openflow-interface.h b/src/openflow/model/openflow-interface.h new file mode 100644 index 000000000..6e5e0afa2 --- /dev/null +++ b/src/openflow/model/openflow-interface.h @@ -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 + */ +#ifndef OPENFLOW_INTERFACE_H +#define OPENFLOW_INTERFACE_H + +#ifdef NS3_OPENFLOW + +#include +#include + +// 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 +#include +#include + +// 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 + * 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 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 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 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 dp, ofp_aggregate_stats_request *s, ofpbuf *buffer); + + int TableStatsDump (Ptr dp, void *state, ofpbuf *buffer); + + int PortStatsInit (const void *body, int body_len, void **state); + int PortStatsDump (Ptr dp, PortStatsState *s, ofpbuf *buffer); + + int PortTableStatsDump (Ptr 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 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; ///< 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 () + .AddConstructor () + ; + return tid; + } + + virtual ~Controller () + { + m_switches.clear (); + } + + /** + * Adds a switch to the controller. + * + * \param swtch The switch to register. + */ + virtual void AddSwitch (Ptr 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 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 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 > 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 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 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 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 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 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 */ diff --git a/src/openflow/model/openflow-switch-net-device.cc b/src/openflow/model/openflow-switch-net-device.cc new file mode 100644 index 000000000..968fccdd6 --- /dev/null +++ b/src/openflow/model/openflow-switch-net-device.cc @@ -0,0 +1,1584 @@ +/* -*- 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 + */ +#ifdef NS3_OPENFLOW + +#include "openflow-switch-net-device.h" + +NS_LOG_COMPONENT_DEFINE ("OpenFlowSwitchNetDevice"); + +namespace ns3 { + +NS_OBJECT_ENSURE_REGISTERED (OpenFlowSwitchNetDevice); + +const char * +OpenFlowSwitchNetDevice::GetManufacturerDescription () +{ + return "The ns-3 team"; +} + +const char * +OpenFlowSwitchNetDevice::GetHardwareDescription () +{ + return "N/A"; +} + +const char * +OpenFlowSwitchNetDevice::GetSoftwareDescription () +{ + return "Simulated OpenFlow Switch"; +} + +const char * +OpenFlowSwitchNetDevice::GetSerialNumber () +{ + return "N/A"; +} + +static uint64_t +GenerateId () +{ + uint8_t ea[ETH_ADDR_LEN]; + eth_addr_random (ea); + return eth_addr_to_uint64 (ea); +} + +TypeId +OpenFlowSwitchNetDevice::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::OpenFlowSwitchNetDevice") + .SetParent () + .AddConstructor () + .AddAttribute ("ID", + "The identification of the OpenFlowSwitchNetDevice/Datapath, needed for OpenFlow compatibility.", + UintegerValue (GenerateId ()), + MakeUintegerAccessor (&OpenFlowSwitchNetDevice::m_id), + MakeUintegerChecker ()) + .AddAttribute ("FlowTableLookupDelay", + "A real switch will have an overhead for looking up in the flow table. For the default, we simulate a standard TCAM on an FPGA.", + TimeValue (NanoSeconds (30)), + MakeTimeAccessor (&OpenFlowSwitchNetDevice::m_lookupDelay), + MakeTimeChecker ()) + .AddAttribute ("Flags", // Note: The Controller can configure this value, overriding the user's setting. + "Flags to turn different functionality on/off, such as whether to inform the controller when a flow expires, or how to handle fragments.", + UintegerValue (0), // Look at the ofp_config_flags enum in openflow/include/openflow.h for options. + MakeUintegerAccessor (&OpenFlowSwitchNetDevice::m_flags), + MakeUintegerChecker ()) + .AddAttribute ("FlowTableMissSendLength", // Note: The Controller can configure this value, overriding the user's setting. + "When forwarding a packet the switch didn't match up to the controller, it can be more efficient to forward only the first x bytes.", + UintegerValue (OFP_DEFAULT_MISS_SEND_LEN), // 128 bytes + MakeUintegerAccessor (&OpenFlowSwitchNetDevice::m_missSendLen), + MakeUintegerChecker ()) + ; + return tid; +} + +OpenFlowSwitchNetDevice::OpenFlowSwitchNetDevice () + : m_node (0), + m_ifIndex (0), + m_mtu (0xffff) +{ + NS_LOG_FUNCTION_NOARGS (); + + m_channel = CreateObject (); + + time_init (); // OFSI's clock; needed to use the buffer storage system. + // m_lastTimeout = time_now (); + + m_controller = 0; + // m_listenPVConn = 0; + + m_chain = chain_create (); + if (m_chain == 0) + { + NS_LOG_ERROR ("Not enough memory to create the flow table."); + } + + m_ports.reserve (DP_MAX_PORTS); + vport_table_init (&m_vportTable); +} + +OpenFlowSwitchNetDevice::~OpenFlowSwitchNetDevice () +{ + NS_LOG_FUNCTION_NOARGS (); +} + +void +OpenFlowSwitchNetDevice::DoDispose () +{ + NS_LOG_FUNCTION_NOARGS (); + + for (Ports_t::iterator b = m_ports.begin (), e = m_ports.end (); b != e; b++) + { + SendPortStatus (*b, OFPPR_DELETE); + b->netdev = 0; + } + m_ports.clear (); + + m_controller = 0; + + chain_destroy (m_chain); + RBTreeDestroy (m_vportTable.table); + m_channel = 0; + m_node = 0; + NetDevice::DoDispose (); +} + +void +OpenFlowSwitchNetDevice::SetController (Ptr c) +{ + if (m_controller != 0) + { + NS_LOG_ERROR ("Controller already set."); + return; + } + + m_controller = c; + m_controller->AddSwitch (this); +} + +int +OpenFlowSwitchNetDevice::AddSwitchPort (Ptr switchPort) +{ + NS_LOG_FUNCTION_NOARGS (); + NS_ASSERT (switchPort != this); + if (!Mac48Address::IsMatchingType (switchPort->GetAddress ())) + { + NS_FATAL_ERROR ("Device does not support eui 48 addresses: cannot be added to switch."); + } + if (!switchPort->SupportsSendFrom ()) + { + NS_FATAL_ERROR ("Device does not support SendFrom: cannot be added to switch."); + } + if (m_address == Mac48Address ()) + { + m_address = Mac48Address::ConvertFrom (switchPort->GetAddress ()); + } + + if (m_ports.size () < DP_MAX_PORTS) + { + ofi::Port p; + p.config = 0; + p.netdev = switchPort; + m_ports.push_back (p); + + // Notify the controller that this port has been added + SendPortStatus (p, OFPPR_ADD); + + NS_LOG_DEBUG ("RegisterProtocolHandler for " << switchPort->GetInstanceTypeId ().GetName ()); + m_node->RegisterProtocolHandler (MakeCallback (&OpenFlowSwitchNetDevice::ReceiveFromDevice, this), + 0, switchPort, true); + m_channel->AddChannel (switchPort->GetChannel ()); + } + else + { + return EXFULL; + } + + return 0; +} + +void +OpenFlowSwitchNetDevice::SetIfIndex (const uint32_t index) +{ + NS_LOG_FUNCTION_NOARGS (); + m_ifIndex = index; +} + +uint32_t +OpenFlowSwitchNetDevice::GetIfIndex (void) const +{ + NS_LOG_FUNCTION_NOARGS (); + return m_ifIndex; +} + +Ptr +OpenFlowSwitchNetDevice::GetChannel (void) const +{ + NS_LOG_FUNCTION_NOARGS (); + return m_channel; +} + +void +OpenFlowSwitchNetDevice::SetAddress (Address address) +{ + NS_LOG_FUNCTION_NOARGS (); + m_address = Mac48Address::ConvertFrom (address); +} + +Address +OpenFlowSwitchNetDevice::GetAddress (void) const +{ + NS_LOG_FUNCTION_NOARGS (); + return m_address; +} + +bool +OpenFlowSwitchNetDevice::SetMtu (const uint16_t mtu) +{ + NS_LOG_FUNCTION_NOARGS (); + m_mtu = mtu; + return true; +} + +uint16_t +OpenFlowSwitchNetDevice::GetMtu (void) const +{ + NS_LOG_FUNCTION_NOARGS (); + return m_mtu; +} + + +bool +OpenFlowSwitchNetDevice::IsLinkUp (void) const +{ + NS_LOG_FUNCTION_NOARGS (); + return true; +} + + +void +OpenFlowSwitchNetDevice::AddLinkChangeCallback (Callback callback) +{ +} + +bool +OpenFlowSwitchNetDevice::IsBroadcast (void) const +{ + NS_LOG_FUNCTION_NOARGS (); + return true; +} + +Address +OpenFlowSwitchNetDevice::GetBroadcast (void) const +{ + NS_LOG_FUNCTION_NOARGS (); + return Mac48Address ("ff:ff:ff:ff:ff:ff"); +} + +bool +OpenFlowSwitchNetDevice::IsMulticast (void) const +{ + NS_LOG_FUNCTION_NOARGS (); + return true; +} + +Address +OpenFlowSwitchNetDevice::GetMulticast (Ipv4Address multicastGroup) const +{ + NS_LOG_FUNCTION (this << multicastGroup); + Mac48Address multicast = Mac48Address::GetMulticast (multicastGroup); + return multicast; +} + + +bool +OpenFlowSwitchNetDevice::IsPointToPoint (void) const +{ + NS_LOG_FUNCTION_NOARGS (); + return false; +} + +bool +OpenFlowSwitchNetDevice::IsBridge (void) const +{ + NS_LOG_FUNCTION_NOARGS (); + return true; +} + +void +OpenFlowSwitchNetDevice::DoOutput (uint32_t packet_uid, int in_port, size_t max_len, int out_port, bool ignore_no_fwd) +{ + if (out_port != OFPP_CONTROLLER) + { + OutputPort (packet_uid, in_port, out_port, ignore_no_fwd); + } + else + { + OutputControl (packet_uid, in_port, max_len, OFPR_ACTION); + } +} + +bool +OpenFlowSwitchNetDevice::Send (Ptr packet, const Address& dest, uint16_t protocolNumber) +{ + NS_LOG_FUNCTION_NOARGS (); + return SendFrom (packet, m_address, dest, protocolNumber); +} + +bool +OpenFlowSwitchNetDevice::SendFrom (Ptr packet, const Address& src, const Address& dest, uint16_t protocolNumber) +{ + NS_LOG_FUNCTION_NOARGS (); + + ofpbuf *buffer = BufferFromPacket (packet,src,dest,GetMtu (),protocolNumber); + + uint32_t packet_uid = save_buffer (buffer); + ofi::SwitchPacketMetadata data; + data.packet = packet; + data.buffer = buffer; + data.protocolNumber = protocolNumber; + data.src = Address (src); + data.dst = Address (dest); + m_packetData.insert (std::make_pair (packet_uid, data)); + + RunThroughFlowTable (packet_uid, -1); + + return true; +} + + +Ptr +OpenFlowSwitchNetDevice::GetNode (void) const +{ + NS_LOG_FUNCTION_NOARGS (); + return m_node; +} + +void +OpenFlowSwitchNetDevice::SetNode (Ptr node) +{ + NS_LOG_FUNCTION_NOARGS (); + m_node = node; +} + +bool +OpenFlowSwitchNetDevice::NeedsArp (void) const +{ + NS_LOG_FUNCTION_NOARGS (); + return true; +} + +void +OpenFlowSwitchNetDevice::SetReceiveCallback (NetDevice::ReceiveCallback cb) +{ + NS_LOG_FUNCTION_NOARGS (); + m_rxCallback = cb; +} + +void +OpenFlowSwitchNetDevice::SetPromiscReceiveCallback (NetDevice::PromiscReceiveCallback cb) +{ + NS_LOG_FUNCTION_NOARGS (); + m_promiscRxCallback = cb; +} + +bool +OpenFlowSwitchNetDevice::SupportsSendFrom () const +{ + NS_LOG_FUNCTION_NOARGS (); + return true; +} + +Address +OpenFlowSwitchNetDevice::GetMulticast (Ipv6Address addr) const +{ + NS_LOG_FUNCTION (this << addr); + return Mac48Address::GetMulticast (addr); +} + +// Add a virtual port table entry. +int +OpenFlowSwitchNetDevice::AddVPort (const ofp_vport_mod *ovpm) +{ + size_t actions_len = ntohs (ovpm->header.length) - sizeof *ovpm; + unsigned int vport = ntohl (ovpm->vport); + unsigned int parent_port = ntohl (ovpm->parent_port); + + // check whether port table entry exists for specified port number + vport_table_entry *vpe = vport_table_lookup (&m_vportTable, vport); + if (vpe != 0) + { + NS_LOG_ERROR ("vport " << vport << " already exists!"); + SendErrorMsg (OFPET_BAD_ACTION, OFPET_VPORT_MOD_FAILED, ovpm, ntohs (ovpm->header.length)); + return EINVAL; + } + + // check whether actions are valid + uint16_t v_code = ofi::ValidateVPortActions (ovpm->actions, actions_len); + if (v_code != ACT_VALIDATION_OK) + { + SendErrorMsg (OFPET_BAD_ACTION, v_code, ovpm, ntohs (ovpm->header.length)); + return EINVAL; + } + + vpe = vport_table_entry_alloc (actions_len); + + vpe->vport = vport; + vpe->parent_port = parent_port; + if (vport < OFPP_VP_START || vport > OFPP_VP_END) + { + NS_LOG_ERROR ("port " << vport << " is not in the virtual port range (" << OFPP_VP_START << "-" << OFPP_VP_END << ")"); + SendErrorMsg (OFPET_BAD_ACTION, OFPET_VPORT_MOD_FAILED, ovpm, ntohs (ovpm->header.length)); + free_vport_table_entry (vpe); // free allocated entry + return EINVAL; + } + + vpe->port_acts->actions_len = actions_len; + memcpy (vpe->port_acts->actions, ovpm->actions, actions_len); + + int error = insert_vport_table_entry (&m_vportTable, vpe); + if (error) + { + NS_LOG_ERROR ("could not insert port table entry for port " << vport); + } + + return error; +} + +ofpbuf * +OpenFlowSwitchNetDevice::BufferFromPacket (Ptr packet, Address src, Address dst, int mtu, uint16_t protocol) +{ + NS_LOG_INFO ("Creating Openflow buffer from packet."); + + /* + * Allocate buffer with some headroom to add headers in forwarding + * to the controller or adding a vlan tag, plus an extra 2 bytes to + * allow IP headers to be aligned on a 4-byte boundary. + */ + const int headroom = 128 + 2; + const int hard_header = VLAN_ETH_HEADER_LEN; + ofpbuf *buffer = ofpbuf_new (headroom + hard_header + mtu); + buffer->data = (char*)buffer->data + headroom + hard_header; + + int l2_length = 0, l3_length = 0, l4_length = 0; + + // Load headers + EthernetHeader eth_hd; + if (packet->PeekHeader (eth_hd)) + { + buffer->l2 = new eth_header; + eth_header* eth_h = (eth_header*)buffer->l2; + dst.CopyTo (eth_h->eth_dst); // Destination Mac Address + src.CopyTo (eth_h->eth_src); // Source Mac Address + eth_h->eth_type = htons (ETH_TYPE_IP); // Ether Type + NS_LOG_INFO ("Parsed EthernetHeader"); + + l2_length = ETH_HEADER_LEN; + } + + // We have to wrap this because PeekHeader has an assert fail if we check for an Ipv4Header that isn't there. + if (protocol == Ipv4L3Protocol::PROT_NUMBER) + { + Ipv4Header ip_hd; + if (packet->PeekHeader (ip_hd)) + { + buffer->l3 = new ip_header; + ip_header* ip_h = (ip_header*)buffer->l3; + ip_h->ip_ihl_ver = IP_IHL_VER (5, IP_VERSION); // Version + ip_h->ip_tos = ip_hd.GetTos (); // Type of Service/Differentiated Services + ip_h->ip_tot_len = packet->GetSize (); // Total Length + ip_h->ip_id = ip_hd.GetIdentification (); // Identification + ip_h->ip_frag_off = ip_hd.GetFragmentOffset (); // Fragment Offset + ip_h->ip_ttl = ip_hd.GetTtl (); // Time to Live + ip_h->ip_proto = ip_hd.GetProtocol (); // Protocol + ip_h->ip_src = htonl (ip_hd.GetSource ().Get ()); // Source Address + ip_h->ip_dst = htonl (ip_hd.GetDestination ().Get ()); // Destination Address + ip_h->ip_csum = csum (&ip_h, sizeof ip_h); // Header Checksum + NS_LOG_INFO ("Parsed Ipv4Header"); + + l3_length = IP_HEADER_LEN; + } + } + else + { + // ARP Packet; the underlying OpenFlow header isn't used to match, so this is probably superfluous. + ArpHeader arp_hd; + if (packet->PeekHeader (arp_hd)) + { + buffer->l3 = new arp_eth_header; + arp_eth_header* arp_h = (arp_eth_header*)buffer->l3; + arp_h->ar_hrd = ARP_HRD_ETHERNET; // Hardware type. + arp_h->ar_pro = ARP_PRO_IP; // Protocol type. + arp_h->ar_op = arp_hd.m_type; // Opcode. + arp_hd.GetDestinationHardwareAddress ().CopyTo (arp_h->ar_tha); // Target hardware address. + arp_hd.GetSourceHardwareAddress ().CopyTo (arp_h->ar_sha); // Sender hardware address. + arp_h->ar_tpa = arp_hd.GetDestinationIpv4Address ().Get (); // Target protocol address. + arp_h->ar_spa = arp_hd.GetSourceIpv4Address ().Get (); // Sender protocol address. + arp_h->ar_hln = sizeof arp_h->ar_tha; // Hardware address length. + arp_h->ar_pln = sizeof arp_h->ar_tpa; // Protocol address length. + NS_LOG_INFO ("Parsed ArpHeader"); + + l3_length = ARP_ETH_HEADER_LEN; + } + } + + TcpHeader tcp_hd; + if (packet->PeekHeader (tcp_hd)) + { + buffer->l4 = new tcp_header; + tcp_header* tcp_h = (tcp_header*)buffer->l4; + tcp_h->tcp_src = htonl (tcp_hd.GetSourcePort ()); // Source Port + tcp_h->tcp_dst = htonl (tcp_hd.GetDestinationPort ()); // Destination Port + tcp_h->tcp_seq = tcp_hd.GetSequenceNumber ().GetValue (); // Sequence Number + tcp_h->tcp_ack = tcp_hd.GetAckNumber ().GetValue (); // ACK Number + tcp_h->tcp_ctl = TCP_FLAGS (tcp_hd.GetFlags ()); // Data Offset + Reserved + Flags + tcp_h->tcp_winsz = tcp_hd.GetWindowSize (); // Window Size + tcp_h->tcp_urg = tcp_hd.GetUrgentPointer (); // Urgent Pointer + tcp_h->tcp_csum = csum (&tcp_h, sizeof tcp_h); // Header Checksum + NS_LOG_INFO ("Parsed TcpHeader"); + + l4_length = TCP_HEADER_LEN; + } + else + { + UdpHeader udp_hd; + if (packet->PeekHeader (udp_hd)) + { + buffer->l4 = new udp_header; + udp_header* udp_h = (udp_header*)buffer->l4; + udp_h->udp_src = htonl (udp_hd.GetSourcePort ()); // Source Port + udp_h->udp_dst = htonl (udp_hd.GetDestinationPort ()); // Destination Port + udp_h->udp_len = htons (UDP_HEADER_LEN + packet->GetSize ()); + + if (protocol == Ipv4L3Protocol::PROT_NUMBER) + { + ip_header* ip_h = (ip_header*)buffer->l3; + uint32_t udp_csum = csum_add32 (0, ip_h->ip_src); + udp_csum = csum_add32 (udp_csum, ip_h->ip_dst); + udp_csum = csum_add16 (udp_csum, IP_TYPE_UDP << 8); + udp_csum = csum_add16 (udp_csum, udp_h->udp_len); + udp_csum = csum_continue (udp_csum, udp_h, sizeof udp_h); + udp_h->udp_csum = csum_finish (csum_continue (udp_csum, buffer->data, buffer->size)); // Header Checksum + } + else + { + udp_h->udp_csum = htons (0); + } + NS_LOG_INFO ("Parsed UdpHeader"); + + l4_length = UDP_HEADER_LEN; + } + } + + // Load Packet data into buffer data + packet->CopyData ((uint8_t*)buffer->data, packet->GetSize ()); + + if (buffer->l4) + { + ofpbuf_push (buffer, buffer->l4, l4_length); + delete (tcp_header*)buffer->l4; + } + if (buffer->l3) + { + ofpbuf_push (buffer, buffer->l3, l3_length); + delete (ip_header*)buffer->l3; + } + if (buffer->l2) + { + ofpbuf_push (buffer, buffer->l2, l2_length); + delete (eth_header*)buffer->l2; + } + + return buffer; +} + +void +OpenFlowSwitchNetDevice::ReceiveFromDevice (Ptr netdev, Ptr packet, uint16_t protocol, + const Address& src, const Address& dst, PacketType packetType) +{ + NS_LOG_FUNCTION_NOARGS (); + NS_LOG_INFO ("--------------------------------------------"); + NS_LOG_DEBUG ("UID is " << packet->GetUid ()); + + if (!m_promiscRxCallback.IsNull ()) + { + m_promiscRxCallback (this, packet, protocol, src, dst, packetType); + } + + Mac48Address dst48 = Mac48Address::ConvertFrom (dst); + NS_LOG_INFO ("Received packet from " << Mac48Address::ConvertFrom (src) << " looking for " << dst48); + + for (size_t i = 0; i < m_ports.size (); i++) + { + if (m_ports[i].netdev == netdev) + { + if (packetType == PACKET_HOST && dst48 == m_address) + { + m_rxCallback (this, packet, protocol, src); + } + else if (packetType == PACKET_BROADCAST || packetType == PACKET_MULTICAST || packetType == PACKET_OTHERHOST) + { + if (packetType == PACKET_OTHERHOST && dst48 == m_address) + { + m_rxCallback (this, packet, protocol, src); + } + else + { + if (packetType != PACKET_OTHERHOST) + { + m_rxCallback (this, packet, protocol, src); + } + + ofi::SwitchPacketMetadata data; + data.packet = packet->Copy (); + + ofpbuf *buffer = BufferFromPacket (data.packet,src,dst,netdev->GetMtu (),protocol); + m_ports[i].rx_packets++; + m_ports[i].rx_bytes += buffer->size; + data.buffer = buffer; + uint32_t packet_uid = save_buffer (buffer); + + data.protocolNumber = protocol; + data.src = Address (src); + data.dst = Address (dst); + m_packetData.insert (std::make_pair (packet_uid, data)); + + RunThroughFlowTable (packet_uid, i); + } + } + + break; + } + } + + // Run periodic execution. + Time now = Simulator::Now(); + if (now >= Seconds (m_lastExecute.GetSeconds () + 1)) // If a second or more has passed from the simulation time, execute. + { + // If port status is modified in any way, notify the controller. + for (size_t i = 0; i < m_ports.size (); i++) + { + if (UpdatePortStatus (m_ports[i])) + { + SendPortStatus (m_ports[i], OFPPR_MODIFY); + } + } + + // If any flows have expired, delete them and notify the controller. + List deleted = LIST_INITIALIZER (&deleted); + sw_flow *f, *n; + chain_timeout (m_chain, &deleted); + LIST_FOR_EACH_SAFE (f, n, sw_flow, node, &deleted) + { + std::ostringstream str; + str << "Flow ["; + for (int i = 0; i < 6; i++) + str << (i!=0?":":"") << std::hex << f->key.flow.dl_src[i]/16 << f->key.flow.dl_src[i]%16; + str << " -> "; + for (int i = 0; i < 6; i++) + str << (i!=0?":":"") << std::hex << f->key.flow.dl_dst[i]/16 << f->key.flow.dl_dst[i]%16; + str << "] expired."; + + NS_LOG_INFO (str.str ()); + SendFlowExpired (f, (ofp_flow_expired_reason)f->reason); + list_remove (&f->node); + flow_free (f); + } + + m_lastExecute = now; + } +} + +int +OpenFlowSwitchNetDevice::OutputAll (uint32_t packet_uid, int in_port, bool flood) +{ + NS_LOG_FUNCTION_NOARGS (); + NS_LOG_INFO ("Flooding over ports."); + + int prev_port = -1; + for (size_t i = 0; i < m_ports.size (); i++) + { + if (i == (unsigned)in_port) // Originating port + { + continue; + } + if (flood && m_ports[i].config & OFPPC_NO_FLOOD) // Port configured to not allow flooding + { + continue; + } + if (prev_port != -1) + { + OutputPort (packet_uid, in_port, prev_port, false); + } + prev_port = i; + } + if (prev_port != -1) + { + OutputPort (packet_uid, in_port, prev_port, false); + } + + return 0; +} + +void +OpenFlowSwitchNetDevice::OutputPacket (uint32_t packet_uid, int out_port) +{ + if (out_port >= 0 && out_port < DP_MAX_PORTS) + { + ofi::Port& p = m_ports[out_port]; + if (p.netdev != 0 && !(p.config & OFPPC_PORT_DOWN)) + { + ofi::SwitchPacketMetadata data = m_packetData.find (packet_uid)->second; + size_t bufsize = data.buffer->size; + NS_LOG_INFO ("Sending packet " << data.packet->GetUid () << " over port " << out_port); + if (p.netdev->SendFrom (data.packet->Copy (), data.src, data.dst, data.protocolNumber)) + { + p.tx_packets++; + p.tx_bytes += bufsize; + } + else + { + p.tx_dropped++; + } + return; + } + } + + NS_LOG_DEBUG ("can't forward to bad port " << out_port); +} + +void +OpenFlowSwitchNetDevice::OutputPort (uint32_t packet_uid, int in_port, int out_port, bool ignore_no_fwd) +{ + NS_LOG_FUNCTION_NOARGS (); + + if (out_port == OFPP_FLOOD) + { + OutputAll (packet_uid, in_port, true); + } + else if (out_port == OFPP_ALL) + { + OutputAll (packet_uid, in_port, false); + } + else if (out_port == OFPP_CONTROLLER) + { + OutputControl (packet_uid, in_port, 0, OFPR_ACTION); + } + else if (out_port == OFPP_IN_PORT) + { + OutputPacket (packet_uid, in_port); + } + else if (out_port == OFPP_TABLE) + { + RunThroughFlowTable (packet_uid, in_port < DP_MAX_PORTS ? in_port : -1, false); + } + else if (out_port >= OFPP_VP_START && out_port <= OFPP_VP_END) + { + // port is a virtual port + NS_LOG_INFO ("packet sent to virtual port " << out_port); + if (in_port < DP_MAX_PORTS) + { + RunThroughVPortTable (packet_uid, in_port, out_port); + } + else + { + RunThroughVPortTable (packet_uid, -1, out_port); + } + } + else if (in_port == out_port) + { + NS_LOG_DEBUG ("can't directly forward to input port"); + } + else + { + OutputPacket (packet_uid, out_port); + } +} + +void* +OpenFlowSwitchNetDevice::MakeOpenflowReply (size_t openflow_len, uint8_t type, ofpbuf **bufferp) +{ + return make_openflow_xid (openflow_len, type, 0, bufferp); +} + +int +OpenFlowSwitchNetDevice::SendOpenflowBuffer (ofpbuf *buffer) +{ + if (m_controller != 0) + { + update_openflow_length (buffer); + m_controller->ReceiveFromSwitch (this, buffer); + } + + return 0; +} + +void +OpenFlowSwitchNetDevice::OutputControl (uint32_t packet_uid, int in_port, size_t max_len, int reason) +{ + NS_LOG_INFO ("Sending packet to controller"); + + ofpbuf* buffer = m_packetData.find (packet_uid)->second.buffer; + size_t total_len = buffer->size; + if (packet_uid != std::numeric_limits::max () && max_len != 0 && buffer->size > max_len) + { + buffer->size = max_len; + } + + ofp_packet_in *opi = (ofp_packet_in*)ofpbuf_push_uninit (buffer, offsetof (ofp_packet_in, data)); + opi->header.version = OFP_VERSION; + opi->header.type = OFPT_PACKET_IN; + opi->header.length = htons (buffer->size); + opi->header.xid = htonl (0); + opi->buffer_id = htonl (packet_uid); + opi->total_len = htons (total_len); + opi->in_port = htons (in_port); + opi->reason = reason; + opi->pad = 0; + SendOpenflowBuffer (buffer); +} + +void +OpenFlowSwitchNetDevice::FillPortDesc (ofi::Port p, ofp_phy_port *desc) +{ + desc->port_no = htons (GetSwitchPortIndex (p)); + + std::ostringstream nm; + nm << "eth" << GetSwitchPortIndex (p); + strncpy ((char *)desc->name, nm.str ().c_str (), sizeof desc->name); + + p.netdev->GetAddress ().CopyTo (desc->hw_addr); + desc->config = htonl (p.config); + desc->state = htonl (p.state); + + // TODO: This should probably be fixed eventually to specify different available features. + desc->curr = 0; // htonl(netdev_get_features(p->netdev, NETDEV_FEAT_CURRENT)); + desc->supported = 0; // htonl(netdev_get_features(p->netdev, NETDEV_FEAT_SUPPORTED)); + desc->advertised = 0; // htonl(netdev_get_features(p->netdev, NETDEV_FEAT_ADVERTISED)); + desc->peer = 0; // htonl(netdev_get_features(p->netdev, NETDEV_FEAT_PEER)); +} + +void +OpenFlowSwitchNetDevice::SendFeaturesReply () +{ + ofpbuf *buffer; + ofp_switch_features *ofr = (ofp_switch_features*)MakeOpenflowReply (sizeof *ofr, OFPT_FEATURES_REPLY, &buffer); + ofr->datapath_id = htonll (m_id); + ofr->n_tables = m_chain->n_tables; + ofr->n_buffers = htonl (N_PKT_BUFFERS); + ofr->capabilities = htonl (OFP_SUPPORTED_CAPABILITIES); + ofr->actions = htonl (OFP_SUPPORTED_ACTIONS); + + for (size_t i = 0; i < m_ports.size (); i++) + { + ofp_phy_port* opp = (ofp_phy_port*)ofpbuf_put_zeros (buffer, sizeof *opp); + FillPortDesc (m_ports[i], opp); + } + + SendOpenflowBuffer (buffer); +} + +void +OpenFlowSwitchNetDevice::SendVPortTableFeatures () +{ + ofpbuf *buffer; + ofp_vport_table_features *ovtfr = (ofp_vport_table_features*)MakeOpenflowReply (sizeof *ovtfr, OFPT_VPORT_TABLE_FEATURES_REPLY, &buffer); + ovtfr->actions = htonl (OFP_SUPPORTED_VPORT_TABLE_ACTIONS); + ovtfr->max_vports = htonl (m_vportTable.max_vports); + ovtfr->max_chain_depth = htons (-1); // support a chain depth of 2^16 + ovtfr->mixed_chaining = true; + SendOpenflowBuffer (buffer); +} + +int +OpenFlowSwitchNetDevice::UpdatePortStatus (ofi::Port& p) +{ + uint32_t orig_config = p.config; + uint32_t orig_state = p.state; + + // Port is always enabled because the Net Device is always enabled. + p.config &= ~OFPPC_PORT_DOWN; + + if (p.netdev->IsLinkUp ()) + { + p.state &= ~OFPPS_LINK_DOWN; + } + else + { + p.state |= OFPPS_LINK_DOWN; + } + + return ((orig_config != p.config) || (orig_state != p.state)); +} + +void +OpenFlowSwitchNetDevice::SendPortStatus (ofi::Port p, uint8_t status) +{ + ofpbuf *buffer; + ofp_port_status *ops = (ofp_port_status*)MakeOpenflowReply (sizeof *ops, OFPT_PORT_STATUS, &buffer); + ops->reason = status; + memset (ops->pad, 0, sizeof ops->pad); + FillPortDesc (p, &ops->desc); + + SendOpenflowBuffer (buffer); + ofpbuf_delete (buffer); +} + +void +OpenFlowSwitchNetDevice::SendFlowExpired (sw_flow *flow, enum ofp_flow_expired_reason reason) +{ + ofpbuf *buffer; + ofp_flow_expired *ofe = (ofp_flow_expired*)MakeOpenflowReply (sizeof *ofe, OFPT_FLOW_EXPIRED, &buffer); + flow_fill_match (&ofe->match, &flow->key); + + ofe->priority = htons (flow->priority); + ofe->reason = reason; + memset (ofe->pad, 0, sizeof ofe->pad); + + ofe->duration = htonl (time_now () - flow->created); + memset (ofe->pad2, 0, sizeof ofe->pad2); + ofe->packet_count = htonll (flow->packet_count); + ofe->byte_count = htonll (flow->byte_count); + SendOpenflowBuffer (buffer); +} + +void +OpenFlowSwitchNetDevice::SendErrorMsg (uint16_t type, uint16_t code, const void *data, size_t len) +{ + ofpbuf *buffer; + ofp_error_msg *oem = (ofp_error_msg*)MakeOpenflowReply (sizeof(*oem) + len, OFPT_ERROR, &buffer); + oem->type = htons (type); + oem->code = htons (code); + memcpy (oem->data, data, len); + SendOpenflowBuffer (buffer); +} + +void +OpenFlowSwitchNetDevice::FlowTableLookup (sw_flow_key key, ofpbuf* buffer, uint32_t packet_uid, int port, bool send_to_controller) +{ + sw_flow *flow = chain_lookup (m_chain, &key); + if (flow != 0) + { + NS_LOG_INFO ("Flow matched"); + flow_used (flow, buffer); + ofi::ExecuteActions (this, packet_uid, buffer, &key, flow->sf_acts->actions, flow->sf_acts->actions_len, false); + } + else + { + NS_LOG_INFO ("Flow not matched."); + + if (send_to_controller) + { + OutputControl (packet_uid, port, m_missSendLen, OFPR_NO_MATCH); + } + } + + // Clean up; at this point we're done with the packet. + m_packetData.erase (packet_uid); + discard_buffer (packet_uid); + ofpbuf_delete (buffer); +} + +void +OpenFlowSwitchNetDevice::RunThroughFlowTable (uint32_t packet_uid, int port, bool send_to_controller) +{ + ofi::SwitchPacketMetadata data = m_packetData.find (packet_uid)->second; + ofpbuf* buffer = data.buffer; + + sw_flow_key key; + key.wildcards = 0; // Lookup cannot take wildcards. + // Extract the matching key's flow data from the packet's headers; if the policy is to drop fragments and the message is a fragment, drop it. + if (flow_extract (buffer, port != -1 ? port : OFPP_NONE, &key.flow) && (m_flags & OFPC_FRAG_MASK) == OFPC_FRAG_DROP) + { + ofpbuf_delete (buffer); + return; + } + + // drop MPLS packets with TTL 1 + if (buffer->l2_5) + { + mpls_header mpls_h; + mpls_h.value = ntohl (*((uint32_t*)buffer->l2_5)); + if (mpls_h.ttl == 1) + { + // increment mpls drop counter + if (port != -1) + { + m_ports[port].mpls_ttl0_dropped++; + } + return; + } + } + + // If we received the packet on a port, and opted not to receive any messages from it... + if (port != -1) + { + uint32_t config = m_ports[port].config; + if (config & (OFPPC_NO_RECV | OFPPC_NO_RECV_STP) + && config & (!eth_addr_equals (key.flow.dl_dst, stp_eth_addr) ? OFPPC_NO_RECV : OFPPC_NO_RECV_STP)) + { + return; + } + } + + NS_LOG_INFO ("Matching against the flow table."); + Simulator::Schedule (m_lookupDelay, &OpenFlowSwitchNetDevice::FlowTableLookup, this, key, buffer, packet_uid, port, send_to_controller); +} + +int +OpenFlowSwitchNetDevice::RunThroughVPortTable (uint32_t packet_uid, int port, uint32_t vport) +{ + ofpbuf* buffer = m_packetData.find (packet_uid)->second.buffer; + + // extract the flow again since we need it + // and the layer pointers may changed + sw_flow_key key; + key.wildcards = 0; + if (flow_extract (buffer, port != -1 ? port : OFPP_NONE, &key.flow) + && (m_flags & OFPC_FRAG_MASK) == OFPC_FRAG_DROP) + { + return 0; + } + + // run through the chain of port table entries + vport_table_entry *vpe = vport_table_lookup (&m_vportTable, vport); + m_vportTable.lookup_count++; + if (vpe) + { + m_vportTable.port_match_count++; + } + while (vpe != 0) + { + ofi::ExecuteVPortActions (this, packet_uid, m_packetData.find (packet_uid)->second.buffer, &key, vpe->port_acts->actions, vpe->port_acts->actions_len); + vport_used (vpe, buffer); // update counters for virtual port + if (vpe->parent_port_ptr == 0) + { + // if a port table's parent_port_ptr is 0 then + // the parent_port should be a physical port + if (vpe->parent_port <= OFPP_VP_START) // done traversing port chain, send packet to output port + { + OutputPort (packet_uid, port != -1 ? port : OFPP_NONE, vpe->parent_port, false); + } + else + { + NS_LOG_ERROR ("virtual port points to parent port\n"); + } + } + else // increment the number of port entries accessed by chaining + { + m_vportTable.chain_match_count++; + } + // move to the parent port entry + vpe = vpe->parent_port_ptr; + } + + return 0; +} + +int +OpenFlowSwitchNetDevice::ReceiveFeaturesRequest (const void *msg) +{ + SendFeaturesReply (); + return 0; +} + +int +OpenFlowSwitchNetDevice::ReceiveVPortTableFeaturesRequest (const void *msg) +{ + SendVPortTableFeatures (); + return 0; +} + +int +OpenFlowSwitchNetDevice::ReceiveGetConfigRequest (const void *msg) +{ + ofpbuf *buffer; + ofp_switch_config *osc = (ofp_switch_config*)MakeOpenflowReply (sizeof *osc, OFPT_GET_CONFIG_REPLY, &buffer); + osc->flags = htons (m_flags); + osc->miss_send_len = htons (m_missSendLen); + + return SendOpenflowBuffer (buffer); +} + +int +OpenFlowSwitchNetDevice::ReceiveSetConfig (const void *msg) +{ + const ofp_switch_config *osc = (ofp_switch_config*)msg; + + int n_flags = ntohs (osc->flags) & (OFPC_SEND_FLOW_EXP | OFPC_FRAG_MASK); + if ((n_flags & OFPC_FRAG_MASK) != OFPC_FRAG_NORMAL && (n_flags & OFPC_FRAG_MASK) != OFPC_FRAG_DROP) + { + n_flags = (n_flags & ~OFPC_FRAG_MASK) | OFPC_FRAG_DROP; + } + + m_flags = n_flags; + m_missSendLen = ntohs (osc->miss_send_len); + return 0; +} + +int +OpenFlowSwitchNetDevice::ReceivePacketOut (const void *msg) +{ + const ofp_packet_out *opo = (ofp_packet_out*)msg; + ofpbuf *buffer; + size_t actions_len = ntohs (opo->actions_len); + + if (actions_len > (ntohs (opo->header.length) - sizeof *opo)) + { + NS_LOG_DEBUG ("message too short for number of actions"); + return -EINVAL; + } + + if (ntohl (opo->buffer_id) == (uint32_t) -1) + { + // FIXME: can we avoid copying data here? + int data_len = ntohs (opo->header.length) - sizeof *opo - actions_len; + buffer = ofpbuf_new (data_len); + ofpbuf_put (buffer, (uint8_t *)opo->actions + actions_len, data_len); + } + else + { + buffer = retrieve_buffer (ntohl (opo->buffer_id)); + if (buffer == 0) + { + return -ESRCH; + } + } + + sw_flow_key key; + flow_extract (buffer, opo->in_port, &key.flow); // ntohs(opo->in_port) + + uint16_t v_code = ofi::ValidateActions (&key, opo->actions, actions_len); + if (v_code != ACT_VALIDATION_OK) + { + SendErrorMsg (OFPET_BAD_ACTION, v_code, msg, ntohs (opo->header.length)); + ofpbuf_delete (buffer); + return -EINVAL; + } + + ofi::ExecuteActions (this, opo->buffer_id, buffer, &key, opo->actions, actions_len, true); + return 0; +} + +int +OpenFlowSwitchNetDevice::ReceivePortMod (const void *msg) +{ + ofp_port_mod* opm = (ofp_port_mod*)msg; + + int port = opm->port_no; // ntohs(opm->port_no); + if (port < DP_MAX_PORTS) + { + ofi::Port& p = m_ports[port]; + + // Make sure the port id hasn't changed since this was sent + Mac48Address hw_addr = Mac48Address (); + hw_addr.CopyFrom (opm->hw_addr); + if (p.netdev->GetAddress () != hw_addr) + { + return 0; + } + + if (opm->mask) + { + uint32_t config_mask = ntohl (opm->mask); + p.config &= ~config_mask; + p.config |= ntohl (opm->config) & config_mask; + } + + if (opm->mask & htonl (OFPPC_PORT_DOWN)) + { + if ((opm->config & htonl (OFPPC_PORT_DOWN)) && (p.config & OFPPC_PORT_DOWN) == 0) + { + p.config |= OFPPC_PORT_DOWN; + // TODO: Possibly disable the Port's Net Device via the appropriate interface. + } + else if ((opm->config & htonl (OFPPC_PORT_DOWN)) == 0 && (p.config & OFPPC_PORT_DOWN)) + { + p.config &= ~OFPPC_PORT_DOWN; + // TODO: Possibly enable the Port's Net Device via the appropriate interface. + } + } + } + + return 0; +} + +// add or remove a virtual port table entry +int +OpenFlowSwitchNetDevice::ReceiveVPortMod (const void *msg) +{ + const ofp_vport_mod *ovpm = (ofp_vport_mod*)msg; + + uint16_t command = ntohs (ovpm->command); + if (command == OFPVP_ADD) + { + return AddVPort (ovpm); + } + else if (command == OFPVP_DELETE) + { + if (remove_vport_table_entry (&m_vportTable, ntohl (ovpm->vport))) + { + SendErrorMsg (OFPET_BAD_ACTION, OFPET_VPORT_MOD_FAILED, ovpm, ntohs (ovpm->header.length)); + } + } + + return 0; +} + +int +OpenFlowSwitchNetDevice::AddFlow (const ofp_flow_mod *ofm) +{ + size_t actions_len = ntohs (ofm->header.length) - sizeof *ofm; + + // Allocate memory. + sw_flow *flow = flow_alloc (actions_len); + if (flow == 0) + { + if (ntohl (ofm->buffer_id) != (uint32_t) -1) + { + discard_buffer (ntohl (ofm->buffer_id)); + } + return -ENOMEM; + } + + flow_extract_match (&flow->key, &ofm->match); + + uint16_t v_code = ofi::ValidateActions (&flow->key, ofm->actions, actions_len); + if (v_code != ACT_VALIDATION_OK) + { + SendErrorMsg (OFPET_BAD_ACTION, v_code, ofm, ntohs (ofm->header.length)); + flow_free (flow); + if (ntohl (ofm->buffer_id) != (uint32_t) -1) + { + discard_buffer (ntohl (ofm->buffer_id)); + } + return -ENOMEM; + } + + // 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); + + // Act. + int error = chain_insert (m_chain, flow); + if (error) + { + if (error == -ENOBUFS) + { + SendErrorMsg (OFPET_FLOW_MOD_FAILED, OFPFMFC_ALL_TABLES_FULL, ofm, ntohs (ofm->header.length)); + } + flow_free (flow); + if (ntohl (ofm->buffer_id) != (uint32_t) -1) + { + discard_buffer (ntohl (ofm->buffer_id)); + } + return error; + } + + NS_LOG_INFO ("Added new flow."); + if (ntohl (ofm->buffer_id) != std::numeric_limits::max ()) + { + ofpbuf *buffer = retrieve_buffer (ofm->buffer_id); // ntohl(ofm->buffer_id) + if (buffer) + { + sw_flow_key key; + flow_used (flow, buffer); + flow_extract (buffer, ofm->match.in_port, &key.flow); // ntohs(ofm->match.in_port); + ofi::ExecuteActions (this, ofm->buffer_id, buffer, &key, ofm->actions, actions_len, false); + ofpbuf_delete (buffer); + } + else + { + return -ESRCH; + } + } + return 0; +} + +int +OpenFlowSwitchNetDevice::ModFlow (const ofp_flow_mod *ofm) +{ + sw_flow_key key; + flow_extract_match (&key, &ofm->match); + + size_t actions_len = ntohs (ofm->header.length) - sizeof *ofm; + + uint16_t v_code = ofi::ValidateActions (&key, ofm->actions, actions_len); + if (v_code != ACT_VALIDATION_OK) + { + SendErrorMsg ((ofp_error_type)OFPET_BAD_ACTION, v_code, ofm, ntohs (ofm->header.length)); + if (ntohl (ofm->buffer_id) != (uint32_t) -1) + { + discard_buffer (ntohl (ofm->buffer_id)); + } + return -ENOMEM; + } + + uint16_t priority = key.wildcards ? ntohs (ofm->priority) : -1; + int strict = (ofm->command == htons (OFPFC_MODIFY_STRICT)) ? 1 : 0; + chain_modify (m_chain, &key, priority, strict, ofm->actions, actions_len); + + if (ntohl (ofm->buffer_id) != std::numeric_limits::max ()) + { + ofpbuf *buffer = retrieve_buffer (ofm->buffer_id); // ntohl (ofm->buffer_id) + if (buffer) + { + sw_flow_key skb_key; + flow_extract (buffer, ofm->match.in_port, &skb_key.flow); // ntohs(ofm->match.in_port); + ofi::ExecuteActions (this, ofm->buffer_id, buffer, &skb_key, ofm->actions, actions_len, false); + ofpbuf_delete (buffer); + } + else + { + return -ESRCH; + } + } + return 0; +} + +int +OpenFlowSwitchNetDevice::ReceiveFlow (const void *msg) +{ + NS_LOG_FUNCTION_NOARGS (); + const ofp_flow_mod *ofm = (ofp_flow_mod*)msg; + uint16_t command = ntohs (ofm->command); + + if (command == OFPFC_ADD) + { + return AddFlow (ofm); + } + else if ((command == OFPFC_MODIFY) || (command == OFPFC_MODIFY_STRICT)) + { + return ModFlow (ofm); + } + else if (command == OFPFC_DELETE) + { + sw_flow_key key; + flow_extract_match (&key, &ofm->match); + return chain_delete (m_chain, &key, ofm->out_port, 0, 0) ? 0 : -ESRCH; + } + else if (command == OFPFC_DELETE_STRICT) + { + sw_flow_key key; + uint16_t priority; + flow_extract_match (&key, &ofm->match); + priority = key.wildcards ? ntohs (ofm->priority) : -1; + return chain_delete (m_chain, &key, ofm->out_port, priority, 1) ? 0 : -ESRCH; + } + else + { + return -ENODEV; + } +} + +int +OpenFlowSwitchNetDevice::StatsDump (ofi::StatsDumpCallback *cb) +{ + ofp_stats_reply *osr; + ofpbuf *buffer; + int err; + + if (cb->done) + { + return 0; + } + + osr = (ofp_stats_reply*)MakeOpenflowReply (sizeof *osr, OFPT_STATS_REPLY, &buffer); + osr->type = htons (cb->s->type); + osr->flags = 0; + + err = cb->s->DoDump (this, cb->state, buffer); + if (err >= 0) + { + if (err == 0) + { + cb->done = true; + } + else + { + // Buffer might have been reallocated, so find our data again. + osr = (ofp_stats_reply*)ofpbuf_at_assert (buffer, 0, sizeof *osr); + osr->flags = ntohs (OFPSF_REPLY_MORE); + } + + int err2 = SendOpenflowBuffer (buffer); + if (err2) + { + err = err2; + } + } + + return err; +} + +void +OpenFlowSwitchNetDevice::StatsDone (ofi::StatsDumpCallback *cb) +{ + if (cb) + { + cb->s->DoCleanup (cb->state); + free (cb->s); + free (cb); + } +} + +int +OpenFlowSwitchNetDevice::ReceiveStatsRequest (const void *oh) +{ + const ofp_stats_request *rq = (ofp_stats_request*)oh; + size_t rq_len = ntohs (rq->header.length); + int type = ntohs (rq->type); + int body_len = rq_len - offsetof (ofp_stats_request, body); + ofi::Stats* st = new ofi::Stats ((ofp_stats_types)type, (unsigned)body_len); + + if (st == 0) + { + return -EINVAL; + } + + ofi::StatsDumpCallback cb; + cb.done = false; + cb.rq = (ofp_stats_request*)xmemdup (rq, rq_len); + cb.s = st; + cb.state = 0; + cb.swtch = this; + + if (cb.s) + { + int err = cb.s->DoInit (rq->body, body_len, &cb.state); + if (err) + { + NS_LOG_WARN ("failed initialization of stats request type " << type << ": " << strerror (-err)); + free (cb.rq); + return err; + } + } + + if (m_controller != 0) + { + m_controller->StartDump (&cb); + } + else + { + NS_LOG_ERROR ("Switch needs to be registered to a controller in order to start the stats reply."); + } + + return 0; +} + +int +OpenFlowSwitchNetDevice::ReceiveEchoRequest (const void *oh) +{ + return SendOpenflowBuffer (make_echo_reply ((ofp_header*)oh)); +} + +int +OpenFlowSwitchNetDevice::ReceiveEchoReply (const void *oh) +{ + return 0; +} + +int +OpenFlowSwitchNetDevice::ForwardControlInput (const void *msg, size_t length) +{ + // Check encapsulated length. + ofp_header *oh = (ofp_header*) msg; + if (ntohs (oh->length) > length) + { + return -EINVAL; + } + assert (oh->version == OFP_VERSION); + + int error = 0; + + // Figure out how to handle it. + switch (oh->type) + { + case OFPT_FEATURES_REQUEST: + error = length < sizeof(ofp_header) ? -EFAULT : ReceiveFeaturesRequest (msg); + break; + case OFPT_GET_CONFIG_REQUEST: + error = length < sizeof(ofp_header) ? -EFAULT : ReceiveGetConfigRequest (msg); + break; + case OFPT_SET_CONFIG: + error = length < sizeof(ofp_switch_config) ? -EFAULT : ReceiveSetConfig (msg); + break; + case OFPT_PACKET_OUT: + error = length < sizeof(ofp_packet_out) ? -EFAULT : ReceivePacketOut (msg); + break; + case OFPT_FLOW_MOD: + error = length < sizeof(ofp_flow_mod) ? -EFAULT : ReceiveFlow (msg); + break; + case OFPT_PORT_MOD: + error = length < sizeof(ofp_port_mod) ? -EFAULT : ReceivePortMod (msg); + break; + case OFPT_STATS_REQUEST: + error = length < sizeof(ofp_stats_request) ? -EFAULT : ReceiveStatsRequest (msg); + break; + case OFPT_ECHO_REQUEST: + error = length < sizeof(ofp_header) ? -EFAULT : ReceiveEchoRequest (msg); + break; + case OFPT_ECHO_REPLY: + error = length < sizeof(ofp_header) ? -EFAULT : ReceiveEchoReply (msg); + break; + case OFPT_VPORT_MOD: + error = length < sizeof(ofp_vport_mod) ? -EFAULT : ReceiveVPortMod (msg); + break; + case OFPT_VPORT_TABLE_FEATURES_REQUEST: + error = length < sizeof(ofp_header) ? -EFAULT : ReceiveVPortTableFeaturesRequest (msg); + break; + default: + SendErrorMsg ((ofp_error_type)OFPET_BAD_REQUEST, (ofp_bad_request_code)OFPBRC_BAD_TYPE, msg, length); + error = -EINVAL; + } + + if (msg != 0) + { + free ((ofpbuf*)msg); + } + return error; +} + +sw_chain* +OpenFlowSwitchNetDevice::GetChain () +{ + return m_chain; +} + +uint32_t +OpenFlowSwitchNetDevice::GetNSwitchPorts (void) const +{ + NS_LOG_FUNCTION_NOARGS (); + return m_ports.size (); +} + +ofi::Port +OpenFlowSwitchNetDevice::GetSwitchPort (uint32_t n) const +{ + NS_LOG_FUNCTION_NOARGS (); + return m_ports[n]; +} + +int +OpenFlowSwitchNetDevice::GetSwitchPortIndex (ofi::Port p) +{ + for (size_t i = 0; i < m_ports.size (); i++) + { + if (m_ports[i].netdev == p.netdev) + { + return i; + } + } + return -1; +} + +vport_table_t +OpenFlowSwitchNetDevice::GetVPortTable () +{ + return m_vportTable; +} + +} // namespace ns3 + +#endif // NS3_OPENFLOW diff --git a/src/openflow/model/openflow-switch-net-device.h b/src/openflow/model/openflow-switch-net-device.h new file mode 100644 index 000000000..875d425dc --- /dev/null +++ b/src/openflow/model/openflow-switch-net-device.h @@ -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 + */ +#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 +#include + +#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 + * . + * 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 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 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 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 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, const Address& dest, uint16_t protocolNumber); + virtual bool SendFrom (Ptr packet, const Address& source, const Address& dest, uint16_t protocolNumber); + virtual Ptr GetNode (void) const; + virtual void SetNode (Ptr 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 netdev, Ptr 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, 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 + * . + * + * 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 m_node; ///< Node this device is installed on. + Ptr 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 PacketData_t; + PacketData_t m_packetData; ///< Packet data + + typedef std::vector Ports_t; + Ports_t m_ports; ///< Switch's ports + + Ptr 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 diff --git a/src/openflow/test/examples-to-run.py b/src/openflow/test/examples-to-run.py new file mode 100644 index 000000000..2e74d7294 --- /dev/null +++ b/src/openflow/test/examples-to-run.py @@ -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 = [] diff --git a/src/openflow/test/openflow-switch-test-suite.cc b/src/openflow/test/openflow-switch-test-suite.cc new file mode 100644 index 000000000..ff8648113 --- /dev/null +++ b/src/openflow/test/openflow-switch-test-suite.cc @@ -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 + */ + + +#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 diff --git a/src/openflow/waf b/src/openflow/waf new file mode 100644 index 000000000..4283ec141 --- /dev/null +++ b/src/openflow/waf @@ -0,0 +1 @@ +exec "`dirname "$0"`"/../../../waf "$@" diff --git a/src/openflow/wscript b/src/openflow/wscript new file mode 100644 index 000000000..55bacfa1a --- /dev/null +++ b/src/openflow/wscript @@ -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') diff --git a/src/wscript b/src/wscript index 89e53d2e3..9b459d142 100644 --- a/src/wscript +++ b/src/wscript @@ -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) diff --git a/test.py b/test.py index c04613c5a..3b300bda4 100755 --- a/test.py +++ b/test.py @@ -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 = [] # From 491eddc2a7875765255ded387c10d47a672239f1 Mon Sep 17 00:00:00 2001 From: Josh Pelkey Date: Fri, 11 Mar 2011 16:20:25 -0500 Subject: [PATCH 2/3] python scan --- .../apidefs/gcc-ILP32/ns3_module_ns3tcp.py | 139 ++++++++++++++ .../apidefs/gcc-ILP32/ns3_module_ns3wifi.py | 139 ++++++++++++++ .../apidefs/gcc-ILP32/ns3_module_openflow.py | 139 ++++++++++++++ .../apidefs/gcc-ILP32/ns3_module_spectrum.py | 6 +- .../gcc-ILP32/ns3modulegen_generated.py | 170 ++++++++++++++---- .../apidefs/gcc-LP64/ns3_module_ns3tcp.py | 139 ++++++++++++++ .../apidefs/gcc-LP64/ns3_module_ns3wifi.py | 139 ++++++++++++++ .../apidefs/gcc-LP64/ns3_module_openflow.py | 139 ++++++++++++++ .../apidefs/gcc-LP64/ns3_module_spectrum.py | 6 +- .../gcc-LP64/ns3modulegen_generated.py | 170 ++++++++++++++---- 10 files changed, 1112 insertions(+), 74 deletions(-) create mode 100644 bindings/python/apidefs/gcc-ILP32/ns3_module_ns3tcp.py create mode 100644 bindings/python/apidefs/gcc-ILP32/ns3_module_ns3wifi.py create mode 100644 bindings/python/apidefs/gcc-ILP32/ns3_module_openflow.py create mode 100644 bindings/python/apidefs/gcc-LP64/ns3_module_ns3tcp.py create mode 100644 bindings/python/apidefs/gcc-LP64/ns3_module_ns3wifi.py create mode 100644 bindings/python/apidefs/gcc-LP64/ns3_module_openflow.py diff --git a/bindings/python/apidefs/gcc-ILP32/ns3_module_ns3tcp.py b/bindings/python/apidefs/gcc-ILP32/ns3_module_ns3tcp.py new file mode 100644 index 000000000..a37d20d52 --- /dev/null +++ b/bindings/python/apidefs/gcc-ILP32/ns3_module_ns3tcp.py @@ -0,0 +1,139 @@ +from pybindgen import Module, FileCodeSink, param, retval, cppclass, typehandlers + +def register_types(module): + root_module = module.get_root() + + + ## Register a nested module for the namespace Config + + nested_module = module.add_cpp_namespace('Config') + register_types_ns3_Config(nested_module) + + + ## Register a nested module for the namespace FatalImpl + + nested_module = module.add_cpp_namespace('FatalImpl') + register_types_ns3_FatalImpl(nested_module) + + + ## Register a nested module for the namespace addressUtils + + nested_module = module.add_cpp_namespace('addressUtils') + register_types_ns3_addressUtils(nested_module) + + + ## Register a nested module for the namespace aodv + + nested_module = module.add_cpp_namespace('aodv') + register_types_ns3_aodv(nested_module) + + + ## Register a nested module for the namespace dot11s + + nested_module = module.add_cpp_namespace('dot11s') + register_types_ns3_dot11s(nested_module) + + + ## Register a nested module for the namespace dsdv + + nested_module = module.add_cpp_namespace('dsdv') + register_types_ns3_dsdv(nested_module) + + + ## Register a nested module for the namespace flame + + nested_module = module.add_cpp_namespace('flame') + register_types_ns3_flame(nested_module) + + + ## Register a nested module for the namespace internal + + nested_module = module.add_cpp_namespace('internal') + register_types_ns3_internal(nested_module) + + + ## Register a nested module for the namespace olsr + + nested_module = module.add_cpp_namespace('olsr') + register_types_ns3_olsr(nested_module) + + +def register_types_ns3_Config(module): + root_module = module.get_root() + + +def register_types_ns3_FatalImpl(module): + root_module = module.get_root() + + +def register_types_ns3_addressUtils(module): + root_module = module.get_root() + + +def register_types_ns3_aodv(module): + root_module = module.get_root() + + +def register_types_ns3_dot11s(module): + root_module = module.get_root() + + +def register_types_ns3_dsdv(module): + root_module = module.get_root() + + +def register_types_ns3_flame(module): + root_module = module.get_root() + + +def register_types_ns3_internal(module): + root_module = module.get_root() + + +def register_types_ns3_olsr(module): + root_module = module.get_root() + + +def register_methods(root_module): + return + +def register_functions(root_module): + module = root_module + register_functions_ns3_Config(module.get_submodule('Config'), root_module) + register_functions_ns3_FatalImpl(module.get_submodule('FatalImpl'), root_module) + register_functions_ns3_addressUtils(module.get_submodule('addressUtils'), root_module) + register_functions_ns3_aodv(module.get_submodule('aodv'), root_module) + register_functions_ns3_dot11s(module.get_submodule('dot11s'), root_module) + register_functions_ns3_dsdv(module.get_submodule('dsdv'), root_module) + register_functions_ns3_flame(module.get_submodule('flame'), root_module) + register_functions_ns3_internal(module.get_submodule('internal'), root_module) + register_functions_ns3_olsr(module.get_submodule('olsr'), root_module) + return + +def register_functions_ns3_Config(module, root_module): + return + +def register_functions_ns3_FatalImpl(module, root_module): + return + +def register_functions_ns3_addressUtils(module, root_module): + return + +def register_functions_ns3_aodv(module, root_module): + return + +def register_functions_ns3_dot11s(module, root_module): + return + +def register_functions_ns3_dsdv(module, root_module): + return + +def register_functions_ns3_flame(module, root_module): + return + +def register_functions_ns3_internal(module, root_module): + return + +def register_functions_ns3_olsr(module, root_module): + return + diff --git a/bindings/python/apidefs/gcc-ILP32/ns3_module_ns3wifi.py b/bindings/python/apidefs/gcc-ILP32/ns3_module_ns3wifi.py new file mode 100644 index 000000000..a37d20d52 --- /dev/null +++ b/bindings/python/apidefs/gcc-ILP32/ns3_module_ns3wifi.py @@ -0,0 +1,139 @@ +from pybindgen import Module, FileCodeSink, param, retval, cppclass, typehandlers + +def register_types(module): + root_module = module.get_root() + + + ## Register a nested module for the namespace Config + + nested_module = module.add_cpp_namespace('Config') + register_types_ns3_Config(nested_module) + + + ## Register a nested module for the namespace FatalImpl + + nested_module = module.add_cpp_namespace('FatalImpl') + register_types_ns3_FatalImpl(nested_module) + + + ## Register a nested module for the namespace addressUtils + + nested_module = module.add_cpp_namespace('addressUtils') + register_types_ns3_addressUtils(nested_module) + + + ## Register a nested module for the namespace aodv + + nested_module = module.add_cpp_namespace('aodv') + register_types_ns3_aodv(nested_module) + + + ## Register a nested module for the namespace dot11s + + nested_module = module.add_cpp_namespace('dot11s') + register_types_ns3_dot11s(nested_module) + + + ## Register a nested module for the namespace dsdv + + nested_module = module.add_cpp_namespace('dsdv') + register_types_ns3_dsdv(nested_module) + + + ## Register a nested module for the namespace flame + + nested_module = module.add_cpp_namespace('flame') + register_types_ns3_flame(nested_module) + + + ## Register a nested module for the namespace internal + + nested_module = module.add_cpp_namespace('internal') + register_types_ns3_internal(nested_module) + + + ## Register a nested module for the namespace olsr + + nested_module = module.add_cpp_namespace('olsr') + register_types_ns3_olsr(nested_module) + + +def register_types_ns3_Config(module): + root_module = module.get_root() + + +def register_types_ns3_FatalImpl(module): + root_module = module.get_root() + + +def register_types_ns3_addressUtils(module): + root_module = module.get_root() + + +def register_types_ns3_aodv(module): + root_module = module.get_root() + + +def register_types_ns3_dot11s(module): + root_module = module.get_root() + + +def register_types_ns3_dsdv(module): + root_module = module.get_root() + + +def register_types_ns3_flame(module): + root_module = module.get_root() + + +def register_types_ns3_internal(module): + root_module = module.get_root() + + +def register_types_ns3_olsr(module): + root_module = module.get_root() + + +def register_methods(root_module): + return + +def register_functions(root_module): + module = root_module + register_functions_ns3_Config(module.get_submodule('Config'), root_module) + register_functions_ns3_FatalImpl(module.get_submodule('FatalImpl'), root_module) + register_functions_ns3_addressUtils(module.get_submodule('addressUtils'), root_module) + register_functions_ns3_aodv(module.get_submodule('aodv'), root_module) + register_functions_ns3_dot11s(module.get_submodule('dot11s'), root_module) + register_functions_ns3_dsdv(module.get_submodule('dsdv'), root_module) + register_functions_ns3_flame(module.get_submodule('flame'), root_module) + register_functions_ns3_internal(module.get_submodule('internal'), root_module) + register_functions_ns3_olsr(module.get_submodule('olsr'), root_module) + return + +def register_functions_ns3_Config(module, root_module): + return + +def register_functions_ns3_FatalImpl(module, root_module): + return + +def register_functions_ns3_addressUtils(module, root_module): + return + +def register_functions_ns3_aodv(module, root_module): + return + +def register_functions_ns3_dot11s(module, root_module): + return + +def register_functions_ns3_dsdv(module, root_module): + return + +def register_functions_ns3_flame(module, root_module): + return + +def register_functions_ns3_internal(module, root_module): + return + +def register_functions_ns3_olsr(module, root_module): + return + diff --git a/bindings/python/apidefs/gcc-ILP32/ns3_module_openflow.py b/bindings/python/apidefs/gcc-ILP32/ns3_module_openflow.py new file mode 100644 index 000000000..a37d20d52 --- /dev/null +++ b/bindings/python/apidefs/gcc-ILP32/ns3_module_openflow.py @@ -0,0 +1,139 @@ +from pybindgen import Module, FileCodeSink, param, retval, cppclass, typehandlers + +def register_types(module): + root_module = module.get_root() + + + ## Register a nested module for the namespace Config + + nested_module = module.add_cpp_namespace('Config') + register_types_ns3_Config(nested_module) + + + ## Register a nested module for the namespace FatalImpl + + nested_module = module.add_cpp_namespace('FatalImpl') + register_types_ns3_FatalImpl(nested_module) + + + ## Register a nested module for the namespace addressUtils + + nested_module = module.add_cpp_namespace('addressUtils') + register_types_ns3_addressUtils(nested_module) + + + ## Register a nested module for the namespace aodv + + nested_module = module.add_cpp_namespace('aodv') + register_types_ns3_aodv(nested_module) + + + ## Register a nested module for the namespace dot11s + + nested_module = module.add_cpp_namespace('dot11s') + register_types_ns3_dot11s(nested_module) + + + ## Register a nested module for the namespace dsdv + + nested_module = module.add_cpp_namespace('dsdv') + register_types_ns3_dsdv(nested_module) + + + ## Register a nested module for the namespace flame + + nested_module = module.add_cpp_namespace('flame') + register_types_ns3_flame(nested_module) + + + ## Register a nested module for the namespace internal + + nested_module = module.add_cpp_namespace('internal') + register_types_ns3_internal(nested_module) + + + ## Register a nested module for the namespace olsr + + nested_module = module.add_cpp_namespace('olsr') + register_types_ns3_olsr(nested_module) + + +def register_types_ns3_Config(module): + root_module = module.get_root() + + +def register_types_ns3_FatalImpl(module): + root_module = module.get_root() + + +def register_types_ns3_addressUtils(module): + root_module = module.get_root() + + +def register_types_ns3_aodv(module): + root_module = module.get_root() + + +def register_types_ns3_dot11s(module): + root_module = module.get_root() + + +def register_types_ns3_dsdv(module): + root_module = module.get_root() + + +def register_types_ns3_flame(module): + root_module = module.get_root() + + +def register_types_ns3_internal(module): + root_module = module.get_root() + + +def register_types_ns3_olsr(module): + root_module = module.get_root() + + +def register_methods(root_module): + return + +def register_functions(root_module): + module = root_module + register_functions_ns3_Config(module.get_submodule('Config'), root_module) + register_functions_ns3_FatalImpl(module.get_submodule('FatalImpl'), root_module) + register_functions_ns3_addressUtils(module.get_submodule('addressUtils'), root_module) + register_functions_ns3_aodv(module.get_submodule('aodv'), root_module) + register_functions_ns3_dot11s(module.get_submodule('dot11s'), root_module) + register_functions_ns3_dsdv(module.get_submodule('dsdv'), root_module) + register_functions_ns3_flame(module.get_submodule('flame'), root_module) + register_functions_ns3_internal(module.get_submodule('internal'), root_module) + register_functions_ns3_olsr(module.get_submodule('olsr'), root_module) + return + +def register_functions_ns3_Config(module, root_module): + return + +def register_functions_ns3_FatalImpl(module, root_module): + return + +def register_functions_ns3_addressUtils(module, root_module): + return + +def register_functions_ns3_aodv(module, root_module): + return + +def register_functions_ns3_dot11s(module, root_module): + return + +def register_functions_ns3_dsdv(module, root_module): + return + +def register_functions_ns3_flame(module, root_module): + return + +def register_functions_ns3_internal(module, root_module): + return + +def register_functions_ns3_olsr(module, root_module): + return + diff --git a/bindings/python/apidefs/gcc-ILP32/ns3_module_spectrum.py b/bindings/python/apidefs/gcc-ILP32/ns3_module_spectrum.py index fc5607a51..6a88b4b14 100644 --- a/bindings/python/apidefs/gcc-ILP32/ns3_module_spectrum.py +++ b/bindings/python/apidefs/gcc-ILP32/ns3_module_spectrum.py @@ -86,15 +86,15 @@ def register_types(module): typehandlers.add_type_alias('std::map< unsigned int, ns3::TxSpectrumModelInfo, std::less< unsigned int >, std::allocator< std::pair< unsigned int const, ns3::TxSpectrumModelInfo > > >', 'ns3::TxSpectrumModelInfoMap_t') typehandlers.add_type_alias('std::map< unsigned int, ns3::TxSpectrumModelInfo, std::less< unsigned int >, std::allocator< std::pair< unsigned int const, ns3::TxSpectrumModelInfo > > >*', 'ns3::TxSpectrumModelInfoMap_t*') typehandlers.add_type_alias('std::map< unsigned int, ns3::TxSpectrumModelInfo, std::less< unsigned int >, std::allocator< std::pair< unsigned int const, ns3::TxSpectrumModelInfo > > >&', 'ns3::TxSpectrumModelInfoMap_t&') + typehandlers.add_type_alias('std::vector< ns3::BandInfo, std::allocator< ns3::BandInfo > >', 'ns3::Bands') + typehandlers.add_type_alias('std::vector< ns3::BandInfo, std::allocator< ns3::BandInfo > >*', 'ns3::Bands*') + typehandlers.add_type_alias('std::vector< ns3::BandInfo, std::allocator< ns3::BandInfo > >&', 'ns3::Bands&') typehandlers.add_type_alias('std::map< unsigned int, ns3::RxSpectrumModelInfo, std::less< unsigned int >, std::allocator< std::pair< unsigned int const, ns3::RxSpectrumModelInfo > > >', 'ns3::RxSpectrumModelInfoMap_t') typehandlers.add_type_alias('std::map< unsigned int, ns3::RxSpectrumModelInfo, std::less< unsigned int >, std::allocator< std::pair< unsigned int const, ns3::RxSpectrumModelInfo > > >*', 'ns3::RxSpectrumModelInfoMap_t*') typehandlers.add_type_alias('std::map< unsigned int, ns3::RxSpectrumModelInfo, std::less< unsigned int >, std::allocator< std::pair< unsigned int const, ns3::RxSpectrumModelInfo > > >&', 'ns3::RxSpectrumModelInfoMap_t&') typehandlers.add_type_alias('uint32_t', 'ns3::SpectrumModelUid_t') typehandlers.add_type_alias('uint32_t*', 'ns3::SpectrumModelUid_t*') typehandlers.add_type_alias('uint32_t&', 'ns3::SpectrumModelUid_t&') - typehandlers.add_type_alias('std::vector< ns3::BandInfo, std::allocator< ns3::BandInfo > >', 'ns3::Bands') - typehandlers.add_type_alias('std::vector< ns3::BandInfo, std::allocator< ns3::BandInfo > >*', 'ns3::Bands*') - typehandlers.add_type_alias('std::vector< ns3::BandInfo, std::allocator< ns3::BandInfo > >&', 'ns3::Bands&') ## Register a nested module for the namespace Config diff --git a/bindings/python/apidefs/gcc-ILP32/ns3modulegen_generated.py b/bindings/python/apidefs/gcc-ILP32/ns3modulegen_generated.py index 279a27e5d..2a664eb92 100644 --- a/bindings/python/apidefs/gcc-ILP32/ns3modulegen_generated.py +++ b/bindings/python/apidefs/gcc-ILP32/ns3modulegen_generated.py @@ -31,19 +31,22 @@ import ns3_module_applications import ns3_module_click import ns3_module_flow_monitor import ns3_module_nix_vector_routing +import ns3_module_openflow import ns3_module_tap_bridge import ns3_module_virtual_net_device import ns3_module_netanim import ns3_module_energy import ns3_module_mesh +import ns3_module_ns3wifi import ns3_module_spectrum import ns3_module_csma import ns3_module_uan import ns3_module_aodv import ns3_module_dsdv -import ns3_module_lte import ns3_module_wimax +import ns3_module_ns3tcp import ns3_module_olsr +import ns3_module_lte def module_init(): root_module = Module('ns3', cpp_namespace='::ns3') @@ -261,6 +264,17 @@ def register_types(module): ns3_module_nix_vector_routing__local.register_types(module) root_module.end_section('ns3_module_nix_vector_routing') + root_module.begin_section('ns3_module_openflow') + ns3_module_openflow.register_types(module) + + try: + import ns3_module_openflow__local + except ImportError: + pass + else: + ns3_module_openflow__local.register_types(module) + + root_module.end_section('ns3_module_openflow') root_module.begin_section('ns3_module_tap_bridge') ns3_module_tap_bridge.register_types(module) @@ -316,6 +330,17 @@ def register_types(module): ns3_module_mesh__local.register_types(module) root_module.end_section('ns3_module_mesh') + root_module.begin_section('ns3_module_ns3wifi') + ns3_module_ns3wifi.register_types(module) + + try: + import ns3_module_ns3wifi__local + except ImportError: + pass + else: + ns3_module_ns3wifi__local.register_types(module) + + root_module.end_section('ns3_module_ns3wifi') root_module.begin_section('ns3_module_spectrum') ns3_module_spectrum.register_types(module) @@ -371,17 +396,6 @@ def register_types(module): ns3_module_dsdv__local.register_types(module) root_module.end_section('ns3_module_dsdv') - root_module.begin_section('ns3_module_lte') - ns3_module_lte.register_types(module) - - try: - import ns3_module_lte__local - except ImportError: - pass - else: - ns3_module_lte__local.register_types(module) - - root_module.end_section('ns3_module_lte') root_module.begin_section('ns3_module_wimax') ns3_module_wimax.register_types(module) @@ -393,6 +407,17 @@ def register_types(module): ns3_module_wimax__local.register_types(module) root_module.end_section('ns3_module_wimax') + root_module.begin_section('ns3_module_ns3tcp') + ns3_module_ns3tcp.register_types(module) + + try: + import ns3_module_ns3tcp__local + except ImportError: + pass + else: + ns3_module_ns3tcp__local.register_types(module) + + root_module.end_section('ns3_module_ns3tcp') root_module.begin_section('ns3_module_olsr') ns3_module_olsr.register_types(module) @@ -404,6 +429,17 @@ def register_types(module): ns3_module_olsr__local.register_types(module) root_module.end_section('ns3_module_olsr') + root_module.begin_section('ns3_module_lte') + ns3_module_lte.register_types(module) + + try: + import ns3_module_lte__local + except ImportError: + pass + else: + ns3_module_lte__local.register_types(module) + + root_module.end_section('ns3_module_lte') module.add_container('std::vector< unsigned int >', 'unsigned int', container_type='vector') module.add_container('std::vector< bool >', 'bool', container_type='vector') module.add_container('std::vector< int >', 'int', container_type='vector') @@ -726,6 +762,17 @@ def register_methods(root_module): ns3_module_nix_vector_routing__local.register_methods(root_module) root_module.end_section('ns3_module_nix_vector_routing') + root_module.begin_section('ns3_module_openflow') + ns3_module_openflow.register_methods(root_module) + + try: + import ns3_module_openflow__local + except ImportError: + pass + else: + ns3_module_openflow__local.register_methods(root_module) + + root_module.end_section('ns3_module_openflow') root_module.begin_section('ns3_module_tap_bridge') ns3_module_tap_bridge.register_methods(root_module) @@ -781,6 +828,17 @@ def register_methods(root_module): ns3_module_mesh__local.register_methods(root_module) root_module.end_section('ns3_module_mesh') + root_module.begin_section('ns3_module_ns3wifi') + ns3_module_ns3wifi.register_methods(root_module) + + try: + import ns3_module_ns3wifi__local + except ImportError: + pass + else: + ns3_module_ns3wifi__local.register_methods(root_module) + + root_module.end_section('ns3_module_ns3wifi') root_module.begin_section('ns3_module_spectrum') ns3_module_spectrum.register_methods(root_module) @@ -836,17 +894,6 @@ def register_methods(root_module): ns3_module_dsdv__local.register_methods(root_module) root_module.end_section('ns3_module_dsdv') - root_module.begin_section('ns3_module_lte') - ns3_module_lte.register_methods(root_module) - - try: - import ns3_module_lte__local - except ImportError: - pass - else: - ns3_module_lte__local.register_methods(root_module) - - root_module.end_section('ns3_module_lte') root_module.begin_section('ns3_module_wimax') ns3_module_wimax.register_methods(root_module) @@ -858,6 +905,17 @@ def register_methods(root_module): ns3_module_wimax__local.register_methods(root_module) root_module.end_section('ns3_module_wimax') + root_module.begin_section('ns3_module_ns3tcp') + ns3_module_ns3tcp.register_methods(root_module) + + try: + import ns3_module_ns3tcp__local + except ImportError: + pass + else: + ns3_module_ns3tcp__local.register_methods(root_module) + + root_module.end_section('ns3_module_ns3tcp') root_module.begin_section('ns3_module_olsr') ns3_module_olsr.register_methods(root_module) @@ -869,6 +927,17 @@ def register_methods(root_module): ns3_module_olsr__local.register_methods(root_module) root_module.end_section('ns3_module_olsr') + root_module.begin_section('ns3_module_lte') + ns3_module_lte.register_methods(root_module) + + try: + import ns3_module_lte__local + except ImportError: + pass + else: + ns3_module_lte__local.register_methods(root_module) + + root_module.end_section('ns3_module_lte') return def register_functions(root_module): @@ -1082,6 +1151,17 @@ def register_functions(root_module): ns3_module_nix_vector_routing__local.register_functions(root_module) root_module.end_section('ns3_module_nix_vector_routing') + root_module.begin_section('ns3_module_openflow') + ns3_module_openflow.register_functions(root_module) + + try: + import ns3_module_openflow__local + except ImportError: + pass + else: + ns3_module_openflow__local.register_functions(root_module) + + root_module.end_section('ns3_module_openflow') root_module.begin_section('ns3_module_tap_bridge') ns3_module_tap_bridge.register_functions(root_module) @@ -1137,6 +1217,17 @@ def register_functions(root_module): ns3_module_mesh__local.register_functions(root_module) root_module.end_section('ns3_module_mesh') + root_module.begin_section('ns3_module_ns3wifi') + ns3_module_ns3wifi.register_functions(root_module) + + try: + import ns3_module_ns3wifi__local + except ImportError: + pass + else: + ns3_module_ns3wifi__local.register_functions(root_module) + + root_module.end_section('ns3_module_ns3wifi') root_module.begin_section('ns3_module_spectrum') ns3_module_spectrum.register_functions(root_module) @@ -1192,17 +1283,6 @@ def register_functions(root_module): ns3_module_dsdv__local.register_functions(root_module) root_module.end_section('ns3_module_dsdv') - root_module.begin_section('ns3_module_lte') - ns3_module_lte.register_functions(root_module) - - try: - import ns3_module_lte__local - except ImportError: - pass - else: - ns3_module_lte__local.register_functions(root_module) - - root_module.end_section('ns3_module_lte') root_module.begin_section('ns3_module_wimax') ns3_module_wimax.register_functions(root_module) @@ -1214,6 +1294,17 @@ def register_functions(root_module): ns3_module_wimax__local.register_functions(root_module) root_module.end_section('ns3_module_wimax') + root_module.begin_section('ns3_module_ns3tcp') + ns3_module_ns3tcp.register_functions(root_module) + + try: + import ns3_module_ns3tcp__local + except ImportError: + pass + else: + ns3_module_ns3tcp__local.register_functions(root_module) + + root_module.end_section('ns3_module_ns3tcp') root_module.begin_section('ns3_module_olsr') ns3_module_olsr.register_functions(root_module) @@ -1225,6 +1316,17 @@ def register_functions(root_module): ns3_module_olsr__local.register_functions(root_module) root_module.end_section('ns3_module_olsr') + root_module.begin_section('ns3_module_lte') + ns3_module_lte.register_functions(root_module) + + try: + import ns3_module_lte__local + except ImportError: + pass + else: + ns3_module_lte__local.register_functions(root_module) + + root_module.end_section('ns3_module_lte') register_functions_ns3_Config(module.get_submodule('Config'), root_module) register_functions_ns3_FatalImpl(module.get_submodule('FatalImpl'), root_module) register_functions_ns3_addressUtils(module.get_submodule('addressUtils'), root_module) diff --git a/bindings/python/apidefs/gcc-LP64/ns3_module_ns3tcp.py b/bindings/python/apidefs/gcc-LP64/ns3_module_ns3tcp.py new file mode 100644 index 000000000..a37d20d52 --- /dev/null +++ b/bindings/python/apidefs/gcc-LP64/ns3_module_ns3tcp.py @@ -0,0 +1,139 @@ +from pybindgen import Module, FileCodeSink, param, retval, cppclass, typehandlers + +def register_types(module): + root_module = module.get_root() + + + ## Register a nested module for the namespace Config + + nested_module = module.add_cpp_namespace('Config') + register_types_ns3_Config(nested_module) + + + ## Register a nested module for the namespace FatalImpl + + nested_module = module.add_cpp_namespace('FatalImpl') + register_types_ns3_FatalImpl(nested_module) + + + ## Register a nested module for the namespace addressUtils + + nested_module = module.add_cpp_namespace('addressUtils') + register_types_ns3_addressUtils(nested_module) + + + ## Register a nested module for the namespace aodv + + nested_module = module.add_cpp_namespace('aodv') + register_types_ns3_aodv(nested_module) + + + ## Register a nested module for the namespace dot11s + + nested_module = module.add_cpp_namespace('dot11s') + register_types_ns3_dot11s(nested_module) + + + ## Register a nested module for the namespace dsdv + + nested_module = module.add_cpp_namespace('dsdv') + register_types_ns3_dsdv(nested_module) + + + ## Register a nested module for the namespace flame + + nested_module = module.add_cpp_namespace('flame') + register_types_ns3_flame(nested_module) + + + ## Register a nested module for the namespace internal + + nested_module = module.add_cpp_namespace('internal') + register_types_ns3_internal(nested_module) + + + ## Register a nested module for the namespace olsr + + nested_module = module.add_cpp_namespace('olsr') + register_types_ns3_olsr(nested_module) + + +def register_types_ns3_Config(module): + root_module = module.get_root() + + +def register_types_ns3_FatalImpl(module): + root_module = module.get_root() + + +def register_types_ns3_addressUtils(module): + root_module = module.get_root() + + +def register_types_ns3_aodv(module): + root_module = module.get_root() + + +def register_types_ns3_dot11s(module): + root_module = module.get_root() + + +def register_types_ns3_dsdv(module): + root_module = module.get_root() + + +def register_types_ns3_flame(module): + root_module = module.get_root() + + +def register_types_ns3_internal(module): + root_module = module.get_root() + + +def register_types_ns3_olsr(module): + root_module = module.get_root() + + +def register_methods(root_module): + return + +def register_functions(root_module): + module = root_module + register_functions_ns3_Config(module.get_submodule('Config'), root_module) + register_functions_ns3_FatalImpl(module.get_submodule('FatalImpl'), root_module) + register_functions_ns3_addressUtils(module.get_submodule('addressUtils'), root_module) + register_functions_ns3_aodv(module.get_submodule('aodv'), root_module) + register_functions_ns3_dot11s(module.get_submodule('dot11s'), root_module) + register_functions_ns3_dsdv(module.get_submodule('dsdv'), root_module) + register_functions_ns3_flame(module.get_submodule('flame'), root_module) + register_functions_ns3_internal(module.get_submodule('internal'), root_module) + register_functions_ns3_olsr(module.get_submodule('olsr'), root_module) + return + +def register_functions_ns3_Config(module, root_module): + return + +def register_functions_ns3_FatalImpl(module, root_module): + return + +def register_functions_ns3_addressUtils(module, root_module): + return + +def register_functions_ns3_aodv(module, root_module): + return + +def register_functions_ns3_dot11s(module, root_module): + return + +def register_functions_ns3_dsdv(module, root_module): + return + +def register_functions_ns3_flame(module, root_module): + return + +def register_functions_ns3_internal(module, root_module): + return + +def register_functions_ns3_olsr(module, root_module): + return + diff --git a/bindings/python/apidefs/gcc-LP64/ns3_module_ns3wifi.py b/bindings/python/apidefs/gcc-LP64/ns3_module_ns3wifi.py new file mode 100644 index 000000000..a37d20d52 --- /dev/null +++ b/bindings/python/apidefs/gcc-LP64/ns3_module_ns3wifi.py @@ -0,0 +1,139 @@ +from pybindgen import Module, FileCodeSink, param, retval, cppclass, typehandlers + +def register_types(module): + root_module = module.get_root() + + + ## Register a nested module for the namespace Config + + nested_module = module.add_cpp_namespace('Config') + register_types_ns3_Config(nested_module) + + + ## Register a nested module for the namespace FatalImpl + + nested_module = module.add_cpp_namespace('FatalImpl') + register_types_ns3_FatalImpl(nested_module) + + + ## Register a nested module for the namespace addressUtils + + nested_module = module.add_cpp_namespace('addressUtils') + register_types_ns3_addressUtils(nested_module) + + + ## Register a nested module for the namespace aodv + + nested_module = module.add_cpp_namespace('aodv') + register_types_ns3_aodv(nested_module) + + + ## Register a nested module for the namespace dot11s + + nested_module = module.add_cpp_namespace('dot11s') + register_types_ns3_dot11s(nested_module) + + + ## Register a nested module for the namespace dsdv + + nested_module = module.add_cpp_namespace('dsdv') + register_types_ns3_dsdv(nested_module) + + + ## Register a nested module for the namespace flame + + nested_module = module.add_cpp_namespace('flame') + register_types_ns3_flame(nested_module) + + + ## Register a nested module for the namespace internal + + nested_module = module.add_cpp_namespace('internal') + register_types_ns3_internal(nested_module) + + + ## Register a nested module for the namespace olsr + + nested_module = module.add_cpp_namespace('olsr') + register_types_ns3_olsr(nested_module) + + +def register_types_ns3_Config(module): + root_module = module.get_root() + + +def register_types_ns3_FatalImpl(module): + root_module = module.get_root() + + +def register_types_ns3_addressUtils(module): + root_module = module.get_root() + + +def register_types_ns3_aodv(module): + root_module = module.get_root() + + +def register_types_ns3_dot11s(module): + root_module = module.get_root() + + +def register_types_ns3_dsdv(module): + root_module = module.get_root() + + +def register_types_ns3_flame(module): + root_module = module.get_root() + + +def register_types_ns3_internal(module): + root_module = module.get_root() + + +def register_types_ns3_olsr(module): + root_module = module.get_root() + + +def register_methods(root_module): + return + +def register_functions(root_module): + module = root_module + register_functions_ns3_Config(module.get_submodule('Config'), root_module) + register_functions_ns3_FatalImpl(module.get_submodule('FatalImpl'), root_module) + register_functions_ns3_addressUtils(module.get_submodule('addressUtils'), root_module) + register_functions_ns3_aodv(module.get_submodule('aodv'), root_module) + register_functions_ns3_dot11s(module.get_submodule('dot11s'), root_module) + register_functions_ns3_dsdv(module.get_submodule('dsdv'), root_module) + register_functions_ns3_flame(module.get_submodule('flame'), root_module) + register_functions_ns3_internal(module.get_submodule('internal'), root_module) + register_functions_ns3_olsr(module.get_submodule('olsr'), root_module) + return + +def register_functions_ns3_Config(module, root_module): + return + +def register_functions_ns3_FatalImpl(module, root_module): + return + +def register_functions_ns3_addressUtils(module, root_module): + return + +def register_functions_ns3_aodv(module, root_module): + return + +def register_functions_ns3_dot11s(module, root_module): + return + +def register_functions_ns3_dsdv(module, root_module): + return + +def register_functions_ns3_flame(module, root_module): + return + +def register_functions_ns3_internal(module, root_module): + return + +def register_functions_ns3_olsr(module, root_module): + return + diff --git a/bindings/python/apidefs/gcc-LP64/ns3_module_openflow.py b/bindings/python/apidefs/gcc-LP64/ns3_module_openflow.py new file mode 100644 index 000000000..a37d20d52 --- /dev/null +++ b/bindings/python/apidefs/gcc-LP64/ns3_module_openflow.py @@ -0,0 +1,139 @@ +from pybindgen import Module, FileCodeSink, param, retval, cppclass, typehandlers + +def register_types(module): + root_module = module.get_root() + + + ## Register a nested module for the namespace Config + + nested_module = module.add_cpp_namespace('Config') + register_types_ns3_Config(nested_module) + + + ## Register a nested module for the namespace FatalImpl + + nested_module = module.add_cpp_namespace('FatalImpl') + register_types_ns3_FatalImpl(nested_module) + + + ## Register a nested module for the namespace addressUtils + + nested_module = module.add_cpp_namespace('addressUtils') + register_types_ns3_addressUtils(nested_module) + + + ## Register a nested module for the namespace aodv + + nested_module = module.add_cpp_namespace('aodv') + register_types_ns3_aodv(nested_module) + + + ## Register a nested module for the namespace dot11s + + nested_module = module.add_cpp_namespace('dot11s') + register_types_ns3_dot11s(nested_module) + + + ## Register a nested module for the namespace dsdv + + nested_module = module.add_cpp_namespace('dsdv') + register_types_ns3_dsdv(nested_module) + + + ## Register a nested module for the namespace flame + + nested_module = module.add_cpp_namespace('flame') + register_types_ns3_flame(nested_module) + + + ## Register a nested module for the namespace internal + + nested_module = module.add_cpp_namespace('internal') + register_types_ns3_internal(nested_module) + + + ## Register a nested module for the namespace olsr + + nested_module = module.add_cpp_namespace('olsr') + register_types_ns3_olsr(nested_module) + + +def register_types_ns3_Config(module): + root_module = module.get_root() + + +def register_types_ns3_FatalImpl(module): + root_module = module.get_root() + + +def register_types_ns3_addressUtils(module): + root_module = module.get_root() + + +def register_types_ns3_aodv(module): + root_module = module.get_root() + + +def register_types_ns3_dot11s(module): + root_module = module.get_root() + + +def register_types_ns3_dsdv(module): + root_module = module.get_root() + + +def register_types_ns3_flame(module): + root_module = module.get_root() + + +def register_types_ns3_internal(module): + root_module = module.get_root() + + +def register_types_ns3_olsr(module): + root_module = module.get_root() + + +def register_methods(root_module): + return + +def register_functions(root_module): + module = root_module + register_functions_ns3_Config(module.get_submodule('Config'), root_module) + register_functions_ns3_FatalImpl(module.get_submodule('FatalImpl'), root_module) + register_functions_ns3_addressUtils(module.get_submodule('addressUtils'), root_module) + register_functions_ns3_aodv(module.get_submodule('aodv'), root_module) + register_functions_ns3_dot11s(module.get_submodule('dot11s'), root_module) + register_functions_ns3_dsdv(module.get_submodule('dsdv'), root_module) + register_functions_ns3_flame(module.get_submodule('flame'), root_module) + register_functions_ns3_internal(module.get_submodule('internal'), root_module) + register_functions_ns3_olsr(module.get_submodule('olsr'), root_module) + return + +def register_functions_ns3_Config(module, root_module): + return + +def register_functions_ns3_FatalImpl(module, root_module): + return + +def register_functions_ns3_addressUtils(module, root_module): + return + +def register_functions_ns3_aodv(module, root_module): + return + +def register_functions_ns3_dot11s(module, root_module): + return + +def register_functions_ns3_dsdv(module, root_module): + return + +def register_functions_ns3_flame(module, root_module): + return + +def register_functions_ns3_internal(module, root_module): + return + +def register_functions_ns3_olsr(module, root_module): + return + diff --git a/bindings/python/apidefs/gcc-LP64/ns3_module_spectrum.py b/bindings/python/apidefs/gcc-LP64/ns3_module_spectrum.py index fc5607a51..6a88b4b14 100644 --- a/bindings/python/apidefs/gcc-LP64/ns3_module_spectrum.py +++ b/bindings/python/apidefs/gcc-LP64/ns3_module_spectrum.py @@ -86,15 +86,15 @@ def register_types(module): typehandlers.add_type_alias('std::map< unsigned int, ns3::TxSpectrumModelInfo, std::less< unsigned int >, std::allocator< std::pair< unsigned int const, ns3::TxSpectrumModelInfo > > >', 'ns3::TxSpectrumModelInfoMap_t') typehandlers.add_type_alias('std::map< unsigned int, ns3::TxSpectrumModelInfo, std::less< unsigned int >, std::allocator< std::pair< unsigned int const, ns3::TxSpectrumModelInfo > > >*', 'ns3::TxSpectrumModelInfoMap_t*') typehandlers.add_type_alias('std::map< unsigned int, ns3::TxSpectrumModelInfo, std::less< unsigned int >, std::allocator< std::pair< unsigned int const, ns3::TxSpectrumModelInfo > > >&', 'ns3::TxSpectrumModelInfoMap_t&') + typehandlers.add_type_alias('std::vector< ns3::BandInfo, std::allocator< ns3::BandInfo > >', 'ns3::Bands') + typehandlers.add_type_alias('std::vector< ns3::BandInfo, std::allocator< ns3::BandInfo > >*', 'ns3::Bands*') + typehandlers.add_type_alias('std::vector< ns3::BandInfo, std::allocator< ns3::BandInfo > >&', 'ns3::Bands&') typehandlers.add_type_alias('std::map< unsigned int, ns3::RxSpectrumModelInfo, std::less< unsigned int >, std::allocator< std::pair< unsigned int const, ns3::RxSpectrumModelInfo > > >', 'ns3::RxSpectrumModelInfoMap_t') typehandlers.add_type_alias('std::map< unsigned int, ns3::RxSpectrumModelInfo, std::less< unsigned int >, std::allocator< std::pair< unsigned int const, ns3::RxSpectrumModelInfo > > >*', 'ns3::RxSpectrumModelInfoMap_t*') typehandlers.add_type_alias('std::map< unsigned int, ns3::RxSpectrumModelInfo, std::less< unsigned int >, std::allocator< std::pair< unsigned int const, ns3::RxSpectrumModelInfo > > >&', 'ns3::RxSpectrumModelInfoMap_t&') typehandlers.add_type_alias('uint32_t', 'ns3::SpectrumModelUid_t') typehandlers.add_type_alias('uint32_t*', 'ns3::SpectrumModelUid_t*') typehandlers.add_type_alias('uint32_t&', 'ns3::SpectrumModelUid_t&') - typehandlers.add_type_alias('std::vector< ns3::BandInfo, std::allocator< ns3::BandInfo > >', 'ns3::Bands') - typehandlers.add_type_alias('std::vector< ns3::BandInfo, std::allocator< ns3::BandInfo > >*', 'ns3::Bands*') - typehandlers.add_type_alias('std::vector< ns3::BandInfo, std::allocator< ns3::BandInfo > >&', 'ns3::Bands&') ## Register a nested module for the namespace Config diff --git a/bindings/python/apidefs/gcc-LP64/ns3modulegen_generated.py b/bindings/python/apidefs/gcc-LP64/ns3modulegen_generated.py index 999752c24..bfda55fd7 100644 --- a/bindings/python/apidefs/gcc-LP64/ns3modulegen_generated.py +++ b/bindings/python/apidefs/gcc-LP64/ns3modulegen_generated.py @@ -31,19 +31,22 @@ import ns3_module_applications import ns3_module_click import ns3_module_flow_monitor import ns3_module_nix_vector_routing +import ns3_module_openflow import ns3_module_tap_bridge import ns3_module_virtual_net_device import ns3_module_netanim import ns3_module_energy import ns3_module_mesh +import ns3_module_ns3wifi import ns3_module_spectrum import ns3_module_csma import ns3_module_uan import ns3_module_aodv import ns3_module_dsdv -import ns3_module_lte import ns3_module_wimax +import ns3_module_ns3tcp import ns3_module_olsr +import ns3_module_lte def module_init(): root_module = Module('ns3', cpp_namespace='::ns3') @@ -261,6 +264,17 @@ def register_types(module): ns3_module_nix_vector_routing__local.register_types(module) root_module.end_section('ns3_module_nix_vector_routing') + root_module.begin_section('ns3_module_openflow') + ns3_module_openflow.register_types(module) + + try: + import ns3_module_openflow__local + except ImportError: + pass + else: + ns3_module_openflow__local.register_types(module) + + root_module.end_section('ns3_module_openflow') root_module.begin_section('ns3_module_tap_bridge') ns3_module_tap_bridge.register_types(module) @@ -316,6 +330,17 @@ def register_types(module): ns3_module_mesh__local.register_types(module) root_module.end_section('ns3_module_mesh') + root_module.begin_section('ns3_module_ns3wifi') + ns3_module_ns3wifi.register_types(module) + + try: + import ns3_module_ns3wifi__local + except ImportError: + pass + else: + ns3_module_ns3wifi__local.register_types(module) + + root_module.end_section('ns3_module_ns3wifi') root_module.begin_section('ns3_module_spectrum') ns3_module_spectrum.register_types(module) @@ -371,17 +396,6 @@ def register_types(module): ns3_module_dsdv__local.register_types(module) root_module.end_section('ns3_module_dsdv') - root_module.begin_section('ns3_module_lte') - ns3_module_lte.register_types(module) - - try: - import ns3_module_lte__local - except ImportError: - pass - else: - ns3_module_lte__local.register_types(module) - - root_module.end_section('ns3_module_lte') root_module.begin_section('ns3_module_wimax') ns3_module_wimax.register_types(module) @@ -393,6 +407,17 @@ def register_types(module): ns3_module_wimax__local.register_types(module) root_module.end_section('ns3_module_wimax') + root_module.begin_section('ns3_module_ns3tcp') + ns3_module_ns3tcp.register_types(module) + + try: + import ns3_module_ns3tcp__local + except ImportError: + pass + else: + ns3_module_ns3tcp__local.register_types(module) + + root_module.end_section('ns3_module_ns3tcp') root_module.begin_section('ns3_module_olsr') ns3_module_olsr.register_types(module) @@ -404,6 +429,17 @@ def register_types(module): ns3_module_olsr__local.register_types(module) root_module.end_section('ns3_module_olsr') + root_module.begin_section('ns3_module_lte') + ns3_module_lte.register_types(module) + + try: + import ns3_module_lte__local + except ImportError: + pass + else: + ns3_module_lte__local.register_types(module) + + root_module.end_section('ns3_module_lte') module.add_container('std::vector< unsigned int >', 'unsigned int', container_type='vector') module.add_container('std::vector< bool >', 'bool', container_type='vector') module.add_container('std::vector< int >', 'int', container_type='vector') @@ -726,6 +762,17 @@ def register_methods(root_module): ns3_module_nix_vector_routing__local.register_methods(root_module) root_module.end_section('ns3_module_nix_vector_routing') + root_module.begin_section('ns3_module_openflow') + ns3_module_openflow.register_methods(root_module) + + try: + import ns3_module_openflow__local + except ImportError: + pass + else: + ns3_module_openflow__local.register_methods(root_module) + + root_module.end_section('ns3_module_openflow') root_module.begin_section('ns3_module_tap_bridge') ns3_module_tap_bridge.register_methods(root_module) @@ -781,6 +828,17 @@ def register_methods(root_module): ns3_module_mesh__local.register_methods(root_module) root_module.end_section('ns3_module_mesh') + root_module.begin_section('ns3_module_ns3wifi') + ns3_module_ns3wifi.register_methods(root_module) + + try: + import ns3_module_ns3wifi__local + except ImportError: + pass + else: + ns3_module_ns3wifi__local.register_methods(root_module) + + root_module.end_section('ns3_module_ns3wifi') root_module.begin_section('ns3_module_spectrum') ns3_module_spectrum.register_methods(root_module) @@ -836,17 +894,6 @@ def register_methods(root_module): ns3_module_dsdv__local.register_methods(root_module) root_module.end_section('ns3_module_dsdv') - root_module.begin_section('ns3_module_lte') - ns3_module_lte.register_methods(root_module) - - try: - import ns3_module_lte__local - except ImportError: - pass - else: - ns3_module_lte__local.register_methods(root_module) - - root_module.end_section('ns3_module_lte') root_module.begin_section('ns3_module_wimax') ns3_module_wimax.register_methods(root_module) @@ -858,6 +905,17 @@ def register_methods(root_module): ns3_module_wimax__local.register_methods(root_module) root_module.end_section('ns3_module_wimax') + root_module.begin_section('ns3_module_ns3tcp') + ns3_module_ns3tcp.register_methods(root_module) + + try: + import ns3_module_ns3tcp__local + except ImportError: + pass + else: + ns3_module_ns3tcp__local.register_methods(root_module) + + root_module.end_section('ns3_module_ns3tcp') root_module.begin_section('ns3_module_olsr') ns3_module_olsr.register_methods(root_module) @@ -869,6 +927,17 @@ def register_methods(root_module): ns3_module_olsr__local.register_methods(root_module) root_module.end_section('ns3_module_olsr') + root_module.begin_section('ns3_module_lte') + ns3_module_lte.register_methods(root_module) + + try: + import ns3_module_lte__local + except ImportError: + pass + else: + ns3_module_lte__local.register_methods(root_module) + + root_module.end_section('ns3_module_lte') return def register_functions(root_module): @@ -1082,6 +1151,17 @@ def register_functions(root_module): ns3_module_nix_vector_routing__local.register_functions(root_module) root_module.end_section('ns3_module_nix_vector_routing') + root_module.begin_section('ns3_module_openflow') + ns3_module_openflow.register_functions(root_module) + + try: + import ns3_module_openflow__local + except ImportError: + pass + else: + ns3_module_openflow__local.register_functions(root_module) + + root_module.end_section('ns3_module_openflow') root_module.begin_section('ns3_module_tap_bridge') ns3_module_tap_bridge.register_functions(root_module) @@ -1137,6 +1217,17 @@ def register_functions(root_module): ns3_module_mesh__local.register_functions(root_module) root_module.end_section('ns3_module_mesh') + root_module.begin_section('ns3_module_ns3wifi') + ns3_module_ns3wifi.register_functions(root_module) + + try: + import ns3_module_ns3wifi__local + except ImportError: + pass + else: + ns3_module_ns3wifi__local.register_functions(root_module) + + root_module.end_section('ns3_module_ns3wifi') root_module.begin_section('ns3_module_spectrum') ns3_module_spectrum.register_functions(root_module) @@ -1192,17 +1283,6 @@ def register_functions(root_module): ns3_module_dsdv__local.register_functions(root_module) root_module.end_section('ns3_module_dsdv') - root_module.begin_section('ns3_module_lte') - ns3_module_lte.register_functions(root_module) - - try: - import ns3_module_lte__local - except ImportError: - pass - else: - ns3_module_lte__local.register_functions(root_module) - - root_module.end_section('ns3_module_lte') root_module.begin_section('ns3_module_wimax') ns3_module_wimax.register_functions(root_module) @@ -1214,6 +1294,17 @@ def register_functions(root_module): ns3_module_wimax__local.register_functions(root_module) root_module.end_section('ns3_module_wimax') + root_module.begin_section('ns3_module_ns3tcp') + ns3_module_ns3tcp.register_functions(root_module) + + try: + import ns3_module_ns3tcp__local + except ImportError: + pass + else: + ns3_module_ns3tcp__local.register_functions(root_module) + + root_module.end_section('ns3_module_ns3tcp') root_module.begin_section('ns3_module_olsr') ns3_module_olsr.register_functions(root_module) @@ -1225,6 +1316,17 @@ def register_functions(root_module): ns3_module_olsr__local.register_functions(root_module) root_module.end_section('ns3_module_olsr') + root_module.begin_section('ns3_module_lte') + ns3_module_lte.register_functions(root_module) + + try: + import ns3_module_lte__local + except ImportError: + pass + else: + ns3_module_lte__local.register_functions(root_module) + + root_module.end_section('ns3_module_lte') register_functions_ns3_Config(module.get_submodule('Config'), root_module) register_functions_ns3_FatalImpl(module.get_submodule('FatalImpl'), root_module) register_functions_ns3_addressUtils(module.get_submodule('addressUtils'), root_module) From 01cd8c0a98f24be23a0b676de5f78c2c75e9f692 Mon Sep 17 00:00:00 2001 From: Lalith Suresh Date: Fri, 11 Mar 2011 21:48:51 +0000 Subject: [PATCH 3/3] Bug 1070: csma/ipv6 examples duplicate NS_LOG_COMPONENT_DEFINE --- examples/ipv6/radvd-two-prefix.cc | 2 +- src/csma/examples/csma-raw-ip-socket.cc | 2 +- src/nix-vector-routing/examples/nix-simple.cc | 2 +- src/wimax/examples/wimax-simple.cc | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/ipv6/radvd-two-prefix.cc b/examples/ipv6/radvd-two-prefix.cc index 0b3c7ef33..42bf13ce8 100644 --- a/examples/ipv6/radvd-two-prefix.cc +++ b/examples/ipv6/radvd-two-prefix.cc @@ -46,7 +46,7 @@ using namespace ns3; -NS_LOG_COMPONENT_DEFINE ("RadvdExample"); +NS_LOG_COMPONENT_DEFINE ("RadvdTwoPrefixExample"); /** * \class StackHelper diff --git a/src/csma/examples/csma-raw-ip-socket.cc b/src/csma/examples/csma-raw-ip-socket.cc index 096dbbc0a..9fba6e81d 100644 --- a/src/csma/examples/csma-raw-ip-socket.cc +++ b/src/csma/examples/csma-raw-ip-socket.cc @@ -38,7 +38,7 @@ using namespace ns3; -NS_LOG_COMPONENT_DEFINE ("CsmaPacketSocketExample"); +NS_LOG_COMPONENT_DEFINE ("CsmaRawIpSocketExample"); static void SinkRx (Ptr p, const Address &ad) { diff --git a/src/nix-vector-routing/examples/nix-simple.cc b/src/nix-vector-routing/examples/nix-simple.cc index d8497cab3..e7ddfb892 100644 --- a/src/nix-vector-routing/examples/nix-simple.cc +++ b/src/nix-vector-routing/examples/nix-simple.cc @@ -40,7 +40,7 @@ using namespace ns3; -NS_LOG_COMPONENT_DEFINE ("FirstScriptExample"); +NS_LOG_COMPONENT_DEFINE ("NixSimpleExample"); int main (int argc, char *argv[]) diff --git a/src/wimax/examples/wimax-simple.cc b/src/wimax/examples/wimax-simple.cc index 612d22420..a8082e613 100644 --- a/src/wimax/examples/wimax-simple.cc +++ b/src/wimax/examples/wimax-simple.cc @@ -54,7 +54,7 @@ #include "ns3/service-flow.h" #include -NS_LOG_COMPONENT_DEFINE ("wimaxIpV4Simulation"); +NS_LOG_COMPONENT_DEFINE ("WimaxSimpleExample"); using namespace ns3;