NS4: A P4-drien Network Simulator (NS3)

 

Please follow the instructions in https://github.com/kphf1995cm/ns4-install and https://github.com/p4db/NS4-DEV to install NS4.

 

[Topology]

 

N0(10.1.1.1, 00:00:00:00:00:01)---------(port 0)P4switch(port 1)-----N1(10.1.1.2, 00:00:00:00:00:03)

 

We will implement the MAC layer Forwarding in P4switch. It means that the packets with MAC addr=00:00:00:00:00:01 will be forwarded to port 0 and the packets with MAC addr=00:00:00:00:00:03 will be forwarded to port 1.

 

l2_switch.p4

header_type ethernet_t {

    fields {

        dstAddr : 48;

        srcAddr : 48;

        etherType : 16;

    }

}

 

header ethernet_t ethernet;

 

parser start {

    return parse_ethernet;

}

 

parser parse_ethernet {

    extract(ethernet);

    return ingress;

}

 

action _drop() {

    drop();

}

 

action forward(port) {

    modify_field(standard_metadata.egress_spec, port);

}

 

table dmac {

    reads {

        ethernet.dstAddr : exact;

    }

    actions {

        forward;

        _drop;

    }

    size : 512;

}

 

control ingress {

    apply(dmac);

}

 

control egress {

}

 

commands.txt

table_set_default dmac _drop

table_add dmac forward 0x000000000001 => 0

table_add dmac forward 0x000000000003 => 1

 

mtype.txt

dmac exact

 

readme.txt

 

 

Put these files under ns-allinone-2.27/ns-2.27/src/ns4/test/l2_switch

 

Compile the l2_switch.p4 to get l2_switch.json

 

Modify global.cc and global.h under model folder

 

global.cc

# include "ns3/log.h"

# include "ns3/global.h"

 

namespace ns3 {

 

  NS_LOG_COMPONENT_DEFINE("P4GlobalVar");

  NS_OBJECT_ENSURE_REGISTERED(P4GlobalVar);

 

  // init default static global variable

  unsigned int P4GlobalVar::g_networkFunc=ROUTER;

  std::string P4GlobalVar::g_flowTablePath="";

  std::string P4GlobalVar::g_p4MatchTypePath="";

  unsigned int P4GlobalVar::g_populateFlowTableWay=LOCAL_CALL;

  std::string P4GlobalVar::g_p4JsonPath="";

 

  std::string P4GlobalVar::g_homePath="/home/user/ns4-install/";

  std::string P4GlobalVar::g_ns3RootName="ns-allinone-3.27/";

  std::string P4GlobalVar::g_ns3SrcName="ns-3.27/";

  std::string P4GlobalVar::g_nfDir=P4GlobalVar::g_homePath+P4GlobalVar::g_ns3RootName+P4GlobalVar::g_ns3SrcName+"src/ns4/test/";

  std::string P4GlobalVar::g_topoDir=P4GlobalVar::g_homePath+P4GlobalVar::g_ns3RootName+P4GlobalVar::g_ns3SrcName+"src/ns4/topo/";

  std::string P4GlobalVar::g_flowTableDir=P4GlobalVar::g_homePath+P4GlobalVar::g_ns3RootName+P4GlobalVar::g_ns3SrcName+"src/ns4/flowtable/";

  unsigned int P4GlobalVar::g_nsType=NS4;

  unsigned int P4GlobalVar::g_runtimeCliTime=10;

  std::map<std::string,unsigned int> P4GlobalVar::g_nfStrUintMap;

 

  unsigned long getTickCount(void)

  {

    unsigned long currentTime=0;

    #ifdef WIN32

      currentTime = GetTickCount();

    #endif

      struct timeval current;

      gettimeofday(&current, NULL);

      currentTime = current.tv_sec * 1000 + current.tv_usec / 1000;

    #ifdef OS_VXWORKS

      ULONGA timeSecond = tickGet() / sysClkRateGet();

      ULONGA timeMilsec = tickGet() % sysClkRateGet() * 1000 / sysClkRateGet();

      currentTime = timeSecond * 1000 + timeMilsec;

    #endif

    return currentTime;

  }

