src/routing/click/ipv4-click-routing.cc
author Lalith Suresh <suresh.lalith@gmail.com>
Mon, 07 Jun 2010 23:35:37 +0530
changeset 6305 c42027138304
parent 6304 0b5b21c2936e
child 6306 70881e747bf6
permissions -rw-r--r--
Ipv4ClickRouting::RouteOutput () added

/* -*-  Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
 * Copyright (c) 2010 Lalith Suresh
 *
 * 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
 *
 * Authors: Lalith Suresh <suresh.lalith@gmail.com>
 */


#ifdef NS3_CLICK

#include "ns3/node.h"
#include "ns3/simulator.h"
#include "ns3/log.h"
#include "ns3/mac48-address.h"
#include "ns3/udp-header.h" // Remove later
#include "ns3/ethernet-header.h"
#include "ns3/ipv4-interface.h"
#include "ns3/ipv4-l3-protocol.h"
#include "ns3/arp-l3-protocol.h"
#include "ns3/llc-snap-header.h"

#include "ipv4-click-routing.h"
#include <string>
#include <map>

#include <cstdlib>
#include <cstdarg>

namespace ns3 {

NS_LOG_COMPONENT_DEFINE ("Ipv4ClickRouting");

std::map < simclick_node_t *, Ptr<Ipv4ClickRouting> > Ipv4ClickRouting::m_clickInstanceFromSimNode;

TypeId
Ipv4ClickRouting::GetTypeId (void)
{
  static TypeId tid = TypeId ("ns3::Ipv4ClickRouting")
      .SetParent<Ipv4RoutingProtocol> ()
      ;

  return tid;
}

Ipv4ClickRouting::Ipv4ClickRouting ()
  : m_nonDefaultName (false),
    m_ipv4 (0)
{}

Ipv4ClickRouting::~Ipv4ClickRouting ()
{}

void
Ipv4ClickRouting::DoStart ()
{
  uint32_t id = m_ipv4->GetObject<Node> ()->GetId ();

  if (!m_nonDefaultName)
    {
      std::stringstream name;
      name << "Node" << id;
      m_nodeName = name.str ();
    }

  m_simNode = new simclick_node_t;
  timerclear (&m_simNode->curtime);

  AddSimNodeToClickMapping ();

  NS_ASSERT (m_clickFile.length () > 0);

  // Even though simclick_click_create() will halt programme execution
  // if it is unable to initialise a Click router, we play safe
  if (simclick_click_create (m_simNode, m_clickFile.c_str ()) >= 0)
    {
      NS_LOG_DEBUG (m_nodeName << " has initialised a Click Router");
      m_clickInitialised = true;
    }
  else
    {
      NS_LOG_DEBUG ("Click Router Initialisation failed for " << m_nodeName);
      m_clickInitialised = false;
    }

  NS_ASSERT (m_clickInitialised == true);
  simclick_click_run (m_simNode);
}

void
Ipv4ClickRouting::SetIpv4 (Ptr<Ipv4> ipv4)
{
  m_ipv4 = ipv4;
}

void
Ipv4ClickRouting::DoDispose ()
{
  m_ipv4 = 0;
  delete m_simNode;
}

void
Ipv4ClickRouting::SetClickFile (std::string clickfile)
{
  m_clickFile = clickfile;
}

void
Ipv4ClickRouting::SetClickRoutingTableElement (std::string name)
{
  m_clickRoutingTableElement = name;
}

void
Ipv4ClickRouting::SetNodeName (std::string name)
{
  m_nodeName = name;
  m_nonDefaultName = true;
}

std::string
Ipv4ClickRouting::GetNodeName ()
{
  return m_nodeName;
}

void
Ipv4ClickRouting::ReceiveFromExtRouter (Ptr<Packet>, bool direction)
{}

int
Ipv4ClickRouting::GetInterfaceId (const char *ifname)
{
  int retval = -1;

  if (strstr(ifname, "tap") || strstr(ifname, "tun"))
    {
      retval = ExtRouter::IFID_KERNELTAP;
    }
  else if (const char *devname = strstr(ifname, "eth"))
    {
      while (*devname && !isdigit((unsigned char) *devname))
        {
          devname++;
        }

      if (*devname)
        {
          retval = atoi(devname) + ExtRouter::IFID_FIRSTIF;
        }
    }
  else if (const char *devname = strstr(ifname, "drop"))
    {
      while (*devname && !isdigit((unsigned char) *devname))
        {
          devname++;
        }
      if (*devname)
        {
          retval = atoi(devname) + ExtRouter::IFID_FIRSTIFDROP;
        }
    }

  if (retval >= (int) m_ipv4->GetNInterfaces ())
    {
      retval = -1;
    }

  return retval;
}

int
Ipv4ClickRouting::IfReady (int ifid)
{
  if (ifid >= 0 && ifid < (int) m_ipv4->GetNInterfaces ())
    {
      return 1;
    }
  else
    {
      return -1;
    }
}

std::string
Ipv4ClickRouting::GetIpAddrFromIfid (int ifid)
{
  NS_ASSERT (ifid != -1);
  std::stringstream addr;
  m_ipv4->GetAddress (ifid, 0).GetLocal ().Print (addr);

  return addr.str ();
}

std::string
Ipv4ClickRouting::GetMacAddrFromIfid (int ifid)
{
  NS_ASSERT (ifid != -1);
  std::stringstream addr;

  Ptr<NetDevice> device = m_ipv4->GetNetDevice (ifid);
  Address devAddr = device->GetAddress ();
  addr << Mac48Address::ConvertFrom(devAddr);

  return addr.str ();
}

void
Ipv4ClickRouting::AddSimNodeToClickMapping ()
{
  m_clickInstanceFromSimNode.insert (std::make_pair (m_simNode, this));
}

Ptr<Ipv4ClickRouting>
Ipv4ClickRouting::GetClickInstanceFromSimNode (simclick_node_t *simnode)
{
  return m_clickInstanceFromSimNode[simnode];
}

void
Ipv4ClickRouting::RunClickEvent ()
{
  m_simNode->curtime.tv_sec = Simulator::Now ().GetSeconds ();
  m_simNode->curtime.tv_usec = Simulator::Now ().GetMicroSeconds () % 1000000;
  simclick_click_run (m_simNode);
}

void
Ipv4ClickRouting::HandleScheduleFromClick (const struct timeval *when)
{
  NS_LOG_DEBUG ("HandleScheduleFromClick at " << when->tv_sec << " " << when->tv_usec << " " << Simulator::Now ());

  double simtime = when->tv_sec + (when->tv_usec / 1.0e6);
  double simdelay = simtime - Simulator::Now ().GetMicroSeconds () / 1.0e6; 

  Simulator::Schedule (Seconds (simdelay), &Ipv4ClickRouting::RunClickEvent, this);
}

void
Ipv4ClickRouting::HandlePacketFromClick (int ifid, int ptype, const unsigned char* data, int len)
{
  NS_LOG_DEBUG ("HandlePacketFromClick"); 

  // Figure out packet's destination here:
  // If ifid == 0, then packet's going up
  // else, packet's going down

  if (ifid == 0)
    {
      NS_LOG_DEBUG ("Incoming packet from tap0. Sending Packet up the stack.");
      Ptr<Ipv4L3Protocol> ipv4l3 = DynamicCast<Ipv4L3Protocol> (m_ipv4);

      Ptr<Packet> p = Create<Packet> (data, len);

      Ipv4Header ipHeader;
      p->RemoveHeader (ipHeader);

      ipv4l3->LocalDeliverFromExternal (p, ipHeader, (uint32_t) ifid);
    }
  else if (ifid)
    {
      NS_LOG_DEBUG ("Incoming packet from eth" << ifid - 1 << " of type " << ptype <<". Sending packet down the stack.");

      Ptr<Packet> p = Create<Packet> (data, len);

      // NetDevice::Send () attaches ethernet headers,
      // so this one's not required.
      EthernetHeader header;
      p->RemoveHeader (header);

      uint16_t protocol;

      if (header.GetLengthType () <= 1500)
        {
          LlcSnapHeader llc;
          p->RemoveHeader (llc);
          protocol = llc.GetType ();
        }
      else
        {
          protocol = header.GetLengthType ();
        }
/*
      if (protocol == 0x0800) // Ipv4: We require this because TCP/UDP packets are sent with source as 0.0.0.0
        {
          Ipv4Header ipHeader;
          p->RemoveHeader (ipHeader);
          ipHeader.SetSource (m_ipv4->GetAddress (ifid, 0).GetLocal ());
          ipHeader.EnableChecksum ();
          p->AddHeader (ipHeader);
        }
*/
      Ptr<NetDevice> netdev = m_ipv4->GetNetDevice (ifid);
      netdev->Send (p, header.GetDestination (), protocol);
    }
}

void
Ipv4ClickRouting::SendPacketToClick (int ifid, int ptype, const unsigned char* data, int len)
{
  m_simNode->curtime.tv_sec = Simulator::Now ().GetSeconds ();
  m_simNode->curtime.tv_usec = Simulator::Now ().GetMicroSeconds () % 1000000;

  // Since packets in ns-3 don't have global Packet ID's and Flow ID's, we
  // feed dummy values into pinfo. This avoids the need to make changes in the Click code
  simclick_simpacketinfo pinfo;
  pinfo.id = 0;
  pinfo.fid = 0;

  simclick_click_send(m_simNode,ifid,ptype,data,len,&pinfo);
}

void
Ipv4ClickRouting::Send (Ptr<Packet> p, Ipv4Address src, Ipv4Address dst)
{
  uint32_t ifid;

  for (ifid = 0; ifid < m_ipv4->GetNInterfaces (); ifid++)
    {
      Ipv4Address addr = m_ipv4->GetAddress (ifid, 0).GetLocal ();

      if (addr == src)
        {
          break;
        }
    }
  
//  NS_ASSERT (ifid < m_ipv4->GetNInterfaces ()); // I doubt this scenario would arise, but just playing safe.

  int len = p->GetSize ();
  uint8_t *buf = new uint8_t [len];
  p->CopyData (buf, len);

  SendPacketToClick (0, SIMCLICK_PTYPE_IP, buf, len);
}

void
Ipv4ClickRouting::Receive (Ptr<Packet> p, Mac48Address receiverAddr, Mac48Address dest)
{
  uint32_t ifid;
  for (ifid = 1; ifid < m_ipv4->GetNInterfaces (); ifid++)
    {
      Ptr<NetDevice> device = m_ipv4->GetNetDevice (ifid);
     
      if (Mac48Address::ConvertFrom (device->GetAddress ()) == receiverAddr)
        {
          break;
        }
    }
  
  int len = p->GetSize ();
  uint8_t *buf = new uint8_t [len];
  p->CopyData (buf, len);

  SendPacketToClick (ifid, SIMCLICK_PTYPE_ETHER, buf, len);
}

Ptr<Ipv4Route>
Ipv4ClickRouting::RouteOutput (Ptr<Packet> p, const Ipv4Header &header, Ptr<NetDevice> oif, Socket::SocketErrno &sockerr)
{
  Ptr<Ipv4Route> rtentry;

  // Probe the Click Routing Table for the required IP
  // This returns a string of the form "InterfaceID GatewayAddr"
  std::string s = simclick_click_read_handler (m_simNode, m_clickRoutingTableElement.c_str (), "lookup 172.16.1.2", 0, 0);

  int pos = s.find (" ");

  int interfaceId = atoi (s.substr (0, pos).c_str ());
  Ipv4Address lol (s.substr (pos + 1).c_str ());

  Ipv4Address destination(lol);

  if (interfaceId != -1)
    {
      rtentry = Create<Ipv4Route> ();
      rtentry->SetDestination (header.GetDestination ());
      // the source address is the interface address that matches
      // the destination address (when multiple are present on the 
      // outgoing interface, one is selected via scoping rules)
      NS_ASSERT (m_ipv4);
      uint32_t numOifAddresses = m_ipv4->GetNAddresses (interfaceId);
      NS_ASSERT (numOifAddresses > 0);
      Ipv4InterfaceAddress ifAddr;
      if (numOifAddresses == 1)
        {
          ifAddr = m_ipv4->GetAddress (interfaceId, 0);
        }
      else
        {
          NS_FATAL_ERROR ("XXX Not implemented yet:  IP aliasing and Click");
        }
      rtentry->SetSource (ifAddr.GetLocal ());
      rtentry->SetGateway (destination);
      rtentry->SetOutputDevice (m_ipv4->GetNetDevice (interfaceId));
      sockerr = Socket::ERROR_NOTERROR;
      NS_LOG_DEBUG ("Found route to " << rtentry->GetDestination () << " via nh " << rtentry->GetGateway () << " with source addr " << rtentry->GetSource () << " and output dev " << rtentry->GetOutputDevice());
    }
  else
    {
      NS_LOG_DEBUG ("Click node " << m_nodeName
                    << ": RouteOutput for dest=" << header.GetDestination ()
                    << " No route to host");
      sockerr = Socket::ERROR_NOROUTETOHOST;
    }

  return rtentry;
}

/*
void
Ipv4ClickRouting::TestInit ()
{
  Ptr<Packet> p = Create<Packet> (100);

  UdpHeader udpheader;
  udpheader.SetSourcePort (3213);
  udpheader.SetDestinationPort (80);

  p->AddHeader (udpheader);

  int payloadsize = p->GetSize ();

  Ipv4Header ipHeader; 
  ipHeader.SetSource (Ipv4Address("172.16.1.1"));
  ipHeader.SetDestination (Ipv4Address("172.16.1.2"));
  ipHeader.SetPayloadSize (payloadsize);
  ipHeader.SetTtl (10);

  ipHeader.EnableChecksum ();

  p->AddHeader (ipHeader);

  Send (p, (Ipv4Address) "172.16.1.1", (Ipv4Address) "172.16.1.2");
}
*/
} // namespace ns3

