/*
 * SPDX-License-Identifier: GPL-2.0-only
 */

#include "ns3/boolean.h"
#include "ns3/bridge-helper.h"
#include "ns3/config.h"
#include "ns3/global-route-manager.h"
#include "ns3/global-routing.h"
#include "ns3/inet-socket-address.h"
#include "ns3/internet-stack-helper.h"
#include "ns3/ipv4-address-helper.h"
#include "ns3/ipv4-global-routing-helper.h"
#include "ns3/ipv4-interface.h"
#include "ns3/ipv4-l3-protocol.h"
#include "ns3/ipv4-packet-info-tag.h"
#include "ns3/ipv4-routing-protocol.h"
#include "ns3/ipv4-routing-table-entry.h"
#include "ns3/ipv4-static-routing-helper.h"
#include "ns3/ipv6-address-helper.h"
#include "ns3/ipv6-global-routing-helper.h"
#include "ns3/ipv6-packet-info-tag.h"
#include "ns3/ipv6-route.h"
#include "ns3/log.h"
#include "ns3/neighbor-cache-helper.h"
#include "ns3/node-container.h"
#include "ns3/node.h"
#include "ns3/packet.h"
#include "ns3/pointer.h"
#include "ns3/simple-channel.h"
#include "ns3/simple-net-device-helper.h"
#include "ns3/simple-net-device.h"
#include "ns3/simulator.h"
#include "ns3/socket-factory.h"
#include "ns3/string.h"
#include "ns3/test.h"
#include "ns3/udp-socket-factory.h"
#include "ns3/uinteger.h"

#include <vector>
using namespace ns3;

NS_LOG_COMPONENT_DEFINE("GlobalRoutingTestSuite");

// This test suite tests the operation of global routing on a few sample
// networks to ensure that routes are built correctly
//
//  Link test:
//      n0 <--------> n1  (point-to-point link)
//  10.1.1.1        10.1.1.2
//      Expected routes:
//         n0:  route to 0.0.0.0 gw 10.1.1.2
//         n1:  route to 0.0.0.0 gw 10.1.1.1
//      Note:  These default routes to 0.0.0.0 are generated by the extension
//             in the global route manager to install default routes via the
//             peer node on a point-to-point link, when the node is on a
//             stub link
//
//  LAN test:
//      n0 <--------> n1  (broadcast link on subnet 10.1.1.0/24)
//      Expected routes:
//         n0:  route to 10.1.1.0 gw 0.0.0.0
//         n1:  route to 10.1.1.0 gw 0.0.0.0
//  Two link test:
//      n0 <--------> n1 <--------> n2  (point-to-point links)
//  10.1.1.1       10.1.1.2/     10.1.2.2
//                 10.1.2.1
//      Expected routes:
//         n0:  route to 0.0.0.0 gw 10.1.1.2
//         n1:  route to 10.1.1.1 gw 10.1.1.1
//              route to 10.1.2.2 gw 10.1.2.2
//              route to 10.1.1.0 gw 10.1.1.1
//              route to 10.1.2.0 gw 10.1.2.2
//         n2:  route to 0.0.0.0 gw 10.1.2.1
//      Note:  These default routes to 0.0.0.0 are generated by the extension
//             in the global route manager to install default routes via the
//             peer node on a point-to-point link, when the node is on a
//             stub link
//  Two LANs test:
//      n0 <--------> n1 <--------> n2  (broadcast links)
//      Expected routes:
//         n0:  route to 10.1.1.0 gw 0.0.0.0
//              route to 0.0.0.0 gw 10.1.1.2
//         n1:  route to 10.1.1.1 gw 10.1.1.1
//              route to 10.1.2.2 gw 10.1.2.2
//              route to 10.1.1.0 gw 10.1.1.1
//              route to 10.1.2.0 gw 10.1.2.2
//         n2:  route to 0.0.0.0 gw 10.1.2.1
//  Bridge test:
//      n0 <--------> n1 <---> Bridge-n2 <---> n3 <-------> n4 (broadcast links)
//         10.1.1.0/24        10.1.2.0/24        10.1.3.0/24
//      Expected routes:
//         n0:  route to 10.1.1.0 gw 0.0.0.0
//              route to 10.1.2.0 gw 10.1.1.2
//              route to 10.1.3.0 gw 10.1.1.2
//         n1:  route to 10.1.1.0 gw 0.0.0.0
//              route to 10.1.2.0 gw 0.0.0.0
//              route to 10.1.3.0 gw 10.1.2.2
//         n3:  route to 10.1.1.0 gw 10.1.2.1
//              route to 10.1.2.0 gw 0.0.0.0
//              route to 10.1.3.0 gw 0.0.0.0
//         n4:  route to 10.1.3.0 gw 0.0.0.0
//              route to 10.1.2.0 gw 10.1.3.1
//              route to 10.1.1.0 gw 10.1.3.1
//  Two Bridge test:
//      n0 <------> n1 <---> Bridge-n2 <---> Bridge-n3 <---> n4 (broadcast links)
//         10.1.1.0/24        10.1.2.0/24
//      Expected routes:
//         n0:  route to 10.1.1.0 gw 0.0.0.0
//              route to 10.1.2.0 gw 10.1.1.2
//         n4:  route to 10.1.2.0 gw 0.0.0.0
//              route to 10.1.1.0 gw 10.1.2.1

/**
 * @ingroup internet-test
 *
 * @brief GlobalRouting Link test
 */
class LinkTest : public TestCase
{
  public:
    void DoSetup() override;
    void DoRun() override;
    LinkTest();

  private:
    NodeContainer m_nodes; //!< Nodes used in the test.
};

LinkTest::LinkTest()
    : TestCase("Global routing on point-to-point link")
{
}

void
LinkTest::DoSetup()
{
    m_nodes.Create(2);
    Ptr<SimpleChannel> channel = CreateObject<SimpleChannel>();
    SimpleNetDeviceHelper simpleHelper;
    simpleHelper.SetNetDevicePointToPointMode(true);
    NetDeviceContainer net = simpleHelper.Install(m_nodes, channel);

    InternetStackHelper internet;
    // By default, InternetStackHelper adds a static and global routing
    // implementation.  We just want the global for this test.
    Ipv4GlobalRoutingHelper ipv4RoutingHelper;
    Ipv6GlobalRoutingHelper ipv6RoutingHelper;
    internet.SetRoutingHelper(ipv4RoutingHelper);
    internet.SetRoutingHelper(ipv6RoutingHelper);
    internet.Install(m_nodes);

    Ipv4AddressHelper ipv4;
    ipv4.SetBase("10.1.1.0", "255.255.255.252");
    Ipv4InterfaceContainer iv4 = ipv4.Assign(net);

    Ipv6AddressHelper ipv6;
    ipv6.SetBase(Ipv6Address("2001:0::"), Ipv6Prefix(64));
    Ipv6InterfaceContainer iv6 = ipv6.Assign(net);
}

void
LinkTest::DoRun()
{
    Ipv4GlobalRoutingHelper::PopulateRoutingTables();
    Ipv6GlobalRoutingHelper::PopulateRoutingTables();

    //----------Ipv4 Tests-------------------------
    Ptr<Ipv4L3Protocol> ipv40 = m_nodes.Get(0)->GetObject<Ipv4L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv40, nullptr, "Error-- no Ipv4 object");
    Ptr<Ipv4L3Protocol> ipv41 = m_nodes.Get(1)->GetObject<Ipv4L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv41, nullptr, "Error-- no Ipv4 object");
    Ptr<Ipv4RoutingProtocol> routing0 = ipv40->GetRoutingProtocol();
    Ptr<Ipv4GlobalRouting> globalRoutingV40 = routing0->GetObject<Ipv4GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingV40, nullptr, "Error-- no Ipv4GlobalRouting object");
    Ptr<Ipv4RoutingProtocol> routingV41 = ipv41->GetRoutingProtocol();
    Ptr<Ipv4GlobalRouting> globalRoutingV41 = routingV41->GetObject<Ipv4GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingV41, nullptr, "Error-- no Ipv4GlobalRouting object");

    // Test that the right number of routes found
    uint32_t nRoutesV40 = globalRoutingV40->GetNRoutes();
    NS_LOG_DEBUG("LinkTest nRoutes0 " << nRoutesV40);
    NS_TEST_ASSERT_MSG_EQ(nRoutesV40, 1, "Error-- not one route");
    Ipv4RoutingTableEntry* routeV4 = globalRoutingV40->GetRoute(0);
    NS_LOG_DEBUG("entry dest " << routeV4->GetDest() << " gw " << routeV4->GetGateway());
    NS_TEST_ASSERT_MSG_EQ(routeV4->GetDest(), Ipv4Address("0.0.0.0"), "Error-- wrong destination");
    NS_TEST_ASSERT_MSG_EQ(routeV4->GetGateway(), Ipv4Address("10.1.1.2"), "Error-- wrong gateway");

    // Test that the right number of routes found
    uint32_t nRoutesV41 = globalRoutingV41->GetNRoutes();
    NS_TEST_ASSERT_MSG_EQ(nRoutesV41, 1, "Error-- not one route");
    NS_LOG_DEBUG("LinkTest nRoutes1 " << nRoutesV41);
    routeV4 = globalRoutingV41->GetRoute(0);
    NS_LOG_DEBUG("entry dest " << routeV4->GetDest() << " gw " << routeV4->GetGateway());
    NS_TEST_ASSERT_MSG_EQ(routeV4->GetDest(), Ipv4Address("0.0.0.0"), "Error-- wrong destination");
    NS_TEST_ASSERT_MSG_EQ(routeV4->GetGateway(), Ipv4Address("10.1.1.1"), "Error-- wrong gateway");

    //-------------Ipv6 Tests---------------------
    Ptr<Ipv6L3Protocol> ipv60 = m_nodes.Get(0)->GetObject<Ipv6L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv60, nullptr, "Error-- no Ipv6 object");
    Ptr<Ipv6L3Protocol> ipv61 = m_nodes.Get(1)->GetObject<Ipv6L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv61, nullptr, "Error-- no Ipv6 object");
    Ptr<Ipv6RoutingProtocol> routingV60 = ipv60->GetRoutingProtocol();
    Ptr<Ipv6GlobalRouting> globalRoutingV60 = routingV60->GetObject<Ipv6GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingV60, nullptr, "Error-- no Ipv6GlobalRouting object");
    Ptr<Ipv6RoutingProtocol> routingV61 = ipv61->GetRoutingProtocol();
    Ptr<Ipv6GlobalRouting> globalRoutingV61 = routingV61->GetObject<Ipv6GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingV61, nullptr, "Error-- no Ipv6GlobalRouting object");

    // Test that the right number of routes found
    uint32_t nRoutesV60 = globalRoutingV60->GetNRoutes();
    NS_LOG_DEBUG("LinkTest nRoutes0 " << nRoutesV60);
    NS_TEST_ASSERT_MSG_EQ(nRoutesV60, 1, "Error-- not one route");
    Ipv6RoutingTableEntry* routeV6 = globalRoutingV60->GetRoute(0);
    NS_LOG_DEBUG("entry dest " << routeV6->GetDest() << " gw " << routeV6->GetGateway());
    NS_TEST_ASSERT_MSG_EQ(routeV6->GetDest(), Ipv6Address("::"), "Error-- wrong destination");
    NS_TEST_ASSERT_MSG_EQ(routeV6->GetDestNetworkPrefix(),
                          Ipv6Prefix::GetZero(),
                          "Error-- wrong destination mask");
    NS_TEST_ASSERT_MSG_EQ(routeV6->GetGateway(),
                          Ipv6Address("fe80::200:ff:fe00:2"),
                          "Error-- wrong gateway");

    // Test that the right number of routes found
    uint32_t nRoutesV61 = globalRoutingV61->GetNRoutes();
    NS_TEST_ASSERT_MSG_EQ(nRoutesV61, 1, "Error-- not one route");
    NS_LOG_DEBUG("LinkTest nRoutes1 " << nRoutesV61);
    routeV6 = globalRoutingV61->GetRoute(0);
    NS_LOG_DEBUG("entry dest " << routeV6->GetDest() << " gw " << routeV6->GetGateway());
    NS_TEST_ASSERT_MSG_EQ(routeV6->GetDest(), Ipv6Address("::"), "Error-- wrong destination");
    NS_TEST_ASSERT_MSG_EQ(routeV6->GetDestNetworkPrefix(),
                          Ipv6Prefix::GetZero(),
                          "Error-- wrong destination mask");
    NS_TEST_ASSERT_MSG_EQ(routeV6->GetGateway(),
                          Ipv6Address("fe80::200:ff:fe00:1"),
                          "Error-- wrong gateway");

    Simulator::Run();
    Simulator::Stop(Seconds(10));
    Simulator::Destroy();
}

/**
 * @ingroup internet-test
 *
 * @brief GlobalRouting LAN test
 */
class LanTest : public TestCase
{
  public:
    void DoSetup() override;
    void DoRun() override;
    LanTest();

  private:
    NodeContainer m_nodes; //!< Nodes used in the test.
};

LanTest::LanTest()
    : TestCase("Global routing on broadcast link")
{
}

void
LanTest::DoSetup()
{
    m_nodes.Create(2);

    Ptr<SimpleChannel> channel = CreateObject<SimpleChannel>();
    SimpleNetDeviceHelper simpleHelper;
    NetDeviceContainer net = simpleHelper.Install(m_nodes, channel);

    InternetStackHelper internet;
    // By default, InternetStackHelper adds a static and global routing
    // implementation.  We just want the global for this test.
    Ipv4GlobalRoutingHelper ipv4RoutingHelper;
    Ipv6GlobalRoutingHelper ipv6RoutingHelper;
    internet.SetRoutingHelper(ipv4RoutingHelper);
    internet.SetRoutingHelper(ipv6RoutingHelper);
    internet.Install(m_nodes);

    Ipv4AddressHelper ipv4;
    ipv4.SetBase("10.1.1.0", "255.255.255.0");
    Ipv4InterfaceContainer i = ipv4.Assign(net);

    Ipv6AddressHelper ipv6;
    ipv6.SetBase(Ipv6Address("2001:0::"), Ipv6Prefix(64));
    Ipv6InterfaceContainer iv6 = ipv6.Assign(net);
}

void
LanTest::DoRun()
{
    Ipv4GlobalRoutingHelper::PopulateRoutingTables();
    Ipv6GlobalRoutingHelper::PopulateRoutingTables();

    //----------Ipv4 Tests-------------------------------
    Ptr<Ipv4L3Protocol> ipv40 = m_nodes.Get(0)->GetObject<Ipv4L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv40, nullptr, "Error-- no Ipv4 object");
    Ptr<Ipv4L3Protocol> ipv41 = m_nodes.Get(1)->GetObject<Ipv4L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv41, nullptr, "Error-- no Ipv4 object");
    Ptr<Ipv4RoutingProtocol> routingv40 = ipv40->GetRoutingProtocol();
    Ptr<Ipv4GlobalRouting> globalRoutingv40 = routingv40->GetObject<Ipv4GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingv40, nullptr, "Error-- no Ipv4GlobalRouting object");
    Ptr<Ipv4RoutingProtocol> routingv41 = ipv41->GetRoutingProtocol();
    Ptr<Ipv4GlobalRouting> globalRoutingv41 = routingv41->GetObject<Ipv4GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingv41, nullptr, "Error-- no Ipv4GlobalRouting object");

    // Test that the right number of routes found
    uint32_t nRoutesv40 = globalRoutingv40->GetNRoutes();
    NS_LOG_DEBUG("LanTest nRoutes0 " << nRoutesv40);
    NS_TEST_ASSERT_MSG_EQ(nRoutesv40, 1, "Error-- more than one entry");
    for (uint32_t i = 0; i < globalRoutingv40->GetNRoutes(); i++)
    {
        Ipv4RoutingTableEntry* routev4 = globalRoutingv40->GetRoute(i);
        NS_LOG_DEBUG("entry dest " << routev4->GetDest() << " gw " << routev4->GetGateway());
    }

    // Test that the right number of routes found
    uint32_t nRoutesv41 = globalRoutingv41->GetNRoutes();
    NS_LOG_DEBUG("LanTest nRoutes1 " << nRoutesv41);
    NS_TEST_ASSERT_MSG_EQ(nRoutesv41, 1, "Error-- more than one entry");
    for (uint32_t i = 0; i < globalRoutingv40->GetNRoutes(); i++)
    {
        Ipv4RoutingTableEntry* routev4 = globalRoutingv41->GetRoute(i);
        NS_LOG_DEBUG("entry dest " << routev4->GetDest() << " gw " << routev4->GetGateway());
    }

    //-------------Ipv6 Tests----------------------
    Ptr<Ipv6L3Protocol> ipv60 = m_nodes.Get(0)->GetObject<Ipv6L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv60, nullptr, "Error-- no Ipv6 object");
    Ptr<Ipv6L3Protocol> ipv61 = m_nodes.Get(1)->GetObject<Ipv6L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv61, nullptr, "Error-- no Ipv6 object");
    Ptr<Ipv6RoutingProtocol> routingv60 = ipv60->GetRoutingProtocol();
    Ptr<Ipv6GlobalRouting> globalRoutingv60 = routingv60->GetObject<Ipv6GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingv60, nullptr, "Error-- no Ipv6GlobalRouting object");
    Ptr<Ipv6RoutingProtocol> routingv61 = ipv61->GetRoutingProtocol();
    Ptr<Ipv6GlobalRouting> globalRoutingv61 = routingv61->GetObject<Ipv6GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingv61, nullptr, "Error-- no Ipv6GlobalRouting object");

    // Test that the right number of routes found
    uint32_t nRoutesv60 = globalRoutingv60->GetNRoutes();
    NS_LOG_DEBUG("LanTest nRoutes0 " << nRoutesv60);
    NS_TEST_ASSERT_MSG_EQ(nRoutesv60, 1, "Error-- more than one entry");
    for (uint32_t i = 0; i < globalRoutingv60->GetNRoutes(); i++)
    {
        Ipv6RoutingTableEntry* routev6 = globalRoutingv60->GetRoute(i);
        NS_LOG_DEBUG("entry dest " << routev6->GetDest() << routev6->GetDestNetworkPrefix()
                                   << " gw " << routev6->GetGateway());
    }

    // Test that the right number of routes found
    uint32_t nRoutesv61 = globalRoutingv61->GetNRoutes();
    NS_LOG_DEBUG("LanTest nRoutes1 " << nRoutesv61);
    NS_TEST_ASSERT_MSG_EQ(nRoutesv61, 1, "Error-- more than one entry");
    for (uint32_t i = 0; i < globalRoutingv60->GetNRoutes(); i++)
    {
        Ipv6RoutingTableEntry* routev6 = globalRoutingv61->GetRoute(i);
        NS_LOG_DEBUG("entry dest " << routev6->GetDest() << routev6->GetDestNetworkPrefix()
                                   << " gw " << routev6->GetGateway());
    }

    Simulator::Run();
    Simulator::Destroy();
}

