Index: src/openflow/model/openflow-switch-net-device.cc |
=================================================================== |
new file mode 100644 |
--- /dev/null |
+++ b/src/openflow/model/openflow-switch-net-device.cc |
@@ -0,0 +1,1584 @@ |
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ |
+/* |
+ * This program is free software; you can redistribute it and/or modify |
+ * it under the terms of the GNU General Public License version 2 as |
+ * published by the Free Software Foundation; |
+ * |
+ * This program is distributed in the hope that it will be useful, |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
+ * GNU General Public License for more details. |
+ * |
+ * You should have received a copy of the GNU General Public License |
+ * along with this program; if not, write to the Free Software |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
+ * |
+ * Author: Blake Hurd <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 |