#ifdef __cplusplus
extern "C"
{
#endif

using namespace ns3;

static int simstrlcpy(char *buf, int len, const std::string &s) {
  if (len)
    {
      len--;

      if ((unsigned) len > s.length())
        {
          len = s.length();
        }

      s.copy(buf, len);
      buf[len] = '\0';
    }
  return 0;
}

// Sends a Packet from Click to the Simulator: Defined in simclick.h
int simclick_sim_send(simclick_node_t *simnode,
                      int ifid, int type, const unsigned char* data, int len,
                      simclick_simpacketinfo *pinfo)
{
  NS_LOG_DEBUG ("simclick_sim_send called at " <<Simulator::Now().GetSeconds()<<": " << ifid << " " << type << " " << data << " "<< len);

  if (simnode == NULL)
  {
    return -1;
  }

  // Bail out if we get a bad ifid
  if (ifid > ExtRouter::IFID_LASTIF)
  {
    return -1;
  }
  
  Ptr<Ipv4ClickRouting> clickInstance = Ipv4ClickRouting::GetClickInstanceFromSimNode (simnode);

  clickInstance->HandlePacketFromClick (ifid, type, data, len);

  return 0;
}

// Click Service Methods: Defined in simclick.h
int simclick_sim_command(simclick_node_t *simnode, int cmd, ...)
{
  va_list val;
  va_start (val, cmd);

  int retval = 0;

  Ptr<Ipv4ClickRouting> clickInstance = Ipv4ClickRouting::GetClickInstanceFromSimNode (simnode);
  switch (cmd) 
  {
    case SIMCLICK_VERSION:
      {
        retval = 0;
        break;
      }

    case SIMCLICK_SUPPORTS:
      {
        int othercmd = va_arg (val, int);
        retval = (othercmd >= SIMCLICK_VERSION && othercmd <= SIMCLICK_GET_NODE_ID);
        break;
      }

    case SIMCLICK_IFID_FROM_NAME:
      {
        const char *ifname = va_arg(val, const char *);

        retval = clickInstance->GetInterfaceId (ifname);
 
        NS_LOG_DEBUG (clickInstance->GetNodeName () << " SIMCLICK_IFID_FROM_NAME: " << ifname << " " << retval);
        break;
      }

    case SIMCLICK_IPADDR_FROM_NAME:
      {
        const char *ifname = va_arg(val, const char *);
        char *buf = va_arg(val, char *);
        int len = va_arg(val, int);

        int ifid = clickInstance->GetInterfaceId (ifname);

        if (ifid >= 0)
          {
            retval = simstrlcpy(buf, len, clickInstance->GetIpAddrFromIfid (ifid));
          }
        else
          {
            retval = -1;
          }

        NS_LOG_DEBUG (clickInstance->GetNodeName () << " SIMCLICK_IPADDR_FROM_NAME: "<< ifname << " "<< buf << " " << len);
        break;
      }

    case SIMCLICK_MACADDR_FROM_NAME:
      {
        const char *ifname = va_arg(val, const char *);
        char *buf = va_arg(val, char *);
        int len = va_arg(val, int);
        int ifid = clickInstance->GetInterfaceId (ifname);

        if (clickInstance->GetInterfaceId (ifname))
          {
            retval = simstrlcpy(buf, len, clickInstance->GetMacAddrFromIfid (ifid));
          }
        else
          {
            retval = -1;
          }

        NS_LOG_DEBUG (clickInstance->GetNodeName () << " SIMCLICK_MACADDR_FROM_NAME: "<< ifname << " "<< buf << " "<< len);
        break;
      }

    case SIMCLICK_SCHEDULE:
      {
        const struct timeval *when = va_arg(val, const struct timeval *);

        clickInstance->HandleScheduleFromClick (when);

        retval = 0;
        NS_LOG_DEBUG (clickInstance->GetNodeName () << " SIMCLICK_SCHEDULE: "<< when->tv_sec << "s and " << when->tv_usec << "usecs later.");

        break;
      }

    case SIMCLICK_GET_NODE_NAME:
      {
        char *buf = va_arg(val, char *);
        int len = va_arg(val, int);
        retval = simstrlcpy(buf, len, clickInstance->GetNodeName ());

        NS_LOG_DEBUG (clickInstance->GetNodeName () << " SIMCLICK_GET_NODE_NAME: " << buf << " " << len);
        break;
      }

    case SIMCLICK_IF_READY:
      {
        int ifid = va_arg(val, int); // Commented out so that optimized build works

        // We're not using a ClickQueue, so we're always ready (for the timebeing)  
        retval = clickInstance->IfReady (ifid);

        NS_LOG_DEBUG (clickInstance->GetNodeName () << " SIMCLICK_IF_READY: " << ifid << " " << Simulator::Now ());
        break;
      }

    case SIMCLICK_TRACE:
      {
        // Used only for tracing
        NS_LOG_DEBUG (clickInstance->GetNodeName () << " Received a call for SIMCLICK_TRACE");
        break;
      }

    case SIMCLICK_GET_NODE_ID:
      {
        // Used only for tracing
        NS_LOG_DEBUG (clickInstance->GetNodeName () << " Received a call for SIMCLICK_GET_NODE_ID");
        break;
      }
  }
  return retval;
}

#ifdef __cplusplus
}
#endif

#endif // NS3_CLICK