  void P4GlobalVar::SetP4MatchTypeJsonPath()

  {

    if(P4GlobalVar::g_networkFunc==FIREWALL)

       {

          P4GlobalVar::g_p4JsonPath=P4GlobalVar::g_nfDir+"firewall/firewall.json";

          P4GlobalVar::g_p4MatchTypePath=P4GlobalVar::g_nfDir + "firewall/mtype.txt";

       }

    if(P4GlobalVar::g_networkFunc==SILKROAD)

       {

          P4GlobalVar::g_p4JsonPath=P4GlobalVar::g_nfDir+"silkroad/silkroad.json";

          P4GlobalVar::g_p4MatchTypePath=P4GlobalVar::g_nfDir + "silkroad/mtype.txt";

       }

    if(P4GlobalVar::g_networkFunc==ROUTER)

       {

          P4GlobalVar::g_p4JsonPath=P4GlobalVar::g_nfDir+"router/router.json";

          P4GlobalVar::g_p4MatchTypePath=P4GlobalVar::g_nfDir + "router/mtype.txt";

       }

    if(P4GlobalVar::g_networkFunc==SIMPLE_ROUTER)

       {

          P4GlobalVar::g_p4JsonPath=P4GlobalVar::g_nfDir+"simple_router/simple_router.json";

          P4GlobalVar::g_p4MatchTypePath=P4GlobalVar::g_nfDir + "simple_router/mtype.txt";

       }

    if(P4GlobalVar::g_networkFunc==COUNTER)

       {

          P4GlobalVar::g_p4JsonPath=P4GlobalVar::g_nfDir+"counter/counter.json";

          P4GlobalVar::g_p4MatchTypePath=P4GlobalVar::g_nfDir + "counter/mtype.txt";

       }

    if(P4GlobalVar::g_networkFunc==METER)

       {

          P4GlobalVar::g_p4JsonPath=P4GlobalVar::g_nfDir+"meter/meter.json";

          P4GlobalVar::g_p4MatchTypePath=P4GlobalVar::g_nfDir + "meter/mtype.txt";

       }

    if(P4GlobalVar::g_networkFunc==REGISTER)

       {

          P4GlobalVar::g_p4JsonPath=P4GlobalVar::g_nfDir+"register/register.json";

          P4GlobalVar::g_p4MatchTypePath=P4GlobalVar::g_nfDir + "register/mtype.txt";

       }

    if(P4GlobalVar::g_networkFunc==L2_SWITCH)

       {

          P4GlobalVar::g_p4JsonPath=P4GlobalVar::g_nfDir+"l2_switch/l2_switch.json";

          P4GlobalVar::g_p4MatchTypePath=P4GlobalVar::g_nfDir + "l2_switch/mtype.txt";

       }

  }

 

  void P4GlobalVar::InitNfStrUintMap()

  {

    P4GlobalVar::g_nfStrUintMap["ROUTER"]=ROUTER;

    P4GlobalVar::g_nfStrUintMap["SIMPLE_ROUTER"]=SIMPLE_ROUTER;

    P4GlobalVar::g_nfStrUintMap["FIREWALL"]=FIREWALL;

    P4GlobalVar::g_nfStrUintMap["SILKROAD"]=SILKROAD;

    P4GlobalVar::g_nfStrUintMap["COUNTER"]=COUNTER;

    P4GlobalVar::g_nfStrUintMap["METER"]=METER;

    P4GlobalVar::g_nfStrUintMap["REGISTER"]=REGISTER;

    P4GlobalVar::g_nfStrUintMap["l2_SWITCH"]=L2_SWITCH;

  }

 

  TypeId P4GlobalVar::GetTypeId(void)

  {

          static TypeId tid = TypeId("ns3::P4GlobalVar")

                  .SetParent<Object>()

                  .SetGroupName("P4GlobalVar")

                  ;

          return tid;

  }

  P4GlobalVar::P4GlobalVar()

  {

          NS_LOG_FUNCTION(this);

  }

 

  P4GlobalVar::~P4GlobalVar()

  {

          NS_LOG_FUNCTION(this);

  }

}

 

