diff --git a/CHANGES.md b/CHANGES.md index 88444b03e..8ece8e2a3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -55,6 +55,7 @@ Changes from ns-3.38 to ns-3-dev * (internet) The function signature of `Ipv4RoutingProtocol::RouteInput` and `Ipv6RoutingProtocol::RouteInput` have changed. The `UnicastForwardCallback` (ucb), `MulticastForwardCallback` (mcb), `LocalDeliverCallback` (lcb) and `ErrorCallback` (ecb) should now be passed as const references. * (olsr) The defines `OLSR_WILL_*` have been replaced by enum `Willingness`. * (wifi) The `WifiCodeRate` typedef was converted to an enum. +* (internet) `InternetStackHelper` can be now used on nodes with an `InternetStack` already installed (it will not install IPv[4,6] twice). ### Changes to build system diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index a11a54ec6..5e98fde90 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -75,6 +75,7 @@ This release has discontinued support for g++-8 compilers. - (wifi) Added 802.11ax dual NAV (basic NAV and intra-BSS NAV) - (wifi) Added 802.11ax Uplink Multi-User Carrier Sense (UL MU CS) mechanism and have it used by non-AP STAs when determining if they can reply to a received Trigger Frame - (wifi) Added support for 802.11ax MU-RTS/CTS protection +- (internet) InternetStackHelper can be now used on nodes with an InternetStack already installed (it will not install IPv[4,6] twice). ### Bugs fixed diff --git a/src/internet/CMakeLists.txt b/src/internet/CMakeLists.txt index 35ac7691b..8d0508e5d 100644 --- a/src/internet/CMakeLists.txt +++ b/src/internet/CMakeLists.txt @@ -262,6 +262,7 @@ endif() set(test_sources test/global-route-manager-impl-test-suite.cc test/icmp-test.cc + test/internet-stack-helper-test-suite.cc test/ipv4-address-generator-test-suite.cc test/ipv4-address-helper-test-suite.cc test/ipv4-deduplication-test.cc diff --git a/src/internet/helper/internet-stack-helper.cc b/src/internet/helper/internet-stack-helper.cc index 6ef7647b0..24d0d7900 100644 --- a/src/internet/helper/internet-stack-helper.cc +++ b/src/internet/helper/internet-stack-helper.cc @@ -290,6 +290,12 @@ InternetStackHelper::InstallAll() const void InternetStackHelper::CreateAndAggregateObjectFromTypeId(Ptr node, const std::string typeId) { + TypeId tid = TypeId::LookupByName(typeId); + if (node->GetObject(tid)) + { + return; + } + ObjectFactory factory; factory.SetTypeId(typeId); Ptr protocol = factory.Create(); @@ -301,13 +307,7 @@ InternetStackHelper::Install(Ptr node) const { if (m_ipv4Enabled) { - if (node->GetObject()) - { - NS_FATAL_ERROR("InternetStackHelper::Install (): Aggregating " - "an InternetStack to a node with an existing Ipv4 object"); - return; - } - + /* IPv4 stack */ CreateAndAggregateObjectFromTypeId(node, "ns3::ArpL3Protocol"); CreateAndAggregateObjectFromTypeId(node, "ns3::Ipv4L3Protocol"); CreateAndAggregateObjectFromTypeId(node, "ns3::Icmpv4L4Protocol"); @@ -318,22 +318,19 @@ InternetStackHelper::Install(Ptr node) const arp->SetAttribute("RequestJitter", StringValue("ns3::ConstantRandomVariable[Constant=0.0]")); } + // Set routing Ptr ipv4 = node->GetObject(); - Ptr ipv4Routing = m_routing->Create(node); - ipv4->SetRoutingProtocol(ipv4Routing); + if (!ipv4->GetRoutingProtocol()) + { + Ptr ipv4Routing = m_routing->Create(node); + ipv4->SetRoutingProtocol(ipv4Routing); + } } if (m_ipv6Enabled) { /* IPv6 stack */ - if (node->GetObject()) - { - NS_FATAL_ERROR("InternetStackHelper::Install (): Aggregating " - "an InternetStack to a node with an existing Ipv6 object"); - return; - } - CreateAndAggregateObjectFromTypeId(node, "ns3::Ipv6L3Protocol"); CreateAndAggregateObjectFromTypeId(node, "ns3::Icmpv6L4Protocol"); if (m_ipv6NsRsJitterEnabled == false) @@ -345,9 +342,11 @@ InternetStackHelper::Install(Ptr node) const } // Set routing Ptr ipv6 = node->GetObject(); - Ptr ipv6Routing = m_routingv6->Create(node); - ipv6->SetRoutingProtocol(ipv6Routing); - + if (!ipv6->GetRoutingProtocol()) + { + Ptr ipv6Routing = m_routingv6->Create(node); + ipv6->SetRoutingProtocol(ipv6Routing); + } /* register IPv6 extensions and options */ ipv6->RegisterExtensions(); ipv6->RegisterOptions(); @@ -357,9 +356,17 @@ InternetStackHelper::Install(Ptr node) const { CreateAndAggregateObjectFromTypeId(node, "ns3::TrafficControlLayer"); CreateAndAggregateObjectFromTypeId(node, "ns3::UdpL4Protocol"); - node->AggregateObject(m_tcpFactory.Create()); - Ptr factory = CreateObject(); - node->AggregateObject(factory); + // note: the following could be changed to + // CreateAndAggregateObjectFromTypeId(node, "ns3::TcpL4Protocol"); + if (!node->GetObject()) + { + node->AggregateObject(m_tcpFactory.Create()); + } + if (!node->GetObject()) + { + Ptr factory = CreateObject(); + node->AggregateObject(factory); + } } if (m_ipv4Enabled) diff --git a/src/internet/helper/internet-stack-helper.h b/src/internet/helper/internet-stack-helper.h index a158837c9..4307741cb 100644 --- a/src/internet/helper/internet-stack-helper.h +++ b/src/internet/helper/internet-stack-helper.h @@ -144,8 +144,8 @@ class InternetStackHelper : public PcapHelperForIpv4, /** * Aggregate implementations of the ns3::Ipv4, ns3::Ipv6, ns3::Udp, and ns3::Tcp classes - * onto the provided node. This method will assert if called on a node that - * already has an Ipv4 object aggregated to it. + * onto the provided node. This method will do nothing if the stacks are already installed, + * and will not overwrite existing stacks parameters. * * \param nodeName The name of the node on which to install the stack. */ @@ -153,8 +153,8 @@ class InternetStackHelper : public PcapHelperForIpv4, /** * Aggregate implementations of the ns3::Ipv4, ns3::Ipv6, ns3::Udp, and ns3::Tcp classes - * onto the provided node. This method will assert if called on a node that - * already has an Ipv4 object aggregated to it. + * onto the provided node. This method will do nothing if the stacks are already installed, + * and will not overwrite existing stacks parameters. * * \param node The node on which to install the stack. */ @@ -162,9 +162,8 @@ class InternetStackHelper : public PcapHelperForIpv4, /** * For each node in the input container, aggregate implementations of the - * ns3::Ipv4, ns3::Ipv6, ns3::Udp, and, ns3::Tcp classes. The program will assert - * if this method is called on a container with a node that already has - * an Ipv4 object aggregated to it. + * ns3::Ipv4, ns3::Ipv6, ns3::Udp, and, ns3::Tcp classes. This method will do nothing if the + * stacks are already installed, and will not overwrite existing stacks parameters. * * \param c NodeContainer that holds the set of nodes on which to install the * new stacks. @@ -306,7 +305,8 @@ class InternetStackHelper : public PcapHelperForIpv4, const Ipv6RoutingHelper* m_routingv6; /** - * \brief create an object from its TypeId and aggregates it to the node + * \brief create an object from its TypeId and aggregates it to the node. Does nothing if + * an object of the same type is already aggregated to the node. * \param node the node * \param typeId the object TypeId */ diff --git a/src/internet/model/ipv6-l3-protocol.cc b/src/internet/model/ipv6-l3-protocol.cc index 56de0ff0c..8c3d6a7c6 100644 --- a/src/internet/model/ipv6-l3-protocol.cc +++ b/src/internet/model/ipv6-l3-protocol.cc @@ -1650,6 +1650,10 @@ Ipv6L3Protocol::BuildHeader(Ipv6Address src, void Ipv6L3Protocol::RegisterExtensions() { + if (m_node->GetObject()) + { + return; + } Ptr ipv6ExtensionDemux = CreateObject(); ipv6ExtensionDemux->SetNode(m_node); @@ -1686,6 +1690,10 @@ Ipv6L3Protocol::RegisterExtensions() void Ipv6L3Protocol::RegisterOptions() { + if (m_node->GetObject()) + { + return; + } Ptr ipv6OptionDemux = CreateObject(); ipv6OptionDemux->SetNode(m_node); diff --git a/src/internet/model/ipv6-l3-protocol.h b/src/internet/model/ipv6-l3-protocol.h index 0024fbf3b..12db07b77 100644 --- a/src/internet/model/ipv6-l3-protocol.h +++ b/src/internet/model/ipv6-l3-protocol.h @@ -368,14 +368,7 @@ class Ipv6L3Protocol : public Ipv6 Ipv6Prefix mask, Ipv6Address defaultRouter); - /** - * \brief Register the IPv6 Extensions. - */ void RegisterExtensions() override; - - /** - * \brief Register the IPv6 Options. - */ void RegisterOptions() override; /** diff --git a/src/internet/model/ipv6.h b/src/internet/model/ipv6.h index 221b4f114..45e8ee610 100644 --- a/src/internet/model/ipv6.h +++ b/src/internet/model/ipv6.h @@ -383,12 +383,14 @@ class Ipv6 : public Object Ptr route) = 0; /** - * \brief Register the IPv6 Extensions. + * \brief Register the IPv6 Extensions. Does nothing if the Extensions have been already + * registered. */ virtual void RegisterExtensions() = 0; /** - * \brief Register the IPv6 Options. + * \brief Register the IPv6 Options. Does nothing if the Options have been already + * registered. */ virtual void RegisterOptions() = 0; diff --git a/src/internet/test/internet-stack-helper-test-suite.cc b/src/internet/test/internet-stack-helper-test-suite.cc new file mode 100644 index 000000000..367ae3947 --- /dev/null +++ b/src/internet/test/internet-stack-helper-test-suite.cc @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2023 Universita' di Firenze + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Tommaso Pecorella + */ + +#include "ns3/internet-stack-helper.h" +#include "ns3/log.h" +#include "ns3/node.h" +#include "ns3/test.h" + +#include + +using namespace ns3; + +NS_LOG_COMPONENT_DEFINE("InternetStackHelperTestSuite"); + +/** + * \ingroup internet-test + * + * \brief InternetStackHelper Test + */ +class InternetStackHelperTestCase : public TestCase +{ + public: + InternetStackHelperTestCase(); + + private: + void DoRun() override; + void DoTeardown() override; +}; + +InternetStackHelperTestCase::InternetStackHelperTestCase() + : TestCase("InternetStackHelperTestCase") +{ +} + +void +InternetStackHelperTestCase::DoRun() +{ + // Checks: + // 1. IPv4 only, add IPv4 + IPv6 (result, IPv4 + IPv6) + // 2. IPv6 only, add IPv4 + IPv6 (result, IPv4 + IPv6) + // 3. IPv4 + IPv6, add IPv4 + IPv6 (result, IPv4 + IPv6) + + Ptr nodeIpv4Only = CreateObject(); + Ptr nodeIpv6Only = CreateObject(); + Ptr nodeIpv46 = CreateObject(); + + InternetStackHelper internet; + + internet.SetIpv4StackInstall(true); + internet.SetIpv6StackInstall(false); + internet.Install(nodeIpv4Only); + + internet.SetIpv4StackInstall(false); + internet.SetIpv6StackInstall(true); + internet.Install(nodeIpv6Only); + + internet.SetIpv4StackInstall(true); + internet.SetIpv6StackInstall(true); + internet.Install(nodeIpv46); + + // Check that the three nodes have only the intended IP stack. + NS_TEST_EXPECT_MSG_NE(nodeIpv4Only->GetObject(), + nullptr, + "IPv4 not found on IPv4-only node (should have been there)"); + NS_TEST_EXPECT_MSG_EQ(nodeIpv4Only->GetObject(), + nullptr, + "IPv6 found on IPv4-only node (should not have been there)"); + + NS_TEST_EXPECT_MSG_EQ(nodeIpv6Only->GetObject(), + nullptr, + "IPv4 found on IPv6-only node (should not have been there)"); + NS_TEST_EXPECT_MSG_NE(nodeIpv6Only->GetObject(), + nullptr, + "IPv6 not found on IPv6-only node (should have been there)"); + + NS_TEST_EXPECT_MSG_NE(nodeIpv46->GetObject(), + nullptr, + "IPv4 not found on dual stack node (should have been there)"); + NS_TEST_EXPECT_MSG_NE(nodeIpv46->GetObject(), + nullptr, + "IPv6 not found on dual stack node (should have been there)"); + + // Now we install IPv4 and IPv6 on the IPv4-only node + // IPv4 is already there, no error should happen. + internet.Install(nodeIpv4Only); + // Now we install IPv4 and IPv6 on the IPv6-only node, + // IPv6 is already there, no error should happen. + internet.Install(nodeIpv6Only); + // Now we install IPv4 and IPv6 on the dual stack node + // IPv4 and IPv6 are already there, no error should happen. + internet.Install(nodeIpv46); + + // Check that the three nodes have both IPv4 and IPv6. + NS_TEST_EXPECT_MSG_NE( + nodeIpv4Only->GetObject(), + nullptr, + "IPv4 not found on IPv4-only, now dual stack node (should have been there)"); + NS_TEST_EXPECT_MSG_NE( + nodeIpv4Only->GetObject(), + nullptr, + "IPv6 not found on IPv4-only, now dual stack node (should have been there)"); + + NS_TEST_EXPECT_MSG_NE( + nodeIpv6Only->GetObject(), + nullptr, + "IPv4 not found on IPv6-only, now dual stack node (should have been there)"); + NS_TEST_EXPECT_MSG_NE( + nodeIpv6Only->GetObject(), + nullptr, + "IPv6 not found on IPv6-only, now dual stack node (should have been there)"); + + NS_TEST_EXPECT_MSG_NE(nodeIpv46->GetObject(), + nullptr, + "IPv4 not found on dual stack node (should have been there)"); + NS_TEST_EXPECT_MSG_NE(nodeIpv46->GetObject(), + nullptr, + "IPv6 not found on dual stack node (should have been there)"); +} + +void +InternetStackHelperTestCase::DoTeardown() +{ + Simulator::Destroy(); +} + +/** + * \ingroup internet-test + * + * \brief InternetStackHelper TestSuite + */ +class InternetStackHelperTestSuite : public TestSuite +{ + public: + InternetStackHelperTestSuite() + : TestSuite("internet-stack-helper", UNIT) + { + AddTestCase(new InternetStackHelperTestCase(), TestCase::QUICK); + } +}; + +static InternetStackHelperTestSuite + g_internetStackHelperTestSuite; //!< Static variable for test initialization