/**
 * @ingroup internet-test
 *
 * @brief GlobalRouting Two Link test
 */
class TwoLinkTest : public TestCase
{
  public:
    void DoSetup() override;
    void DoRun() override;
    TwoLinkTest();

  private:
    NodeContainer m_nodes; //!< Nodes used in the test.
};

TwoLinkTest::TwoLinkTest()
    : TestCase("Global routing across two hops (point-to-point links)")
{
}

void
TwoLinkTest::DoSetup()
{
    m_nodes.Create(3);

    Ptr<SimpleChannel> channel = CreateObject<SimpleChannel>();
    SimpleNetDeviceHelper simpleHelper;
    simpleHelper.SetNetDevicePointToPointMode(true);
    NetDeviceContainer net = simpleHelper.Install(m_nodes.Get(0), channel);
    net.Add(simpleHelper.Install(m_nodes.Get(1), channel));

    Ptr<SimpleChannel> channel2 = CreateObject<SimpleChannel>();
    SimpleNetDeviceHelper simpleHelper2;
    simpleHelper2.SetNetDevicePointToPointMode(true);
    NetDeviceContainer net2 = simpleHelper.Install(m_nodes.Get(1), channel2);
    net2.Add(simpleHelper2.Install(m_nodes.Get(2), channel2));

    InternetStackHelper internet;
    // By default, InternetStackHelper adds a static and global routing
    // implementation.  We just want the global for this test.
    Ipv4GlobalRoutingHelper ipv4RoutingHelper;
    Ipv6GlobalRoutingHelper ipv6RoutingHelper;
    internet.SetRoutingHelper(ipv4RoutingHelper);
    internet.SetRoutingHelper(ipv6RoutingHelper);

    internet.Install(m_nodes);

    Ipv4AddressHelper ipv4;
    ipv4.SetBase("10.1.1.0", "255.255.255.252");
    Ipv4InterfaceContainer i = ipv4.Assign(net);
    ipv4.SetBase("10.1.2.0", "255.255.255.252");
    Ipv4InterfaceContainer i2 = ipv4.Assign(net2);

    Ipv6AddressHelper ipv6;
    ipv6.SetBase(Ipv6Address("2001:0::"), Ipv6Prefix(64));
    ipv6.Assign(net);
    ipv6.SetBase(Ipv6Address("2001:1::"), Ipv6Prefix(64));
    ipv6.Assign(net2);
}

void
TwoLinkTest::DoRun()
{
    Ipv4GlobalRoutingHelper::PopulateRoutingTables();
    Ipv6GlobalRoutingHelper::PopulateRoutingTables();

    //-----Ipv4 Tests-----------------
    Ptr<Ipv4L3Protocol> ipv40 = m_nodes.Get(0)->GetObject<Ipv4L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv40, nullptr, "Error-- no Ipv4 object");
    Ptr<Ipv4L3Protocol> ipv41 = m_nodes.Get(1)->GetObject<Ipv4L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv41, nullptr, "Error-- no Ipv4 object");
    Ptr<Ipv4L3Protocol> ipv42 = m_nodes.Get(2)->GetObject<Ipv4L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv42, nullptr, "Error-- no Ipv4 object");
    Ptr<Ipv4RoutingProtocol> routingv40 = ipv40->GetRoutingProtocol();
    Ptr<Ipv4GlobalRouting> globalRoutingv40 = routingv40->GetObject<Ipv4GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingv40, nullptr, "Error-- no Ipv4GlobalRouting object");
    Ptr<Ipv4RoutingProtocol> routingv41 = ipv41->GetRoutingProtocol();
    Ptr<Ipv4GlobalRouting> globalRoutingv41 = routingv41->GetObject<Ipv4GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingv41, nullptr, "Error-- no Ipv4GlobalRouting object");
    Ptr<Ipv4RoutingProtocol> routingv42 = ipv42->GetRoutingProtocol();
    Ptr<Ipv4GlobalRouting> globalRoutingv42 = routingv42->GetObject<Ipv4GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingv42, nullptr, "Error-- no Ipv4GlobalRouting object");

    // node n0
    // Test that the right number of routes found
    uint32_t nRoutesv40 = globalRoutingv40->GetNRoutes();
    NS_LOG_DEBUG("TwoLinkTest nRoutes0 " << nRoutesv40);
    NS_TEST_ASSERT_MSG_EQ(nRoutesv40, 1, "Error-- wrong number of links");

    Ipv4RoutingTableEntry* routev4 = globalRoutingv40->GetRoute(0);
    NS_LOG_DEBUG("entry dest " << routev4->GetDest() << " gw " << routev4->GetGateway());
    NS_TEST_ASSERT_MSG_EQ(routev4->GetDest(), Ipv4Address("0.0.0.0"), "Error-- wrong destination");
    NS_TEST_ASSERT_MSG_EQ(routev4->GetGateway(), Ipv4Address("10.1.1.2"), "Error-- wrong gateway");

    // node n1
    // Test that the right number of routes found
    uint32_t nRoutesv41 = globalRoutingv41->GetNRoutes();
    NS_LOG_DEBUG("TwoLinkTest nRoutes1 " << nRoutesv41);
    NS_TEST_ASSERT_MSG_EQ(nRoutesv41, 4, "Error-- not one route");

    routev4 = globalRoutingv41->GetRoute(0);
    NS_LOG_DEBUG("TwoLinkTest entry dest " << routev4->GetDest() << " gw "
                                           << routev4->GetGateway());
    NS_TEST_ASSERT_MSG_EQ(routev4->GetDest(), Ipv4Address("10.1.1.1"), "Error-- wrong destination");
    NS_TEST_ASSERT_MSG_EQ(routev4->GetGateway(), Ipv4Address("10.1.1.1"), "Error-- wrong gateway");
    routev4 = globalRoutingv41->GetRoute(1);
    NS_LOG_DEBUG("TwoLinkTest entry dest " << routev4->GetDest() << " gw "
                                           << routev4->GetGateway());
    NS_TEST_ASSERT_MSG_EQ(routev4->GetDest(), Ipv4Address("10.1.2.2"), "Error-- wrong destination");
    NS_TEST_ASSERT_MSG_EQ(routev4->GetGateway(), Ipv4Address("10.1.2.2"), "Error-- wrong gateway");
    routev4 = globalRoutingv41->GetRoute(2);
    NS_LOG_DEBUG("TwoLinkTest entry dest " << routev4->GetDest() << " gw "
                                           << routev4->GetGateway());
    NS_TEST_ASSERT_MSG_EQ(routev4->GetDest(), Ipv4Address("10.1.1.0"), "Error-- wrong destination");
    NS_TEST_ASSERT_MSG_EQ(routev4->GetGateway(), Ipv4Address("10.1.1.1"), "Error-- wrong gateway");
    routev4 = globalRoutingv41->GetRoute(3);
    NS_LOG_DEBUG("TwoLinkTest entry dest " << routev4->GetDest() << " gw "
                                           << routev4->GetGateway());
    NS_TEST_ASSERT_MSG_EQ(routev4->GetDest(), Ipv4Address("10.1.2.0"), "Error-- wrong destination");
    NS_TEST_ASSERT_MSG_EQ(routev4->GetGateway(), Ipv4Address("10.1.2.2"), "Error-- wrong gateway");

    // node n2
    // Test that the right number of routes found
    uint32_t nRoutesv42 = globalRoutingv42->GetNRoutes();
    NS_LOG_DEBUG("TwoLinkTest nRoutes2 " << nRoutesv42);
    NS_TEST_ASSERT_MSG_EQ(nRoutesv42, 1, "Error-- wrong number of links");

    routev4 = globalRoutingv42->GetRoute(0);
    NS_LOG_DEBUG("entry dest " << routev4->GetDest() << " gw " << routev4->GetGateway());
    NS_TEST_ASSERT_MSG_EQ(routev4->GetDest(), Ipv4Address("0.0.0.0"), "Error-- wrong destination");
    NS_TEST_ASSERT_MSG_EQ(routev4->GetGateway(), Ipv4Address("10.1.2.1"), "Error-- wrong gateway");

    //--------------Ipv6 Tests-------------
    Ptr<Ipv6L3Protocol> ipv60 = m_nodes.Get(0)->GetObject<Ipv6L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv60, nullptr, "Error-- no Ipv6 object");
    Ptr<Ipv6L3Protocol> ipv61 = m_nodes.Get(1)->GetObject<Ipv6L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv61, nullptr, "Error-- no Ipv6 object");
    Ptr<Ipv6L3Protocol> ipv62 = m_nodes.Get(2)->GetObject<Ipv6L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv62, nullptr, "Error-- no Ipv6 object");
    Ptr<Ipv6RoutingProtocol> routingv60 = ipv60->GetRoutingProtocol();
    Ptr<Ipv6GlobalRouting> globalRoutingv60 = routingv60->GetObject<Ipv6GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingv60, nullptr, "Error-- no Ipv6GlobalRouting object");
    Ptr<Ipv6RoutingProtocol> routingv61 = ipv61->GetRoutingProtocol();
    Ptr<Ipv6GlobalRouting> globalRoutingv61 = routingv61->GetObject<Ipv6GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingv61, nullptr, "Error-- no Ipv6GlobalRouting object");
    Ptr<Ipv6RoutingProtocol> routingv62 = ipv62->GetRoutingProtocol();
    Ptr<Ipv6GlobalRouting> globalRoutingv62 = routingv62->GetObject<Ipv6GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingv62, nullptr, "Error-- no Ipv4GlobalRouting object");

    // node n0
    // Test that the right number of routes found
    uint32_t nRoutesv60 = globalRoutingv60->GetNRoutes();
    NS_LOG_DEBUG("TwoLinkTest nRoutes0 " << nRoutesv60);
    NS_TEST_ASSERT_MSG_EQ(nRoutesv60, 1, "Error-- wrong number of links");

    Ipv6RoutingTableEntry* routev6 = globalRoutingv60->GetRoute(0);
    NS_LOG_DEBUG("entry dest " << routev6->GetDest() << " gw " << routev6->GetGateway());
    NS_TEST_ASSERT_MSG_EQ(routev6->GetDest(), Ipv6Address("::"), "Error-- wrong destination");
    NS_TEST_ASSERT_MSG_EQ(routev6->GetDestNetworkPrefix(),
                          Ipv6Prefix::GetZero(),
                          "Error-- wrong destination mask");
    NS_TEST_ASSERT_MSG_EQ(routev6->GetGateway(),
                          Ipv6Address("fe80::200:ff:fe00:2"),
                          "Error-- wrong gateway");

    // node n1
    // Test that the right number of routes found
    uint32_t nRoutesv61 = globalRoutingv61->GetNRoutes();
    NS_LOG_DEBUG("TwoLinkTest nRoutes1 " << nRoutesv61);
    NS_TEST_ASSERT_MSG_EQ(nRoutesv61, 4, "Error-- not one route");

    routev6 = globalRoutingv61->GetRoute(0);
    NS_LOG_DEBUG("TwoLinkTest entry dest " << routev6->GetDest() << " gw "
                                           << routev6->GetGateway());
    NS_TEST_ASSERT_MSG_EQ(routev6->GetDest(),
                          Ipv6Address("2001::200:ff:fe00:1"),
                          "Error-- wrong destination");
    NS_TEST_ASSERT_MSG_EQ(routev6->GetDestNetworkPrefix(),
                          Ipv6Prefix(128),
                          "Error-- wrong destination mask");
    NS_TEST_ASSERT_MSG_EQ(routev6->GetGateway(),
                          Ipv6Address("fe80::200:ff:fe00:1"),
                          "Error-- wrong gateway");

    routev6 = globalRoutingv61->GetRoute(1);
    NS_LOG_DEBUG("TwoLinkTest entry dest " << routev6->GetDest() << " gw "
                                           << routev6->GetGateway());
    NS_TEST_ASSERT_MSG_EQ(routev6->GetDest(),
                          Ipv6Address("2001:1::200:ff:fe00:4"),
                          "Error-- wrong destination");
    NS_TEST_ASSERT_MSG_EQ(routev6->GetDestNetworkPrefix(),
                          Ipv6Prefix(128),
                          "Error-- wrong destination mask");
    NS_TEST_ASSERT_MSG_EQ(routev6->GetGateway(),
                          Ipv6Address("fe80::200:ff:fe00:4"),
                          "Error-- wrong gateway");

    routev6 = globalRoutingv61->GetRoute(2);
    NS_LOG_DEBUG("TwoLinkTest entry dest " << routev6->GetDest() << " gw "
                                           << routev6->GetGateway());
    NS_TEST_ASSERT_MSG_EQ(routev6->GetDest(), Ipv6Address("2001::"), "Error-- wrong destination");
    NS_TEST_ASSERT_MSG_EQ(routev6->GetDestNetworkPrefix(),
                          Ipv6Prefix(64),
                          "Error-- wrong destination mask");
    NS_TEST_ASSERT_MSG_EQ(routev6->GetGateway(),
                          Ipv6Address("fe80::200:ff:fe00:1"),
                          "Error-- wrong gateway");

    routev6 = globalRoutingv61->GetRoute(3);
    NS_LOG_DEBUG("TwoLinkTest entry dest " << routev6->GetDest() << " gw "
                                           << routev6->GetGateway());
    NS_TEST_ASSERT_MSG_EQ(routev6->GetDest(), Ipv6Address("2001:1::"), "Error-- wrong destination");
    NS_TEST_ASSERT_MSG_EQ(routev6->GetDestNetworkPrefix(),
                          Ipv6Prefix(64),
                          "Error-- wrong destination mask");
    NS_TEST_ASSERT_MSG_EQ(routev6->GetGateway(),
                          Ipv6Address("fe80::200:ff:fe00:4"),
                          "Error-- wrong gateway");

    // node n2
    // Test that the right number of routes found
    uint32_t nRoutesv62 = globalRoutingv62->GetNRoutes();
    NS_LOG_DEBUG("TwoLinkTest nRoutes2 " << nRoutesv62);
    NS_TEST_ASSERT_MSG_EQ(nRoutesv62, 1, "Error-- wrong number of links");

    routev6 = globalRoutingv62->GetRoute(0);
    NS_LOG_DEBUG("entry dest " << routev6->GetDest() << " gw " << routev6->GetGateway());
    NS_TEST_ASSERT_MSG_EQ(routev6->GetDest(), Ipv6Address("::"), "Error-- wrong destination");
    NS_TEST_ASSERT_MSG_EQ(routev6->GetDestNetworkPrefix(),
                          Ipv6Prefix::GetZero(),
                          "Error-- wrong destination mask");
    NS_TEST_ASSERT_MSG_EQ(routev6->GetGateway(),
                          Ipv6Address("fe80::200:ff:fe00:3"),
                          "Error-- wrong gateway");

    Simulator::Run();
    Simulator::Destroy();
}

/**
 * @ingroup internet-test
 *
 * @brief GlobalRouting Two LAN test
 */
class TwoLanTest : public TestCase
{
  public:
    void DoSetup() override;
    void DoRun() override;
    TwoLanTest();

  private:
    NodeContainer m_nodes; //!< Nodes used in the test.
};

TwoLanTest::TwoLanTest()
    : TestCase("Global routing across two hops (broadcast links)")
{
}

void
TwoLanTest::DoSetup()
{
    m_nodes.Create(3);

    Ptr<SimpleChannel> channel = CreateObject<SimpleChannel>();
    SimpleNetDeviceHelper simpleHelper;
    NetDeviceContainer net = simpleHelper.Install(m_nodes.Get(0), channel);
    net.Add(simpleHelper.Install(m_nodes.Get(1), channel));

    Ptr<SimpleChannel> channel2 = CreateObject<SimpleChannel>();
    SimpleNetDeviceHelper simpleHelper2;
    NetDeviceContainer net2 = simpleHelper.Install(m_nodes.Get(1), channel2);
    net2.Add(simpleHelper2.Install(m_nodes.Get(2), channel2));

    InternetStackHelper internet;
    // By default, InternetStackHelper adds a static and global routing
    // implementation.  We just want the global for this test.
    Ipv4GlobalRoutingHelper ipv4RoutingHelper;
    Ipv6GlobalRoutingHelper ipv6RoutingHelper;
    internet.SetRoutingHelper(ipv4RoutingHelper);
    internet.SetRoutingHelper(ipv6RoutingHelper);
    internet.Install(m_nodes);

    Ipv4AddressHelper ipv4;
    ipv4.SetBase("10.1.1.0", "255.255.255.0");
    Ipv4InterfaceContainer i = ipv4.Assign(net);
    ipv4.SetBase("10.1.2.0", "255.255.255.0");
    Ipv4InterfaceContainer i2 = ipv4.Assign(net2);

    Ipv6AddressHelper ipv6;
    ipv6.SetBase(Ipv6Address("2001:0::"), Ipv6Prefix(64));
    ipv6.Assign(net);
    ipv6.SetBase(Ipv6Address("2001:1::"), Ipv6Prefix(64));
    ipv6.Assign(net2);
}

