개인 공부

NS-3 독학 - 5.fifth.cc

Beige00 2024. 1. 15. 14:32

fifth.cc Topology

주어진 example Topology를 보면 2개의 노드가 "TCP" protocol을 사용해서 p2p link 연결이 되어있다.

해당 channel은 transmission rate 5 Mbps, delay : 2 ms 이며, IP는 10.1.1.0, SubnetMask 255.255.255.0을 사용하는 것 같다.

주어진 주석을 보면 다음과 같이 쓰여있다.

주석

대충 읽어보면 sender socket 쪽의  TCP cwnd의 변화를 보고싶고, 이를 Tracing 해보자는 것 같다.

 

일반적으로, flow를 만들기 위해 on-off application을 사용한다.

그러나 on-off application socket은 Application이 시작될 때까지 생성되지 않으며, 따라서 configuration time에 socket을 tracing할 수가 없고, start time 이후에 접근한다고 해도 socket은 public이 아니기에 해당 tracing value를 가져오기 어렵다는 문제점이 있다.

그래서 우리는 간단한 on-off application을 직접 만들 것이다.

결론적으로 우선 socket을 만들고 connect 시킨다음, 구현한 simple on-off application에 주어 이를 source node에 설치시킬 것이다.

/**
 * Congestion window change callback
 *
 * \param oldCwnd Old congestion window.
 * \param newCwnd New congestion window.
 */
static void
CwndChange(uint32_t oldCwnd, uint32_t newCwnd)
{
    NS_LOG_UNCOND(Simulator::Now().GetSeconds() << "\t" << newCwnd);
}

/**
 * Rx drop callback
 *
 * \param p The dropped packet.
 */
static void
RxDrop(Ptr<const Packet> p)
{
    NS_LOG_UNCOND("RxDrop at " << Simulator::Now().GetSeconds());
}

int
main(int argc, char* argv[])
{
    CommandLine cmd(__FILE__);
    cmd.Parse(argc, argv);

    // In the following three lines, TCP NewReno is used as the congestion
    // control algorithm, the initial congestion window of a TCP connection is
    // set to 1 packet, and the classic fast recovery algorithm is used. Note
    // that this configuration is used only to demonstrate how TCP parameters
    // can be configured in ns-3. Otherwise, it is recommended to use the default
    // settings of TCP in ns-3.
    Config::SetDefault("ns3::TcpL4Protocol::SocketType", StringValue("ns3::TcpNewReno"));
    Config::SetDefault("ns3::TcpSocket::InitialCwnd", UintegerValue(1));
    Config::SetDefault("ns3::TcpL4Protocol::RecoveryType",
                       TypeIdValue(TypeId::LookupByName("ns3::TcpClassicRecovery")));

    NodeContainer nodes;
    nodes.Create(2);

    PointToPointHelper pointToPoint;
    pointToPoint.SetDeviceAttribute("DataRate", StringValue("5Mbps"));
    pointToPoint.SetChannelAttribute("Delay", StringValue("2ms"));

    NetDeviceContainer devices;
    devices = pointToPoint.Install(nodes);

    Ptr<RateErrorModel> em = CreateObject<RateErrorModel>();
    em->SetAttribute("ErrorRate", DoubleValue(0.00001));
    devices.Get(1)->SetAttribute("ReceiveErrorModel", PointerValue(em));

    InternetStackHelper stack;
    stack.Install(nodes);

    Ipv4AddressHelper address;
    address.SetBase("10.1.1.0", "255.255.255.252");
    Ipv4InterfaceContainer interfaces = address.Assign(devices);

    uint16_t sinkPort = 8080;
    Address sinkAddress(InetSocketAddress(interfaces.GetAddress(1), sinkPort));
    PacketSinkHelper packetSinkHelper("ns3::TcpSocketFactory",
                                      InetSocketAddress(Ipv4Address::GetAny(), sinkPort));
    ApplicationContainer sinkApps = packetSinkHelper.Install(nodes.Get(1));
    sinkApps.Start(Seconds(0.));
    sinkApps.Stop(Seconds(20.));

    Ptr<Socket> ns3TcpSocket = Socket::CreateSocket(nodes.Get(0), TcpSocketFactory::GetTypeId());
    ns3TcpSocket->TraceConnectWithoutContext("CongestionWindow", MakeCallback(&CwndChange));

    Ptr<TutorialApp> app = CreateObject<TutorialApp>();
    app->Setup(ns3TcpSocket, sinkAddress, 1040, 1000, DataRate("1Mbps"));
    nodes.Get(0)->AddApplication(app);
    app->SetStartTime(Seconds(1.));
    app->SetStopTime(Seconds(20.));

    devices.Get(1)->TraceConnectWithoutContext("PhyRxDrop", MakeCallback(&RxDrop));

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

    return 0;
}

