void
LinkControlHelper::setErrorRate(Ptr<Node> node1, Ptr<Node> node2, double errorRate)
{
  NS_LOG_FUNCTION(node1 << node2 << errorRate);

  NS_ASSERT(node1 != nullptr && node2 != nullptr);
  NS_ASSERT(errorRate <= 1.0);

  Ptr<ndn::L3Protocol> ndn1 = node1->GetObject<ndn::L3Protocol>();
  Ptr<ndn::L3Protocol> ndn2 = node2->GetObject<ndn::L3Protocol>();

  NS_ASSERT(ndn1 != nullptr && ndn2 != nullptr);

  // iterate over all faces to find the right one
  for (const auto& face : ndn1->getForwarder()->getFaceTable()) {
    auto transport = dynamic_cast<NetDeviceTransport*>(face.getTransport());
    if (transport == nullptr)
      continue;

    Ptr<PointToPointNetDevice> nd1 = transport->GetNetDevice()->GetObject<PointToPointNetDevice>();
    if (nd1 == nullptr)
      continue;

    Ptr<Channel> channel = nd1->GetChannel();
    if (channel == nullptr)
      continue;

    Ptr<PointToPointChannel> ppChannel = DynamicCast<PointToPointChannel>(channel);

    Ptr<NetDevice> nd2 = ppChannel->GetDevice(0);
    if (nd2->GetNode() == node1)
      nd2 = ppChannel->GetDevice(1);

    if (nd2->GetNode() == node2) {
      ObjectFactory errorFactory("ns3::RateErrorModel");
      errorFactory.Set("ErrorUnit", StringValue("ERROR_UNIT_PACKET"));
      errorFactory.Set("ErrorRate", DoubleValue(errorRate));
      if (errorRate <= 0) {
        errorFactory.Set("IsEnabled", BooleanValue(false));
      }

      nd1->SetAttribute("ReceiveErrorModel", PointerValue(errorFactory.Create<ErrorModel>()));
      nd2->SetAttribute("ReceiveErrorModel", PointerValue(errorFactory.Create<ErrorModel>()));
      return;
    }
  }
  NS_FATAL_ERROR("There is no link to fail between the requested nodes");
}
int
main(int argc, char* argv[])
{
  // setting default parameters for PointToPoint links and channels
  Config::SetDefault("ns3::PointToPointNetDevice::DataRate", StringValue("1Mbps"));
  Config::SetDefault("ns3::PointToPointChannel::Delay", StringValue("10ms"));
  Config::SetDefault("ns3::DropTailQueue::MaxPackets", StringValue("20"));

  // Read optional command-line parameters (e.g., enable visualizer with ./waf --run=<> --visualize
  CommandLine cmd;
  cmd.Parse(argc, argv);

  ofstream file1("/tmp/topo1.txt");
  file1 << "router\n\n"
        << "#node	city	y	x	mpi-partition\n"
        << "A1	NA	1	1	1\n"
        << "B1	NA	80	-40	1\n"
        << "C1	NA	80	40	1\n"
        << "A2	NA	1	1	1\n"
        << "B2	NA	80	-40	1\n"
        << "C2	NA	80	40	1\n\n"
        << "link\n\n"
        << "# from  to  capacity	metric	delay	queue\n"
        << "A1	    B1	10Mbps		100	1ms	100\n"
        << "A1	    C1	10Mbps		50	1ms	100\n"
        << "B1	    C1	10Mbps		1	1ms	100\n"
        << "A2	    B2	10Mbps		50	1ms	100\n"
        << "A2	    C2	10Mbps		100	1ms	100\n"
        << "B2	    C2	10Mbps		1	1ms	100\n";
  file1.close();

  AnnotatedTopologyReader topologyReader("");
  topologyReader.SetFileName("/tmp/topo1.txt");
  topologyReader.Read();

  // Install NDN stack on all nodes
  ndn::StackHelper ndnHelper;
  ndnHelper.InstallAll();

  topologyReader.ApplyOspfMetric();

  ndn::GlobalRoutingHelper ndnGlobalRoutingHelper;
  ndnGlobalRoutingHelper.InstallAll();

  ndnGlobalRoutingHelper.AddOrigins("/test/prefix", Names::Find<Node>("C1"));
  ndnGlobalRoutingHelper.AddOrigins("/test/prefix", Names::Find<Node>("C2"));
  ndn::GlobalRoutingHelper::CalculateRoutes();

  auto printFib = [](Ptr<Node> node) {
    auto ndn = node->GetObject<ndn::L3Protocol>();
    for (const auto& entry : ndn->getForwarder()->getFib()) {
      cout << entry.getPrefix() << " (";

      bool isFirst = true;
      for (auto& nextHop : entry.getNextHops()) {
        cout << *nextHop.getFace();
        auto face = dynamic_pointer_cast<ndn::NetDeviceFace>(nextHop.getFace());
        if (face == nullptr)
          continue;

        cout << " towards ";

        if (!isFirst)
          cout << ", ";
        cout << Names::FindName(face->GetNetDevice()->GetChannel()->GetDevice(1)->GetNode());
        isFirst = false;
      }
      cout << ")" << endl;
    }
  };

  cout << "FIB content on node A1" << endl;
  printFib(Names::Find<Node>("A1"));

  cout << "FIB content on node A2" << endl;
  printFib(Names::Find<Node>("A2"));

  Simulator::Stop(Seconds(20.0));
  Simulator::Run();
  Simulator::Destroy();

  return 0;
}