void
TwoLanTest::DoRun()
{
    Ipv4GlobalRoutingHelper::PopulateRoutingTables();
    Ipv6GlobalRoutingHelper::PopulateRoutingTables();

    //----------Ipv4 Tests------------
    Ptr<Ipv4L3Protocol> ipv40 = m_nodes.Get(0)->GetObject<Ipv4L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv40, nullptr, "Error-- no Ipv4 object");
    Ptr<Ipv4L3Protocol> ipv41 = m_nodes.Get(1)->GetObject<Ipv4L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv41, nullptr, "Error-- no Ipv4 object");
    Ptr<Ipv4L3Protocol> ipv42 = m_nodes.Get(2)->GetObject<Ipv4L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv42, nullptr, "Error-- no Ipv4 object");
    Ptr<Ipv4RoutingProtocol> routingv40 = ipv40->GetRoutingProtocol();
    Ptr<Ipv4GlobalRouting> globalRoutingv40 = routingv40->GetObject<Ipv4GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingv40, nullptr, "Error-- no Ipv4GlobalRouting object");
    Ptr<Ipv4RoutingProtocol> routingv41 = ipv41->GetRoutingProtocol();
    Ptr<Ipv4GlobalRouting> globalRoutingv41 = routingv41->GetObject<Ipv4GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingv41, nullptr, "Error-- no Ipv4GlobalRouting object");
    Ptr<Ipv4RoutingProtocol> routingv42 = ipv42->GetRoutingProtocol();
    Ptr<Ipv4GlobalRouting> globalRoutingv42 = routingv42->GetObject<Ipv4GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingv42, nullptr, "Error-- no Ipv4GlobalRouting object");

    // Test that the right number of routes found
    uint32_t nRoutesv40 = globalRoutingv40->GetNRoutes();
    NS_LOG_DEBUG("TwoLanTest nRoutes0 " << nRoutesv40);
    NS_TEST_ASSERT_MSG_EQ(nRoutesv40, 2, "Error-- not two entries");
    Ipv4RoutingTableEntry* routev4 = globalRoutingv40->GetRoute(0);
    NS_LOG_DEBUG("entry dest " << routev4->GetDest() << " gw " << routev4->GetGateway());
    NS_TEST_ASSERT_MSG_EQ(routev4->GetDest(), Ipv4Address("10.1.1.0"), "Error-- wrong destination");
    NS_TEST_ASSERT_MSG_EQ(routev4->GetGateway(), Ipv4Address("0.0.0.0"), "Error-- wrong gateway");
    routev4 = globalRoutingv40->GetRoute(1);
    NS_LOG_DEBUG("entry dest " << routev4->GetDest() << " gw " << routev4->GetGateway());
    NS_TEST_ASSERT_MSG_EQ(routev4->GetDest(), Ipv4Address("10.1.2.0"), "Error-- wrong destination");
    NS_TEST_ASSERT_MSG_EQ(routev4->GetGateway(), Ipv4Address("10.1.1.2"), "Error-- wrong gateway");

    // Test that the right number of routes found
    uint32_t nRoutesv41 = globalRoutingv41->GetNRoutes();
    NS_LOG_DEBUG("TwoLanTest nRoutes1 " << nRoutesv41);
    NS_TEST_ASSERT_MSG_EQ(nRoutesv41, 2, "Error-- not two entries");
    routev4 = globalRoutingv41->GetRoute(0);
    NS_LOG_DEBUG("TwoLanTest entry dest " << routev4->GetDest() << " gw " << routev4->GetGateway());
    NS_TEST_ASSERT_MSG_EQ(routev4->GetDest(), Ipv4Address("10.1.1.0"), "Error-- wrong destination");
    NS_TEST_ASSERT_MSG_EQ(routev4->GetGateway(), Ipv4Address("0.0.0.0"), "Error-- wrong gateway");
    routev4 = globalRoutingv41->GetRoute(1);
    NS_LOG_DEBUG("TwoLanTest entry dest " << routev4->GetDest() << " gw " << routev4->GetGateway());
    NS_TEST_ASSERT_MSG_EQ(routev4->GetDest(), Ipv4Address("10.1.2.0"), "Error-- wrong destination");
    NS_TEST_ASSERT_MSG_EQ(routev4->GetGateway(), Ipv4Address("0.0.0.0"), "Error-- wrong gateway");

    //-------------Ipv6 Tests------------
    Ptr<Ipv6L3Protocol> ipv60 = m_nodes.Get(0)->GetObject<Ipv6L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv60, nullptr, "Error-- no Ipv4 object");
    Ptr<Ipv6L3Protocol> ipv61 = m_nodes.Get(1)->GetObject<Ipv6L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv61, nullptr, "Error-- no Ipv4 object");
    Ptr<Ipv6L3Protocol> ipv62 = m_nodes.Get(2)->GetObject<Ipv6L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv62, nullptr, "Error-- no Ipv4 object");
    Ptr<Ipv6RoutingProtocol> routingv60 = ipv60->GetRoutingProtocol();
    Ptr<Ipv6GlobalRouting> globalRoutingv60 = routingv60->GetObject<Ipv6GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingv60, nullptr, "Error-- no Ipv4GlobalRouting object");
    Ptr<Ipv6RoutingProtocol> routingv61 = ipv61->GetRoutingProtocol();
    Ptr<Ipv6GlobalRouting> globalRoutingv61 = routingv61->GetObject<Ipv6GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingv61, nullptr, "Error-- no Ipv4GlobalRouting object");
    Ptr<Ipv6RoutingProtocol> routingv62 = ipv62->GetRoutingProtocol();
    Ptr<Ipv6GlobalRouting> globalRoutingv62 = routingv62->GetObject<Ipv6GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingv62, nullptr, "Error-- no Ipv4GlobalRouting object");

    // Test that the right number of routes found
    uint32_t nRoutesv60 = globalRoutingv60->GetNRoutes();
    NS_LOG_DEBUG("TwoLanTest nRoutes0 " << nRoutesv60);
    NS_TEST_ASSERT_MSG_EQ(nRoutesv60, 2, "Error-- not two entries");

    Ipv6RoutingTableEntry* routev6 = globalRoutingv60->GetRoute(0);
    NS_LOG_DEBUG("entry dest " << routev6->GetDest() << " gw " << routev6->GetGateway());
    NS_TEST_ASSERT_MSG_EQ(routev6->GetDest(), Ipv6Address("2001::"), "Error-- wrong destination");
    NS_TEST_ASSERT_MSG_EQ(routev6->GetDestNetworkPrefix(),
                          Ipv6Prefix(64),
                          "Error-- wrong destination mask");
    NS_TEST_ASSERT_MSG_EQ(routev6->GetGateway(), Ipv6Address("::"), "Error-- wrong gateway");

    routev6 = globalRoutingv60->GetRoute(1);
    NS_LOG_DEBUG("entry dest " << routev6->GetDest() << " gw " << routev6->GetGateway());
    NS_TEST_ASSERT_MSG_EQ(routev6->GetDest(), Ipv6Address("2001:1::"), "Error-- wrong destination");
    NS_TEST_ASSERT_MSG_EQ(routev6->GetDestNetworkPrefix(),
                          Ipv6Prefix(64),
                          "Error-- wrong destination mask");
    NS_TEST_ASSERT_MSG_EQ(routev6->GetGateway(),
                          Ipv6Address("fe80::200:ff:fe00:2"),
                          "Error-- wrong gateway");

    // Test that the right number of routes found
    uint32_t nRoutesv61 = globalRoutingv61->GetNRoutes();
    NS_LOG_DEBUG("TwoLanTest nRoutes1 " << nRoutesv61);
    NS_TEST_ASSERT_MSG_EQ(nRoutesv61, 2, "Error-- not two entries");

    routev6 = globalRoutingv61->GetRoute(0);
    NS_LOG_DEBUG("TwoLanTest entry dest " << routev6->GetDest() << " gw " << routev6->GetGateway());
    NS_TEST_ASSERT_MSG_EQ(routev6->GetDest(), Ipv6Address("2001::"), "Error-- wrong destination");
    NS_TEST_ASSERT_MSG_EQ(routev6->GetDestNetworkPrefix(),
                          Ipv6Prefix(64),
                          "Error-- wrong destination mask");
    NS_TEST_ASSERT_MSG_EQ(routev6->GetGateway(), Ipv6Address("::"), "Error-- wrong gateway");

    routev6 = globalRoutingv61->GetRoute(1);
    NS_LOG_DEBUG("TwoLanTest entry dest " << routev6->GetDest() << " gw " << routev6->GetGateway());
    NS_TEST_ASSERT_MSG_EQ(routev6->GetDest(), Ipv6Address("2001:1::"), "Error-- wrong destination");
    NS_TEST_ASSERT_MSG_EQ(routev6->GetDestNetworkPrefix(),
                          Ipv6Prefix(64),
                          "Error-- wrong destination mask");
    NS_TEST_ASSERT_MSG_EQ(routev6->GetGateway(), Ipv6Address("::"), "Error-- wrong gateway");

    Simulator::Run();
    Simulator::Destroy();
}

/**
 * @ingroup internet-test
 *
 * @brief GlobalRouting Bridge test
 */
class BridgeTest : public TestCase
{
  public:
    void DoSetup() override;
    void DoRun() override;
    BridgeTest();

  private:
    NodeContainer m_nodes; //!< Nodes used in the test.
};

BridgeTest::BridgeTest()
    : TestCase("Global routing across bridging topology (bug 2102)")
{
}

void
BridgeTest::DoSetup()
{
    m_nodes.Create(5);

    // connect node0 to node1
    Ptr<SimpleChannel> channel = CreateObject<SimpleChannel>();
    SimpleNetDeviceHelper simpleHelper;
    NetDeviceContainer net = simpleHelper.Install(m_nodes.Get(0), channel);
    net.Add(simpleHelper.Install(m_nodes.Get(1), channel));

    NetDeviceContainer bridgeFacingDevices;
    NetDeviceContainer switchDevices;

    // connect node1 to node2 (switch)
    Ptr<SimpleChannel> channel2 = CreateObject<SimpleChannel>();
    SimpleNetDeviceHelper simpleHelper2;
    NetDeviceContainer net2 = simpleHelper2.Install(m_nodes.Get(1), channel2);
    net2.Add(simpleHelper2.Install(m_nodes.Get(2), channel2));
    bridgeFacingDevices.Add(net2.Get(0));
    switchDevices.Add(net2.Get(1));

    // connect node2 (switch) to node3
    Ptr<SimpleChannel> channel3 = CreateObject<SimpleChannel>();
    SimpleNetDeviceHelper simpleHelper3;
    NetDeviceContainer net3 = simpleHelper3.Install(m_nodes.Get(2), channel3);
    net3.Add(simpleHelper3.Install(m_nodes.Get(3), channel3));
    bridgeFacingDevices.Add(net3.Get(1));
    switchDevices.Add(net3.Get(0));

    // connect node3 to node4
    Ptr<SimpleChannel> channel4 = CreateObject<SimpleChannel>();
    SimpleNetDeviceHelper simpleHelper4;
    NetDeviceContainer net4 = simpleHelper4.Install(m_nodes.Get(3), channel4);
    net4.Add(simpleHelper4.Install(m_nodes.Get(4), channel4));

    Ptr<Node> switchNode = m_nodes.Get(2);
    BridgeHelper bridge;
    bridge.Install(switchNode, switchDevices);

    InternetStackHelper internet;
    // By default, InternetStackHelper adds a static and global routing
    // implementation.  We just want the global for this test.
    Ipv4GlobalRoutingHelper ipv4RoutingHelper;
    Ipv6GlobalRoutingHelper ipv6RoutingHelper;
    internet.SetRoutingHelper(ipv4RoutingHelper);
    internet.SetRoutingHelper(ipv6RoutingHelper);

    internet.Install(m_nodes.Get(0));
    internet.Install(m_nodes.Get(1));
    // m_nodes.Get (2) is bridge node
    internet.Install(m_nodes.Get(3));
    internet.Install(m_nodes.Get(4));

    Ipv4AddressHelper address;
    address.SetBase("10.1.1.0", "255.255.255.0");
    address.Assign(net);

    address.SetBase("10.1.2.0", "255.255.255.0");
    address.Assign(bridgeFacingDevices);

    address.SetBase("10.1.3.0", "255.255.255.0");
    address.Assign(net4);

    Ipv6AddressHelper ipv6;
    ipv6.SetBase(Ipv6Address("2001:0::"), Ipv6Prefix(64));
    ipv6.Assign(net);
    ipv6.SetBase(Ipv6Address("2001:1::"), Ipv6Prefix(64));
    ipv6.Assign(bridgeFacingDevices);
    ipv6.SetBase(Ipv6Address("2001:2::"), Ipv6Prefix(64));
    ipv6.Assign(net4);
}