코드 전문이다.

더보기

코드를 보면 TCPnewReno를 프로토콜로 사용하는 상황을 가정하고 있음을 알 수 있다.

따라서 Config path는

"/NodeList/[i]/$ns3::TcpL4Protocol/SocketList/[i]/$ns3::TcpNewReno/CongestionWindow"

이 될 것이다.

TCPnewReno.h를 보면 m_cWnd가 TracedValue<uint32_t>로 존재한다.

그러므로 TracedValueCallback의 Namespace를 찾아보면 다음과 같은 정의를 확인할 수 있다.

따라서 Callback Signature는 

static void

functionName(uint32_t oldCwnd, uint32_t newCwnd){}의 형태를 띄어야한다.

1. static void
CwndChange(uint32_t oldCwnd, uint32_t newCwnd)
{
    NS_LOG_UNCOND(Simulator::Now().GetSeconds() << "\t" << newCwnd);
} :

간단한 Sink 함수이다. (정의 과정은 접은글 참고)

Connect된 Cwnd에 변경이 발생될 시, 발생한 시간 + 새로운 Cnwd를 LOG에 기록한다.

 

2.static void
RxDrop(Ptr<const Packet> p)
{
    NS_LOG_UNCOND("RxDrop at " << Simulator::Now().GetSeconds());
}:

rxdrop/s: 리눅스 buffer 의 부족으로 패킷을 받는도중 초당 drop 된 패킷 수.

Rx drop이 발생 시 언제 발생한지를 출력하는 Sink 함수이다.

point to point NetDevice의 PhyRxDropo trace source와 연결 된다.

 

3. CommandLine cmd(__FILE__);
    cmd.Parse(argc, argv); :
CommandLine 초기화

 

4.  Config::SetDefault("ns3::TcpL4Protocol::SocketType", StringValue("ns3::TcpNewReno"));
    Config::SetDefault("ns3::TcpSocket::InitialCwnd", UintegerValue(1));
    Config::SetDefault("ns3::TcpL4Protocol::RecoveryType",
    TypeIdValue(TypeId::LookupByName("ns3::TcpClassicRecovery"))); :

Config::SetDefault는 1번째 String parameter에 해당하는 속성을 StringValue로 초기화한다.

따라서 사용할 Default Tcp Protocol를 설정하는 단계이다.

 

5. NodeContainer nodes;
    nodes.Create(2);

 

    PointToPointHelper pointToPoint;
    pointToPoint.SetDeviceAttribute("DataRate", StringValue("5Mbps"));
    pointToPoint.SetChannelAttribute("Delay", StringValue("2ms"));

    NetDeviceContainer devices;
    devices = pointToPoint.Install(nodes); :

Topology에 작성되어있는데로 2개의 node에 P2P link를 생성, 설치한다.

 

6. Ptr<RateErrorModel> em = CreateObject<RateErrorModel>();
    em->SetAttribute("ErrorRate", DoubleValue(0.00001));
    devices.Get(1)->SetAttribute("ReceiveErrorModel", PointerValue(em)); :
RateErrorModel은 전송된 비트 중에서 특정 비율로 오류를 발생시키는 모델이다.따라서 발생시킬 오류의 비율을 설정하고, node에 설치함으로써 해당 노드에서 오류를 발생시킬 수 있다.이 코드에서는 node 1에 설치하였다.

 

7. InternetStackHelper stack;
    stack.Install(nodes);
    Ipv4AddressHelper address;
    address.SetBase("10.1.1.0", "255.255.255.252");
    Ipv4InterfaceContainer interfaces = address.Assign(devices); :

