--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/apidefs/gcc-ILP32/ns3_module_openflow.py Sat Mar 12 18:35:56 2011 +0000
@@ -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
+
--- a/bindings/python/apidefs/gcc-ILP32/ns3_module_spectrum.py Sat Mar 12 18:34:30 2011 +0000
+++ b/bindings/python/apidefs/gcc-ILP32/ns3_module_spectrum.py Sat Mar 12 18:35:56 2011 +0000
@@ -86,15 +86,15 @@
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
--- a/bindings/python/apidefs/gcc-ILP32/ns3modulegen_generated.py Sat Mar 12 18:34:30 2011 +0000
+++ b/bindings/python/apidefs/gcc-ILP32/ns3modulegen_generated.py Sat Mar 12 18:35:56 2011 +0000
@@ -31,6 +31,7 @@
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
@@ -263,6 +264,17 @@
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)
@@ -750,6 +762,17 @@
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)
@@ -1128,6 +1151,17 @@
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)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bindings/python/apidefs/gcc-LP64/ns3_module_openflow.py Sat Mar 12 18:35:56 2011 +0000
@@ -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
+
--- a/bindings/python/apidefs/gcc-LP64/ns3_module_spectrum.py Sat Mar 12 18:34:30 2011 +0000
+++ b/bindings/python/apidefs/gcc-LP64/ns3_module_spectrum.py Sat Mar 12 18:35:56 2011 +0000
@@ -86,15 +86,15 @@
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
--- a/bindings/python/apidefs/gcc-LP64/ns3modulegen_generated.py Sat Mar 12 18:34:30 2011 +0000
+++ b/bindings/python/apidefs/gcc-LP64/ns3modulegen_generated.py Sat Mar 12 18:35:56 2011 +0000
@@ -31,6 +31,7 @@
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
@@ -263,6 +264,17 @@
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)
@@ -750,6 +762,17 @@
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)
@@ -1128,6 +1151,17 @@
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)
--- a/examples/ipv6/radvd-two-prefix.cc Sat Mar 12 18:34:30 2011 +0000
+++ b/examples/ipv6/radvd-two-prefix.cc Sat Mar 12 18:35:56 2011 +0000
@@ -46,7 +46,7 @@
using namespace ns3;
-NS_LOG_COMPONENT_DEFINE ("RadvdExample");
+NS_LOG_COMPONENT_DEFINE ("RadvdTwoPrefixExample");
/**
* \class StackHelper
--- a/src/csma/examples/csma-raw-ip-socket.cc Sat Mar 12 18:34:30 2011 +0000
+++ b/src/csma/examples/csma-raw-ip-socket.cc Sat Mar 12 18:35:56 2011 +0000
@@ -38,7 +38,7 @@
using namespace ns3;
-NS_LOG_COMPONENT_DEFINE ("CsmaPacketSocketExample");
+NS_LOG_COMPONENT_DEFINE ("CsmaRawIpSocketExample");
static void SinkRx (Ptr<const Packet> p, const Address &ad)
{
--- a/src/nix-vector-routing/examples/nix-simple.cc Sat Mar 12 18:34:30 2011 +0000
+++ b/src/nix-vector-routing/examples/nix-simple.cc Sat Mar 12 18:35:56 2011 +0000
@@ -40,7 +40,7 @@
using namespace ns3;
-NS_LOG_COMPONENT_DEFINE ("FirstScriptExample");
+NS_LOG_COMPONENT_DEFINE ("NixSimpleExample");
int
main (int argc, char *argv[])
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/openflow/doc/openflow-switch.h Sat Mar 12 18:35:56 2011 +0000
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2011 Blake Hurd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ *
+ * Author: Blake Hurd <naimorai@gmail.com>
+ */
+
+/**
+ * \ingroup devices
+ * \defgroup OpenFlowSwitchModel OpenFlow Switch Model
+ *
+ * \section OpenFlowSwitchModelOverview OpenFlow Switch Model Overview
+ *
+ * The ns-3 OpenFlowSwitch device models an OpenFlow-enabled switch. It is designed to
+ * express basic use of the OpenFlow protocol, with the maintaining of a virtual
+ * Flow Table and TCAM to provide OpenFlow-like results.
+ *
+ * The functionality comes down to the Controllers, which send messages to the
+ * switch that configure its flows, producing different effects. Controllers can
+ * be added by the user, under the ofi namespace extends ofi::Controller. To
+ * demonstrate this, a DropController, which creates flows for ignoring every single
+ * packet, and LearningController, which effectively makes the switch a more complicated
+ * BridgeNetDevice. A user versed in a standard OFSID, and/or OF protocol, can write
+ * virtual controllers to create switches of all kinds of types.
+ *
+ * \section OpenFlowSwitchModel OpenFlow Switch Model
+ *
+ * The OpenFlowSwitch device behaves somewhat according to the diagram setup as a classical OFSID
+ * switch, with a few modifications made for a proper simulation environment.
+ *
+ * Normal OF-enabled Switch
+ * -----------------------------------
+ * | Secure Channel | <--OF Protocol--> | Controller is external |
+ * | Hardware or Software Flow Table |
+ * -----------------------------------
+ *
+ * ns-3 OF-enabled Switch (module)
+ * -------------------------------------
+ * | m_controller->ReceiveFromSwitch() | <--OF Protocol--> | Controller is internal |
+ * | Software Flow Table, virtual TCAM |
+ * -------------------------------------
+ *
+ * In essence, there are two differences:
+ * # No SSL, Embedded Controller: Instead of a secure channel and connecting to an
+ * outside location for the Controller program/machine, we currently only allow a
+ * Controller extended from ofi::Controller, an extension of an ns3::Object. This
+ * means ns-3 programmers cannot model the SSL part of the interface or possibility
+ * of network failure. The connection to the OpenFlowSwitch is local and there aren't any
+ * reasons for the channel/connection to break down. <<This difference may be an
+ * option in the future. Using EmuNetDevices, it should be possible to engage an
+ * external Controller program/machine, and thus work with controllers designed
+ * outside of the ns-3 environment, that simply use the proper OF protocol when
+ * communicating messages to the switch through a tap device.>>
+ *
+ * # Virtual Flow Table, TCAM: Typical OF-enabled switches are implemented on a hardware
+ * TCAM. The OFSID we turn into a library includes a modelled software TCAM, that produces
+ * the same results as a hardware TCAM. We include an attribute FlowTableLookupDelay, which
+ * allows a simple delay of using the TCAM to be modelled. We don't endeavor to make this
+ * delay more complicated, based on the tasks we are running on the TCAM, that is a possible
+ * future improvement.
+ *
+ * \section OpenFlowSwitchNetDeviceModel OpenFlow Switch Net Device Model
+ *
+ * The OpenFlowSwitch network device is aimed to model an OpenFlow switch, with a TCAM and a connection
+ * to a controller program. With some tweaking, it can model every switch type, as per OpenFlow's
+ * extensibility. It outsources the complexity of the switch ports to NetDevices of the user's choosing.
+ * It should be noted that these NetDevices must behave like practical switch ports, i.e. a Mac Address
+ * is assigned, and nothing more. It also must support a SendFrom function so
+ * that the OpenFlowSwitch can forward across that port.
+ *
+ * The OpenFlowSwitchNetDevice provides following Attributes:
+ *
+ * - FlowTableLookUpDelay: This time gets run off the clock when making a lookup in our Flow Table.
+ * - Flags: OpenFlow specific configuration flags. They are defined in the ofp_config_flags enum. Choices include:
+ * OFPC_SEND_FLOW_EXP (OpenFlowSwitch notifies controller when a flow has expired),
+ * OFPC_FRAG_NORMAL (Match fragment against Flow table),
+ * OFPC_FRAG_DROP (Drop fragments),
+ * OFPC_FRAG_REASM (Reassemble only if OFPC_IP_REASM set, which is currently impossible,
+ * because switch implementation does not support IP reassembly)
+ * OFPC_FRAG_MASK (Mask Fragments)
+ * - FlowTableMissSendLength: When the packet doesn't match in our Flow Table, and we forward to the controller,
+ * this sets # of bytes forwarded (packet is not forwarded in its entirety, unless specified).
+ *
+ * \section OpenFlowSwitchModelSummary OpenFlow Switch Model Summary
+ *
+ * The ns-3 OpenFlowSwitch device models an OpenFlow-enabled switch. It is designed to
+ * express basic use of the OpenFlow protocol, with the maintaining of a virtual
+ * Flow Table and TCAM to provide OpenFlow-like results.
+ *
+ * The functionality comes down to the Controllers, which send messages to the
+ * switch that configure its flows, producing different effects. Controllers can
+ * be added by the user, under the ofi namespace extends ofi::Controller. To
+ * demonstrate this, a DropController, which creates flows for ignoring every single
+ * packet, and LearningController, which effectively makes the switch a more complicated
+ * BridgeNetDevice. A user versed in a standard OFSID, and/or OF protocol, can write
+ * virtual controllers to create switches of all kinds of types.
+ *
+ * In order to use the OpenFlowSwitch module, you must create and link the
+ * OFSID (OpenFlow Software Implementation Distribution) to ns-3.
+ * To do this:
+ *
+ * #1 Obtain the OFSID code. An ns-3 specific OFSID branch is provided
+ * to ensure operation with ns-3. The OFSID has several dependencies
+ * include libxml2, libdl, and the boost libraries. Use mercurial to
+ * download this branch and waf to build the library:
+ *
+ * $ hg clone http://code.nsnam.org/bhurd/openflow
+ * $ cd openflow
+ * $ ./waf configure
+ * $ ./waf build
+ *
+ * #2 Your OFSID is now built into a libopenflow.a library! To
+ * link to an ns-3 build with this switch module, run from the ns-3-dev
+ * (or whatever you have named your distribution):
+ *
+ * $ ./waf configure --with-openflow=path/to/openflow
+ *
+ * #3 Under "---- Summary of optional NS-3 features:", you should see
+ * "NS-3 OpenFlow Integration : enabled", indicating the library
+ * has been linked to ns-3. Run:
+ *
+ * $ ./waf build
+ *
+ * to build ns-3 and activate the OpenFlowSwitch module.
+ *
+ * Once set up, you have access to some tests:
+ *
+ * For a test suite for the switch module, run:
+ *
+ * $ ./test.py --suite=openflow
+ *
+ * For an example demonstrating its use in a simple learning controller/switch, run:
+ *
+ * $ ./waf --run openflow-switch
+ *
+ * To see it in detailed logging, run:
+ *
+ * $ ./waf --run "openflow-switch -v"
+ *
+ * If you have any questions, read the wiki <http://www.nsnam.org/wiki/index.php/GSOC2010OpenFlow>
+ * first, and consider posting to the ns-3 developers mailing list <http://www.isi.edu/nsnam/ns/ns-dev-list.html>,
+ * but feel free to reach me at <naimorai@gmail.com>
+ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/openflow/examples/openflow-switch.cc Sat Mar 12 18:35:56 2011 +0000
@@ -0,0 +1,215 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// Network topology
+//
+// n0 n1
+// | |
+// ----------
+// | Switch |
+// ----------
+// | |
+// n2 n3
+//
+//
+// - CBR/UDP flows from n0 to n1 and from n3 to n0
+// - DropTail queues
+// - Tracing of queues and packet receptions to file "openflow-switch.tr"
+// - If order of adding nodes and netdevices is kept:
+// n0 = 00:00:00;00:00:01, n1 = 00:00:00:00:00:03, n3 = 00:00:00:00:00:07
+// and port number corresponds to node number, so port 0 is connected to n0, for example.
+
+#include <iostream>
+#include <fstream>
+
+#include "ns3/core-module.h"
+#include "ns3/network-module.h"
+#include "ns3/csma-module.h"
+#include "ns3/internet-module.h"
+#include "ns3/applications-module.h"
+#include "ns3/openflow-module.h"
+#include "ns3/log.h"
+
+using namespace ns3;
+
+NS_LOG_COMPONENT_DEFINE ("OpenFlowCsmaSwitchExample");
+
+bool verbose = false;
+bool use_drop = false;
+ns3::Time timeout = ns3::Seconds (0);
+
+bool
+SetVerbose (std::string value)
+{
+ verbose = true;
+ return true;
+}
+
+bool
+SetDrop (std::string value)
+{
+ use_drop = true;
+ return true;
+}
+
+bool
+SetTimeout (std::string value)
+{
+ try {
+ timeout = ns3::Seconds (atof (value.c_str ()));
+ return true;
+ }
+ catch (...) { return false; }
+ return false;
+}
+
+int
+main (int argc, char *argv[])
+{
+ #ifdef NS3_OPENFLOW
+ //
+ // Allow the user to override any of the defaults and the above Bind() at
+ // run-time, via command-line arguments
+ //
+ CommandLine cmd;
+ cmd.AddValue ("v", "Verbose (turns on logging).", MakeCallback (&SetVerbose));
+ cmd.AddValue ("verbose", "Verbose (turns on logging).", MakeCallback (&SetVerbose));
+ cmd.AddValue ("d", "Use Drop Controller (Learning if not specified).", MakeCallback (&SetDrop));
+ cmd.AddValue ("drop", "Use Drop Controller (Learning if not specified).", MakeCallback (&SetDrop));
+ cmd.AddValue ("t", "Learning Controller Timeout (has no effect if drop controller is specified).", MakeCallback( &SetTimeout));
+ cmd.AddValue ("timeout", "Learning Controller Timeout (has no effect if drop controller is specified).", MakeCallback( &SetTimeout));
+
+ cmd.Parse (argc, argv);
+
+ if (verbose)
+ {
+ LogComponentEnable ("OpenFlowCsmaSwitchExample", LOG_LEVEL_INFO);
+ LogComponentEnable ("OpenFlowInterface", LOG_LEVEL_INFO);
+ LogComponentEnable ("OpenFlowSwitchNetDevice", LOG_LEVEL_INFO);
+ }
+
+ //
+ // Explicitly create the nodes required by the topology (shown above).
+ //
+ NS_LOG_INFO ("Create nodes.");
+ NodeContainer terminals;
+ terminals.Create (4);
+
+ NodeContainer csmaSwitch;
+ csmaSwitch.Create (1);
+
+ NS_LOG_INFO ("Build Topology");
+ CsmaHelper csma;
+ csma.SetChannelAttribute ("DataRate", DataRateValue (5000000));
+ csma.SetChannelAttribute ("Delay", TimeValue (MilliSeconds (2)));
+
+ // Create the csma links, from each terminal to the switch
+ NetDeviceContainer terminalDevices;
+ NetDeviceContainer switchDevices;
+ for (int i = 0; i < 4; i++)
+ {
+ NetDeviceContainer link = csma.Install (NodeContainer (terminals.Get (i), csmaSwitch));
+ terminalDevices.Add (link.Get (0));
+ switchDevices.Add (link.Get (1));
+ }
+
+ // Create the switch netdevice, which will do the packet switching
+ Ptr<Node> switchNode = csmaSwitch.Get (0);
+ OpenFlowSwitchHelper swtch;
+
+ if (use_drop)
+ {
+ Ptr<ns3::ofi::DropController> controller = CreateObject<ns3::ofi::DropController> ();
+ swtch.Install (switchNode, switchDevices, controller);
+ }
+ else
+ {
+ Ptr<ns3::ofi::LearningController> controller = CreateObject<ns3::ofi::LearningController> ();
+ if (!timeout.IsZero ()) controller->SetAttribute ("ExpirationTime", TimeValue (timeout));
+ swtch.Install (switchNode, switchDevices, controller);
+ }
+
+ // Add internet stack to the terminals
+ InternetStackHelper internet;
+ internet.Install (terminals);
+
+ // We've got the "hardware" in place. Now we need to add IP addresses.
+ NS_LOG_INFO ("Assign IP Addresses.");
+ Ipv4AddressHelper ipv4;
+ ipv4.SetBase ("10.1.1.0", "255.255.255.0");
+ ipv4.Assign (terminalDevices);
+
+ // Create an OnOff application to send UDP datagrams from n0 to n1.
+ NS_LOG_INFO ("Create Applications.");
+ uint16_t port = 9; // Discard port (RFC 863)
+
+ OnOffHelper onoff ("ns3::UdpSocketFactory",
+ Address (InetSocketAddress (Ipv4Address ("10.1.1.2"), port)));
+ onoff.SetAttribute ("OnTime", RandomVariableValue (ConstantVariable (1)));
+ onoff.SetAttribute ("OffTime", RandomVariableValue (ConstantVariable (0)));
+
+ ApplicationContainer app = onoff.Install (terminals.Get (0));
+ // Start the application
+ app.Start (Seconds (1.0));
+ app.Stop (Seconds (10.0));
+
+ // Create an optional packet sink to receive these packets
+ PacketSinkHelper sink ("ns3::UdpSocketFactory",
+ Address (InetSocketAddress (Ipv4Address::GetAny (), port)));
+ app = sink.Install (terminals.Get (1));
+ app.Start (Seconds (0.0));
+
+ //
+ // Create a similar flow from n3 to n0, starting at time 1.1 seconds
+ //
+ onoff.SetAttribute ("Remote",
+ AddressValue (InetSocketAddress (Ipv4Address ("10.1.1.1"), port)));
+ app = onoff.Install (terminals.Get (3));
+ app.Start (Seconds (1.1));
+ app.Stop (Seconds (10.0));
+
+ app = sink.Install (terminals.Get (0));
+ app.Start (Seconds (0.0));
+
+ NS_LOG_INFO ("Configure Tracing.");
+
+ //
+ // Configure tracing of all enqueue, dequeue, and NetDevice receive events.
+ // Trace output will be sent to the file "openflow-switch.tr"
+ //
+ AsciiTraceHelper ascii;
+ csma.EnableAsciiAll (ascii.CreateFileStream ("openflow-switch.tr"));
+
+ //
+ // Also configure some tcpdump traces; each interface will be traced.
+ // The output files will be named:
+ // openflow-switch-<nodeId>-<interfaceId>.pcap
+ // and can be read by the "tcpdump -r" command (use "-tt" option to
+ // display timestamps correctly)
+ //
+ csma.EnablePcapAll ("openflow-switch", false);
+
+ //
+ // Now, do the actual simulation.
+ //
+ NS_LOG_INFO ("Run Simulation.");
+ Simulator::Run ();
+ Simulator::Destroy ();
+ NS_LOG_INFO ("Done.");
+ #else
+ NS_LOG_INFO ("NS-3 OpenFlow is not enabled. Cannot run simulation.");
+ #endif // NS3_OPENFLOW
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/openflow/examples/wscript Sat Mar 12 18:35:56 2011 +0000
@@ -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'
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/openflow/helper/openflow-switch-helper.cc Sat Mar 12 18:35:56 2011 +0000
@@ -0,0 +1,97 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2011 Blake Hurd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Blake Hurd <naimorai@gmail.com>
+ */
+#ifdef NS3_OPENFLOW
+
+#include "openflow-switch-helper.h"
+#include "ns3/log.h"
+#include "ns3/openflow-switch-net-device.h"
+#include "ns3/openflow-interface.h"
+#include "ns3/node.h"
+#include "ns3/names.h"
+
+NS_LOG_COMPONENT_DEFINE ("OpenFlowSwitchHelper");
+
+namespace ns3 {
+
+OpenFlowSwitchHelper::OpenFlowSwitchHelper ()
+{
+ NS_LOG_FUNCTION_NOARGS ();
+ m_deviceFactory.SetTypeId ("ns3::OpenFlowSwitchNetDevice");
+}
+
+void
+OpenFlowSwitchHelper::SetDeviceAttribute (std::string n1, const AttributeValue &v1)
+{
+ NS_LOG_FUNCTION_NOARGS ();
+ m_deviceFactory.Set (n1, v1);
+}
+
+NetDeviceContainer
+OpenFlowSwitchHelper::Install (Ptr<Node> node, NetDeviceContainer c, Ptr<ns3::ofi::Controller> controller)
+{
+ NS_LOG_FUNCTION_NOARGS ();
+ NS_LOG_INFO ("**** Install switch device on node " << node->GetId ());
+
+ NetDeviceContainer devs;
+ Ptr<OpenFlowSwitchNetDevice> dev = m_deviceFactory.Create<OpenFlowSwitchNetDevice> ();
+ devs.Add (dev);
+ node->AddDevice (dev);
+
+ NS_LOG_INFO ("**** Set up Controller");
+ dev->SetController (controller);
+
+ for (NetDeviceContainer::Iterator i = c.Begin (); i != c.End (); ++i)
+ {
+ NS_LOG_INFO ("**** Add SwitchPort " << *i);
+ dev->AddSwitchPort (*i);
+ }
+ return devs;
+}
+
+NetDeviceContainer
+OpenFlowSwitchHelper::Install (Ptr<Node> node, NetDeviceContainer c)
+{
+ NS_LOG_FUNCTION_NOARGS ();
+ NS_LOG_INFO ("**** Install switch device on node " << node->GetId ());
+
+ NetDeviceContainer devs;
+ Ptr<OpenFlowSwitchNetDevice> dev = m_deviceFactory.Create<OpenFlowSwitchNetDevice> ();
+ devs.Add (dev);
+ node->AddDevice (dev);
+
+ for (NetDeviceContainer::Iterator i = c.Begin (); i != c.End (); ++i)
+ {
+ NS_LOG_INFO ("**** Add SwitchPort " << *i);
+ dev->AddSwitchPort (*i);
+ }
+ return devs;
+}
+
+NetDeviceContainer
+OpenFlowSwitchHelper::Install (std::string nodeName, NetDeviceContainer c)
+{
+ NS_LOG_FUNCTION_NOARGS ();
+ Ptr<Node> node = Names::Find<Node> (nodeName);
+ return Install (node, c);
+}
+
+} // namespace ns3
+
+#endif // NS3_OPENFLOW
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/openflow/helper/openflow-switch-helper.h Sat Mar 12 18:35:56 2011 +0000
@@ -0,0 +1,105 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2011 Blake Hurd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Blake Hurd <naimorai@gmail.com>
+ */
+#ifdef NS3_OPENFLOW
+
+#ifndef OPENFLOW_SWITCH_HELPER_H
+#define OPENFLOW_SWITCH_HELPER_H
+
+#include "ns3/openflow-interface.h"
+#include "ns3/net-device-container.h"
+#include "ns3/object-factory.h"
+#include <string>
+
+namespace ns3 {
+
+class Node;
+class AttributeValue;
+class Controller;
+
+/**
+ * \brief Add capability to switch multiple LAN segments (IEEE 802.1D bridging)
+ */
+class OpenFlowSwitchHelper
+{
+public:
+ /*
+ * Construct a OpenFlowSwitchHelper
+ */
+ OpenFlowSwitchHelper ();
+
+ /**
+ * Set an attribute on each ns3::OpenFlowSwitchNetDevice created by
+ * OpenFlowSwitchHelper::Install
+ *
+ * \param n1 the name of the attribute to set
+ * \param v1 the value of the attribute to set
+ */
+ void
+ SetDeviceAttribute (std::string n1, const AttributeValue &v1);
+
+ /**
+ * This method creates an ns3::OpenFlowSwitchNetDevice with the attributes
+ * configured by OpenFlowSwitchHelper::SetDeviceAttribute, adds the device
+ * to the node, attaches the given NetDevices as ports of the
+ * switch, and sets up a controller connection using the provided
+ * Controller.
+ *
+ * \param node The node to install the device in
+ * \param c Container of NetDevices to add as switch ports
+ * \param controller The controller connection.
+ * \returns A container holding the added net device.
+ */
+ NetDeviceContainer
+ Install (Ptr<Node> node, NetDeviceContainer c, Ptr<ns3::ofi::Controller> controller);
+
+ /**
+ * This method creates an ns3::OpenFlowSwitchNetDevice with the attributes
+ * configured by OpenFlowSwitchHelper::SetDeviceAttribute, adds the device
+ * to the node, and attaches the given NetDevices as ports of the
+ * switch.
+ *
+ * \param node The node to install the device in
+ * \param c Container of NetDevices to add as switch ports
+ * \returns A container holding the added net device.
+ */
+ NetDeviceContainer
+ Install (Ptr<Node> node, NetDeviceContainer c);
+
+ /**
+ * This method creates an ns3::OpenFlowSwitchNetDevice with the attributes
+ * configured by OpenFlowSwitchHelper::SetDeviceAttribute, adds the device
+ * to the node, and attaches the given NetDevices as ports of the
+ * switch.
+ *
+ * \param nodeName The name of the node to install the device in
+ * \param c Container of NetDevices to add as switch ports
+ * \returns A container holding the added net device.
+ */
+ NetDeviceContainer
+ Install (std::string nodeName, NetDeviceContainer c);
+
+private:
+ ObjectFactory m_deviceFactory;
+};
+
+} // namespace ns3
+
+#endif // NS3_OPENFLOW
+#endif /* OPENFLOW_SWITCH_HELPER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/openflow/model/openflow-interface.cc Sat Mar 12 18:35:56 2011 +0000
@@ -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 <naimorai@gmail.com>
+ */
+#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<size_t>::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<OpenFlowSwitchNetDevice> 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<OpenFlowSwitchNetDevice> 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<OpenFlowSwitchNetDevice> 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<OpenFlowSwitchNetDevice> 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<OpenFlowSwitchNetDevice> 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<OpenFlowSwitchNetDevice> 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<OpenFlowSwitchNetDevice> 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<OpenFlowSwitchNetDevice> 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<OpenFlowSwitchNetDevice> 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<LearningController> ()
+ .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<OpenFlowSwitchNetDevice> 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<OpenFlowSwitchNetDevice> swtch, uint64_t packet_uid, ofpbuf* buffer, sw_flow_key *key, const ofp_action_header *actions, size_t actions_len, int ignore_no_fwd)
+{
+ 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<OpenFlowSwitchNetDevice> 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/openflow/model/openflow-interface.h Sat Mar 12 18:35:56 2011 +0000
@@ -0,0 +1,567 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Blake Hurd <naimorai@gmail.com>
+ */
+#ifndef OPENFLOW_INTERFACE_H
+#define OPENFLOW_INTERFACE_H
+
+#ifdef NS3_OPENFLOW
+
+#include <assert.h>
+#include <errno.h>
+
+// Include OFSI code
+#include "ns3/simulator.h"
+#include "ns3/log.h"
+#include "ns3/net-device.h"
+#include "ns3/packet.h"
+#include "ns3/address.h"
+#include "ns3/nstime.h"
+#include "ns3/mac48-address.h"
+
+#include <set>
+#include <map>
+#include <limits>
+
+// Include main header and Vendor Extension files
+#include "openflow.h"
+#include "nicira-ext.h"
+#include "ericsson-ext.h"
+
+extern "C"
+{
+// Inexplicably, the OpenFlow implementation uses these two reserved words as member names.
+#define private _private
+#define delete _delete
+#define list List
+
+// Include OFSI Library files
+#include "csum.h"
+#include "poll-loop.h"
+#include "rconn.h"
+#include "stp.h"
+#include "vconn.h"
+#include "xtoxll.h"
+
+// Include OFSI Switch files
+#include "chain.h"
+#include "table.h"
+#include "datapath.h" // The functions below are defined in datapath.c
+uint32_t save_buffer (ofpbuf *);
+ofpbuf * retrieve_buffer (uint32_t id);
+void discard_buffer (uint32_t id);
+#include "dp_act.h" // The functions below are defined in dp_act.c
+void set_vlan_vid (ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah);
+void set_vlan_pcp (ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah);
+void strip_vlan (ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah);
+void set_dl_addr (ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah);
+void set_nw_addr (ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah);
+void set_tp_port (ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah);
+void set_mpls_label (ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah);
+void set_mpls_exp (ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah);
+#include "pt_act.h" // The function below is defined in pt_act.c
+void update_checksums (ofpbuf *buffer, const sw_flow_key *key, uint32_t old_word, uint32_t new_word);
+
+#undef list
+#undef private
+#undef delete
+}
+
+// Capabilities supported by this implementation.
+#define OFP_SUPPORTED_CAPABILITIES ( OFPC_FLOW_STATS \
+ | OFPC_TABLE_STATS \
+ | OFPC_PORT_STATS \
+ | OFPC_MULTI_PHY_TX \
+ | OFPC_VPORT_TABLE)
+
+// Actions supported by this implementation.
+#define OFP_SUPPORTED_ACTIONS ( (1 << OFPAT_OUTPUT) \
+ | (1 << OFPAT_SET_VLAN_VID) \
+ | (1 << OFPAT_SET_VLAN_PCP) \
+ | (1 << OFPAT_STRIP_VLAN) \
+ | (1 << OFPAT_SET_DL_SRC) \
+ | (1 << OFPAT_SET_DL_DST) \
+ | (1 << OFPAT_SET_NW_SRC) \
+ | (1 << OFPAT_SET_NW_DST) \
+ | (1 << OFPAT_SET_TP_SRC) \
+ | (1 << OFPAT_SET_TP_DST) \
+ | (1 << OFPAT_SET_MPLS_LABEL) \
+ | (1 << OFPAT_SET_MPLS_EXP) )
+
+#define OFP_SUPPORTED_VPORT_TABLE_ACTIONS ( (1 << OFPPAT_OUTPUT) \
+ | (1 << OFPPAT_POP_MPLS) \
+ | (1 << OFPPAT_PUSH_MPLS) \
+ | (1 << OFPPAT_SET_MPLS_LABEL) \
+ | (1 << OFPPAT_SET_MPLS_EXP) ) \
+
+namespace ns3 {
+
+class OpenFlowSwitchNetDevice;
+
+namespace ofi {
+
+/**
+ * \brief Port and its metadata.
+ *
+ * We need to store port metadata, because OpenFlow dictates that there
+ * exists a type of request where the Controller asks for data about a
+ * port, or multiple ports. Otherwise, we'd refer to it via Ptr<NetDevice>
+ * everywhere.
+ */
+struct Port
+{
+ Port () : config (0),
+ state (0),
+ netdev (0),
+ rx_packets (0),
+ tx_packets (0),
+ rx_bytes (0),
+ tx_bytes (0),
+ tx_dropped (0),
+ mpls_ttl0_dropped (0)
+ {
+ }
+
+ uint32_t config; ///< Some subset of OFPPC_* flags.
+ uint32_t state; ///< Some subset of OFPPS_* flags.
+ Ptr<NetDevice> netdev;
+ unsigned long long int rx_packets, tx_packets;
+ unsigned long long int rx_bytes, tx_bytes;
+ unsigned long long int tx_dropped;
+ unsigned long long int mpls_ttl0_dropped;
+};
+
+class Stats
+{
+ public:
+ Stats (ofp_stats_types _type, size_t body_len);
+
+ /**
+ * \brief Prepares to dump some kind of statistics on the connected OpenFlowSwitchNetDevice.
+ *
+ * \param body Body member of the struct ofp_stats_request.
+ * \param body_len Length of the body member.
+ * \param state State information.
+ * \return 0 if successful, otherwise a negative error code.
+ */
+ int DoInit (const void *body, int body_len, void **state);
+
+ /**
+ * \brief Appends statistics for OpenFlowSwitchNetDevice to 'buffer'.
+ *
+ * \param swtch The OpenFlowSwitchNetDevice this callback is associated with.
+ * \param state State information.
+ * \param buffer Buffer to append stats reply to.
+ * \return 1 if it should be called again later with another buffer, 0 if it is done, or a negative errno value on failure.
+ */
+ int DoDump (Ptr<OpenFlowSwitchNetDevice> swtch, void *state, ofpbuf *buffer);
+
+ /**
+ * \brief Cleans any state created by the init or dump functions.
+ *
+ * May not be implemented if no cleanup is required.
+ *
+ * \param state State information to clear.
+ */
+ void DoCleanup (void *state);
+
+ /**
+ * \brief State of the FlowStats request/reply.
+ */
+ struct FlowStatsState
+ {
+ int table_idx;
+ sw_table_position position;
+ ofp_flow_stats_request rq;
+ time_t now;
+
+ ofpbuf *buffer;
+ };
+
+ /**
+ * \brief State of the PortStats request/reply.
+ */
+ struct PortStatsState
+ {
+ uint32_t num_ports; ///< Number of ports in host byte order
+ uint32_t *ports; ///< Array of ports in network byte order
+ };
+
+ ofp_stats_types type;
+ private:
+ int DescStatsDump (void *state, ofpbuf *buffer);
+
+ int FlowStatsInit (const void *body, int body_len, void **state);
+ int (*FlowDumpCallback)(sw_flow *flow, void *state);
+ int FlowStatsDump (Ptr<OpenFlowSwitchNetDevice> dp, FlowStatsState *s, ofpbuf *buffer);
+
+ int AggregateStatsInit (const void *body, int body_len, void **state);
+ int (*AggregateDumpCallback)(sw_flow *flow, void *state);
+ int AggregateStatsDump (Ptr<OpenFlowSwitchNetDevice> dp, ofp_aggregate_stats_request *s, ofpbuf *buffer);
+
+ int TableStatsDump (Ptr<OpenFlowSwitchNetDevice> dp, void *state, ofpbuf *buffer);
+
+ int PortStatsInit (const void *body, int body_len, void **state);
+ int PortStatsDump (Ptr<OpenFlowSwitchNetDevice> dp, PortStatsState *s, ofpbuf *buffer);
+
+ int PortTableStatsDump (Ptr<OpenFlowSwitchNetDevice> dp, void *state, ofpbuf *buffer);
+};
+
+/**
+ * \brief Class for handling flow table actions.
+ */
+struct Action
+{
+ /**
+ * \param type Type of Flow Table Action.
+ * \return true if the provided type is a type of flow table action.
+ */
+ static bool IsValidType (ofp_action_type type);
+
+ /**
+ * \brief Validates the action on whether its data is valid or not.
+ *
+ * \param type Type of action to validate.
+ * \param len Length of the action data.
+ * \param key Matching key for the flow that is tied to this action.
+ * \param ah Action's data header.
+ * \return ACT_VALIDATION_OK if the action checks out, otherwise an error type.
+ */
+ static uint16_t Validate (ofp_action_type type, size_t len, const sw_flow_key *key, const ofp_action_header *ah);
+
+ /**
+ * \brief Executes the action.
+ *
+ * \param type Type of action to execute.
+ * \param buffer Buffer of the Packet if it's needed for the action.
+ * \param key Matching key for the flow that is tied to this action.
+ * \param ah Action's data header.
+ */
+ static void Execute (ofp_action_type type, ofpbuf *buffer, sw_flow_key *key, const ofp_action_header *ah);
+};
+
+/**
+ * \brief Class for handling virtual port table actions.
+ */
+struct VPortAction
+{
+ /**
+ * \param type Type of virtual port table Action.
+ * \return true if the provided type is a type of virtual port table action.
+ */
+ static bool IsValidType (ofp_vport_action_type type);
+
+ /**
+ * \brief Validates the action on whether its data is valid or not.
+ *
+ * \param type Type of action to validate.
+ * \param len Length of the action data.
+ * \param ah Action's data header.
+ * \return ACT_VALIDATION_OK if the action checks out, otherwise an error type.
+ */
+ static uint16_t Validate (ofp_vport_action_type type, size_t len, const ofp_action_header *ah);
+
+ /**
+ * \brief Executes the action.
+ *
+ * \param type Type of action to execute.
+ * \param buffer Buffer of the Packet if it's needed for the action.
+ * \param key Matching key for the flow that is tied to this action.
+ * \param ah Action's data header.
+ */
+ static void Execute (ofp_vport_action_type type, ofpbuf *buffer, const sw_flow_key *key, const ofp_action_header *ah);
+};
+
+/**
+ * \brief Class for handling Ericsson Vendor-defined actions.
+ */
+struct EricssonAction
+{
+ /**
+ * \param type Type of Ericsson Vendor-defined Action.
+ * \return true if the provided type is a type of Ericsson Vendor-defined action.
+ */
+ static bool IsValidType (er_action_type type);
+
+ /**
+ * \brief Validates the action on whether its data is valid or not.
+ *
+ * \param type Type of action to validate.
+ * \param len Length of the action data.
+ * \return ACT_VALIDATION_OK if the action checks out, otherwise an error type.
+ */
+ static uint16_t Validate (er_action_type type, size_t len);
+
+ /**
+ * \brief Executes the action.
+ *
+ * \param type Type of action to execute.
+ * \param buffer Buffer of the Packet if it's needed for the action.
+ * \param key Matching key for the flow that is tied to this action.
+ * \param ah Action's data header.
+ */
+ static void Execute (er_action_type type, ofpbuf *buffer, const sw_flow_key *key, const er_action_header *ah);
+};
+
+/**
+ * \brief Callback for a stats dump request.
+ */
+struct StatsDumpCallback
+{
+ bool done; ///< Whether we are done requesting stats.
+ ofp_stats_request *rq; ///< Current stats request.
+ Stats *s; ///< Handler of the stats request.
+ void *state; ///< Stats request state data.
+ Ptr<OpenFlowSwitchNetDevice> swtch; ///< The switch that we're requesting data from.
+};
+
+/**
+ * \brief Packet Metadata, allows us to track the packet's metadata as it passes through the switch.
+ */
+struct SwitchPacketMetadata
+{
+ Ptr<Packet> packet; ///< The Packet itself.
+ ofpbuf* buffer; ///< The OpenFlow buffer as created from the Packet, with its data and headers.
+ uint16_t protocolNumber; ///< Protocol type of the Packet when the Packet is received
+ Address src; ///< Source Address of the Packet when the Packet is received
+ Address dst; ///< Destination Address of the Packet when the Packet is received.
+};
+
+/**
+ * \brief An interface for a Controller of OpenFlowSwitchNetDevices
+ *
+ * Follows the OpenFlow specification for a controller.
+ */
+class Controller : public Object
+{
+public:
+ static TypeId GetTypeId (void)
+ {
+ static TypeId tid = TypeId ("ns3::ofi::Controller")
+ .SetParent<Object> ()
+ .AddConstructor<Controller> ()
+ ;
+ return tid;
+ }
+
+ virtual ~Controller ()
+ {
+ m_switches.clear ();
+ }
+
+ /**
+ * Adds a switch to the controller.
+ *
+ * \param swtch The switch to register.
+ */
+ virtual void AddSwitch (Ptr<OpenFlowSwitchNetDevice> swtch);
+
+ /**
+ * A switch calls this method to pass a message on to the Controller.
+ *
+ * \param swtch The switch the message was received from.
+ * \param buffer The message.
+ */
+ virtual void ReceiveFromSwitch (Ptr<OpenFlowSwitchNetDevice> swtch, ofpbuf* buffer)
+ {
+ }
+
+ /**
+ * \brief Starts a callback-based, reliable, possibly multi-message reply to a request made by the controller.
+ *
+ * If an incoming request needs to have a reliable reply that might
+ * require multiple messages, it can use StartDump() to set up
+ * a callback that will be called as buffer space for replies.
+ *
+ * A stats request made by the controller is processed by the switch,
+ * the switch then calls this method to tell the controller to start
+ * asking for information. By default (it can be overridden), the
+ * controller stops all work to run through the callback. ReceiveFromSwitch
+ * must be defined appropriately to handle the status reply messages
+ * generated by the switch, or otherwise the status reply messages will be sent
+ * and discarded.
+ *
+ * \param cb The callback data.
+ */
+ void StartDump (StatsDumpCallback* cb);
+
+protected:
+ /**
+ * \internal
+ *
+ * However the controller is implemented, this method is to
+ * be used to pass a message on to a switch.
+ *
+ * \param swtch The switch to receive the message.
+ * \param msg The message to send.
+ * \param length The length of the message.
+ */
+ virtual void SendToSwitch (Ptr<OpenFlowSwitchNetDevice> swtch, void * msg, size_t length);
+
+ /**
+ * \internal
+ *
+ * Construct flow data from a matching key to build a flow
+ * entry for adding, modifying, or deleting a flow.
+ *
+ * \param key The matching key data; used to create a flow that matches the packet.
+ * \param buffer_id The OpenFlow Buffer ID; used to run the actions on the packet if we add or modify the flow.
+ * \param command Whether to add, modify, or delete this flow.
+ * \param acts List of actions to execute.
+ * \param actions_len Length of the actions buffer.
+ * \param idle_timeout Flow expires if left inactive for this amount of time (specify OFP_FLOW_PERMANENT to disable feature).
+ * \param hard_timeout Flow expires after this amount of time (specify OFP_FLOW_PERMANENT to disable feature).
+ * \return Flow data that when passed to SetFlow will add, modify, or delete a flow it defines.
+ */
+ ofp_flow_mod* BuildFlow (sw_flow_key key, uint32_t buffer_id, uint16_t command, void* acts, size_t actions_len, int idle_timeout, int hard_timeout);
+
+ /**
+ * \internal
+ *
+ * Get the packet type on the buffer, which can then be used
+ * to determine how to handle the buffer.
+ *
+ * \param buffer The packet in OpenFlow buffer format.
+ * \return The packet type, as defined in the ofp_type struct.
+ */
+ uint8_t GetPacketType (ofpbuf* buffer);
+
+ typedef std::set<Ptr<OpenFlowSwitchNetDevice> > Switches_t;
+ Switches_t m_switches; ///< The collection of switches registered to this controller.
+};
+
+/**
+ * Demonstration of a Drop controller. When a connected switch
+ * passes it a packet the switch doesn't recognize, the controller
+ * configures the switch to make a flow that drops alike packets.
+ */
+class DropController : public Controller
+{
+public:
+ void ReceiveFromSwitch (Ptr<OpenFlowSwitchNetDevice> swtch, ofpbuf* buffer);
+};
+
+/**
+ * Demonstration of a Learning controller. When a connected switch
+ * passes it a packet the switch doesn't recognize, the controller
+ * delves into its learned states and figures out if we know what
+ * port the packet is supposed to go to, flooding if unknown, and
+ * adjusts the switch's flow table accordingly.
+ */
+class LearningController : public Controller
+{
+public:
+ static TypeId GetTypeId (void);
+
+ virtual ~LearningController ()
+ {
+ m_learnState.clear ();
+ }
+
+ void ReceiveFromSwitch (Ptr<OpenFlowSwitchNetDevice> swtch, ofpbuf* buffer);
+
+protected:
+ struct LearnedState
+ {
+ uint32_t port; ///< Learned port.
+ };
+ Time m_expirationTime; ///< Time it takes for learned MAC state entry/created flow to expire.
+ typedef std::map<Mac48Address, LearnedState> LearnState_t;
+ LearnState_t m_learnState; ///< Learned state data.
+};
+
+/**
+ * \brief Executes a list of flow table actions.
+ *
+ * \param swtch OpenFlowSwitchNetDevice these actions are being executed on.
+ * \param packet_uid Packet UID; used to fetch the packet and its metadata.
+ * \param buffer The Packet OpenFlow buffer.
+ * \param key The matching key for the flow tied to this list of actions.
+ * \param actions A buffer of actions.
+ * \param actions_len Length of actions buffer.
+ * \param ignore_no_fwd If true, during port forwarding actions, ports that are set to not forward are forced to forward.
+ */
+void ExecuteActions (Ptr<OpenFlowSwitchNetDevice> swtch, uint64_t packet_uid, ofpbuf* buffer, sw_flow_key *key, const ofp_action_header *actions, size_t actions_len, int ignore_no_fwd);
+
+/**
+ * \brief Validates a list of flow table actions.
+ *
+ * \param key The matching key for the flow tied to this list of actions.
+ * \param actions A buffer of actions.
+ * \param actions_len Length of actions buffer.
+ * \return If the action list validates, ACT_VALIDATION_OK is returned. Otherwise, a code for the OFPET_BAD_ACTION error type is returned.
+ */
+uint16_t ValidateActions (const sw_flow_key *key, const ofp_action_header *actions, size_t actions_len);
+
+/**
+ * \brief Executes a list of virtual port table entry actions.
+ *
+ * \param swtch OpenFlowSwitchNetDevice these actions are being executed on.
+ * \param packet_uid Packet UID; used to fetch the packet and its metadata.
+ * \param buffer The Packet OpenFlow buffer.
+ * \param key The matching key for the flow tied to this list of actions.
+ * \param actions A buffer of actions.
+ * \param actions_len Length of actions buffer.
+ */
+void ExecuteVPortActions (Ptr<OpenFlowSwitchNetDevice> swtch, uint64_t packet_uid, ofpbuf* buffer, sw_flow_key *key, const ofp_action_header *actions, size_t actions_len);
+
+/**
+ * \brief Validates a list of virtual port table entry actions.
+ *
+ * \param actions A buffer of actions.
+ * \param actions_len Length of actions buffer.
+ * \return If the action list validates, ACT_VALIDATION_OK is returned. Otherwise, a code for the OFPET_BAD_ACTION error type is returned.
+ */
+uint16_t ValidateVPortActions (const ofp_action_header *actions, size_t actions_len);
+
+/**
+ * \brief Executes a vendor-defined action.
+ *
+ * \param buffer The Packet OpenFlow buffer.
+ * \param key The matching key for the flow tied to this list of actions.
+ * \param ah Header of the action.
+ */
+void ExecuteVendor (ofpbuf *buffer, const sw_flow_key *key, const ofp_action_header *ah);
+
+/**
+ * \brief Validates a vendor-defined action.
+ *
+ * \param key The matching key for the flow tied to this list of actions.
+ * \param ah Header of the action.
+ * \param len Length of the action.
+ * \return If the action list validates, ACT_VALIDATION_OK is returned. Otherwise, a code for the OFPET_BAD_ACTION error type is returned.
+ */
+uint16_t ValidateVendor (const sw_flow_key *key, const ofp_action_header *ah, uint16_t len);
+
+/*
+ * From datapath.c
+ * Buffers are identified to userspace by a 31-bit opaque ID. We divide the ID
+ * into a buffer number (low bits) and a cookie (high bits). The buffer number
+ * is an index into an array of buffers. The cookie distinguishes between
+ * different packets that have occupied a single buffer. Thus, the more
+ * buffers we have, the lower-quality the cookie...
+ */
+#define PKT_BUFFER_BITS 8
+#define N_PKT_BUFFERS (1 << PKT_BUFFER_BITS)
+#define PKT_BUFFER_MASK (N_PKT_BUFFERS - 1)
+#define PKT_COOKIE_BITS (32 - PKT_BUFFER_BITS)
+
+}
+
+}
+
+#endif // NS3_OPENFLOW
+#endif /* OPENFLOW_INTERFACE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/openflow/model/openflow-switch-net-device.cc Sat Mar 12 18:35:56 2011 +0000
@@ -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 <naimorai@gmail.com>
+ */
+#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<NetDevice> ()
+ .AddConstructor<OpenFlowSwitchNetDevice> ()
+ .AddAttribute ("ID",
+ "The identification of the OpenFlowSwitchNetDevice/Datapath, needed for OpenFlow compatibility.",
+ UintegerValue (GenerateId ()),
+ MakeUintegerAccessor (&OpenFlowSwitchNetDevice::m_id),
+ MakeUintegerChecker<uint64_t> ())
+ .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<uint16_t> ())
+ .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<uint16_t> ())
+ ;
+ return tid;
+}
+
+OpenFlowSwitchNetDevice::OpenFlowSwitchNetDevice ()
+ : m_node (0),
+ m_ifIndex (0),
+ m_mtu (0xffff)
+{
+ NS_LOG_FUNCTION_NOARGS ();
+
+ m_channel = CreateObject<BridgeChannel> ();
+
+ 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<ofi::Controller> c)
+{
+ if (m_controller != 0)
+ {
+ NS_LOG_ERROR ("Controller already set.");
+ return;
+ }
+
+ m_controller = c;
+ m_controller->AddSwitch (this);
+}
+
+int
+OpenFlowSwitchNetDevice::AddSwitchPort (Ptr<NetDevice> 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<Channel>
+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<void> 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> packet, const Address& dest, uint16_t protocolNumber)
+{
+ NS_LOG_FUNCTION_NOARGS ();
+ return SendFrom (packet, m_address, dest, protocolNumber);
+}
+
+bool
+OpenFlowSwitchNetDevice::SendFrom (Ptr<Packet> 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<Node>
+OpenFlowSwitchNetDevice::GetNode (void) const
+{
+ NS_LOG_FUNCTION_NOARGS ();
+ return m_node;
+}
+
+void
+OpenFlowSwitchNetDevice::SetNode (Ptr<Node> 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> 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<NetDevice> netdev, Ptr<const Packet> 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<uint32_t>::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<uint32_t>::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<uint32_t>::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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/openflow/model/openflow-switch-net-device.h Sat Mar 12 18:35:56 2011 +0000
@@ -0,0 +1,550 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Blake Hurd <naimorai@gmail.com>
+ */
+#ifdef NS3_OPENFLOW
+
+#ifndef OPENFLOW_SWITCH_NET_DEVICE_H
+#define OPENFLOW_SWITCH_NET_DEVICE_H
+
+#include "ns3/simulator.h"
+#include "ns3/log.h"
+#include "ns3/mac48-address.h"
+
+#include "ns3/ethernet-header.h"
+#include "ns3/arp-header.h"
+#include "ns3/tcp-header.h"
+#include "ns3/udp-header.h"
+
+#include "ns3/ipv4-l3-protocol.h"
+#include "ns3/arp-l3-protocol.h"
+
+#include "ns3/bridge-channel.h"
+#include "ns3/node.h"
+#include "ns3/enum.h"
+#include "ns3/string.h"
+#include "ns3/integer.h"
+#include "ns3/uinteger.h"
+
+#include <map>
+#include <set>
+
+#include "openflow-interface.h"
+
+namespace ns3 {
+
+/**
+ * \ingroup devices
+ * \defgroup openflow OpenFlow
+ *
+ * \brief A net device that switches multiple LAN segments via an OpenFlow-compatible flow table
+ *
+ * The OpenFlowSwitchNetDevice object aggregates multiple netdevices as ports
+ * and acts like a switch. It implements OpenFlow-compatibility,
+ * according to the OpenFlow Switch Specification v0.8.9
+ * <www.openflowswitch.org/documents/openflow-spec-v0.8.9.pdf>.
+ * It implements a flow table that all received packets are run through.
+ * It implements a connection to a controller via a subclass of the Controller class,
+ * which can send messages to manipulate the flow table, thereby manipulating
+ * how the OpenFlow switch behaves.
+ *
+ * There are two controllers available in the original package. DropController
+ * builds a flow for each received packet to drop all packets it matches (this
+ * demonstrates the flow table's basic implementation), and the LearningController
+ * implements a "learning switch" algorithm (see 802.1D), where incoming unicast
+ * frames from one port may occasionally be forwarded throughout all other ports,
+ * but usually they are forwarded only to a single correct output port.
+ *
+ * \attention The Spanning Tree Protocol part of 802.1D is not
+ * implemented. Therefore, you have to be careful not to create
+ * bridging loops, or else the network will collapse.
+ *
+ * \attention Each NetDevice used must only be assigned a Mac Address, adding it
+ * to an Ipv4 or Ipv6 layer will cause an error. It also must support a SendFrom
+ * call.
+ */
+
+/**
+ * \ingroup switch
+ * \brief A net device that switches multiple LAN segments via an OpenFlow-compatible flow table
+ */
+class OpenFlowSwitchNetDevice : public NetDevice
+{
+public:
+ static TypeId GetTypeId (void);
+
+ /**
+ * \name OpenFlowSwitchNetDevice Description Data
+ * \brief These four data describe the OpenFlowSwitchNetDevice as if it were a real OpenFlow switch.
+ *
+ * There is a type of stats request that OpenFlow switches are supposed
+ * to handle that returns the description of the OpenFlow switch. Currently
+ * manufactured by "The ns-3 team", software description is "Simulated
+ * OpenFlow Switch", and the other two are "N/A".
+ */
+ //\{
+ static const char * GetManufacturerDescription ();
+ static const char * GetHardwareDescription ();
+ static const char * GetSoftwareDescription ();
+ static const char * GetSerialNumber ();
+ //\}
+
+ OpenFlowSwitchNetDevice ();
+ virtual ~OpenFlowSwitchNetDevice ();
+
+ /**
+ * \brief Set up the Switch's controller connection.
+ *
+ * \param c Pointer to a Controller.
+ */
+ void SetController (Ptr<ofi::Controller> c);
+
+ /**
+ * \brief Add a 'port' to a switch device
+ *
+ * This method adds a new switch port to a OpenFlowSwitchNetDevice, so that
+ * the new switch port NetDevice becomes part of the switch and L2
+ * frames start being forwarded to/from this NetDevice.
+ *
+ * \attention The netdevice that is being added as switch port must
+ * _not_ have an IP address. In order to add IP connectivity to a
+ * bridging node you must enable IP on the OpenFlowSwitchNetDevice itself,
+ * never on its port netdevices.
+ *
+ * \param switchPort The port to add.
+ * \return 0 if everything's ok, otherwise an error number.
+ * \sa #EXFULL
+ */
+ int AddSwitchPort (Ptr<NetDevice> switchPort);
+
+ /**
+ * \brief Add a virtual port to a switch device
+ *
+ * The Ericsson OFSID has the concept of virtual ports and virtual
+ * port tables. These are implemented in the OpenFlowSwitchNetDevice, but
+ * don't have an understood use [perhaps it may have to do with
+ * MPLS integration].
+ *
+ * \sa #EINVAL
+ *
+ * \param ovpm The data for adding a virtual port.
+ * \return 0 if everything's ok, otherwise an error number.
+ */
+ int AddVPort (const ofp_vport_mod *ovpm);
+
+ /**
+ * \brief Stats callback is ready for a dump.
+ *
+ * Controllers have a callback system for status requests which calls this function.
+ *
+ * \param cb_ The callback data.
+ * \return 0 if everything's ok, otherwise an error number.
+ */
+ int StatsDump (ofi::StatsDumpCallback *cb_);
+
+ /**
+ * \brief Stats callback is done.
+ *
+ * Controllers have a callback system for status requests which calls this function.
+ *
+ * \param cb_ The callback data.
+ */
+ void StatsDone (ofi::StatsDumpCallback *cb_);
+
+ /**
+ * \brief Called from the OpenFlow Interface to output the Packet on either a Port or the Controller
+ *
+ * \param packet_uid Packet UID; used to fetch the packet and its metadata.
+ * \param in_port The index of the port the Packet was initially received on.
+ * \param max_len The maximum number of bytes the caller wants to be sent; a value of 0 indicates the entire packet should be sent. Used when outputting to controller.
+ * \param out_port The port we want to output on.
+ * \param ignore_no_fwd If true, Ports that are set to not forward are forced to forward.
+ */
+ void DoOutput (uint32_t packet_uid, int in_port, size_t max_len, int out_port, bool ignore_no_fwd);
+
+ /**
+ * \brief The registered controller calls this method when sending a message to the switch.
+ *
+ * \param msg The message received from the controller.
+ * \param length Length of the message.
+ * \return 0 if everything's ok, otherwise an error number.
+ */
+ int ForwardControlInput (const void *msg, size_t length);
+
+ /**
+ * \return The flow table chain.
+ */
+ sw_chain* GetChain ();
+
+ /**
+ * \return Number of switch ports attached to this switch.
+ */
+ uint32_t GetNSwitchPorts (void) const;
+
+ /**
+ * \param p The Port to get the index of.
+ * \return The index of the provided Port.
+ */
+ int GetSwitchPortIndex (ofi::Port p);
+
+ /**
+ * \param n index of the Port.
+ * \return The Port.
+ */
+ ofi::Port GetSwitchPort (uint32_t n) const;
+
+ /**
+ * \return The virtual port table.
+ */
+ vport_table_t GetVPortTable ();
+
+ ///\name From NetDevice
+ //\{
+ virtual void SetIfIndex (const uint32_t index);
+ virtual uint32_t GetIfIndex (void) const;
+ virtual Ptr<Channel> GetChannel (void) const;
+ virtual void SetAddress (Address address);
+ virtual Address GetAddress (void) const;
+ virtual bool SetMtu (const uint16_t mtu);
+ virtual uint16_t GetMtu (void) const;
+ virtual bool IsLinkUp (void) const;
+ virtual void AddLinkChangeCallback (Callback<void> callback);
+ virtual bool IsBroadcast (void) const;
+ virtual Address GetBroadcast (void) const;
+ virtual bool IsMulticast (void) const;
+ virtual Address GetMulticast (Ipv4Address multicastGroup) const;
+ virtual bool IsPointToPoint (void) const;
+ virtual bool IsBridge (void) const;
+ virtual bool Send (Ptr<Packet> packet, const Address& dest, uint16_t protocolNumber);
+ virtual bool SendFrom (Ptr<Packet> packet, const Address& source, const Address& dest, uint16_t protocolNumber);
+ virtual Ptr<Node> GetNode (void) const;
+ virtual void SetNode (Ptr<Node> node);
+ virtual bool NeedsArp (void) const;
+ virtual void SetReceiveCallback (NetDevice::ReceiveCallback cb);
+ virtual void SetPromiscReceiveCallback (NetDevice::PromiscReceiveCallback cb);
+ virtual bool SupportsSendFrom () const;
+ virtual Address GetMulticast (Ipv6Address addr) const;
+ //\}
+
+protected:
+ virtual void DoDispose (void);
+
+ /**
+ * \internal
+ *
+ * Called when a packet is received on one of the switch's ports.
+ *
+ * \param netdev The port the packet was received on.
+ * \param packet The Packet itself.
+ * \param protocol The protocol defining the Packet.
+ * \param src The source address of the Packet.
+ * \param dst The destination address of the Packet.
+ * \param PacketType Type of the packet.
+ */
+ void ReceiveFromDevice (Ptr<NetDevice> netdev, Ptr<const Packet> packet, uint16_t protocol, const Address& src, const Address& dst, PacketType packetType);
+
+ /**
+ * \internal
+ *
+ * Takes a packet and generates an OpenFlow buffer from it, loading the packet data as well as its headers.
+ *
+ * \param packet The packet.
+ * \param src The source address.
+ * \param dst The destination address.
+ * \param mtu The Maximum Transmission Unit.
+ * \param protocol The protocol defining the packet.
+ * \return The OpenFlow Buffer created from the packet.
+ */
+ ofpbuf * BufferFromPacket (Ptr<Packet> packet, Address src, Address dst, int mtu, uint16_t protocol);
+
+private:
+ /**
+ * \internal
+ *
+ * Add a flow.
+ *
+ * \sa #ENOMEM, #ENOBUFS, #ESRCH
+ *
+ * \param ofm The flow data to add.
+ * \return 0 if everything's ok, otherwise an error number.
+ */
+ int AddFlow (const ofp_flow_mod *ofm);
+
+ /**
+ * \internal
+ *
+ * Modify a flow.
+ *
+ * \param ofm The flow data to modify.
+ * \return 0 if everything's ok, otherwise an error number.
+ */
+ int ModFlow (const ofp_flow_mod *ofm);
+
+ /**
+ * \internal
+ *
+ * Send packets out all the ports except the originating one
+ *
+ * \param packet_uid Packet UID; used to fetch the packet and its metadata.
+ * \param in_port The index of the port the Packet was initially received on. This port doesn't forward when flooding.
+ * \param flood If true, don't send out on the ports with flooding disabled.
+ * \return 0 if everything's ok, otherwise an error number.
+ */
+ int OutputAll (uint32_t packet_uid, int in_port, bool flood);
+
+ /**
+ * \internal
+ *
+ * Sends a copy of the Packet over the provided output port
+ *
+ * \param packet_uid Packet UID; used to fetch the packet and its metadata.
+ */
+ void OutputPacket (uint32_t packet_uid, int out_port);
+
+ /**
+ * \internal
+ *
+ * Seeks to send out a Packet over the provided output port. This is called generically
+ * when we may or may not know the specific port we're outputting on. There are many
+ * pre-set types of port options besides a Port that's hooked to our OpenFlowSwitchNetDevice.
+ * For example, it could be outputting as a flood, or seeking to output to the controller.
+ *
+ * \param packet_uid Packet UID; used to fetch the packet and its metadata.
+ * \param in_port The index of the port the Packet was initially received on.
+ * \param out_port The port we want to output on.
+ * \param ignore_no_fwd If true, Ports that are set to not forward are forced to forward.
+ */
+ void OutputPort (uint32_t packet_uid, int in_port, int out_port, bool ignore_no_fwd);
+
+ /**
+ * \internal
+ *
+ * Sends a copy of the Packet to the controller. If the packet can be saved
+ * in an OpenFlow buffer, then only the first 'max_len' bytes of the packet
+ * are sent; otherwise, all of the packet is sent.
+ *
+ * \param packet_uid Packet UID; used to fetch the packet and its metadata.
+ * \param in_port The index of the port the Packet was initially received on.
+ * \param max_len The maximum number of bytes that the caller wants to be sent; a value of 0 indicates the entire packet should be sent.
+ * \param reason Why the packet is being sent.
+ */
+ void OutputControl (uint32_t packet_uid, int in_port, size_t max_len, int reason);
+
+ /**
+ * \internal
+ *
+ * If an error message happened during the controller's request, send it to the controller.
+ *
+ * \param type The type of error.
+ * \param code The error code.
+ * \param data The faulty data that lead to the error.
+ * \param len The length of the faulty data.
+ */
+ void SendErrorMsg (uint16_t type, uint16_t code, const void *data, size_t len);
+
+ /**
+ * \internal
+ *
+ * Send a reply about this OpenFlow switch's features to the controller.
+ *
+ * List of capabilities and actions to support are found in the specification
+ * <www.openflowswitch.org/documents/openflow-spec-v0.8.9.pdf>.
+ *
+ * Supported capabilities and actions are defined in the openflow interface.
+ * To recap, flow status, flow table status, port status, virtual port table
+ * status can all be requested. It can also transmit over multiple physical
+ * interfaces.
+ *
+ * It supports every action: outputting over a port, and all of the flow table
+ * manipulation actions: setting the 802.1q VLAN ID, the 802.1q priority,
+ * stripping the 802.1 header, setting the Ethernet source address and destination,
+ * setting the IP source address and destination, setting the TCP/UDP source address
+ * and destination, and setting the MPLS label and EXP bits.
+ *
+ * \attention Capabilities STP (Spanning Tree Protocol) and IP packet
+ * reassembly are not currently supported.
+ *
+ */
+ void SendFeaturesReply ();
+
+ /**
+ * \internal
+ *
+ * Send a reply to the controller that a specific flow has expired.
+ *
+ * \param flow The flow that expired.
+ * \param reason The reason for sending this expiration notification.
+ */
+ void SendFlowExpired (sw_flow *flow, enum ofp_flow_expired_reason reason);
+
+ /**
+ * \internal
+ *
+ * Send a reply about a Port's status to the controller.
+ *
+ * \param p The port to get status from.
+ * \param status The reason for sending this reply.
+ */
+ void SendPortStatus (ofi::Port p, uint8_t status);
+
+ /**
+ * \internal
+ *
+ * Send a reply about this OpenFlow switch's virtual port table features to the controller.
+ */
+ void SendVPortTableFeatures ();
+
+ /**
+ * \internal
+ *
+ * Send a message to the controller. This method is the key
+ * to communicating with the controller, it does the actual
+ * sending. The other Send methods call this one when they
+ * are ready to send a message.
+ *
+ * \param buffer Buffer of the message to send out.
+ * \return 0 if successful, otherwise an error number.
+ */
+ int SendOpenflowBuffer (ofpbuf *buffer);
+
+ /**
+ * \internal
+ *
+ * Run the packet through the flow table. Looks up in the flow table for a match.
+ * If it doesn't match, it forwards the packet to the registered controller, if the flag is set.
+ *
+ * \param packet_uid Packet UID; used to fetch the packet and its metadata.
+ * \param port The port this packet was received over.
+ * \param send_to_controller If set, sends to the controller if the packet isn't matched.
+ */
+ void RunThroughFlowTable (uint32_t packet_uid, int port, bool send_to_controller = true);
+
+ /**
+ * \internal
+ *
+ * Run the packet through the vport table. As with AddVPort,
+ * this doesn't have an understood use yet.
+ *
+ * \param packet_uid Packet UID; used to fetch the packet and its metadata.
+ * \param port The port this packet was received over.
+ * \param vport The virtual port this packet identifies itself by.
+ * \return 0 if everything's ok, otherwise an error number.
+ */
+ int RunThroughVPortTable (uint32_t packet_uid, int port, uint32_t vport);
+
+ /**
+ * \internal
+ *
+ * Called by RunThroughFlowTable on a scheduled delay
+ * to account for the flow table lookup overhead.
+ *
+ * \param key Matching key to look up in the flow table.
+ * \param buffer Buffer of the packet received.
+ * \param packet_uid Packet UID; used to fetch the packet and its metadata.
+ * \param port The port the packet was received over.
+ * \param send_to_controller
+ */
+ void FlowTableLookup (sw_flow_key key, ofpbuf* buffer, uint32_t packet_uid, int port, bool send_to_controller);
+
+ /**
+ * \internal
+ *
+ * Update the port status field of the switch port.
+ * A non-zero return value indicates some field has changed.
+ *
+ * \param p A reference to a Port; used to change its config and flag fields.
+ * \return true if the status of the Port is changed, false if unchanged (was already the right status).
+ */
+ int UpdatePortStatus (ofi::Port& p);
+
+ /**
+ * \internal
+ *
+ * Fill out a description of the switch port.
+ *
+ * \param p The port to get the description from.
+ * \param desc A pointer to the description message; used to fill the description message with the data from the port.
+ */
+ void FillPortDesc (ofi::Port p, ofp_phy_port *desc);
+
+ /**
+ * \internal
+ *
+ * Generates an OpenFlow reply message based on the type.
+ *
+ * \param openflow_len Length of the reply to make.
+ * \param type Type of reply message to make.
+ * \param bufferp Message buffer; used to make the reply.
+ * \return The OpenFlow reply message.
+ */
+ void* MakeOpenflowReply (size_t openflow_len, uint8_t type, ofpbuf **bufferp);
+
+ /**
+ * \internal
+ * \name Receive Methods
+ *
+ * Actions to do when a specific OpenFlow message/packet is received
+ *
+ * \param msg The OpenFlow message received.
+ * \return 0 if everything's ok, otherwise an error number.
+ */
+ //\{
+ int ReceiveFeaturesRequest (const void *msg);
+ int ReceiveGetConfigRequest (const void *msg);
+ int ReceiveSetConfig (const void *msg);
+ int ReceivePacketOut (const void *msg);
+ int ReceiveFlow (const void *msg);
+ int ReceivePortMod (const void *msg);
+ int ReceiveStatsRequest (const void *oh);
+ int ReceiveEchoRequest (const void *oh);
+ int ReceiveEchoReply (const void *oh);
+ int ReceiveVPortMod (const void *msg);
+ int ReceiveVPortTableFeaturesRequest (const void *msg);
+ //\}
+
+ /// Callbacks
+ NetDevice::ReceiveCallback m_rxCallback;
+ NetDevice::PromiscReceiveCallback m_promiscRxCallback;
+
+ Mac48Address m_address; ///< Address of this device.
+ Ptr<Node> m_node; ///< Node this device is installed on.
+ Ptr<BridgeChannel> m_channel; ///< Collection of port channels into the Switch Channel.
+ uint32_t m_ifIndex; ///< Interface Index
+ uint16_t m_mtu; ///< Maximum Transmission Unit
+
+ typedef std::map<uint32_t,ofi::SwitchPacketMetadata> PacketData_t;
+ PacketData_t m_packetData; ///< Packet data
+
+ typedef std::vector<ofi::Port> Ports_t;
+ Ports_t m_ports; ///< Switch's ports
+
+ Ptr<ofi::Controller> m_controller; ///< Connection to controller.
+
+ uint64_t m_id; ///< Unique identifier for this switch, needed for OpenFlow
+ Time m_lookupDelay; ///< Flow Table Lookup Delay [overhead].
+
+ Time m_lastExecute; ///< Last time the periodic execution occurred.
+ uint16_t m_flags; ///< Flags; configurable by the controller.
+ uint16_t m_missSendLen; ///< Flow Table Miss Send Length; configurable by the controller.
+
+ sw_chain *m_chain; ///< Flow Table; forwarding rules.
+ vport_table_t m_vportTable; ///< Virtual Port Table
+};
+
+} // namespace ns3
+
+#endif /* OPENFLOW_SWITCH_NET_DEVICE_H */
+#endif // NS3_OPENFLOW
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/openflow/test/examples-to-run.py Sat Mar 12 18:35:56 2011 +0000
@@ -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 = []
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/openflow/test/openflow-switch-test-suite.cc Sat Mar 12 18:35:56 2011 +0000
@@ -0,0 +1,193 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2011 Blake Hurd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ *
+ * Author: Blake Hurd <naimorai@gmail.com>
+ */
+
+
+#ifdef NS3_OPENFLOW
+
+// An essential include is test.h
+#include "ns3/test.h"
+
+#include "ns3/openflow-switch-net-device.h"
+#include "ns3/openflow-interface.h"
+
+// Do not put your test classes in namespace ns3. You may find it useful
+// to use the using directive to access the ns3 namespace directly
+using namespace ns3;
+
+// This is an example TestCase.
+class SwitchFlowTableTestCase : public TestCase
+{
+public:
+ SwitchFlowTableTestCase () : TestCase ("Switch test case")
+ {
+ m_chain = chain_create ();
+ }
+
+ virtual ~SwitchFlowTableTestCase ()
+ {
+ chain_destroy (m_chain);
+ }
+
+private:
+ virtual void DoRun (void);
+
+ sw_chain* m_chain;
+};
+
+void
+SwitchFlowTableTestCase::DoRun (void)
+{
+ // Flow Table implementation is used by the OpenFlowSwitchNetDevice under the chain_ methods
+ // we should test its implementation to verify the flow table works.
+
+ // Initialization
+ time_init (); // OFSI requires this, otherwise we crash before we can do anything.
+
+ size_t actions_len = 0; // Flow is created with 0 actions.
+ int output_port = 0; // Flow will be modified later with an action to output on port 0.
+
+ Mac48Address dl_src ("00:00:00:00:00:00"), dl_dst ("00:00:00:00:00:01");
+ Ipv4Address nw_src ("192.168.1.1"), nw_dst ("192.168.1.2");
+ int tp_src = 5000, tp_dst = 80;
+
+ // Create an sw_flow_key; in actual usage this is generated from the received packet's headers.
+ sw_flow_key key;
+ key.wildcards = 0;
+
+ key.flow.in_port = htons (0);
+
+ key.flow.dl_vlan = htons (OFP_VLAN_NONE);
+ key.flow.dl_type = htons (ETH_TYPE_IP);
+ key.flow.nw_proto = htons (IP_TYPE_UDP);
+
+ key.flow.reserved = 0;
+ key.flow.mpls_label1 = htonl (MPLS_INVALID_LABEL);
+ key.flow.mpls_label2 = htonl (MPLS_INVALID_LABEL);
+
+ // Set Mac Addresses
+ dl_src.CopyTo (key.flow.dl_src);
+ dl_dst.CopyTo (key.flow.dl_dst);
+
+ // Set IP Addresses
+ key.flow.nw_src = htonl (nw_src.Get ());
+ key.flow.nw_dst = htonl (nw_dst.Get ());
+
+ // Set TCP/UDP Ports
+ key.flow.tp_src = htonl (tp_src);
+ key.flow.tp_dst = htonl (tp_dst);
+
+ // Create flow
+ ofp_flow_mod ofm;
+ ofm.header.version = OFP_VERSION;
+ ofm.header.type = OFPT_FLOW_MOD;
+ ofm.header.length = htons (sizeof (ofp_flow_mod) + actions_len);
+ ofm.command = htons (OFPFC_ADD);
+ ofm.idle_timeout = htons (OFP_FLOW_PERMANENT);
+ ofm.hard_timeout = htons (OFP_FLOW_PERMANENT);
+ ofm.buffer_id = htonl (-1);
+ ofm.priority = OFP_DEFAULT_PRIORITY;
+
+ ofm.match.wildcards = key.wildcards; // Wildcard fields
+ ofm.match.in_port = key.flow.in_port; // Input switch port
+ memcpy (ofm.match.dl_src, key.flow.dl_src, sizeof ofm.match.dl_src); // Ethernet source address.
+ memcpy (ofm.match.dl_dst, key.flow.dl_dst, sizeof ofm.match.dl_dst); // Ethernet destination address.
+ ofm.match.dl_vlan = key.flow.dl_vlan; // Input VLAN OFP_VLAN_NONE;
+ ofm.match.dl_type = key.flow.dl_type; // Ethernet frame type ETH_TYPE_IP;
+ ofm.match.nw_proto = key.flow.nw_proto; // IP Protocol
+ ofm.match.nw_src = key.flow.nw_src; // IP source address
+ ofm.match.nw_dst = key.flow.nw_dst; // IP destination address
+ ofm.match.tp_src = key.flow.tp_src; // TCP/UDP source port
+ ofm.match.tp_dst = key.flow.tp_dst; // TCP/UDP destination port
+ ofm.match.mpls_label1 = key.flow.mpls_label1; // Top of label stack
+ ofm.match.mpls_label2 = key.flow.mpls_label1; // Second label (if available)
+
+ // Build a sw_flow from the ofp_flow_mod
+ sw_flow *flow = flow_alloc (actions_len);
+ NS_TEST_ASSERT_MSG_NE (flow, 0, "Cannot allocate memory for the flow.");
+
+ flow_extract_match (&flow->key, &ofm.match);
+
+ // Fill out flow.
+ flow->priority = flow->key.wildcards ? ntohs (ofm.priority) : -1;
+ flow->idle_timeout = ntohs (ofm.idle_timeout);
+ flow->hard_timeout = ntohs (ofm.hard_timeout);
+ flow->used = flow->created = time_now ();
+ flow->sf_acts->actions_len = actions_len;
+ flow->byte_count = 0;
+ flow->packet_count = 0;
+ memcpy (flow->sf_acts->actions, ofm.actions, actions_len);
+
+ // Insert the flow into the Flow Table
+ NS_TEST_ASSERT_MSG_EQ (chain_insert (m_chain, flow), 0, "Flow table failed to insert Flow.");
+
+ // Use key to match the flow to verify we created it correctly.
+ NS_TEST_ASSERT_MSG_NE (chain_lookup (m_chain, &key), 0, "Key provided doesn't match to the flow that was created from it.");
+
+ // Modify key to make sure the flow doesn't match it.
+ dl_dst.CopyTo (key.flow.dl_src);
+ dl_src.CopyTo (key.flow.dl_dst);
+ key.flow.nw_src = htonl (nw_dst.Get ());
+ key.flow.nw_dst = htonl (nw_src.Get ());
+ key.flow.tp_src = htonl (tp_dst);
+ key.flow.tp_dst = htonl (tp_src);
+
+ NS_TEST_ASSERT_MSG_EQ (chain_lookup (m_chain, &key), 0, "Key provided shouldn't match the flow but it does.");
+
+ // Modify key back to matching the flow so we can test flow modification.
+ dl_dst.CopyTo (key.flow.dl_dst);
+ dl_src.CopyTo (key.flow.dl_src);
+ key.flow.nw_src = htonl (nw_src.Get ());
+ key.flow.nw_dst = htonl (nw_dst.Get ());
+ key.flow.tp_src = htonl (tp_src);
+ key.flow.tp_dst = htonl (tp_dst);
+
+ // Testing Flow Modification; chain_modify should return 1, for 1 flow modified.
+ // Create output-to-port action
+ ofp_action_output acts[1];
+ acts[0].type = htons (OFPAT_OUTPUT);
+ acts[0].len = htons (sizeof (ofp_action_output));
+ acts[0].port = output_port;
+
+ uint16_t priority = key.wildcards ? ntohs (ofm.priority) : -1;
+ NS_TEST_ASSERT_MSG_EQ (chain_modify (m_chain, &key, priority, false, (const ofp_action_header*)acts, sizeof (acts)), 1, "Flow table failed to modify Flow.");
+
+ // Testing Flow Deletion; chain_delete should return 1, for 1 flow deleted.
+ // Note: By providing chain_delete with output_port, the flow must have an action that outputs on that port in order to delete the flow.
+ // This is how we verify that our action was truly added via the flow modification.
+ NS_TEST_ASSERT_MSG_EQ (chain_delete (m_chain, &key, output_port, 0, 0), 1, "Flow table failed to delete Flow.");
+ NS_TEST_ASSERT_MSG_EQ (chain_lookup (m_chain, &key), 0, "Key provided shouldn't match the flow but it does.");
+}
+
+class SwitchTestSuite : public TestSuite
+{
+public:
+ SwitchTestSuite ();
+};
+
+SwitchTestSuite::SwitchTestSuite () : TestSuite ("openflow", UNIT)
+{
+ AddTestCase (new SwitchFlowTableTestCase);
+}
+
+// Do not forget to allocate an instance of this TestSuite
+SwitchTestSuite switchTestSuite;
+
+#endif // NS3_OPENFLOW
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/openflow/waf Sat Mar 12 18:35:56 2011 +0000
@@ -0,0 +1,1 @@
+exec "`dirname "$0"`"/../../../waf "$@"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/openflow/wscript Sat Mar 12 18:35:56 2011 +0000
@@ -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')
--- a/src/wimax/examples/wimax-simple.cc Sat Mar 12 18:34:30 2011 +0000
+++ b/src/wimax/examples/wimax-simple.cc Sat Mar 12 18:35:56 2011 +0000
@@ -54,7 +54,7 @@
#include "ns3/service-flow.h"
#include <iostream>
-NS_LOG_COMPONENT_DEFINE ("wimaxIpV4Simulation");
+NS_LOG_COMPONENT_DEFINE ("WimaxSimpleExample");
using namespace ns3;
--- a/src/wscript Sat Mar 12 18:34:30 2011 +0000
+++ b/src/wscript Sat Mar 12 18:35:56 2011 +0000
@@ -35,6 +35,7 @@
'aodv',
'dsdv',
'click',
+ 'openflow',
'mobility',
'wifi',
'netanim',
@@ -59,6 +60,7 @@
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 @@
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)
--- a/test.py Sat Mar 12 18:34:30 2011 +0000
+++ b/test.py Sat Mar 12 18:35:56 2011 +0000
@@ -51,12 +51,14 @@
"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 = []
#