void
BridgeTest::DoRun()
{
    Ipv4GlobalRoutingHelper::PopulateRoutingTables();
    Ipv6GlobalRoutingHelper::PopulateRoutingTables();

    //------------Ipv4 Tests--------------------
    Ptr<Ipv4L3Protocol> ipv40 = m_nodes.Get(0)->GetObject<Ipv4L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv40, nullptr, "Error-- no Ipv4 object");
    Ptr<Ipv4RoutingProtocol> routingv40 = ipv40->GetRoutingProtocol();
    NS_TEST_ASSERT_MSG_NE(routingv40, nullptr, "Error-- no Ipv4 routing protocol object");
    Ptr<Ipv4GlobalRouting> globalRoutingv40 = routingv40->GetObject<Ipv4GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingv40, nullptr, "Error-- no Ipv4GlobalRouting object");

    Ptr<Ipv4L3Protocol> ipv41 = m_nodes.Get(1)->GetObject<Ipv4L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv41, nullptr, "Error-- no Ipv4 object");
    Ptr<Ipv4RoutingProtocol> routingv41 = ipv41->GetRoutingProtocol();
    NS_TEST_ASSERT_MSG_NE(routingv41, nullptr, "Error-- no Ipv4 routing protocol object");
    Ptr<Ipv4GlobalRouting> globalRoutingv41 = routingv41->GetObject<Ipv4GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingv41, nullptr, "Error-- no Ipv4GlobalRouting object");

    // Skip to n4
    Ptr<Ipv4L3Protocol> ipv44 = m_nodes.Get(4)->GetObject<Ipv4L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv44, nullptr, "Error-- no Ipv4 object");
    Ptr<Ipv4RoutingProtocol> routingv44 = ipv44->GetRoutingProtocol();
    NS_TEST_ASSERT_MSG_NE(routingv44, nullptr, "Error-- no Ipv4 routing protocol object");
    Ptr<Ipv4GlobalRouting> globalRoutingv44 = routingv44->GetObject<Ipv4GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingv44, nullptr, "Error-- no Ipv4GlobalRouting object");

    Ipv4RoutingTableEntry* routev4 = nullptr;
    // n0
    // Test that the right number of routes found
    uint32_t nRoutesv40 = globalRoutingv40->GetNRoutes();
    NS_LOG_DEBUG("BridgeTest nRoutes0 " << nRoutesv40);
    NS_TEST_ASSERT_MSG_EQ(nRoutesv40, 3, "Error-- not three entries");
    for (uint32_t i = 0; i < globalRoutingv40->GetNRoutes(); i++)
    {
        routev4 = globalRoutingv40->GetRoute(i);
        NS_LOG_DEBUG("entry dest " << routev4->GetDest() << " gw " << routev4->GetGateway());
    }
    // Spot check the last route
    if (routev4)
    {
        NS_TEST_ASSERT_MSG_EQ(routev4->GetDest(),
                              Ipv4Address("10.1.3.0"),
                              "Error-- wrong destination");
        NS_TEST_ASSERT_MSG_EQ(routev4->GetGateway(),
                              Ipv4Address("10.1.1.2"),
                              "Error-- wrong gateway");
    }

    // n1
    // Test that the right number of routes found
    routev4 = nullptr;
    uint32_t nRoutesv41 = globalRoutingv41->GetNRoutes();
    NS_LOG_DEBUG("BridgeTest nRoutes1 " << nRoutesv41);
    NS_TEST_ASSERT_MSG_EQ(nRoutesv41, 3, "Error-- not three entries");
    for (uint32_t i = 0; i < globalRoutingv41->GetNRoutes(); i++)
    {
        routev4 = globalRoutingv41->GetRoute(i);
        NS_LOG_DEBUG("entry dest " << routev4->GetDest() << " gw " << routev4->GetGateway());
    }
    // Spot check the last route
    if (routev4)
    {
        NS_TEST_ASSERT_MSG_EQ(routev4->GetDest(),
                              Ipv4Address("10.1.3.0"),
                              "Error-- wrong destination");
        NS_TEST_ASSERT_MSG_EQ(routev4->GetGateway(),
                              Ipv4Address("10.1.2.2"),
                              "Error-- wrong gateway");
    }

    // skip n2 and n3, just verify n4
    NS_LOG_DEBUG("BridgeTest skip print out of n2 and n3, go next to node n4");

    // n4
    routev4 = nullptr;
    // Test that the right number of routes found
    uint32_t nRoutesv44 = globalRoutingv44->GetNRoutes();
    NS_LOG_DEBUG("BridgeTest nRoutes4 " << nRoutesv44);
    NS_TEST_ASSERT_MSG_EQ(nRoutesv44, 3, "Error-- not three entries");
    for (uint32_t i = 0; i < globalRoutingv44->GetNRoutes(); i++)
    {
        routev4 = globalRoutingv44->GetRoute(i);
        NS_LOG_DEBUG("entry dest " << routev4->GetDest() << " gw " << routev4->GetGateway());
    }
    // Spot check the last route
    if (routev4)
    {
        NS_TEST_ASSERT_MSG_EQ(routev4->GetDest(),
                              Ipv4Address("10.1.1.0"),
                              "Error-- wrong destination");
        NS_TEST_ASSERT_MSG_EQ(routev4->GetGateway(),
                              Ipv4Address("10.1.3.1"),
                              "Error-- wrong gateway");
    }

    //------------------Ipv6 Tests----------------
    Ptr<Ipv6L3Protocol> ipv60 = m_nodes.Get(0)->GetObject<Ipv6L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv60, nullptr, "Error-- no Ipv4 object");
    Ptr<Ipv6RoutingProtocol> routingv60 = ipv60->GetRoutingProtocol();
    NS_TEST_ASSERT_MSG_NE(routingv60, nullptr, "Error-- no Ipv4 routing protocol object");
    Ptr<Ipv6GlobalRouting> globalRoutingv60 = routingv60->GetObject<Ipv6GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingv60, nullptr, "Error-- no Ipv4GlobalRouting object");

    Ptr<Ipv6L3Protocol> ipv61 = m_nodes.Get(1)->GetObject<Ipv6L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv61, nullptr, "Error-- no Ipv4 object");
    Ptr<Ipv6RoutingProtocol> routingv61 = ipv61->GetRoutingProtocol();
    NS_TEST_ASSERT_MSG_NE(routingv61, nullptr, "Error-- no Ipv4 routing protocol object");
    Ptr<Ipv6GlobalRouting> globalRoutingv61 = routingv61->GetObject<Ipv6GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingv61, nullptr, "Error-- no Ipv4GlobalRouting object");

    // Skip to n4
    Ptr<Ipv6L3Protocol> ipv64 = m_nodes.Get(4)->GetObject<Ipv6L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv64, nullptr, "Error-- no Ipv4 object");
    Ptr<Ipv6RoutingProtocol> routingv64 = ipv64->GetRoutingProtocol();
    NS_TEST_ASSERT_MSG_NE(routingv64, nullptr, "Error-- no Ipv4 routing protocol object");
    Ptr<Ipv6GlobalRouting> globalRoutingv64 = routingv64->GetObject<Ipv6GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingv64, nullptr, "Error-- no Ipv4GlobalRouting object");

    Ipv6RoutingTableEntry* routev6 = nullptr;
    // n0
    // Test that the right number of routes found
    uint32_t nRoutesv60 = globalRoutingv60->GetNRoutes();
    NS_LOG_DEBUG("BridgeTest nRoutes0 " << nRoutesv60);
    NS_TEST_ASSERT_MSG_EQ(nRoutesv60, 3, "Error-- not three entries");
    for (uint32_t i = 0; i < globalRoutingv60->GetNRoutes(); i++)
    {
        routev6 = globalRoutingv60->GetRoute(i);
        NS_LOG_DEBUG("entry dest " << routev6->GetDest() << " gw " << routev6->GetGateway());
    }
    // Spot check the last route
    if (routev6)
    {
        NS_TEST_ASSERT_MSG_EQ(routev6->GetDest(),
                              Ipv6Address("2001:2::"),
                              "Error-- wrong destination");
        NS_TEST_ASSERT_MSG_EQ(routev6->GetDestNetworkPrefix(),
                              Ipv6Prefix(64),
                              "Error-- wrong destination mask");
        NS_TEST_ASSERT_MSG_EQ(routev6->GetGateway(),
                              Ipv6Address("fe80::200:ff:fe00:2"),
                              "Error-- wrong gateway");
    }

    // n1
    // Test that the right number of routes found
    routev6 = nullptr;
    uint32_t nRoutesv61 = globalRoutingv61->GetNRoutes();
    NS_LOG_DEBUG("BridgeTest nRoutes1 " << nRoutesv61);
    NS_TEST_ASSERT_MSG_EQ(nRoutesv61, 3, "Error-- not three entries");
    for (uint32_t i = 0; i < globalRoutingv61->GetNRoutes(); i++)
    {
        routev6 = globalRoutingv61->GetRoute(i);
        NS_LOG_DEBUG("entry dest " << routev6->GetDest() << " gw " << routev6->GetGateway());
    }
    // Spot check the last route
    if (routev6)
    {
        NS_TEST_ASSERT_MSG_EQ(routev6->GetDest(),
                              Ipv6Address("2001:2::"),
                              "Error-- wrong destination");
        NS_TEST_ASSERT_MSG_EQ(routev6->GetDestNetworkPrefix(),
                              Ipv6Prefix(64),
                              "Error-- wrong destination mask");
        NS_TEST_ASSERT_MSG_EQ(routev6->GetGateway(),
                              Ipv6Address("fe80::200:ff:fe00:6"),
                              "Error-- wrong gateway");
    }

    // skip n2 and n3, just verify n4
    NS_LOG_DEBUG("BridgeTest skip print out of n2 and n3, go next to node n4");

    // n4
    routev6 = nullptr;
    // Test that the right number of routes found
    uint32_t nRoutesv64 = globalRoutingv64->GetNRoutes();
    NS_LOG_DEBUG("BridgeTest nRoutes4 " << nRoutesv64);
    NS_TEST_ASSERT_MSG_EQ(nRoutesv64, 3, "Error-- not three entries");
    for (uint32_t i = 0; i < globalRoutingv64->GetNRoutes(); i++)
    {
        routev6 = globalRoutingv64->GetRoute(i);
        NS_LOG_DEBUG("entry dest " << routev6->GetDest() << " gw " << routev6->GetGateway());
    }
    // Spot check the last route
    if (routev6)
    {
        NS_TEST_ASSERT_MSG_EQ(routev6->GetDest(),
                              Ipv6Address("2001::"),
                              "Error-- wrong destination");
        NS_TEST_ASSERT_MSG_EQ(routev6->GetDestNetworkPrefix(),
                              Ipv6Prefix(64),
                              "Error-- wrong destination mask");
        NS_TEST_ASSERT_MSG_EQ(routev6->GetGateway(),
                              Ipv6Address("fe80::200:ff:fe00:7"),
                              "Error-- wrong gateway");
    }
    Simulator::Run();
    Simulator::Destroy();
}

/**
 * @ingroup internet-test
 *
 * @brief GlobalRouting Two bridges test
 */
class TwoBridgeTest : public TestCase
{
  public:
    void DoSetup() override;
    void DoRun() override;
    TwoBridgeTest();

  private:
    NodeContainer m_nodes; //!< Nodes used in the test.
};

TwoBridgeTest::TwoBridgeTest()
    : TestCase("Global routing across two bridges")
{
}

void
TwoBridgeTest::DoSetup()
{
    m_nodes.Create(5);

    // connect node0 to node1
    Ptr<SimpleChannel> channel = CreateObject<SimpleChannel>();
    SimpleNetDeviceHelper simpleHelper;
    NetDeviceContainer net = simpleHelper.Install(m_nodes.Get(0), channel);
    net.Add(simpleHelper.Install(m_nodes.Get(1), channel));

    NetDeviceContainer bridgeFacingDevices;
    NetDeviceContainer switchn2Devices;
    NetDeviceContainer switchn3Devices;

    // connect node1 to node2 (switch)
    Ptr<SimpleChannel> channel2 = CreateObject<SimpleChannel>();
    SimpleNetDeviceHelper simpleHelper2;
    NetDeviceContainer net2 = simpleHelper2.Install(m_nodes.Get(1), channel2);
    net2.Add(simpleHelper2.Install(m_nodes.Get(2), channel2));
    bridgeFacingDevices.Add(net2.Get(0));
    switchn2Devices.Add(net2.Get(1));

    // connect node2 (switch) to node3
    Ptr<SimpleChannel> channel3 = CreateObject<SimpleChannel>();
    SimpleNetDeviceHelper simpleHelper3;
    NetDeviceContainer net3 = simpleHelper3.Install(m_nodes.Get(2), channel3);
    net3.Add(simpleHelper3.Install(m_nodes.Get(3), channel3));
    switchn2Devices.Add(net3.Get(0));
    switchn3Devices.Add(net3.Get(1));

    // connect node3 to node4
    Ptr<SimpleChannel> channel4 = CreateObject<SimpleChannel>();
    SimpleNetDeviceHelper simpleHelper4;
    NetDeviceContainer net4 = simpleHelper4.Install(m_nodes.Get(3), channel4);
    net4.Add(simpleHelper4.Install(m_nodes.Get(4), channel4));
    switchn3Devices.Add(net4.Get(0));
    bridgeFacingDevices.Add(net4.Get(1));

    Ptr<Node> switchn2Node = m_nodes.Get(2);
    BridgeHelper bridgen2Helper;
    bridgen2Helper.Install(switchn2Node, switchn2Devices);

    Ptr<Node> switchn3Node = m_nodes.Get(3);
    BridgeHelper bridgen3Helper;
    bridgen3Helper.Install(switchn3Node, switchn3Devices);

    InternetStackHelper internet;
    // By default, InternetStackHelper adds a static and global routing
    // implementation.  We just want the global for this test.
    Ipv4GlobalRoutingHelper ipv4RoutingHelper;
    Ipv6GlobalRoutingHelper ipv6RoutingHelper;
    internet.SetRoutingHelper(ipv4RoutingHelper);
    internet.SetRoutingHelper(ipv6RoutingHelper);

    internet.Install(m_nodes.Get(0));
    internet.Install(m_nodes.Get(1));
    // m_nodes.Get (2) is bridge node
    // m_nodes.Get (3) is bridge node
    internet.Install(m_nodes.Get(4));

    Ipv4AddressHelper addressv4;
    addressv4.SetBase("10.1.1.0", "255.255.255.0");
    addressv4.Assign(net);

    addressv4.SetBase("10.1.2.0", "255.255.255.0");
    addressv4.Assign(bridgeFacingDevices);

    Ipv6AddressHelper addressv6;
    addressv6.SetBase(Ipv6Address("2001:0::"), Ipv6Prefix(64));
    addressv6.Assign(net);

    addressv6.SetBase(Ipv6Address("2001:1::"), Ipv6Prefix(64));
    addressv6.Assign(bridgeFacingDevices);
}

void
TwoBridgeTest::DoRun()
{
    Ipv4GlobalRoutingHelper::PopulateRoutingTables();
    Ipv6GlobalRoutingHelper::PopulateRoutingTables();
    //------------Ipv4 Tests-------------------
    Ptr<Ipv4L3Protocol> ipv40 = m_nodes.Get(0)->GetObject<Ipv4L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv40, nullptr, "Error-- no Ipv4 object");
    Ptr<Ipv4RoutingProtocol> routingv40 = ipv40->GetRoutingProtocol();
    NS_TEST_ASSERT_MSG_NE(routingv40, nullptr, "Error-- no Ipv4 routing protocol object");
    Ptr<Ipv4GlobalRouting> globalRoutingv40 = routingv40->GetObject<Ipv4GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingv40, nullptr, "Error-- no Ipv4GlobalRouting object");

    // Skip to n4
    Ptr<Ipv4L3Protocol> ipv44 = m_nodes.Get(4)->GetObject<Ipv4L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv44, nullptr, "Error-- no Ipv4 object");
    Ptr<Ipv4RoutingProtocol> routingv44 = ipv44->GetRoutingProtocol();
    NS_TEST_ASSERT_MSG_NE(routingv44, nullptr, "Error-- no Ipv4 routing protocol object");
    Ptr<Ipv4GlobalRouting> globalRoutingv44 = routingv44->GetObject<Ipv4GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingv44, nullptr, "Error-- no Ipv4GlobalRouting object");

    Ipv4RoutingTableEntry* routev4 = nullptr;
    // n0
    // Test that the right number of routes found
    uint32_t nRoutesv40 = globalRoutingv40->GetNRoutes();
    NS_LOG_DEBUG("BridgeTest nRoutes0 " << nRoutesv40);
    NS_TEST_ASSERT_MSG_EQ(nRoutesv40, 2, "Error-- not two entries");
    for (uint32_t i = 0; i < globalRoutingv40->GetNRoutes(); i++)
    {
        routev4 = globalRoutingv40->GetRoute(i);
        NS_LOG_DEBUG("entry dest " << routev4->GetDest() << " gw " << routev4->GetGateway());
    }
    // Spot check the last route
    if (routev4)
    {
        NS_TEST_ASSERT_MSG_EQ(routev4->GetDest(),
                              Ipv4Address("10.1.2.0"),
                              "Error-- wrong destination");
        NS_TEST_ASSERT_MSG_EQ(routev4->GetGateway(),
                              Ipv4Address("10.1.1.2"),
                              "Error-- wrong gateway");
    }
    // skip n2 and n3, just verify n4
    NS_LOG_DEBUG("BridgeTest skip print out of n1-n3, go next to node n4");

    // n4
    // Test that the right number of routes found
    routev4 = nullptr;
    uint32_t nRoutesv44 = globalRoutingv44->GetNRoutes();
    NS_LOG_DEBUG("BridgeTest nRoutes4 " << nRoutesv44);
    NS_TEST_ASSERT_MSG_EQ(nRoutesv44, 2, "Error-- not two entries");
    for (uint32_t i = 0; i < globalRoutingv44->GetNRoutes(); i++)
    {
        routev4 = globalRoutingv44->GetRoute(i);
        NS_LOG_DEBUG("entry dest " << routev4->GetDest() << " gw " << routev4->GetGateway());
    }
    // Spot check the last route
    if (routev4)
    {
        NS_TEST_ASSERT_MSG_EQ(routev4->GetDest(),
                              Ipv4Address("10.1.1.0"),
                              "Error-- wrong destination");
        NS_TEST_ASSERT_MSG_EQ(routev4->GetGateway(),
                              Ipv4Address("10.1.2.1"),
                              "Error-- wrong gateway");
    }

    //----------Ipv6 Tests--------------
    Ptr<Ipv6L3Protocol> ipv60 = m_nodes.Get(0)->GetObject<Ipv6L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv60, nullptr, "Error-- no Ipv4 object");
    Ptr<Ipv6RoutingProtocol> routingv60 = ipv60->GetRoutingProtocol();
    NS_TEST_ASSERT_MSG_NE(routingv60, nullptr, "Error-- no Ipv4 routing protocol object");
    Ptr<Ipv6GlobalRouting> globalRoutingv60 = routingv60->GetObject<Ipv6GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingv60, nullptr, "Error-- no Ipv4GlobalRouting object");

    // Skip to n4
    Ptr<Ipv6L3Protocol> ipv64 = m_nodes.Get(4)->GetObject<Ipv6L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ipv64, nullptr, "Error-- no Ipv4 object");
    Ptr<Ipv6RoutingProtocol> routingv64 = ipv64->GetRoutingProtocol();
    NS_TEST_ASSERT_MSG_NE(routingv64, nullptr, "Error-- no Ipv4 routing protocol object");
    Ptr<Ipv6GlobalRouting> globalRoutingv64 = routingv64->GetObject<Ipv6GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRoutingv64, nullptr, "Error-- no Ipv4GlobalRouting object");

    Ipv6RoutingTableEntry* routev6 = nullptr;
    // n0
    // Test that the right number of routes found
    uint32_t nRoutesv60 = globalRoutingv60->GetNRoutes();
    NS_LOG_DEBUG("BridgeTest nRoutes0 " << nRoutesv60);
    NS_TEST_ASSERT_MSG_EQ(nRoutesv60, 2, "Error-- not two entries");
    for (uint32_t i = 0; i < globalRoutingv60->GetNRoutes(); i++)
    {
        routev6 = globalRoutingv60->GetRoute(i);
        NS_LOG_DEBUG("entry dest " << routev6->GetDest() << " gw " << routev6->GetGateway());
    }
    // Spot check the last route
    if (routev6)
    {
        NS_TEST_ASSERT_MSG_EQ(routev6->GetDest(),
                              Ipv6Address("2001:1::"),
                              "Error-- wrong destination");
        NS_TEST_ASSERT_MSG_EQ(routev6->GetDestNetworkPrefix(),
                              Ipv6Prefix(64),
                              "Error-- wrong destination mask");
        NS_TEST_ASSERT_MSG_EQ(routev6->GetGateway(),
                              Ipv6Address("fe80::200:ff:fe00:2"),
                              "Error-- wrong gateway");
    }
    // skip n2 and n3, just verify n4
    NS_LOG_DEBUG("BridgeTest skip print out of n1-n3, go next to node n4");

    // n4
    // Test that the right number of routes found
    routev6 = nullptr;
    uint32_t nRoutesv64 = globalRoutingv64->GetNRoutes();
    NS_LOG_DEBUG("BridgeTest nRoutes4 " << nRoutesv44);
    NS_TEST_ASSERT_MSG_EQ(nRoutesv64, 2, "Error-- not two entries");
    for (uint32_t i = 0; i < globalRoutingv64->GetNRoutes(); i++)
    {
        routev6 = globalRoutingv64->GetRoute(i);
        NS_LOG_DEBUG("entry dest " << routev6->GetDest() << " gw " << routev6->GetGateway());
    }
    // Spot check the last route
    if (routev6)
    {
        NS_TEST_ASSERT_MSG_EQ(routev6->GetDest(),
                              Ipv6Address("2001::"),
                              "Error-- wrong destination");
        NS_TEST_ASSERT_MSG_EQ(routev6->GetDestNetworkPrefix(),
                              Ipv6Prefix(64),
                              "Error-- wrong destination mask");
        NS_TEST_ASSERT_MSG_EQ(routev6->GetGateway(),
                              Ipv6Address("fe80::200:ff:fe00:3"),
                              "Error-- wrong gateway");
    }

    Simulator::Destroy();
}

/**
 * @ingroup internet-test
 *
 * @brief IPv4 Dynamic GlobalRouting test
 */
class Ipv4DynamicGlobalRoutingTestCase : public TestCase
{
  public:
    Ipv4DynamicGlobalRoutingTestCase();
    ~Ipv4DynamicGlobalRoutingTestCase() override;

  private:
    /**
     * @brief Send some data
     * @param index Index of the socket to use.
     */
    void SendData(uint8_t index);