global.h

/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */

/*

* Copyright (c) YEAR COPYRIGHTHOLDER

*

* 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: PengKuang <kphf1995cm@outlook.com>

*/

 

#ifndef GLOBAL_H

#define GLOBAL_H

#include <cstring>

#include <map>

#include "ns3/object.h"

#include <sys/time.h>

 

namespace ns3 {

 

#define LOCAL_CALL 0

#define RUNTIME_CLI 1

#define NS3 1

#define NS4 0

 

// nf info

unsigned const int ROUTER = 0;

unsigned const int FIREWALL = 1;

unsigned const int SILKROAD = 2;

unsigned const int SIMPLE_ROUTER = 3;

unsigned const int COUNTER = 4;

unsigned const int METER = 5;

unsigned const int REGISTER = 6;

unsigned const int L2_SWITCH = 7;

// match type

unsigned const int EXACT = 0;

unsigned const int LPM = 1;

unsigned const int TERNARY = 2;

unsigned const int VALID = 3;

unsigned const int RANGE = 4;

 

//get current time (ms)

unsigned long getTickCount(void);

 

class P4GlobalVar : public Object

{

public:

        static TypeId GetTypeId(void);

 

        // siwtch info

        static unsigned int g_networkFunc;

        static std::string g_p4MatchTypePath;

        static std::string g_flowTablePath;

        static std::string g_p4JsonPath;

        static unsigned int g_populateFlowTableWay;

 

        // path info

        static std::string g_homePath;

        static std::string g_ns3RootName;

        static std::string g_ns3SrcName;

        static unsigned int g_nsType;

        static std::string g_nfDir;

        static std::string g_topoDir;

        static std::string g_flowTableDir;

 

        // runtime CLI wait time

        static unsigned int g_runtimeCliTime;//s

 

        static std::map<std::string,unsigned int> g_nfStrUintMap;  

        //set g_p4MatchTypePath,g_p4JsonPath according to g_networkFunc

        static void SetP4MatchTypeJsonPath();

        static void InitNfStrUintMap();

 

private:

        P4GlobalVar();

        ~P4GlobalVar();

        P4GlobalVar(const P4GlobalVar&);

        P4GlobalVar& operator= (const P4GlobalVar&);

};

}

#endif /*GLOBAL_H*/

 

 

 

test-p4.cc (put this file under scratch)

#include <iostream>

#include <fstream>

#include <string>

#include <cassert>

#include <unistd.h>

#include <sys/time.h>

#include <netinet/in.h>

#include <unordered_map>

 

#include "ns3/core-module.h"

#include "ns3/network-module.h"

#include "ns3/applications-module.h"

#include "ns3/csma-module.h"

#include "ns3/internet-module.h"

#include "ns3/p4-helper.h"

#include "ns3/v4ping-helper.h"

 

#include "ns3/global.h"

#include <bm/SimpleSwitch.h>

#include <bm/bm_runtime/bm_runtime.h>

#include <bm/bm_sim/target_parser.h>

 

using namespace ns3;

 

NS_LOG_COMPONENT_DEFINE ("P4Test");

 

