A Discrete-Event Network Simulator
API
tunnel-controller.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2016 University of Campinas (Unicamp)
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation;
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  *
17  * Author: Luciano Jerez Chaves <ljerezchaves@gmail.com>
18  */
19 
20 #include "tunnel-controller.h"
21 
22 #include <ns3/epc-gtpu-header.h>
23 
24 namespace ns3
25 {
26 
27 NS_LOG_COMPONENT_DEFINE("TunnelController");
28 NS_OBJECT_ENSURE_REGISTERED(TunnelController);
29 
31 {
32  NS_LOG_FUNCTION(this);
33 }
34 
36 {
37  NS_LOG_FUNCTION(this);
38 }
39 
40 TypeId
42 {
43  static TypeId tid = TypeId("ns3::TunnelController")
45  .SetGroupName("OFSwitch13")
46  .AddConstructor<TunnelController>();
47  return tid;
48 }
49 
50 void
52 {
53  NS_LOG_FUNCTION(this << ipAddr << macAddr);
54 
55  std::pair<Ipv4Address, Mac48Address> entry(ipAddr, macAddr);
56  std::pair<IpMacMap_t::iterator, bool> ret;
57  ret = m_arpTable.insert(entry);
58  if (ret.second == true)
59  {
60  NS_LOG_INFO("New ARP entry: " << ipAddr << " - " << macAddr);
61  return;
62  }
63  NS_ABORT_MSG("This IP already exists in ARP table.");
64 }
65 
66 void
67 TunnelController::SaveTunnelEndpoint(uint64_t dpId, uint32_t portNo, Ipv4Address ipAddr)
68 {
69  NS_LOG_FUNCTION(this << dpId << portNo << ipAddr);
70 
71  DpPortPair_t key(dpId, portNo);
72  std::pair<DpPortPair_t, Ipv4Address> entry(key, ipAddr);
73  std::pair<DpPortIpMap_t::iterator, bool> ret;
74  ret = m_endpointTable.insert(entry);
75  if (ret.second == true)
76  {
77  NS_LOG_INFO("New endpoint entry: " << dpId << "/" << portNo << " - " << ipAddr);
78  return;
79  }
80  NS_ABORT_MSG("This endpoint already exists in tunnel endpoint table.");
81 }
82 
83 void
85 {
86  NS_LOG_FUNCTION(this);
87 
88  m_arpTable.clear();
89  m_endpointTable.clear();
91 }
92 
93 void
95 {
96  NS_LOG_FUNCTION(this << swtch);
97 
98  // Get the switch datapath ID
99  uint64_t swDpId = swtch->GetDpId();
100 
101  // Send ARP requests to controller.
102  DpctlExecute(swDpId,
103  "flow-mod cmd=add,table=0,prio=16 eth_type=0x0806 "
104  "apply:output=ctrl");
105 
106  // Table miss entry.
107  DpctlExecute(swDpId, "flow-mod cmd=add,table=0,prio=0 apply:output=ctrl");
108 }
109 
110 ofl_err
111 TunnelController::HandlePacketIn(struct ofl_msg_packet_in* msg,
113  uint32_t xid)
114 {
115  NS_LOG_FUNCTION(this << swtch << xid);
116 
117  // Get the switch datapath ID
118  uint64_t swDpId = swtch->GetDpId();
119 
120  struct ofl_match_tlv* tlv;
121  enum ofp_packet_in_reason reason = msg->reason;
122  if (reason == OFPR_NO_MATCH)
123  {
124  char* msgStr = ofl_structs_match_to_string(msg->match, nullptr);
125  NS_LOG_INFO("Packet in match: " << msgStr);
126  free(msgStr);
127 
128  // Get input port
129  uint32_t inPort;
130  tlv = oxm_match_lookup(OXM_OF_IN_PORT, (struct ofl_match*)msg->match);
131  memcpy(&inPort, tlv->value, OXM_LENGTH(OXM_OF_IN_PORT));
132 
133  if (inPort == 1)
134  {
135  // IP packets entering the switch from the physical port 1 are coming
136  // from the host node. In this case, identify and set TEID and tunnel
137  // endpoint IPv4 address into tunnel metadata, and output the packet
138  // on the logical port 2.
139  Ipv4Address dstAddr = GetTunnelEndpoint(swDpId, 2);
140  uint64_t tunnelId = static_cast<uint64_t>(dstAddr.Get()) << 32;
141  tunnelId |= 0xFFFF;
142  char tunnelIdStr[20];
143  sprintf(tunnelIdStr, "0x%016lX", tunnelId);
144 
145  std::ostringstream cmd;
146  cmd << "flow-mod cmd=add,table=0,prio=11,buffer=" << msg->buffer_id
147  << " in_port=1,eth_type=0x0800 "
148  << "write:set_field=tunn_id:" << tunnelIdStr << ",output=2";
149  DpctlExecute(swDpId, cmd.str());
150 
151  // All handlers must free the message when everything is ok
152  ofl_msg_free((struct ofl_msg_header*)msg, nullptr);
153  return 0;
154  }
155  else if (inPort == 2)
156  {
157  // IP packets entering the switch from the logical port have already
158  // been de-encapsulated by the logical port operation, and the tunnel
159  // id must match the arbitrary value 0xFFFF defined set above. Theses
160  // packets must be forwarded to the host on the physical port 1. In
161  // this case, the OpenFlow switch is acting as a router, and we need
162  // to set the host destination MAC addresses. Note that the packet
163  // leaving the OpenFlow pipeline will not be sent to the IP layer, so
164  // no ARP resolution is available and we need to do it manually here.
165  Ipv4Address dstIp = ExtractIpv4Address(OXM_OF_IPV4_DST, (struct ofl_match*)msg->match);
166  Mac48Address dstMac = GetArpEntry(dstIp);
167 
168  std::ostringstream cmd;
169  cmd << "flow-mod cmd=add,table=0,prio=10,buffer=" << msg->buffer_id
170  << " in_port=2,eth_type=0x0800,tunn_id=0xFFFF "
171  << "write:set_field=eth_dst:" << dstMac << ",output=1";
172  DpctlExecute(swDpId, cmd.str());
173 
174  // All handlers must free the message when everything is ok
175  ofl_msg_free((struct ofl_msg_header*)msg, nullptr);
176  return 0;
177  }
178 
179  NS_LOG_ERROR("This packet in was not supposed to be sent here.");
180  }
181  else if (reason == OFPR_ACTION)
182  {
183  // Get Ethernet frame type
184  uint16_t ethType;
185  tlv = oxm_match_lookup(OXM_OF_ETH_TYPE, (struct ofl_match*)msg->match);
186  memcpy(&ethType, tlv->value, OXM_LENGTH(OXM_OF_ETH_TYPE));
187 
188  // Check for ARP packet
189  if (ethType == ArpL3Protocol::PROT_NUMBER)
190  {
191  return HandleArpPacketIn(msg, swtch, xid);
192  }
193  }
194 
195  NS_LOG_WARN("Ignoring packet sent to controller.");
196 
197  // All handlers must free the message when everything is ok
198  ofl_msg_free((struct ofl_msg_header*)msg, nullptr);
199  return 0;
200 }
201 
204 {
205  NS_LOG_FUNCTION(this << ip);
206 
207  IpMacMap_t::iterator ret;
208  ret = m_arpTable.find(ip);
209  if (ret != m_arpTable.end())
210  {
211  NS_LOG_INFO("Found ARP entry: " << ip << " - " << ret->second);
212  return ret->second;
213  }
214  NS_ABORT_MSG("No ARP information for this IP.");
215 }
216 
218 TunnelController::GetTunnelEndpoint(uint64_t dpId, uint32_t portNo)
219 {
220  NS_LOG_FUNCTION(this << dpId << portNo);
221 
222  DpPortPair_t key(dpId, portNo);
223  DpPortIpMap_t::iterator ret;
224  ret = m_endpointTable.find(key);
225  if (ret != m_endpointTable.end())
226  {
227  NS_LOG_INFO("Found endpoint entry: " << dpId << "/" << portNo << " - " << ret->second);
228  return ret->second;
229  }
230  NS_ABORT_MSG("No tunnel endpoint information for this datapath + port.");
231 }
232 
233 ofl_err
234 TunnelController::HandleArpPacketIn(struct ofl_msg_packet_in* msg,
236  uint32_t xid)
237 {
238  NS_LOG_FUNCTION(this << swtch << xid);
239 
240  struct ofl_match_tlv* tlv;
241 
242  // Get ARP operation
243  uint16_t arpOp;
244  tlv = oxm_match_lookup(OXM_OF_ARP_OP, (struct ofl_match*)msg->match);
245  memcpy(&arpOp, tlv->value, OXM_LENGTH(OXM_OF_ARP_OP));
246 
247  // Get input port
248  uint32_t inPort;
249  tlv = oxm_match_lookup(OXM_OF_IN_PORT, (struct ofl_match*)msg->match);
250  memcpy(&inPort, tlv->value, OXM_LENGTH(OXM_OF_IN_PORT));
251 
252  // Check for ARP request
253  if (arpOp == ArpHeader::ARP_TYPE_REQUEST)
254  {
255  // Get target IP address
256  Ipv4Address dstIp = ExtractIpv4Address(OXM_OF_ARP_TPA, (struct ofl_match*)msg->match);
257 
258  // Get target MAC address from ARP table
259  Mac48Address dstMac = GetArpEntry(dstIp);
260  NS_LOG_INFO("Got ARP request for IP " << dstIp << ", resolved to " << dstMac);
261 
262  // Get source IP address
263  Ipv4Address srcIp;
264  srcIp = ExtractIpv4Address(OXM_OF_ARP_SPA, (struct ofl_match*)msg->match);
265 
266  // Get Source MAC address
267  Mac48Address srcMac;
268  tlv = oxm_match_lookup(OXM_OF_ARP_SHA, (struct ofl_match*)msg->match);
269  srcMac.CopyFrom(tlv->value);
270 
271  // Create the ARP reply packet
272  Ptr<Packet> pkt = CreateArpReply(dstMac, dstIp, srcMac, srcIp);
273  uint8_t pktData[pkt->GetSize()];
274  pkt->CopyData(pktData, pkt->GetSize());
275 
276  // Send the ARP reply within an OpenFlow PacketOut message
277  struct ofl_msg_packet_out reply;
278  reply.header.type = OFPT_PACKET_OUT;
279  reply.buffer_id = OFP_NO_BUFFER;
280  reply.in_port = inPort;
281  reply.data_length = pkt->GetSize();
282  reply.data = &pktData[0];
283 
284  // Send the ARP replay back to the input port
285  struct ofl_action_output* action =
286  (struct ofl_action_output*)xmalloc(sizeof(struct ofl_action_output));
287  action->header.type = OFPAT_OUTPUT;
288  action->port = OFPP_IN_PORT;
289  action->max_len = 0;
290 
291  reply.actions_num = 1;
292  reply.actions = (struct ofl_action_header**)&action;
293 
294  int error = SendToSwitch(swtch, (struct ofl_msg_header*)&reply, xid);
295  free(action);
296  if (error)
297  {
298  NS_LOG_ERROR("Error sending packet out with ARP request.");
299  }
300  }
301  else
302  {
303  NS_LOG_WARN("Not supposed to get ARP reply. Ignoring...");
304  }
305 
306  // All handlers must free the message when everything is ok
307  ofl_msg_free((struct ofl_msg_header*)msg, nullptr);
308  return 0;
309 }
310 
312 TunnelController::ExtractIpv4Address(uint32_t oxm_of, struct ofl_match* match)
313 {
314  switch (oxm_of)
315  {
316  case static_cast<uint32_t>(OXM_OF_ARP_SPA):
317  case static_cast<uint32_t>(OXM_OF_ARP_TPA):
318  case static_cast<uint32_t>(OXM_OF_IPV4_DST):
319  case static_cast<uint32_t>(OXM_OF_IPV4_SRC): {
320  uint32_t ip;
321  int size = OXM_LENGTH(oxm_of);
322  struct ofl_match_tlv* tlv = oxm_match_lookup(oxm_of, match);
323  memcpy(&ip, tlv->value, size);
324  return Ipv4Address(ntohl(ip));
325  }
326  default:
327  NS_ABORT_MSG("Invalid IP field.");
328  }
329 }
330 
333  Ipv4Address srcIp,
334  Mac48Address dstMac,
335  Ipv4Address dstIp)
336 {
337  NS_LOG_FUNCTION(this << srcMac << srcIp << dstMac << dstIp);
338 
339  Ptr<Packet> packet = Create<Packet>();
340 
341  // ARP header
342  ArpHeader arp;
343  arp.SetReply(srcMac, srcIp, dstMac, dstIp);
344  packet->AddHeader(arp);
345 
346  // Ethernet header
347  EthernetHeader eth(false);
348  eth.SetSource(srcMac);
349  eth.SetDestination(dstMac);
350  if (packet->GetSize() < 46)
351  {
352  uint8_t buffer[46];
353  memset(buffer, 0, 46);
354  Ptr<Packet> padd = Create<Packet>(buffer, 46 - packet->GetSize());
355  packet->AddAtEnd(padd);
356  }
358  packet->AddHeader(eth);
359 
360  // Ethernet trailer
361  EthernetTrailer trailer;
362  if (Node::ChecksumEnabled())
363  {
364  trailer.EnableFcs(true);
365  }
366  trailer.CalcFcs(packet);
367  packet->AddTrailer(trailer);
368 
369  return packet;
370 }
371 
372 } // namespace ns3
The packet header for an ARP packet.
Definition: arp-header.h:36
void SetReply(Address sourceHardwareAddress, Ipv4Address sourceProtocolAddress, Address destinationHardwareAddress, Ipv4Address destinationProtocolAddress)
Set the ARP reply parameters.
Definition: arp-header.cc:49
static const uint16_t PROT_NUMBER
ARP protocol number (0x0806)
Packet header for Ethernet.
void SetDestination(Mac48Address destination)
void SetLengthType(uint16_t size)
void SetSource(Mac48Address source)
Packet trailer for Ethernet.
void EnableFcs(bool enable)
Enable or disable FCS checking and calculations.
void CalcFcs(Ptr< const Packet > p)
Updates the Fcs Field to the correct FCS.
Ipv4 addresses are stored in host order in this class.
Definition: ipv4-address.h:43
uint32_t Get() const
Get the host-order 32-bit IP address.
an EUI-48 address
Definition: mac48-address.h:46
void CopyFrom(const uint8_t buffer[6])
static bool ChecksumEnabled()
Definition: node.cc:290
OpenFlow 1.3 controller base class that can handle a collection of OpenFlow switches and provides the...
int SendToSwitch(Ptr< const RemoteSwitch > swtch, struct ofl_msg_header *msg, uint32_t xid=0)
Send a OFLib message to a registered switch.
int DpctlExecute(uint64_t dpId, const std::string textCmd)
Execute a dpctl command to interact with the remote switch.
void DoDispose() override
Destructor implementation.
void AddAtEnd(Ptr< const Packet > packet)
Concatenate the input packet at the end of the current packet.
Definition: packet.cc:354
void AddHeader(const Header &header)
Add header to this packet.
Definition: packet.cc:268
uint32_t GetSize() const
Returns the the size in bytes of the packet (including the zero-filled initial payload).
Definition: packet.h:863
uint32_t CopyData(uint8_t *buffer, uint32_t size) const
Copy the packet contents to a byte buffer.
Definition: packet.cc:400
void AddTrailer(const Trailer &trailer)
Add trailer to this packet.
Definition: packet.cc:324
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:78
This controller is responsible for installing the forwarding rules on the switches.
ofl_err HandleArpPacketIn(struct ofl_msg_packet_in *msg, Ptr< const RemoteSwitch > swtch, uint32_t xid)
Handle packet-in messages sent from switch with ARP message.
TunnelController()
Default constructor.
IpMacMap_t m_arpTable
ARP resolution table.
Ipv4Address ExtractIpv4Address(uint32_t oxm_of, struct ofl_match *match)
Extract an IPv4 address from packet match.
static TypeId GetTypeId()
Register this type.
void SaveTunnelEndpoint(uint64_t dpId, uint32_t portNo, Ipv4Address ipAddr)
Save the pair datapath ID + port no / IP address in tunnel endpoint table.
~TunnelController() override
Dummy destructor, see DoDispose.
void DoDispose() override
Destructor implementation.
Ipv4Address GetTunnelEndpoint(uint64_t dpId, uint32_t portNo)
Perform tunnel endpoint resolution.
Ptr< Packet > CreateArpReply(Mac48Address srcMac, Ipv4Address srcIp, Mac48Address dstMac, Ipv4Address dstIp)
Create a Packet with an ARP reply, encapsulated inside of an Ethernet frame (with header and trailer.
DpPortIpMap_t m_endpointTable
Tunnel endpoint resolution table.
Mac48Address GetArpEntry(Ipv4Address ip)
Perform ARP resolution.
ofl_err HandlePacketIn(struct ofl_msg_packet_in *msg, Ptr< const RemoteSwitch > swtch, uint32_t xid) override
Handle a packet in message sent by the switch to this controller.
std::pair< uint64_t, uint32_t > DpPortPair_t
A pair identifying OpenFlow datapath id and port number.
void SaveArpEntry(Ipv4Address ipAddr, Mac48Address macAddr)
Save the pair IP / MAC address in ARP table.
void HandshakeSuccessful(Ptr< const RemoteSwitch > swtch) override
Function invoked after a successfully handshake procedure between this controller and a remote switch...
a unique identifier for an interface.
Definition: type-id.h:60
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:935
#define NS_ABORT_MSG(msg)
Unconditional abnormal program termination with a message.
Definition: abort.h:49
#define NS_LOG_ERROR(msg)
Use NS_LOG to output a message of level LOG_ERROR.
Definition: log.h:254
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_LOG_WARN(msg)
Use NS_LOG to output a message of level LOG_WARN.
Definition: log.h:261
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition: log.h:275
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition: object-base.h:46
Every class exported by the ns3 library is enclosed in the ns3 namespace.
cmd
Definition: second.py:33