    /**
     * @brief Shutdown a socket
     * @param index Index of the socket to close.
     */
    void ShutDownSock(uint8_t index);

    /**
     * Handle an incoming packet
     * @param socket The input socket.
     */
    void HandleRead(Ptr<Socket> socket);
    void DoRun() override;

    uint16_t m_count;                                      //!< Number of packets received.
    std::vector<std::pair<Ptr<Socket>, bool>> m_sendSocks; //!< Sending sockets.
    DataRate m_dataRate;                                   //!< Data rate.
    uint16_t m_packetSize;                                 //!< Packet size.
    std::vector<uint8_t>
        m_firstInterface; //!< Packets received on the 1st interface at a given time.
    std::vector<uint8_t>
        m_secondInterface; //!< Packets received on the 2nd interface at a given time.
    NodeContainer c;       //!< nodes in the topology
};

// Add some help text to this case to describe what it is intended to test
Ipv4DynamicGlobalRoutingTestCase::Ipv4DynamicGlobalRoutingTestCase()
    : TestCase("Ipv4 Dynamic global routing example"),
      m_count(0)
{
    m_firstInterface.resize(16, 0);
    m_secondInterface.resize(16, 0);
    m_dataRate = DataRate("2kbps");
    m_packetSize = 50;
}

Ipv4DynamicGlobalRoutingTestCase::~Ipv4DynamicGlobalRoutingTestCase()
{
    for (auto iter = m_sendSocks.begin(); iter != m_sendSocks.end(); iter++)
    {
        if (iter->second)
        {
            iter->second = false;
            iter->first->Close();
            iter->first = nullptr;
        }
    }
}

void
Ipv4DynamicGlobalRoutingTestCase::HandleRead(Ptr<Socket> socket)
{
    Ptr<Packet> packet;
    Address from;
    while ((packet = socket->RecvFrom(from)))
    {
        if (packet->GetSize() == 0)
        { // EOF
            break;
        }
        Ipv4PacketInfoTag tag;
        bool found;
        found = packet->PeekPacketTag(tag);
        uint8_t now = static_cast<uint8_t>(Simulator::Now().GetSeconds());
        if (found)
        {
            if (tag.GetRecvIf() == 1)
            {
                m_firstInterface[now]++;
            }
            if (tag.GetRecvIf() == 2)
            {
                m_secondInterface[now]++;
            }
            m_count++;
        }
    }
}

void
Ipv4DynamicGlobalRoutingTestCase::SendData(uint8_t index)
{
    if (!m_sendSocks[index].second)
    {
        return;
    }
    Ptr<Packet> packet = Create<Packet>(m_packetSize);
    m_sendSocks[index].first->Send(packet);

    Time tNext(MicroSeconds(m_packetSize * 8 * 1e6 / m_dataRate.GetBitRate()));
    Simulator::Schedule(tNext, &Ipv4DynamicGlobalRoutingTestCase::SendData, this, index);
}

void
Ipv4DynamicGlobalRoutingTestCase::ShutDownSock(uint8_t index)
{
    m_sendSocks[index].second = false;
    m_sendSocks[index].first->Close();
    m_sendSocks[index].first = nullptr;
}

// Test derived from examples/routing/dynamic-global-routing.cc
//
// Network topology
//
//  n0
//     \ p-p
//      \          (shared csma/cd)
//       n2 -------------------------n3
//      /            |        |
//     / p-p        n4        n5 ---------- n6
//   n1                             p-p
//   |                                      |
//   ----------------------------------------
//                p-p
//
// Test that for node n6, the interface facing n5 receives packets at
// times (1-2), (4-6), (8-10), (11-12), (14-16) and the interface
// facing n1 receives packets at times (2-4), (6-8), (12-13)
//
void
Ipv4DynamicGlobalRoutingTestCase::DoRun()
{
    // The below value configures the default behavior of global routing.
    // By default, it is disabled.  To respond to interface events, set to true
    Config::SetDefault("ns3::Ipv4GlobalRouting::RespondToInterfaceEvents", BooleanValue(true));

    c.Create(7);
    NodeContainer n0n2 = NodeContainer(c.Get(0), c.Get(2));
    NodeContainer n1n2 = NodeContainer(c.Get(1), c.Get(2));
    NodeContainer n5n6 = NodeContainer(c.Get(5), c.Get(6));
    NodeContainer n1n6 = NodeContainer(c.Get(1), c.Get(6));
    NodeContainer n2345 = NodeContainer(c.Get(2), c.Get(3), c.Get(4), c.Get(5));

    InternetStackHelper internet;
    Ipv4GlobalRoutingHelper routingHelper;
    internet.SetRoutingHelper(routingHelper);
    internet.Install(c);

    // We create the channels first without any IP addressing information
    SimpleNetDeviceHelper devHelper;

    devHelper.SetNetDevicePointToPointMode(true);
    NetDeviceContainer d0d2 = devHelper.Install(n0n2);
    NetDeviceContainer d1d6 = devHelper.Install(n1n6);
    NetDeviceContainer d1d2 = devHelper.Install(n1n2);
    NetDeviceContainer d5d6 = devHelper.Install(n5n6);

    devHelper.SetNetDevicePointToPointMode(false);
    NetDeviceContainer d2345 = devHelper.Install(n2345);

    // Later, we add IP addresses.
    Ipv4AddressHelper ipv4;
    ipv4.SetBase("10.1.1.0", "255.255.255.0");
    ipv4.Assign(d0d2);

    ipv4.SetBase("10.1.2.0", "255.255.255.0");
    ipv4.Assign(d1d2);

    ipv4.SetBase("10.1.3.0", "255.255.255.0");
    Ipv4InterfaceContainer i5i6 = ipv4.Assign(d5d6);

    ipv4.SetBase("10.250.1.0", "255.255.255.0");
    ipv4.Assign(d2345);

    ipv4.SetBase("172.16.1.0", "255.255.255.0");
    Ipv4InterfaceContainer i1i6 = ipv4.Assign(d1d6);

    // Create router nodes, initialize routing database and set up the routing
    // tables in the nodes.
    Ipv4GlobalRoutingHelper::PopulateRoutingTables();

    // Create the applications to send UDP datagrams of size
    // 50 bytes at a rate of 2 Kb/s
    TypeId tid = TypeId::LookupByName("ns3::UdpSocketFactory");
    uint16_t port = 9; // Discard port (RFC 863)

    std::pair<Ptr<Socket>, bool> sendSockA;
    sendSockA.first = Socket::CreateSocket(c.Get(1), tid);
    sendSockA.first->Bind();
    sendSockA.first->Connect(InetSocketAddress(i5i6.GetAddress(1), port));
    sendSockA.second = true;
    m_sendSocks.push_back(sendSockA);
    Simulator::Schedule(Seconds(1), &Ipv4DynamicGlobalRoutingTestCase::SendData, this, 0);
    Simulator::Schedule(Seconds(10), &Ipv4DynamicGlobalRoutingTestCase::ShutDownSock, this, 0);

    std::pair<Ptr<Socket>, bool> sendSockB;
    sendSockB.first = Socket::CreateSocket(c.Get(1), tid);
    sendSockB.first->Bind();
    sendSockB.first->Connect(InetSocketAddress(i1i6.GetAddress(1), port));
    sendSockB.second = true;
    m_sendSocks.push_back(sendSockB);
    Simulator::Schedule(Seconds(11), &Ipv4DynamicGlobalRoutingTestCase::SendData, this, 1);
    Simulator::Schedule(Seconds(16), &Ipv4DynamicGlobalRoutingTestCase::ShutDownSock, this, 1);

    // Create an optional packet sink to receive these packets
    Ptr<Socket> sink2 = Socket::CreateSocket(c.Get(6), tid);
    sink2->Bind(Address(InetSocketAddress(Ipv4Address::GetAny(), port)));
    sink2->Listen();
    sink2->ShutdownSend();

    sink2->SetRecvPktInfo(true);
    sink2->SetRecvCallback(MakeCallback(&Ipv4DynamicGlobalRoutingTestCase::HandleRead, this));

    Ptr<Node> n1 = c.Get(1);
    Ptr<Ipv4> ipv41 = n1->GetObject<Ipv4>();
    // The first ifIndex is 0 for loopback, then the first p2p is numbered 1,
    // then the next p2p is numbered 2
    uint32_t ipv4ifIndex1 = 2;

    Simulator::Schedule(Seconds(2), &Ipv4::SetDown, ipv41, ipv4ifIndex1);
    Simulator::Schedule(Seconds(4), &Ipv4::SetUp, ipv41, ipv4ifIndex1);

    Ptr<Node> n6 = c.Get(6);
    Ptr<Ipv4> ipv46 = n6->GetObject<Ipv4>();
    // The first ifIndex is 0 for loopback, then the first p2p is numbered 1,
    // then the next p2p is numbered 2
    uint32_t ipv4ifIndex6 = 2;
    Simulator::Schedule(Seconds(6), &Ipv4::SetDown, ipv46, ipv4ifIndex6);
    Simulator::Schedule(Seconds(8), &Ipv4::SetUp, ipv46, ipv4ifIndex6);

    Simulator::Schedule(Seconds(12), &Ipv4::SetDown, ipv41, ipv4ifIndex1);
    Simulator::Schedule(Seconds(14), &Ipv4::SetUp, ipv41, ipv4ifIndex1);

    Simulator::Run();

    NS_TEST_ASSERT_MSG_EQ(m_count, 70, "Dynamic global routing did not deliver all packets");
    // Test that for node n6, the interface facing n1 receives packets at
    // times (1-2), (4-6), (8-10), (11-12), (14-16) and the interface
    // facing n5 receives packets at times (2-4), (6-8), (12-13)

    // Note: there are two sending sockets, both on n1.
    // The first socket sends packets at time (1-10), the second at time (11, 16).
    // The first socket sends packets to n6, targeting the address facing n5.
    // The second socket sends packets to n6, targeting the address facing n1.
    // This actually doesn't matter, as n6 will accept packets sent to the "wrong" address.
    //
    // The shortest path netween n1 and n6 is the direct one, but the topology changes during the
    // simulation:
    // - (2-4): removal from n1 of the interface toward n6
    // - (6-8): removal from n6 of the interface toward n1
    // - (12-14): removal from n1 of the interface toward n6
    // When the link is broken, packets are rerouted though the longest (and only) path, reaching
    // n6 though n5.

    std::vector<uint8_t> firstInterfaceTest{0, 5, 0, 0, 5, 5, 0, 0, 5, 5, 0, 5, 0, 0, 5, 5};
    std::vector<uint8_t> secondInterfaceTest{0, 0, 5, 5, 0, 0, 5, 5, 0, 0, 0, 0, 5, 5, 0, 0};

    for (uint32_t index = 0; index < firstInterfaceTest.size(); index++)
    {
        NS_TEST_ASSERT_MSG_EQ(firstInterfaceTest[index],
                              m_firstInterface[index],
                              "Dynamic global routing did deliver the wrong number of packets "
                              "to the first interface at time "
                                  << index);
    }

    for (uint32_t index = 0; index < secondInterfaceTest.size(); index++)
    {
        NS_TEST_ASSERT_MSG_EQ(secondInterfaceTest[index],
                              m_secondInterface[index],
                              "Dynamic global routing did deliver the wrong number of packets "
                              "to the second interface at time "
                                  << index);
    }

    Simulator::Destroy();
}

/**
 * @ingroup internet-test
 *
 * @brief IPv6 Dynamic GlobalRouting test
 */
class Ipv6DynamicGlobalRoutingTestCase : public TestCase
{
  public:
    Ipv6DynamicGlobalRoutingTestCase();
    ~Ipv6DynamicGlobalRoutingTestCase() override;

  private:
    /**
     * @brief Send some data
     * @param index Index of the socket to use.
     */
    void SendData(uint8_t index);

    /**
     * @brief Shutdown a socket
     * @param index Index of the socket to close.
     */
    void ShutDownSock(uint8_t index);

    /**
     * Handle an incoming packet
     * @param socket The input socket.
     */
    void HandleRead(Ptr<Socket> socket);
    /**
     * @brief This function does two things, set the specified ipv6 interface as up and
     * Adds the specified Ipv6Address to the interface.
     * @param ipv6 The Ipv6 object
     * @param ifIndex The index of the interface
     * @param address The Ipv6Address to be added to the interface
     */
    void SetInterfaceUp(Ptr<Ipv6> ipv6, uint32_t ifIndex, Ipv6InterfaceAddress address);

    void DoRun() override;

    uint16_t m_count;                                      //!< Number of packets received.
    std::vector<std::pair<Ptr<Socket>, bool>> m_sendSocks; //!< Sending sockets.
    DataRate m_dataRate;                                   //!< Data rate.
    uint16_t m_packetSize;                                 //!< Packet size.
    std::vector<uint8_t>
        m_firstInterface; //!< Packets received on the 1st interface at a given time.
    std::vector<uint8_t>
        m_secondInterface; //!< Packets received on the 2nd interface at a given time.
    NodeContainer c;       //!< nodes in the topology
};

// Add some help text to this case to describe what it is intended to test
Ipv6DynamicGlobalRoutingTestCase::Ipv6DynamicGlobalRoutingTestCase()
    : TestCase("Ipv6 Dynamic global routing example"),
      m_count(0)
{
    m_firstInterface.resize(16, 0);
    m_secondInterface.resize(16, 0);
    m_dataRate = DataRate("2kbps");
    m_packetSize = 50;
}

Ipv6DynamicGlobalRoutingTestCase::~Ipv6DynamicGlobalRoutingTestCase()
{
    for (auto iter = m_sendSocks.begin(); iter != m_sendSocks.end(); iter++)
    {
        if (iter->second)
        {
            iter->second = false;
            iter->first->Close();
            iter->first = nullptr;
        }
    }
}

void
Ipv6DynamicGlobalRoutingTestCase::HandleRead(Ptr<Socket> socket)
{
    Ptr<Packet> packet;
    Address from;

    while ((packet = socket->RecvFrom(from)))
    {
        if (packet->GetSize() == 0)
        { // EOF
            break;
        }
        Ipv6PacketInfoTag tag;
        bool found;
        found = packet->PeekPacketTag(tag);
        uint8_t now = static_cast<uint8_t>(Simulator::Now().GetSeconds());
        if (found)
        {
            if (tag.GetRecvIf() == 1)
            {
                m_firstInterface[now]++;
            }
            if (tag.GetRecvIf() == 2)
            {
                m_secondInterface[now]++;
            }
            m_count++;
        }
    }
}

void
Ipv6DynamicGlobalRoutingTestCase::SendData(uint8_t index)
{
    if (!m_sendSocks[index].second)
    {
        return;
    }
    Ptr<Packet> packet = Create<Packet>(m_packetSize);
    m_sendSocks[index].first->Send(packet);

    Time tNext(MicroSeconds(m_packetSize * 8 * 1e6 / m_dataRate.GetBitRate()));
    Simulator::Schedule(tNext, &Ipv6DynamicGlobalRoutingTestCase::SendData, this, index);
}

void
Ipv6DynamicGlobalRoutingTestCase::ShutDownSock(uint8_t index)
{
    m_sendSocks[index].second = false;
    m_sendSocks[index].first->Close();
    m_sendSocks[index].first = nullptr;
}

void
Ipv6DynamicGlobalRoutingTestCase::SetInterfaceUp(Ptr<Ipv6> ipv6,
                                                 uint32_t ifIndex,
                                                 Ipv6InterfaceAddress address)
{
    ipv6->SetUp(ifIndex);
    ipv6->AddAddress(ifIndex, address);
}