int main (int argc, char *argv[]) {

 

    LogComponentEnable ("P4Test", LOG_LEVEL_LOGIC);

    LogComponentEnable ("P4Helper", LOG_LEVEL_LOGIC);

    LogComponentEnable ("P4NetDevice", LOG_LEVEL_LOGIC);

 

    // Initialize global variable

    //

    P4GlobalVar::g_homePath = "/home/user/ns4-install/";

    P4GlobalVar::g_ns3RootName = "ns-allinone-3.27/";

    P4GlobalVar::g_ns3SrcName = "ns-3.27/";

    P4GlobalVar::g_nfDir = P4GlobalVar::g_homePath + P4GlobalVar::g_ns3RootName + P4GlobalVar::g_ns3SrcName + "src/ns4/test/";

    P4GlobalVar::g_nsType=NS4;

    P4GlobalVar::g_runtimeCliTime = 100;

 

    CommandLine cmd;

    cmd.Parse (argc, argv);

 

    NS_LOG_INFO ("Create nodes.");

    NodeContainer nodes;

    nodes.Create(2);

 

    NodeContainer csmaSwitch;

    csmaSwitch.Create(1);

 

    NS_LOG_INFO ("Build Topology");

    CsmaHelper csma;

    csma.SetChannelAttribute ("DataRate", DataRateValue (5000000));

    csma.SetChannelAttribute ("Delay", TimeValue (MilliSeconds (2)));

 

    NetDeviceContainer terminalDevices;

    NetDeviceContainer switchDevices;

 

    for (int i = 0; i < 2; i ++) {

        NetDeviceContainer link = csma.Install(NodeContainer(nodes.Get(i), csmaSwitch.Get(0)));

        terminalDevices.Add(link.Get(0));

        switchDevices.Add(link.Get(1));

    }

 

    Ptr<Node> switchNode = csmaSwitch.Get(0);

 

    if (P4GlobalVar::g_nsType==NS4) //ns4 mode

    {

        P4GlobalVar::g_populateFlowTableWay=LOCAL_CALL;//LOCAL_CALL RUNTIME_CLI

        P4GlobalVar::g_networkFunc=L2_SWITCH;

        P4GlobalVar::SetP4MatchTypeJsonPath();

        P4GlobalVar::g_flowTablePath=P4GlobalVar::g_nfDir+"l2_switch/commands.txt";

 

        P4Helper bridge;

        NS_LOG_INFO("P4 bridge established");

        bridge.Install (switchNode, switchDevices);

    } else {  //ns3 mode

       BridgeHelper bridge;

       bridge.Install (switchNode, switchDevices);

    }

 

    InternetStackHelper internet;

    internet.Install (nodes);

 

    Ipv4AddressHelper ipv4;

    ipv4.SetBase ("10.1.1.0", "255.255.255.0");

    Ipv4InterfaceContainer addresses = ipv4.Assign(terminalDevices);

 

    //we use static arp. No ARP packets will be sent.

    Ptr<ArpCache> arp = CreateObject<ArpCache>();

    arp->SetAliveTimeout(Seconds(3600 * 24 * 365));

    ArpCache::Entry * entry = arp->Add(Ipv4Address ("10.1.1.1"));

    entry->SetMacAddress(Mac48Address("00:00:00:00:00:01"));

    entry->MarkPermanent();

    std::pair<Ptr<Ipv4>, uint32_t> returnValue = addresses.Get(1);

    Ptr<Ipv4> myipv4 = returnValue.first;

    uint32_t index = returnValue.second;

    Ptr<Ipv4Interface> iface =  myipv4->GetObject<Ipv4L3Protocol> ()->GetInterface (index);

    iface->SetAttribute("ArpCache", PointerValue(arp));

 

    arp = CreateObject<ArpCache>();

    arp->SetAliveTimeout(Seconds(3600 * 24 * 365));

    entry = arp->Add(Ipv4Address ("10.1.1.2"));

    entry->SetMacAddress(Mac48Address("00:00:00:00:00:03"));

    entry->MarkPermanent();

    returnValue = addresses.Get(0);

    myipv4 = returnValue.first;

    index = returnValue.second;

    iface =  myipv4->GetObject<Ipv4L3Protocol> ()->GetInterface (index);

    iface->SetAttribute("ArpCache", PointerValue(arp));

 

    // Create Applications

    NS_LOG_INFO ("Create Applications.");

 

    V4PingHelper pingHelper = V4PingHelper (addresses.GetAddress(1));

    pingHelper.SetAttribute ("Verbose", BooleanValue (true));

    ApplicationContainer pingApps = pingHelper.Install (nodes.Get(0));

    pingApps.Start (Seconds (1));

 

    Packet::EnablePrinting ();

 

    Simulator::Stop (Seconds (3));

    Simulator::Run ();

    Simulator::Destroy ();

    return 0;

}

 

Execution

 

Back to NS3 Learning Guide

Last Modified: 2022/2/28 done

 

[Author]

Dr. Chih-Heng Ke

Department of Computer Science and Information Engineering, National Quemoy University, Kinmen, Taiwan

Email: smallko@gmail.com