555 lines
20 KiB
C++
555 lines
20 KiB
C++
/* -*- 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>
|
|
*/
|
|
|
|
/**
|
|
* \defgroup openflow OpenFlow Switch Device
|
|
* This section documents the API of the ns-3 OpenFlow module. For a generic functional description, please refer to the ns-3 manual.
|
|
*/
|
|
|
|
#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 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 openflow
|
|
* \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
|