// Test derived from examples/routing/dynamic-global-routing.cc
//
// Network topology
//
//  n0
//     \ p-p
//      \          (shared csma/cd)
//       n2 -------------------------n3
//      /            |        |
//     / p-p        n4        n5 ---------- n6
//   n1                             p-p
//   |                                      |
//   ----------------------------------------
//                p-p
//
// Test that for node n6, the interface facing n5 receives packets at
// times (1-2), (4-6), (8-10), (11-12), (14-16) and the interface
// facing n1 receives packets at times (2-4), (6-8), (12-13)
//
void
Ipv6DynamicGlobalRoutingTestCase::DoRun()
{
    // The below value configures the default behavior of global routing.
    // By default, it is disabled.  To respond to interface events, set to true
    Config::SetDefault("ns3::Ipv6GlobalRouting::RespondToInterfaceEvents", BooleanValue(true));

    c.Create(7);
    NodeContainer n0n2 = NodeContainer(c.Get(0), c.Get(2));
    NodeContainer n1n2 = NodeContainer(c.Get(1), c.Get(2));
    NodeContainer n5n6 = NodeContainer(c.Get(5), c.Get(6));
    NodeContainer n1n6 = NodeContainer(c.Get(1), c.Get(6));
    NodeContainer n2345 = NodeContainer(c.Get(2), c.Get(3), c.Get(4), c.Get(5));

    Ipv6GlobalRoutingHelper ipv6RoutingHelper;
    InternetStackHelper internet;
    internet.SetRoutingHelper(ipv6RoutingHelper);

    internet.Install(c);

    // We create the channels first without any IP addressing information
    SimpleNetDeviceHelper devHelper;

    devHelper.SetNetDevicePointToPointMode(true);
    NetDeviceContainer d0d2 = devHelper.Install(n0n2);
    NetDeviceContainer d1d6 = devHelper.Install(n1n6);
    NetDeviceContainer d1d2 = devHelper.Install(n1n2);
    NetDeviceContainer d5d6 = devHelper.Install(n5n6);

    devHelper.SetNetDevicePointToPointMode(false);
    NetDeviceContainer d2345 = devHelper.Install(n2345);

    // Later, we add IP addresses.
    Ipv6AddressHelper ipv6;
    ipv6.SetBase("2001:1::", Ipv6Prefix(64));
    ipv6.Assign(d0d2);
    ipv6.SetBase("2001:2::", Ipv6Prefix(64));
    ipv6.Assign(d1d2);
    ipv6.SetBase("2001:3::", Ipv6Prefix(64));
    Ipv6InterfaceContainer i5i6 = ipv6.Assign(d5d6);
    ipv6.SetBase("2001:4::", Ipv6Prefix(64));
    ipv6.Assign(d2345);
    ipv6.SetBase("2001:5::", Ipv6Prefix(64));
    Ipv6InterfaceContainer i1i6 = ipv6.Assign(d1d6);

    // Create router nodes, initialize routing database and set up the routing
    // tables in the nodes.
    Ipv6GlobalRoutingHelper::PopulateRoutingTables();

    NeighborCacheHelper neighborCache;
    neighborCache.SetDynamicNeighborCache(true);
    neighborCache.PopulateNeighborCache();

    // Create the applications to send UDP datagrams of size
    // 50 bytes at a rate of 2 Kb/s
    TypeId tid = TypeId::LookupByName("ns3::UdpSocketFactory");
    uint16_t port = 9; // Discard port (RFC 863)

    std::pair<Ptr<Socket>, bool> sendSockA;
    sendSockA.first = Socket::CreateSocket(c.Get(1), tid);
    sendSockA.first->Bind();
    sendSockA.first->Connect(Inet6SocketAddress(i5i6.GetAddress(1, 1), port));
    sendSockA.second = true;
    m_sendSocks.push_back(sendSockA);
    Simulator::Schedule(Seconds(1), &Ipv6DynamicGlobalRoutingTestCase::SendData, this, 0);
    Simulator::Schedule(Seconds(10), &Ipv6DynamicGlobalRoutingTestCase::ShutDownSock, this, 0);

    std::pair<Ptr<Socket>, bool> sendSockB;
    sendSockB.first = Socket::CreateSocket(c.Get(1), tid);
    sendSockB.first->Bind();
    sendSockB.first->Connect(Inet6SocketAddress(i1i6.GetAddress(1, 1), port));
    sendSockB.second = true;
    m_sendSocks.push_back(sendSockB);
    Simulator::Schedule(Seconds(11), &Ipv6DynamicGlobalRoutingTestCase::SendData, this, 1);
    Simulator::Schedule(Seconds(16), &Ipv6DynamicGlobalRoutingTestCase::ShutDownSock, this, 1);

    // Create an optional packet sink to receive these packets
    Ptr<Socket> sink2 = Socket::CreateSocket(c.Get(6), tid);
    sink2->Bind(Address(Inet6SocketAddress(Ipv6Address::GetAny(), port)));
    sink2->Listen();
    sink2->ShutdownSend();

    sink2->SetRecvPktInfo(true);
    sink2->SetRecvCallback(MakeCallback(&Ipv6DynamicGlobalRoutingTestCase::HandleRead, this));

    Ptr<Node> n1 = c.Get(1);
    Ptr<Ipv6> ipv41 = n1->GetObject<Ipv6>();
    // The first ifIndex is 0 for loopback, then the first p2p is numbered 1,
    // then the next p2p is numbered 2
    uint32_t ipv4ifIndex1 = 2;

    Simulator::Schedule(Seconds(2), &Ipv6::SetDown, ipv41, ipv4ifIndex1);
    Simulator::Schedule(Seconds(4),
                        &Ipv6DynamicGlobalRoutingTestCase::SetInterfaceUp,
                        this,
                        ipv41,
                        ipv4ifIndex1,
                        Ipv6InterfaceAddress(Ipv6Address("2001:5::200:ff:fe00:3"), Ipv6Prefix(64)));

    Ptr<Node> n6 = c.Get(6);
    Ptr<Ipv6> ipv46 = n6->GetObject<Ipv6>();
    // The first ifIndex is 0 for loopback, then the first p2p is numbered 1,
    // then the next p2p is numbered 2
    uint32_t ipv4ifIndex6 = 2;
    Simulator::Schedule(Seconds(6), &Ipv6::SetDown, ipv46, ipv4ifIndex6);
    Simulator::Schedule(Seconds(8),
                        &Ipv6DynamicGlobalRoutingTestCase::SetInterfaceUp,
                        this,
                        ipv46,
                        ipv4ifIndex6,
                        Ipv6InterfaceAddress(Ipv6Address("2001:5::200:ff:fe00:4"), Ipv6Prefix(64)));

    Simulator::Schedule(Seconds(12), &Ipv6::SetDown, ipv41, ipv4ifIndex1);
    Simulator::Schedule(Seconds(14),
                        &Ipv6DynamicGlobalRoutingTestCase::SetInterfaceUp,
                        this,
                        ipv41,
                        ipv4ifIndex1,
                        Ipv6InterfaceAddress(Ipv6Address("2001:5::200:ff:fe00:3"), Ipv6Prefix(64)));

    Simulator::Run();

    NS_TEST_ASSERT_MSG_EQ(m_count, 70, "Dynamic global routing did not deliver all packets");
    // Test that for node n6, the interface facing n1 receives packets at
    // times (1-2), (4-6), (8-10), (11-12), (14-16) and the interface
    // facing n5 receives packets at times (2-4), (6-8), (12-13)

    // Note: there are two sending sockets, both on n1.
    // The first socket sends packets at time (1-10), the second at time (11, 16).
    // The first socket sends packets to n6, targeting the address facing n5.
    // The second socket sends packets to n6, targeting the address facing n1.
    // This actually doesn't matter, as n6 will accept packets sent to the "wrong" address.
    //
    // The shortest path netween n1 and n6 is the direct one, but the topology changes during the
    // simulation:
    // - (2-4): removal from n1 of the interface toward n6
    // - (6-8): removal from n6 of the interface toward n1
    // - (12-14): removal from n1 of the interface toward n6
    // When the link is broken, packets are rerouted though the longest (and only) path, reaching
    // n6 though n5.

    std::vector<uint8_t> firstInterfaceTest{0, 5, 0, 0, 5, 5, 0, 0, 5, 5, 0, 5, 0, 0, 5, 5};
    std::vector<uint8_t> secondInterfaceTest{0, 0, 5, 5, 0, 0, 5, 5, 0, 0, 0, 0, 5, 5, 0, 0};

    for (uint32_t index = 0; index < firstInterfaceTest.size(); index++)
    {
        NS_TEST_ASSERT_MSG_EQ(firstInterfaceTest[index],
                              m_firstInterface[index],
                              "Dynamic global routing did deliver the wrong number of packets "
                              "to the first interface at time "
                                  << index);
    }

    for (uint32_t index = 0; index < secondInterfaceTest.size(); index++)
    {
        NS_TEST_ASSERT_MSG_EQ(secondInterfaceTest[index],
                              m_secondInterface[index],
                              "Dynamic global routing did deliver the wrong number of packets "
                              "to the second interface at time "
                                  << index);
    }

    Simulator::Destroy();
}

/**
 * @ingroup internet-test
 *
 * @brief IPv4 Dynamic GlobalRouting /32 test
 */
class Ipv4GlobalRoutingSlash32TestCase : public TestCase
{
  public:
    Ipv4GlobalRoutingSlash32TestCase();
    ~Ipv4GlobalRoutingSlash32TestCase() override;

    Ptr<Packet> m_receivedPacket; //!< number of received packets

    /**
     * @brief Receive a packet.
     * @param socket The receiving socket.
     */
    void ReceivePkt(Ptr<Socket> socket);
    /**
     * @brief Send a packet.
     * @param socket The sending socket.
     * @param to The address of the receiver.
     */
    void DoSendData(Ptr<Socket> socket, std::string to);
    /**
     * @brief Send a packet.
     * @param socket The sending socket.
     * @param to The address of the receiver.
     */
    void SendData(Ptr<Socket> socket, std::string to);

  private:
    void DoRun() override;
};

// Add some help text to this case to describe what it is intended to test
Ipv4GlobalRoutingSlash32TestCase::Ipv4GlobalRoutingSlash32TestCase()
    : TestCase("Slash 32 global routing example")
{
}

Ipv4GlobalRoutingSlash32TestCase::~Ipv4GlobalRoutingSlash32TestCase()
{
}

void
Ipv4GlobalRoutingSlash32TestCase::ReceivePkt(Ptr<Socket> socket)
{
    uint32_t availableData [[maybe_unused]] = socket->GetRxAvailable();
    m_receivedPacket = socket->Recv(std::numeric_limits<uint32_t>::max(), 0);
    NS_TEST_ASSERT_MSG_EQ(availableData,
                          m_receivedPacket->GetSize(),
                          "Received packet size is not equal to Rx buffer size");
}

void
Ipv4GlobalRoutingSlash32TestCase::DoSendData(Ptr<Socket> socket, std::string to)
{
    Address realTo = InetSocketAddress(Ipv4Address(to.c_str()), 1234);
    NS_TEST_EXPECT_MSG_EQ(socket->SendTo(Create<Packet>(123), 0, realTo), 123, "100");
}

void
Ipv4GlobalRoutingSlash32TestCase::SendData(Ptr<Socket> socket, std::string to)
{
    m_receivedPacket = Create<Packet>();
    Simulator::ScheduleWithContext(socket->GetNode()->GetId(),
                                   Seconds(60),
                                   &Ipv4GlobalRoutingSlash32TestCase::DoSendData,
                                   this,
                                   socket,
                                   to);
    Simulator::Stop(Seconds(66));
    Simulator::Run();
}

// Test program for this 3-router scenario, using global routing
//
// (a.a.a.a/32)A<--x.x.x.0/30-->B<--y.y.y.0/30-->C(c.c.c.c/32)
//
void
Ipv4GlobalRoutingSlash32TestCase::DoRun()
{
    Ptr<Node> nA = CreateObject<Node>();
    Ptr<Node> nB = CreateObject<Node>();
    Ptr<Node> nC = CreateObject<Node>();

    NodeContainer c = NodeContainer(nA, nB, nC);

    InternetStackHelper internet;
    internet.Install(c);

    // simple links
    NodeContainer nAnB = NodeContainer(nA, nB);
    NodeContainer nBnC = NodeContainer(nB, nC);

    SimpleNetDeviceHelper devHelper;

    Ptr<SimpleNetDevice> deviceA = CreateObject<SimpleNetDevice>();
    deviceA->SetAddress(Mac48Address::Allocate());
    nA->AddDevice(deviceA);

    NetDeviceContainer dAdB = devHelper.Install(nAnB);
    NetDeviceContainer dBdC = devHelper.Install(nBnC);

    Ptr<SimpleNetDevice> deviceC = CreateObject<SimpleNetDevice>();
    deviceC->SetAddress(Mac48Address::Allocate());
    nC->AddDevice(deviceC);

    // Later, we add IP addresses.
    Ipv4AddressHelper ipv4;
    ipv4.SetBase("10.1.1.0", "255.255.255.252");
    Ipv4InterfaceContainer iAiB = ipv4.Assign(dAdB);

    ipv4.SetBase("10.1.1.4", "255.255.255.252");
    Ipv4InterfaceContainer iBiC = ipv4.Assign(dBdC);

    Ptr<Ipv4> ipv4A = nA->GetObject<Ipv4>();
    Ptr<Ipv4> ipv4B = nB->GetObject<Ipv4>();
    Ptr<Ipv4> ipv4C = nC->GetObject<Ipv4>();

    int32_t ifIndexA = ipv4A->AddInterface(deviceA);
    int32_t ifIndexC = ipv4C->AddInterface(deviceC);

    Ipv4InterfaceAddress ifInAddrA =
        Ipv4InterfaceAddress(Ipv4Address("172.16.1.1"), Ipv4Mask("/32"));
    ipv4A->AddAddress(ifIndexA, ifInAddrA);
    ipv4A->SetMetric(ifIndexA, 1);
    ipv4A->SetUp(ifIndexA);

    Ipv4InterfaceAddress ifInAddrC =
        Ipv4InterfaceAddress(Ipv4Address("192.168.1.1"), Ipv4Mask("/32"));
    ipv4C->AddAddress(ifIndexC, ifInAddrC);
    ipv4C->SetMetric(ifIndexC, 1);
    ipv4C->SetUp(ifIndexC);

    // Create router nodes, initialize routing database and set up the routing
    // tables in the nodes.
    Ipv4GlobalRoutingHelper::PopulateRoutingTables();

    // Create the UDP sockets
    Ptr<SocketFactory> rxSocketFactory = nC->GetObject<UdpSocketFactory>();
    Ptr<Socket> rxSocket = rxSocketFactory->CreateSocket();
    NS_TEST_EXPECT_MSG_EQ(rxSocket->Bind(InetSocketAddress(Ipv4Address("192.168.1.1"), 1234)),
                          0,
                          "trivial");
    rxSocket->SetRecvCallback(MakeCallback(&Ipv4GlobalRoutingSlash32TestCase::ReceivePkt, this));

    Ptr<SocketFactory> txSocketFactory = nA->GetObject<UdpSocketFactory>();
    Ptr<Socket> txSocket = txSocketFactory->CreateSocket();
    txSocket->SetAllowBroadcast(true);

    // ------ Now the tests ------------

    // Unicast test
    SendData(txSocket, "192.168.1.1");
    NS_TEST_EXPECT_MSG_EQ(m_receivedPacket->GetSize(),
                          123,
                          "Static routing with /32 did not deliver all packets.");

    Simulator::Destroy();
}

/**
 * @ingroup internet-test
 *
 * @brief This TestCase tests if ECMP Route Calculation works. It does not check the
 * correctness of the routes.
 */
class EcmpRouteCalculationTestCase : public TestCase
{
  public:
    EcmpRouteCalculationTestCase();
    void DoSetup() override;
    void DoRun() override;

  private:
    NodeContainer nodes; //!< Nodes used in the test.
};

EcmpRouteCalculationTestCase::EcmpRouteCalculationTestCase()
    : TestCase("ECMP Route Calculation TestCase")
{
}

void
EcmpRouteCalculationTestCase::DoSetup()
{
    /*
        This TestCase checks the resolution of issue #1243 in the issue tracker.
        The problem was that Global Routing failed to calculate next hops when going from a
        network vertex to a router vertex when ECMP Routes were involved.

        //         Network Topology
        //
        //
        //          ------n1------
        //         /              \
        //        /                \
        //       n0                n3----n4----n5
        //        \                /
        //         \              /
        //          ------n2------
        //
        //    Link n0-n1: 10.1.1.1/30,10.1.1.2/30
        //    Link n0-n2: 10.1.2.1/30,10.1.2.2/30
        //    Link n1-n3: 10.1.3.1/30,10.1.3.2/30
        //    Link n2-n3: 10.1.4.1/30,10.1.4.2/30
        //    Link n3-n4: 10.1.5.1/24,10.1.5.2/24
        //    Link n4-n5: 10.1.6.1/24,10.1.6.2/24
        //
        //     Note: Link n4-n5 is a LAN LINK. All others are simple P2P links.
    */

    nodes.Create(6);

    Ipv4GlobalRoutingHelper globalhelperv4;
    Ipv6GlobalRoutingHelper globalhelperv6;

    InternetStackHelper stack;
    stack.SetRoutingHelper(globalhelperv4);
    stack.SetRoutingHelper(globalhelperv6);
    stack.Install(nodes);
    SimpleNetDeviceHelper devHelper;
    devHelper.SetNetDevicePointToPointMode(true);

    Ptr<SimpleChannel> channel1 = CreateObject<SimpleChannel>();
    NetDeviceContainer d01 = devHelper.Install(nodes.Get(0), channel1);
    d01.Add(devHelper.Install(nodes.Get(1), channel1));

    Ptr<SimpleChannel> channel2 = CreateObject<SimpleChannel>();
    NetDeviceContainer d23 = devHelper.Install(nodes.Get(2), channel2);
    d23.Add(devHelper.Install(nodes.Get(3), channel2));

    Ptr<SimpleChannel> channel3 = CreateObject<SimpleChannel>();
    NetDeviceContainer d02 = devHelper.Install(nodes.Get(0), channel3);
    d02.Add(devHelper.Install(nodes.Get(2), channel3));

    Ptr<SimpleChannel> channel4 = CreateObject<SimpleChannel>();
    NetDeviceContainer d34 = devHelper.Install(nodes.Get(3), channel4);
    d34.Add(devHelper.Install(nodes.Get(4), channel4));

    Ptr<SimpleChannel> channel5 = CreateObject<SimpleChannel>();
    NetDeviceContainer d13 = devHelper.Install(nodes.Get(1), channel5);
    d13.Add(devHelper.Install(nodes.Get(3), channel5));

    devHelper.SetNetDevicePointToPointMode(false);

    Ptr<SimpleChannel> channel6 = CreateObject<SimpleChannel>();
    NetDeviceContainer d45 = devHelper.Install(nodes.Get(4), channel6);
    d45.Add(devHelper.Install(nodes.Get(5), channel6));

    // Assign IP addresses to the devices
    Ipv4AddressHelper address;
    address.SetBase("10.1.1.0", "255.255.255.252");
    Ipv4InterfaceContainer i01 = address.Assign(d01);

    address.SetBase("10.1.2.0", "255.255.255.252");
    Ipv4InterfaceContainer i02 = address.Assign(d02);

    address.SetBase("10.1.3.0", "255.255.255.252");
    Ipv4InterfaceContainer i13 = address.Assign(d13);

    address.SetBase("10.1.4.0", "255.255.255.224");
    Ipv4InterfaceContainer i23 = address.Assign(d23);

    address.SetBase("10.1.5.0", "255.255.255.0");
    Ipv4InterfaceContainer i34 = address.Assign(d34);

    address.SetBase("10.1.6.0", "255.255.255.0");
    Ipv4InterfaceContainer i45 = address.Assign(d45);

    Ipv6AddressHelper ipv6;
    ipv6.SetBase("2001:1::", Ipv6Prefix(64));
    ipv6.Assign(d01);
    ipv6.SetBase("2001:2::", Ipv6Prefix(64));
    ipv6.Assign(d02);
    ipv6.SetBase("2001:3::", Ipv6Prefix(64));
    ipv6.Assign(d13);
    ipv6.SetBase("2001:4::", Ipv6Prefix(64));
    ipv6.Assign(d23);
    ipv6.SetBase("2001:5::", Ipv6Prefix(64));
    ipv6.Assign(d34);
    ipv6.SetBase("2001:6::", Ipv6Prefix(64));
    ipv6.Assign(d45);
}