토폴로지에 적혀있는데로 Ipv4 address를 P2P link node set에 설정한다.

이전까지 하던데로 10.1.1.0에 Subnet mask를 255.255.255.0으로 사용할 줄 알았는데,

255.255.255.252로 사용하였다. (시뮬레이션에 영향을 주는 요소는 아니다.)

 

8. uint16_t sinkPort = 8080;
    Address sinkAddress(InetSocketAddress(interfaces.GetAddress(1), sinkPort));
    PacketSinkHelper packetSinkHelper("ns3::TcpSocketFactory",
                                      InetSocketAddress(Ipv4Address::GetAny(), sinkPort));
    ApplicationContainer sinkApps = packetSinkHelper.Install(nodes.Get(1));
    sinkApps.Start(Seconds(0.));
    sinkApps.Stop(Seconds(20.)); :

이제 Application을 만들어줘야한다. 프로그램의 Port 번호는 8080으로 할당될 것이며, sinkAdress IPv4 Address는 당연히 10.1.1.2일 것이다. (node 1에 sink될 것이다.)

PacketSinkHelper는 특정 노드에 패킷 수신을 담당하는 PacketSink Application을 설정하는데 사용한다.

문서에 정의되어있는 Constructor의 구조를 보면,
ns3::PacketSinkHelper::PacketSinkHelper (std::string  protocol,Address  address) 과 같이 작성이 되어있다.

따라서 해당 소스코드의 초기화 문을 보면, Protocol은 Tcp를 사용할 것이므로 TcpSocketFactory, 주소는 임의의 Ipv4Address, Port 8080로 sinkApplication을 만드는 것을 볼 수 있다.

( GetAny(){0.0.0.0}으로 Packetsink Application을 구성해야 모든 인터페이스에서의 수신을 허용하게 된다. )

이렇게 구성된 Sink Application을 node 1에 설치해주면, node 1은 sink Application으로 동작하게 될 것이다.

(PacketSink Application은 트래픽을 받고 소비하는 application 이다.)

 

9.  Ptr<Socket> ns3TcpSocket = Socket::CreateSocket(nodes.Get(0), TcpSocketFactory::GetTypeId());
    ns3TcpSocket->TraceConnectWithoutContext("CongestionWindow", MakeCallback(&CwndChange)); :

ns3TcpSocket이라는 Tcp socket Trace Source를 만들어준다.(node 0)

그리고 Connect를 해준다.

 

10.Ptr<TutorialApp> app = CreateObject<TutorialApp>();
    app->Setup(ns3TcpSocket, sinkAddress, 1040, 1000, DataRate("1Mbps"));
    nodes.Get(0)->AddApplication(app);
    app->SetStartTime(Seconds(1.));
    app->SetStopTime(Seconds(20.)); :

Setup 정의

TutorialApp 객체 app을 tcp socket, sinkAddress(10.1.1.2, 8080), 1040 bytes, 1000 개의 packet, Datarate 1Mbps로 초기화한다.

이후 node 0에 sink application을 추가하고 application을 실행시킨다.

이러면 node 0의 Application이 node 1에 지속적으로 sink를 할 것이다.

최종적으로 node 0은 node 1에 계속 패킷을 전송할 것이고 node 1의 PacketSink app은 GetAny, 8080으로 설정되어있으므로 node 0의 패킷을 받을 것이다. 이 때 Error rate에 따라 일정 비율로 Error가 발생할 것이다.

 

11. devices.Get(1)->TraceConnectWithoutContext("PhyRxDrop", MakeCallback(&RxDrop)); :

node 1에서 오류 발생을 확인하기 위해 Trace sink 함수를 연결해준다.

 


* 실행 결과

RxDrop이 일어난 시점 이후 Sender(node 0)의 Cwnd가 감소하는 모습을 볼 수 있다.

 

'개인 공부' 카테고리의 다른 글

NS-3 독학 - 完. Seventh.cc  (0) 2024.01.15
NS-3 독학 - 6. sixth.cc  (0) 2024.01.15
NS-3 독학 - 4.fourth.cc(Tracing)  (0) 2024.01.13
NS-3 독학 - 3.Third.cc  (0) 2024.01.12
NS-3 독학 - 2. Second.cc  (0) 2024.01.11