void
EcmpRouteCalculationTestCase::DoRun()
{
    // The purpose of this test is to make sure the code doesn't crash when calculating ECMP routes
    // with a topology described in issue #1243. It does not look into the correctness of the
    // routes. We have other tests to make sure the routes are correct.
    Ipv4GlobalRoutingHelper::PopulateRoutingTables();
    Ipv6GlobalRoutingHelper::PopulateRoutingTables();

    Simulator::Run();
    Simulator::Stop(Seconds(2));
    Simulator::Destroy();
}

/**
 * @ingroup internet-test
 *
 * @brief TestCase to check calls to GlobalRoutingProtocol API. This TestCase Checks the output
 * returned by the GlobalRoutingProtocol API to Ipv4L3 Layer.
 */
class GlobalRoutingv4ProtocolTestCase : public TestCase
{
  public:
    GlobalRoutingv4ProtocolTestCase();
    ~GlobalRoutingv4ProtocolTestCase() override;

  private:
    void DoSetup() override;
    void DoRun() override;
    /**
     * @brief Checks the Path taken by packets by calling RouteInput() and RouteOutput() APIs of
     * GlobalRoutingProtocol. This mimics how Ipv4L3Protocol calls these APIs.
     * @param pathNodes Vector of nodes in the path. This includes the source node,Intermediate
     * nextHop nodes and the destination node.
     * @param path Vector of Ipv4Addresses in the path. This includes the source egress IpAddress,
     * destination ingress IpAddress and intermediate nextHop Node's ingress Ipaddress that we need
     * to check.It does not include the egress addresses for intermediate nodes.
     */
    void CheckPath(std::vector<Ptr<Node>> pathNodes, std::vector<Ipv4Address> path);

    /**
     * @brief Callback function for RouteInput() API of GlobalRoutingProtocol.
     * @param route Route to be used for forwarding
     * @param packet Packet to be forwarded
     * @param header IPv4 header of the packet
     */
    void MyUnicastCallback(Ptr<Ipv4Route> route,
                           Ptr<const Packet> packet,
                           const Ipv4Header& header);
    /**
     * @brief Callback function for RouteInput() API of GlobalRoutingProtocol.
     * @param packet Packet to be locally delivered
     * @param header IPv4 header of the packet
     * @param iif Ingress Interface index
     */
    void MyLocalDeliverCallback(Ptr<const Packet> packet, const Ipv4Header& header, uint32_t iif);
    /**
     * @brief Callback function for RouteInput() API of GlobalRoutingProtocol.
     * @param packet Packet to be locally delivered
     * @param header IPv4 header of the packet
     * @param errno_ Socket error number
     */
    void MyErrorCallback(Ptr<const Packet> packet,
                         const Ipv4Header& header,
                         Socket::SocketErrno errno_);

    NodeContainer nodes;                                 //!< Nodes used in the test.
    Ptr<Ipv4Route> m_lastRoute;                          //!< Route to be tested.
    Ipv4RoutingProtocol::UnicastForwardCallback m_ucb;   ///< Unicast forward callback
    Ipv4RoutingProtocol::MulticastForwardCallback m_mcb; ///< Multicast forward callback
    Ipv4RoutingProtocol::LocalDeliverCallback m_lcb;     ///< Local delivery callback
    Ipv4RoutingProtocol::ErrorCallback m_ecb;            ///< Error callback
};

// Signature: void (Ptr<Ipv4Route> route, Ptr<const Packet> packet, const Ipv4Header &header)
void
GlobalRoutingv4ProtocolTestCase::MyUnicastCallback(Ptr<Ipv4Route> route,
                                                   Ptr<const Packet> packet,
                                                   const Ipv4Header& header)
{
    NS_LOG_DEBUG("Unicast Forward Callback called.");
    m_lastRoute = route;
}

// Signature: void (Ptr<const Packet>, const Ipv4Header&, uint32_t)
void
GlobalRoutingv4ProtocolTestCase::MyLocalDeliverCallback(Ptr<const Packet> packet,
                                                        const Ipv4Header& header,
                                                        uint32_t iif)
{
    NS_LOG_DEBUG("Local Deliver Callback called.");
}

// Signature: void (Ptr<const Packet>, const Ipv4Header&, Socket::SocketErrno)
void
GlobalRoutingv4ProtocolTestCase::MyErrorCallback(Ptr<const Packet> packet,
                                                 const Ipv4Header& header,
                                                 Socket::SocketErrno errno_)
{
    NS_LOG_DEBUG("Error Callback called.");
    // Fail the test if this callback is called
    NS_TEST_ASSERT_MSG_EQ(1, 1, "Error Callback called in RouteInput");
}

GlobalRoutingv4ProtocolTestCase::GlobalRoutingv4ProtocolTestCase()
    : TestCase("Test API calls to GlobalRoutingProtocol")
{
    m_ucb = MakeCallback(&GlobalRoutingv4ProtocolTestCase::MyUnicastCallback, this);
    m_lcb = MakeCallback(&GlobalRoutingv4ProtocolTestCase::MyLocalDeliverCallback, this);
    m_ecb = MakeCallback(&GlobalRoutingv4ProtocolTestCase::MyErrorCallback, this);
    // Multicast callback is not used in unicast test, can bind to null or dummy
    Ipv4RoutingProtocol::MulticastForwardCallback mcb;
}

GlobalRoutingv4ProtocolTestCase::~GlobalRoutingv4ProtocolTestCase()
{
}

void
GlobalRoutingv4ProtocolTestCase::CheckPath(std::vector<Ptr<Node>> pathNodes,
                                           std::vector<Ipv4Address> path)
{
    uint32_t pathSize = path.size();

    Ptr<Ipv4L3Protocol> ip = pathNodes[0]->GetObject<Ipv4L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ip, nullptr, "Error-- no Ipv4 object at source node");
    Ptr<Ipv4RoutingProtocol> routing = ip->GetRoutingProtocol();
    NS_TEST_ASSERT_MSG_NE(routing,
                          nullptr,
                          "Error-- no Ipv4 routing protocol object at source node");
    Ptr<Ipv4GlobalRouting> globalRouting = routing->GetObject<Ipv4GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRouting,
                          nullptr,
                          "Error-- no Ipv4GlobalRouting object at source node");

    Socket::SocketErrno errno_;
    Ptr<NetDevice> oif(nullptr);
    Ptr<Packet> packet = Create<Packet>();
    Ipv4Header ipHeader;
    ipHeader.SetSource(path[0]);
    ipHeader.SetDestination(path[pathSize - 1]);

    // for the source node we need to call RouteOutput()
    m_lastRoute = globalRouting->RouteOutput(packet, ipHeader, oif, errno_);
    // for the rest of the nodes we need to call RouteInput()
    for (uint32_t i = 1; i < pathSize; i++)
    {
        // for each iteration except the last one check that the UnicastForward Callback is called.
        // For the last node that is the destination node LocalDelivery Callback will be called.
        if (i != pathSize - 1)
        {
            NS_TEST_ASSERT_MSG_EQ(m_lastRoute->GetGateway(), path[i], "Error-- wrong gateway");
        }

        ip = pathNodes[i]->GetObject<Ipv4L3Protocol>();
        NS_TEST_ASSERT_MSG_NE(ip, nullptr, "Error-- no Ipv4 object at node");
        routing = ip->GetRoutingProtocol();
        NS_TEST_ASSERT_MSG_NE(routing, nullptr, "Error-- no Ipv4 routing protocol object at node");
        globalRouting = routing->GetObject<Ipv4GlobalRouting>();
        NS_TEST_ASSERT_MSG_NE(globalRouting,
                              nullptr,
                              "Error-- no Ipv4GlobalRouting object at node");

        Ptr<Ipv4Interface> interf = ip->GetInterface(ip->GetInterfaceForAddress(path[i]));
        Ptr<NetDevice> idevice = interf->GetDevice();

        // call RouteInput() for the next node in the path
        globalRouting->RouteInput(packet, ipHeader, idevice, m_ucb, m_mcb, m_lcb, m_ecb);
    }
}

void
GlobalRoutingv4ProtocolTestCase::DoSetup()
{
    /**
     * This test case is designed to test the overall functionality of the GlobalRoutingProtocol.
     * It tests the calls to the GlobalRoutingProtocol's RouteOutput() and RouteInput() APIs.
     * It mimics how Ipv4L3Protocol calls these APIs. The topology covers most of the cases
     * that are expected to be handled by the GlobalRoutingProtocol.
     */

    //
    //
    // Network topology
    //
    //  n0
    //     \ p-p
    //      \          (shared csma/cd)
    //       n2 -------------------------n3
    //      /            |        |
    //     / p-p        n4        n5 ---------- n6-------n7----Bridge-n8--------n9
    //   n1                             p-p     |              10.1.4.0/24
    //   |                                      |
    //   ----------------------------------------
    //                p-p
    //
    //
    //

    nodes.Create(10);
    NodeContainer n2345 = NodeContainer(nodes.Get(2), nodes.Get(3), nodes.Get(4), nodes.Get(5));

    // We create the channels first without any IP addressing information
    SimpleNetDeviceHelper devHelper;

    devHelper.SetNetDevicePointToPointMode(true);

    Ptr<SimpleChannel> channel1 = CreateObject<SimpleChannel>();
    NetDeviceContainer d0d2 = devHelper.Install(nodes.Get(0), channel1);
    d0d2.Add(devHelper.Install(nodes.Get(2), channel1));

    Ptr<SimpleChannel> channel2 = CreateObject<SimpleChannel>();
    NetDeviceContainer d1d6 = devHelper.Install(nodes.Get(1), channel2);
    d1d6.Add(devHelper.Install(nodes.Get(6), channel2));

    Ptr<SimpleChannel> channel3 = CreateObject<SimpleChannel>();
    NetDeviceContainer d1d2 = devHelper.Install(nodes.Get(1), channel3);
    d1d2.Add(devHelper.Install(nodes.Get(2), channel3));

    Ptr<SimpleChannel> channel4 = CreateObject<SimpleChannel>();
    NetDeviceContainer d5d6 = devHelper.Install(nodes.Get(5), channel4);
    d5d6.Add(devHelper.Install(nodes.Get(6), channel4));

    Ptr<SimpleChannel> channel5 = CreateObject<SimpleChannel>();
    devHelper.SetNetDevicePointToPointMode(false);
    NetDeviceContainer d6d7 = devHelper.Install(nodes.Get(6), channel5);
    d6d7.Add(devHelper.Install(nodes.Get(7), channel5));

    devHelper.SetNetDevicePointToPointMode(false);
    NetDeviceContainer d2345 = devHelper.Install(n2345);

    // handle the bridge
    // connect node 7 to node 8(Switch)
    Ptr<SimpleChannel> channel6 = CreateObject<SimpleChannel>();
    NetDeviceContainer d78 = devHelper.Install(nodes.Get(7), channel6);
    d78.Add(devHelper.Install(nodes.Get(8), channel6));

    // connect node 8(switch) to node 9
    Ptr<SimpleChannel> channel7 = CreateObject<SimpleChannel>();
    NetDeviceContainer d89 = devHelper.Install(nodes.Get(8), channel7);
    d89.Add(devHelper.Install(nodes.Get(9), channel7));

    NetDeviceContainer bridgeFacingDevices;
    NetDeviceContainer switchDevices;
    bridgeFacingDevices.Add(d78.Get(0));
    switchDevices.Add(d78.Get(1));
    bridgeFacingDevices.Add(d89.Get(1));
    switchDevices.Add(d89.Get(0));

    Ptr<Node> switchNode = nodes.Get(8);
    BridgeHelper bridge;
    bridge.Install(switchNode, switchDevices);

    InternetStackHelper internet;
    Ipv4GlobalRoutingHelper glbrouting;
    internet.SetRoutingHelper(glbrouting);
    internet.Install(nodes.Get(0));
    internet.Install(nodes.Get(1));
    internet.Install(nodes.Get(2));
    internet.Install(nodes.Get(3));
    internet.Install(nodes.Get(4));
    internet.Install(nodes.Get(5));
    internet.Install(nodes.Get(6));
    internet.Install(nodes.Get(7));
    // node 8 is a bridge node
    internet.Install(nodes.Get(9));

    // Later, we add IP addresses.
    Ipv4AddressHelper ipv4;
    ipv4.SetBase("10.1.1.0", "255.255.255.0");
    Ipv4InterfaceContainer i0d2 = ipv4.Assign(d0d2);

    ipv4.SetBase("10.1.2.0", "255.255.255.0");
    Ipv4InterfaceContainer i1d2 = ipv4.Assign(d1d2);

    ipv4.SetBase("10.1.3.0", "255.255.255.0");
    Ipv4InterfaceContainer i5i6 = ipv4.Assign(d5d6);

    ipv4.SetBase("10.250.1.0", "255.255.255.0");
    Ipv4InterfaceContainer i2345 = ipv4.Assign(d2345);

    ipv4.SetBase("172.16.1.0", "255.255.255.0");
    Ipv4InterfaceContainer i1i6 = ipv4.Assign(d1d6);

    ipv4.SetBase("10.1.4.0", "255.255.255.0");
    Ipv4InterfaceContainer i67 = ipv4.Assign(d6d7);

    ipv4.SetBase("10.1.5.0", "255.255.255.0");
    Ipv4InterfaceContainer i79 = ipv4.Assign(bridgeFacingDevices);
}

void
GlobalRoutingv4ProtocolTestCase::DoRun()
{
    // Create router nodes, initialize routing database and set up the routing
    // tables in the nodes.
    GlobalRouteManager<Ipv4Manager>::ResetRouterId();

    Ipv4GlobalRoutingHelper::PopulateRoutingTables();

    // tests-------------------------
    // test 1:check path from n0 to n6
    // test 2:check path from n1 to n5
    // test 3:check path from n1 to n9
    // test 4:check path from n8 to n0

    // Test 1: check path from n0 to n6
    std::vector<Ipv4Address>
        pathToCheck1; // includes the source egress destination ingress and intermediate next hops
                      // addresses that we need to check.It does not include the egress addresses
                      // for intermediate nodes.
    pathToCheck1.emplace_back("10.1.1.1");
    pathToCheck1.emplace_back("10.1.1.2");
    pathToCheck1.emplace_back("10.1.2.1");
    pathToCheck1.emplace_back("172.16.1.2");

    std::vector<Ptr<Node>>
        pathNodes1; // nodes in the path. Includes the source node and the destination node.
    pathNodes1.push_back(nodes.Get(0));
    pathNodes1.push_back(nodes.Get(2));
    pathNodes1.push_back(nodes.Get(1));
    pathNodes1.push_back(nodes.Get(6));

    CheckPath(pathNodes1, pathToCheck1);

    // Test 2:check path from n1 to n5
    std::vector<Ipv4Address> pathToCheck2;
    pathToCheck2.emplace_back("10.1.2.1");
    pathToCheck2.emplace_back("10.1.2.2");
    pathToCheck2.emplace_back("10.250.1.4");

    std::vector<Ptr<Node>> pathNodes2;
    pathNodes2.push_back(nodes.Get(1));
    pathNodes2.push_back(nodes.Get(2));
    pathNodes2.push_back(nodes.Get(5));

    CheckPath(pathNodes2, pathToCheck2);

    // Test 3:check path from n1 to n9
    std::vector<Ipv4Address> pathToCheck3;
    pathToCheck3.emplace_back("172.16.1.1");
    pathToCheck3.emplace_back("172.16.1.2");
    pathToCheck3.emplace_back("10.1.4.2");
    pathToCheck3.emplace_back("10.1.5.2");

    std::vector<Ptr<Node>> pathNodes3;
    pathNodes3.push_back(nodes.Get(1));
    pathNodes3.push_back(nodes.Get(6));
    pathNodes3.push_back(nodes.Get(7));
    pathNodes3.push_back(nodes.Get(9));

    CheckPath(pathNodes3, pathToCheck3);

    // Test 4:check path from n9 to n0
    std::vector<Ipv4Address> pathToCheck4;
    pathToCheck4.emplace_back("10.1.5.2");
    pathToCheck4.emplace_back("10.1.5.1");
    pathToCheck4.emplace_back("10.1.4.1");
    pathToCheck4.emplace_back("10.1.3.1");
    pathToCheck4.emplace_back("10.250.1.1");
    pathToCheck4.emplace_back("10.1.1.1");

    std::vector<Ptr<Node>> pathNodes4;
    pathNodes4.push_back(nodes.Get(9));
    pathNodes4.push_back(nodes.Get(7));
    pathNodes4.push_back(nodes.Get(6));
    pathNodes4.push_back(nodes.Get(5));
    pathNodes4.push_back(nodes.Get(2));
    pathNodes4.push_back(nodes.Get(0));

    CheckPath(pathNodes4, pathToCheck4);

    Simulator::Run();
    Simulator::Stop(Seconds(10));
    Simulator::Destroy();
}

/**
 * @ingroup internet-test
 *
 * @brief TestCase to check calls to GlobalRoutingProtocol API. This TestCase Checks the output
 * returned by the GlobalRoutingProtocol API to Ipv4L3 Layer.
 */
class GlobalRoutingv6ProtocolTestCase : public TestCase
{
  public:
    GlobalRoutingv6ProtocolTestCase();
    ~GlobalRoutingv6ProtocolTestCase() override;

  private:
    void DoSetup() override;
    void DoRun() override;
    /**
     * @brief Checks the Path taken by packets by calling RouteInput() and RouteOutput() APIs of
     * GlobalRoutingProtocol. This mimics how Ipv4L3Protocol calls these APIs.
     * @param pathNodes Vector of nodes in the path. This includes the source node,Intermediate
     * nextHop nodes and the destination node.
     * @param path Vector of Ipv4Addresses in the path. This includes the source egress IpAddress,
     * destination ingress IpAddress and intermediate nextHop Node's ingress Ipaddress that we need
     * to check.It does not include the egress addresses for intermediate nodes.
     */
    void CheckPath(std::vector<Ptr<Node>> pathNodes, std::vector<Ipv6Address> path);

    /**
     * @brief Callback function for RouteInput() API of GlobalRoutingProtocol.
     *
     * @param idev The ingress netdevice
     * @param rtentry The route entry
     * @param p The packet
     * @param header The IPv6 header of the packet
     */
    void MyUnicastCallback(Ptr<const NetDevice> idev,
                           Ptr<Ipv6Route> rtentry,
                           Ptr<const Packet> p,
                           const Ipv6Header& header);
    /**
     * @brief Callback function for RouteInput() API of GlobalRoutingProtocol.
     * @param packet Packet to be locally delivered
     * @param header IPv6 header of the packet
     * @param iif Ingress Interface index
     */
    void MyLocalDeliverCallback(Ptr<const Packet> packet, const Ipv6Header& header, uint32_t iif);
    /**
     * @brief Callback function for RouteInput() API of GlobalRoutingProtocol.
     * @param packet Packet to be locally delivered
     * @param header IPv6 header of the packet
     * @param errno_ Socket error number
     */
    void MyErrorCallback(Ptr<const Packet> packet,
                         const Ipv6Header& header,
                         Socket::SocketErrno errno_);

    NodeContainer nodes;                                 //!< Nodes used in the test.
    Ptr<Ipv6Route> m_lastRoute;                          //!< Route to be tested.
    Ipv6RoutingProtocol::UnicastForwardCallback m_ucb;   ///< Unicast forward callback
    Ipv6RoutingProtocol::MulticastForwardCallback m_mcb; ///< Multicast forward callback
    Ipv6RoutingProtocol::LocalDeliverCallback m_lcb;     ///< Local delivery callback
    Ipv6RoutingProtocol::ErrorCallback m_ecb;            ///< Error callback
};

// Signature: void (Ptr<Ipv6Route> route, Ptr<const Packet> packet, const Ipv4Header &header)
void
GlobalRoutingv6ProtocolTestCase::MyUnicastCallback(Ptr<const NetDevice> idev,
                                                   Ptr<Ipv6Route> rtentry,
                                                   Ptr<const Packet> p,
                                                   const Ipv6Header& header)
{
    NS_LOG_DEBUG("Unicast Forward Callback called.");
    m_lastRoute = rtentry;
}

// Signature: void (Ptr<const Packet>, const Ipv4Header&, uint32_t)
void
GlobalRoutingv6ProtocolTestCase::MyLocalDeliverCallback(Ptr<const Packet> packet,
                                                        const Ipv6Header& header,
                                                        uint32_t iif)
{
    NS_LOG_DEBUG("Local Deliver Callback called.");
}

// Signature: void (Ptr<const Packet>, const Ipv4Header&, Socket::SocketErrno)
void
GlobalRoutingv6ProtocolTestCase::MyErrorCallback(Ptr<const Packet> packet,
                                                 const Ipv6Header& header,
                                                 Socket::SocketErrno errno_)
{
    NS_LOG_DEBUG("Error Callback called.");
    // Fail the test if this callback is called
    NS_TEST_ASSERT_MSG_EQ(1, 1, "Error Callback called in RouteInput");
}

GlobalRoutingv6ProtocolTestCase::GlobalRoutingv6ProtocolTestCase()
    : TestCase("Test API calls to GlobalRoutingProtocol")
{
    m_ucb = MakeCallback(&GlobalRoutingv6ProtocolTestCase::MyUnicastCallback, this);
    m_lcb = MakeCallback(&GlobalRoutingv6ProtocolTestCase::MyLocalDeliverCallback, this);
    m_ecb = MakeCallback(&GlobalRoutingv6ProtocolTestCase::MyErrorCallback, this);
    // Multicast callback is not used in unicast test, can bind to null or dummy
    Ipv6RoutingProtocol::MulticastForwardCallback mcb;
}

GlobalRoutingv6ProtocolTestCase::~GlobalRoutingv6ProtocolTestCase()
{
}

void
GlobalRoutingv6ProtocolTestCase::CheckPath(std::vector<Ptr<Node>> pathNodes,
                                           std::vector<Ipv6Address> path)
{
    uint32_t pathSize = path.size();

    Ptr<Ipv6L3Protocol> ip = pathNodes[0]->GetObject<Ipv6L3Protocol>();
    NS_TEST_ASSERT_MSG_NE(ip, nullptr, "Error-- no Ipv4 object at source node");
    Ptr<Ipv6RoutingProtocol> routing = ip->GetRoutingProtocol();
    NS_TEST_ASSERT_MSG_NE(routing,
                          nullptr,
                          "Error-- no Ipv4 routing protocol object at source node");
    Ptr<Ipv6GlobalRouting> globalRouting = routing->GetObject<Ipv6GlobalRouting>();
    NS_TEST_ASSERT_MSG_NE(globalRouting,
                          nullptr,
                          "Error-- no Ipv4GlobalRouting object at source node");

    Socket::SocketErrno errno_;
    Ptr<NetDevice> oif(nullptr);
    Ptr<Packet> packet = Create<Packet>();
    Ipv6Header ipHeader;
    ipHeader.SetSource(path[0]);

    ipHeader.SetDestination(path[pathSize - 1]);

    // for the source node we need to call RouteOutput()
    m_lastRoute = globalRouting->RouteOutput(packet, ipHeader, oif, errno_);
    // for the rest of the nodes we need to call RouteInput()
    for (uint32_t i = 1; i < pathSize; i++)
    {
        // for each iteration except the last one check that the UnicastForward Callback is called.
        // For the last node that is the destination node LocalDelivery Callback will be called.
        if (i != pathSize - 1)
        {
            NS_TEST_ASSERT_MSG_EQ(m_lastRoute->GetGateway(), path[i], "Error-- wrong gateway");
        }

        ip = pathNodes[i]->GetObject<Ipv6L3Protocol>();
        NS_TEST_ASSERT_MSG_NE(ip, nullptr, "Error-- no Ipv6 object at node");
        routing = ip->GetRoutingProtocol();
        NS_TEST_ASSERT_MSG_NE(routing, nullptr, "Error-- no Ipv6 routing protocol object at node");
        globalRouting = routing->GetObject<Ipv6GlobalRouting>();
        NS_TEST_ASSERT_MSG_NE(globalRouting,
                              nullptr,
                              "Error-- no Ipv4GlobalRouting object at node");

        Ptr<Ipv6Interface> interf = ip->GetInterface(ip->GetInterfaceForAddress(path[i]));
        Ptr<NetDevice> idevice = interf->GetDevice();

        // call RouteInput() for the next node in the path
        globalRouting->RouteInput(packet, ipHeader, idevice, m_ucb, m_mcb, m_lcb, m_ecb);
    }
}

void
GlobalRoutingv6ProtocolTestCase::DoSetup()
{
    /**
     * This test case is designed to test the overall functionality of the GlobalRoutingProtocol.
     * It tests the calls to the GlobalRoutingProtocol's RouteOutput() and RouteInput() APIs.
     * It mimics how Ipv4L3Protocol calls these APIs. The topology covers most of the cases
     * that are expected to be handled by the GlobalRoutingProtocol.
     */

    //
    //
    // Network topology
    //
    //  n0
    //     \ p-p
    //      \          (shared csma/cd)
    //       n2 -------------------------n3
    //      /            |        |
    //     / p-p        n4        n5 ---------- n6-------n7----Bridge-n8--------n9
    //   n1                             p-p     |              10.1.4.0/24
    //   |                                      |
    //   ----------------------------------------
    //                p-p
    //
    //
    //

    nodes.Create(10);
    NodeContainer n2345 = NodeContainer(nodes.Get(2), nodes.Get(3), nodes.Get(4), nodes.Get(5));

    // We create the channels first without any IP addressing information
    SimpleNetDeviceHelper devHelper;

    devHelper.SetNetDevicePointToPointMode(true);

    Ptr<SimpleChannel> channel1 = CreateObject<SimpleChannel>();
    NetDeviceContainer d0d2 = devHelper.Install(nodes.Get(0), channel1);
    d0d2.Add(devHelper.Install(nodes.Get(2), channel1));

    Ptr<SimpleChannel> channel2 = CreateObject<SimpleChannel>();
    NetDeviceContainer d1d6 = devHelper.Install(nodes.Get(1), channel2);
    d1d6.Add(devHelper.Install(nodes.Get(6), channel2));

    Ptr<SimpleChannel> channel3 = CreateObject<SimpleChannel>();
    NetDeviceContainer d1d2 = devHelper.Install(nodes.Get(1), channel3);
    d1d2.Add(devHelper.Install(nodes.Get(2), channel3));

    Ptr<SimpleChannel> channel4 = CreateObject<SimpleChannel>();
    NetDeviceContainer d5d6 = devHelper.Install(nodes.Get(5), channel4);
    d5d6.Add(devHelper.Install(nodes.Get(6), channel4));

    Ptr<SimpleChannel> channel5 = CreateObject<SimpleChannel>();
    devHelper.SetNetDevicePointToPointMode(false);
    NetDeviceContainer d6d7 = devHelper.Install(nodes.Get(6), channel5);
    d6d7.Add(devHelper.Install(nodes.Get(7), channel5));

    devHelper.SetNetDevicePointToPointMode(false);
    NetDeviceContainer d2345 = devHelper.Install(n2345);

    // handle the bridge
    // connect node 7 to node 8(Switch)
    Ptr<SimpleChannel> channel6 = CreateObject<SimpleChannel>();
    NetDeviceContainer d78 = devHelper.Install(nodes.Get(7), channel6);
    d78.Add(devHelper.Install(nodes.Get(8), channel6));

    // connect node 8(switch) to node 9
    Ptr<SimpleChannel> channel7 = CreateObject<SimpleChannel>();
    NetDeviceContainer d89 = devHelper.Install(nodes.Get(8), channel7);
    d89.Add(devHelper.Install(nodes.Get(9), channel7));

    NetDeviceContainer bridgeFacingDevices;
    NetDeviceContainer switchDevices;
    bridgeFacingDevices.Add(d78.Get(0));
    switchDevices.Add(d78.Get(1));
    bridgeFacingDevices.Add(d89.Get(1));
    switchDevices.Add(d89.Get(0));

    Ptr<Node> switchNode = nodes.Get(8);
    BridgeHelper bridge;
    bridge.Install(switchNode, switchDevices);

    InternetStackHelper internet;
    Ipv6GlobalRoutingHelper glbrouting;
    internet.SetRoutingHelper(glbrouting);
    internet.Install(nodes.Get(0));
    internet.Install(nodes.Get(1));
    internet.Install(nodes.Get(2));
    internet.Install(nodes.Get(3));
    internet.Install(nodes.Get(4));
    internet.Install(nodes.Get(5));
    internet.Install(nodes.Get(6));
    internet.Install(nodes.Get(7));
    // node 8 is a bridge node
    internet.Install(nodes.Get(9));

    // Later, we add IP addresses.
    Ipv6AddressHelper ipv6;
    ipv6.SetBase("2001:1::", Ipv6Prefix(64));
    Ipv6InterfaceContainer i0d2 = ipv6.Assign(d0d2);

    ipv6.SetBase("2001:2::", Ipv6Prefix(64));
    Ipv6InterfaceContainer i1d2 = ipv6.Assign(d1d2);

    ipv6.SetBase("2001:3::", Ipv6Prefix(64));
    Ipv6InterfaceContainer i5i6 = ipv6.Assign(d5d6);

    ipv6.SetBase("2001:4::", Ipv6Prefix(64));
    Ipv6InterfaceContainer i2345 = ipv6.Assign(d2345);

    ipv6.SetBase("2001:5::", Ipv6Prefix(64));
    Ipv6InterfaceContainer i1i6 = ipv6.Assign(d1d6);

    ipv6.SetBase("2001:6::", Ipv6Prefix(64));
    Ipv6InterfaceContainer i67 = ipv6.Assign(d6d7);

    ipv6.SetBase("2001:7::", Ipv6Prefix(64));
    Ipv6InterfaceContainer i79 = ipv6.Assign(bridgeFacingDevices);
}

void
GlobalRoutingv6ProtocolTestCase::DoRun()
{
    // Create router nodes, initialize routing database and set up the routing
    // tables in the nodes.
    GlobalRouteManager<Ipv6Manager>::ResetRouterId();

    Ipv6GlobalRoutingHelper::PopulateRoutingTables();

    // tests-------------------------
    // test 1:check path from n0 to n6
    // test 2:check path from n1 to n5
    // test 3:check path from n1 to n9
    // test 4:check path from n8 to n0

    // Test 1: check path from n0 to n6
    std::vector<Ipv6Address>
        pathToCheck1; // includes the source egress destination ingress and intermediate next hops
                      // addresses that we need to check.It does not include the egress addresses
                      // for intermediate nodes.
    pathToCheck1.emplace_back("2001:1::200:ff:fe00:1");
    pathToCheck1.emplace_back("fe80::200:ff:fe00:2");
    pathToCheck1.emplace_back("fe80::200:ff:fe00:5");
    pathToCheck1.emplace_back("2001:5::200:ff:fe00:4");

    std::vector<Ptr<Node>>
        pathNodes1; // nodes in the path. Includes the source node and the destination node.
    pathNodes1.push_back(nodes.Get(0));
    pathNodes1.push_back(nodes.Get(2));
    pathNodes1.push_back(nodes.Get(1));
    pathNodes1.push_back(nodes.Get(6));
    CheckPath(pathNodes1, pathToCheck1);

    // Test 2:check path from n1 to n5
    std::vector<Ipv6Address> pathToCheck2;
    pathToCheck2.emplace_back("2001:2::200:ff:fe00:5");
    pathToCheck2.emplace_back("fe80::200:ff:fe00:4");
    pathToCheck2.emplace_back("2001:3::200:ff:fe00:7");
    std::vector<Ptr<Node>> pathNodes2;
    pathNodes2.push_back(nodes.Get(1));
    pathNodes2.push_back(nodes.Get(6));
    pathNodes2.push_back(nodes.Get(5));

    CheckPath(pathNodes2, pathToCheck2);
    // Test 3:check path from n1 to n9
    std::vector<Ipv6Address> pathToCheck3;
    pathToCheck3.emplace_back("2001:2::200:ff:fe00:6");
    pathToCheck3.emplace_back("fe80::200:ff:fe00:4");
    pathToCheck3.emplace_back("fe80::200:ff:fe00:a");
    pathToCheck3.emplace_back("2001:7::200:ff:fe00:12");

    std::vector<Ptr<Node>> pathNodes3;
    pathNodes3.push_back(nodes.Get(1));
    pathNodes3.push_back(nodes.Get(6));
    pathNodes3.push_back(nodes.Get(7));
    pathNodes3.push_back(nodes.Get(9));

    CheckPath(pathNodes3, pathToCheck3);

    // Test 4:check path from n9 to n0
    std::vector<Ipv6Address> pathToCheck4;
    pathToCheck4.emplace_back("2001:7::200:ff:fe00:12");
    pathToCheck4.emplace_back("fe80::200:ff:fe00:f");
    pathToCheck4.emplace_back("fe80::200:ff:fe00:9");
    pathToCheck4.emplace_back("fe80::200:ff:fe00:3");
    pathToCheck4.emplace_back("fe80::200:ff:fe00:6");
    pathToCheck4.emplace_back("2001:1::200:ff:fe00:1");

    std::vector<Ptr<Node>> pathNodes4;
    pathNodes4.push_back(nodes.Get(9));
    pathNodes4.push_back(nodes.Get(7));
    pathNodes4.push_back(nodes.Get(6));
    pathNodes4.push_back(nodes.Get(1));
    pathNodes4.push_back(nodes.Get(2));
    pathNodes4.push_back(nodes.Get(0));
    CheckPath(pathNodes4, pathToCheck4);

    Simulator::Run();
    Simulator::Stop(Seconds(10));
    Simulator::Destroy();
}

/**
 * @ingroup internet-test
 *
 * @brief IPv4 GlobalRouting TestSuite
 */
class Ipv4GlobalRoutingTestSuite : public TestSuite
{
  public:
    Ipv4GlobalRoutingTestSuite();
};

Ipv4GlobalRoutingTestSuite::Ipv4GlobalRoutingTestSuite()
    : TestSuite("global-routing", Type::UNIT)
{
    AddTestCase(new LinkTest, TestCase::Duration::QUICK);
    AddTestCase(new LanTest, TestCase::Duration::QUICK);
    AddTestCase(new TwoLinkTest, TestCase::Duration::QUICK);
    AddTestCase(new TwoLanTest, TestCase::Duration::QUICK);
    AddTestCase(new BridgeTest, TestCase::Duration::QUICK);
    AddTestCase(new TwoBridgeTest, TestCase::Duration::QUICK);
    AddTestCase(new Ipv4DynamicGlobalRoutingTestCase, TestCase::Duration::QUICK);
    AddTestCase(new Ipv6DynamicGlobalRoutingTestCase, TestCase::Duration::QUICK);
    AddTestCase(new Ipv4GlobalRoutingSlash32TestCase, TestCase::Duration::QUICK);
    AddTestCase(new EcmpRouteCalculationTestCase, TestCase::Duration::QUICK);
    AddTestCase(new GlobalRoutingv4ProtocolTestCase, TestCase::Duration::QUICK);
    AddTestCase(new GlobalRoutingv6ProtocolTestCase, TestCase::Duration::QUICK);
}

static Ipv4GlobalRoutingTestSuite
    g_globalRoutingTestSuite; //!< Static variable for test initialization
