'Network/Ns2_Lecture'에 해당되는 글 60건

  1. 2009.01.18 NS2 스크립트 - 유선환경 시뮬레이션
  2. 2009.01.18 Description of the ns-2 modifications
  3. 2009.01.18 5.3 Node Configuration Interface
  4. 2009.01.18 ns-2 Software and Simulation Results
  5. 2009.01.18 Implementing a New Manet Unicast Routing Protocol in NS2 Version 0.2
  6. 2009.01.18 NS2 - 무선환경 시뮬레이션 파라메터 설명
  7. 2009.01.18 network components, ns by example
  8. 2009.01.18 NS by Example - Network Components
  9. 2009.01.18 802.11 MAC code in NS-2 (version 2.28)
  10. 2009.01.18 802.11 MAC code in NS-2 (version 2.28)

NS2 스크립트 - 유선환경 시뮬레이션

|
# Simulator 인스턴스 생성 후 ns 할당
# Simulator는 단 한개 존재
set ns [new Simulator] ;
 
#trace할 파일 열기
set trace [open basic_ex.tr w]
# tr trace
$ns trace-all $trace
#x-window를 지원하는 trace
set namtrace [open basic_ex.nam w]
$ns namtrace-all $namtrace
#node는 simulator의 멤버 함수
#Node 인스턴스 생성 후 n0, n1에 할당
set n0 [$ns node]
set n1 [$ns node]
#duplex-link는 simulator의 멤버 함수
#duplex-link <노드1> <노드2> <대역폭> <딜레이> <큐타입>
#$n0 $n1 10Mb 2ms DropTail은 인수들
#노드 n0 노드 n1 사이에 양방향 링크 생성
$ns duplex-link $n0 $n1 10Mb 2ms DropTail
#Agent/UDP 인스턴스 생성 후 udp0에 할당
#Agent/UDP는 Agent를 상속
#UDP : 비신뢰 전송
#TCP : 신뢰 전송, 전송 제어
# 생성 : set tcp0 [new Agent/TCP] #(Tahoe), set tcp1 [new Agent/TCP/Reno], set tcp2 [new Agent/TCP/newReno]
set udp0 [new Agent/UDP]

#attach-agnet는 simulator의 멤버함수
#$n0 $udp0는 인수들
#$n0에 $udp0를 부착 (스택을 쌓아 올림)
$ns attach-agent $n0 $udp0

#Application/Traffic/CBR 인스턴스 생성후
#cbr0에 할당
#CBR(Constant Bit Rate) : 정해진 패킷 크기로 정해진 주기로 전송
#CBR 주요 속성 : packetSize_(패킷크기, 단위 Byte), interval_(패킷 전송간격, 단위는 sec), rate_
#FTP(File Transfer Protocol) : 무한 크기의 파일 전송
#FTP 생성 : set ftp0 [new Application/FTP]
set cbr0 [new Application/Traffic/CBR]

#attach-agent는 CBR/Traffic/CBR의 멤버 함수가 아니다.
$cbr0 attach-agent $udp0
#Agent/Null 인스턴스 생성 후 null0에 할당
#Agent/Null은 Agent를 상속
#Null : 패킷을 받는 일만 함, UDP의 수신자 역확을 함
#TCP 수신은 TCPSink로 함 : set tcp0 [new Agent/TCPSink]
set null0 [new Agent/Null]

#$n1에 $null0 부착 (스택을 쌓아올림)
$ns attach-agent $n1 $null0
#connect는 Simulator의 멤버 함수
#udp0 $null0는 인수들
#인수로 받은 에이전트들을 연결함
# connetc <srcAgent> <dstAgent>
$ns connect $udp0 $null0
#at은 Simulator의 멤버 함수
#at <시간> <명령어> : 시뮬레이션상의 '시간'에 '명령어'실행 (now:시뮬레이션 상의 가상시간 리턴)
#인자로 시간과 명령어가 옴
#명령어에 공백이 있는 경우 ""
#실시간이 아닌 virtual time임
#halt : simulation 종료
$ns at 1.0 "$cbr0 start"
$ns at 10.0 "$ns halt"
#run은 Simulator의 멤버함수
#스케쥴된 이벤트 시작
#하앙 맨마지막에 실행
$ns run

'Network > Ns2_Lecture' 카테고리의 다른 글

NS2 trace  (0) 2009.01.18
awk  (0) 2009.01.18
Description of the ns-2 modifications  (0) 2009.01.18
5.3 Node Configuration Interface  (0) 2009.01.18
ns-2 Software and Simulation Results  (0) 2009.01.18
And

Description of the ns-2 modifications

|

Description of the ns-2 modifications

Jump to the files

This page describes some modifications I did in ns-2 that could be included in the main version. While changing the simulator to have multiple wireless interfaces listening on different channels on a single node I found several parts of the basic ns-2 structures quite annoying. They were unreadable, almost impossible to understand, etc...For example, in the class Node there was a method called nextchannel(). One could expect to get the "next" channel by calling this function but actually you proceed to the next node connected to some channel. To which channel? You cannot know this, because the channel controls the pointers of this... Which directly leads to one of the most annoying flaws in the design: if you have two channels on the same node both will use the same pointers to create a linked list. This can lead to lists with loops in it.

There are some more problems very similar to that. If you want to add an interface to a node, you would have to call "interface->insertnode(&ifhead_);" on the node... For me it looks like adding a node to the interface and not the other way round.

Annother annoying thing are all the different *Node classes (ParentNode, NixNode, ...). For example Node is dereive from ParentNode but both have address_ and nodeid_. This can result in problems. I changed the address function of the Node to be const. Then all the time the ParentNode was used to call address() a different value was returned. The problem could easily be solved by simply deleting the duplicated entries from the Node class.


What I did

I did some code cleanup with all those lists mysteriously created by ns-2. First of all I used std::list datastructures wherever possible. In addition, in the wireless channel class I changed the logic of the list based improvement: instead of keeping an own list of nodes, the already existing list of interfaces is used. Instead of getting all affected nodes all affected interfaces are searched, which is much more straight forward.

Update: I changed the data structure for the nodes to b a vector. That way I can do a binary search for get_node_by_address. For large scenarios this can be considerably faster.


Why it should be included in the ns-2

The code is now clearer, easier to read, and it is now easier to use multiple wireless interfaces on a single node (although, to make it comfortable more code is needed and I haven't yet tested it thoroughly enough).

I ran the validation tests on the modified version and the results were the same as on the unmodified version. Actually, i also found a bug in a validation test (using dsr as routing protocol instead of DSR).


Adding multiple wireless interfaces

To add more than one wireless interface to a node this code might be an example
set chan_1 [new $val(chan)]
set chan_2 [new $val(chan)]

$ns_ node-config -adhocRouting $val(rp) \
             -llType $val(ll) \
             -macType $val(mac) \
             -ifqType $val(ifq) \
             -ifqLen $val(ifqlen) \
             -antType $val(ant) \
             -propType $val(prop) \
             -phyType $val(netif) \
             -channel $chan_1 \
             -topoInstance $topo \
             -agentTrace ON \
             -routerTrace ON \
             -macTrace ON \
             -movementTrace OFF

set node1 [$ns_ node]
$ns_ node-config  -channel $chan_2
set node2 [$ns_ node]

Now you have two nodes with one interface each. They are on different channels and cannot hear each other. The folowing is a conveniance function, i simply add this to my simulation scripts.

proc addInterface { node channel } {
    puts "Adding channel [$channel get-channel-id] to node [$node node-addr]"
    global ns_ val
    set propInstance [$ns_ set propInstance_]
    set topoInstance [$ns_ set topoInstance_]
    set inerrProc_ ""
    set outerrProc_ ""
    set FECProc_ ""
    if { [$ns_ info vars inerrProc_] ne "" } {
        set inerrProc_ [$ns_ set inerrProc_]
    }
    if { [$ns_ info vars outerrProc_] ne "" } {
        set outerrProc_ [$ns_ set outerrProc_]
    }
    if { [$ns_ info vars FECProc_] ne "" } {
        set FECProc_ [$ns_ set FECProc_]
    }
    $node add-interface $channel $propInstance $val(ll) $val(mac) \
        $val(ifq) $val(ifqlen) $val(netif) $val(ant) $topoInstance \
        $inerrProc_ $outerrProc_ $FECProc_
    puts "Node [$node node-addr] has now [expr $nifs+1] interfaces"
}

Now I add a second interface to the two nodes.

add-interface $node1 $chan_2
add-interface $node2 $chan_1

As result I now have two nodes with two wireless interfaces: one listening on channel1 and the other one listening on channel2.

Keep in mind that the routing has to select the proper interface for communication. As far as i know, the included routing protocols only use a single interface (the first one created).

Communication on channel1 does not affect communication on channel2


Files

Here you can get the patch against the cvs version of ns-2

patch for the cvs version (0.2)

patch for the cvs version

Here you can download a modified version of the ns-2 source code modified ns-2 source code


Open Issues

Quite a lot actually...

  • most important for me is the improved support for multiple interfaces on a single node. This also includes channel switching, dynamic creation and configuration of interfaces, etc...
  • more code cleanup could be done in the (Mobile)Node and the interfaces code
  • can't we get rid of the GridKeeper? It does not seem to be in use...
  • It seems that TORA cannot be used anymore, shouldn't all malfunctioning modules be removed? They could be moved to some kind of attic in which they wait for someone to fix them (If nobody cares about the modules they will be forgotten)
  • (I will not repeat everything that has been said over and over again: packet format, evil otcl, ... )
from : http://www-i4.informatik.rwth-aachen.de/mcg/projects/NS-2%20Modifications

And

5.3 Node Configuration Interface

|

5.3 Node Configuration Interface

[]Simulator::node-config accommodates flexible and modular construction of different node definitions within the same base Node class. For instance, to create a mobile node capable of wireless communication, one no longer needs a specialized node creation command, e.g., []dsdv-create-mobile-node; instead, one changes default configuration parameters, such as

$ns node-config -adhocRouting dsdv
before actually creating the node with the command: $ns node. Together with routing modules, this allows one to combine ``arbitrary'' routing functionalities within a single node without resorting to multiple inheritance and other fancy object gimmicks. We will describe this in more detail in Section 5.5. The functions and procedures relevant to the new node APIs may be found in tcl/lib/ns-node.tcl.

The node configuration interface consists of two parts. The first part deals with node configuration, while the second part actually creates nodes of the specified type. We have already seen the latter in Section 5.1, in this section we will describe the configuration part.

Node configuration essentially consists of defining the different node characteristics before creating them. They may consist of the type of addressing structure used in the simulation, defining the network components for mobilenodes, turning on or off the trace options at Agent/Router/MAC levels, selecting the type of adhoc routing protocol for wireless nodes or defining their energy model.

As an example, node-configuration for a wireless, mobile node that runs AODV as its adhoc routing protocol in a hierarchical topology would be as shown below. We decide to turn tracing on at the agent and router level only. Also we assume a topology has been instantiated with "set topo [new Topography]". The node-config command would look like the following:

  \$ns_ node-config -addressType hierarchical \bs 
                   -adhocRouting AODV \bs
                   -llType LL \bs
                   -macType Mac/802_11 \bs 
                   -ifqType Queue/DropTail/PriQueue \bs
                   -ifqLen 50 \bs
                   -antType Antenna/OmniAntenna \bs 
                   -propType Propagation/TwoRayGround \bs
                   -phyType Phy/WirelessPhy \bs
                   -topologyInstance \$topo \bs
                   -channel Channel/WirelessChannel \bs 
                   -agentTrace ON \bs 
                   -routerTrace ON \bs
                   -macTrace OFF \bs
                   -movementTrace OFF

The default values for all the above options are NULL except -addressingType whose default value is flat. The option -reset can be used to reset all node-config parameters to their default value.

Note that the config command can be broken down into separate lines like

        \$ns_ node-config -addressingType hier
        \$ns_ node-config -macTrace ON
The options that need to be changed may only be called. For example after configuring for AODV mobilenodes as shown above (and after creating AODV mobilenodes), we may configure for AODV base-station nodes in the following way:
        \$ns_ node-config -wiredRouting ON
While all other features for base-station nodes and mobilenodes are same, the base-station nodes are capable of wired routing, while mobilenodes are not. In this way we can change node-configuration only when it is required.

All node instances created after a given node-configuration command will have the same property unless a part or all of the node-config command is executed with different parameter values. And all parameter values remain unchanged unless they are expicitly changed. So after creation of the AODV base-station and mobilenodes, if we want to create simple nodes, we will use the following node-configuration command:

        \$ns_ node-config -reset
This will set all parameter values to their default setting which basically defines configuration of a simple node.

Currently, this type of node configuration is oriented towards wireless and satellite nodes. Table 5.1 lists the available options for these kinds of nodes. The example scripts tcl/ex/simple-wireless.tcl and tcl/ex/sat-mixed.tcl provide usage examples.


from : http://www.cubinlab.ee.unimelb.edu.au/~jrid/Docs/Manuel-NS2/node45.html
Table 5.1: Available options for node configuration (see tcl/lib/ns-lib.tcl).
option available values default
general
addressType flat, hierarchical flat
MPLS ON, OFF OFF
both satellite- and wireless-oriented
wiredRouting ON, OFF OFF
llType LL, LL/Sat ""
macType Mac/802_11, Mac/Csma/Ca, Mac/Sat,
Mac/Sat/UnslottedAloha, Mac/Tdma ""
ifqType Queue/DropTail, Queue/DropTail/PriQueue ""
phyType Phy/WirelessPhy, Phy/Sat ""
wireless-oriented
adhocRouting DIFFUSION/RATE, DIFFUSION/PROB, DSDV,
DSR, FLOODING, OMNIMCAST, AODV, TORA ""
propType Propagation/TwoRayGround, Propagation/Shadowing ""
propInstance Propagation/TwoRayGround, Propagation/Shadowing ""
antType Antenna/OmniAntenna ""
channel Channel/WirelessChannel, Channel/Sat ""
topoInstance <topology file> ""
mobileIP ON, OFF OFF
energyModel EnergyModel ""
initialEnergy <value in Joules> ""
rxPower <value in W> ""
txPower <value in W> ""
idlePower <value in W> ""
agentTrace ON, OFF OFF
routerTrace ON, OFF OFF
macTrace ON, OFF OFF
movementTrace ON, OFF OFF
errProc UniformErrorProc ""
FECProc ? ?
toraDebug ON, OFF OFF
satellite-oriented
satNodeType polar, geo, terminal, geo-repeater ""
downlinkBW <bandwidth value, e.g. "2Mb"> ""

And

ns-2 Software and Simulation Results

|

ns-2 Software and Simulation Results

Simulations results presented in [1] were all performed in ns-2.
Step-by-step instructions to add CSFQ and FRED to ns-2 are described here.
Scripts for the three simulations described below are here.

Unless otherwise specified all links have 10 Mbps capacity and 1 ms propagation delay. All packets are 1000 bytes long, and all output buffers can hold 64 packets. In the RED and FRED implementations min_thresh is 16 and max_thresh is 32. In CSFQ both the average interval for estimating flow rate (K), and the constant Kc (used to detect whether a link is congested or not) are 100 ms. In addition, we use the following notations:

  • CSFQ - Core-Stateless Fair Queueing
  • DRR - Deficit Round Robin
  • FRED - Flow Random Early Drop
  • RED - Random Early Detection

Below we give the results for the scripts available on-line. The simulations were performed in ns version 2.1b2. Note that some of the results are slightly different from the ones reported in [1]. This is due to some differences in the default TCP settings between the versions 2.1b2 and 2.0 of ns. (The results reported in [1] were obtained with version 2.0.) Below we give in parenthesis the corresponding sections and figures from the paper.

Warning: Depending on the ns version you use, it is possible that due to the differences in the default settings or other changes to obtain slightly different results.

1. A Simple Experiment (See Section 3.6)

This experiment shows the bandwidth distribution for the following simple scenario .

When all flows are UDP the throughputs of flows 1 and 2 on link 1 are shown here , while the throughput of flows 1, 2 and 3 on link 2 are given here.

Similarly, when all flows are TCP the throughputs of flows 1 and 2 on link 1 are shown here , while the throughput of flows 1, 2 and 3 on link 2 are shown here.

2. Single Congested Link (See Section 3.1)

In the following simulations we consider a 10 Mbps bottleneck link shared by up to 32 flows.

2.1. All flows UDP (See Figure 3.a)

In this simulation we consider 32 UDP flows, indexed from 0 where flow i sends i + 1 more than its fair share of 0.3125 Mbps. The throughputs of each flow averaged over a 10 sec interval under DRR, CSFQ, FRED, FREDL, RED and FIFO, are shown here.

2.2. One UDP flow and 31 TCP flows (See Figure 3.b)

In this simulation we consider how effective is each discipline in protecting the TCP flows from an ill-behaved UDP that sends at 10 Mbps (i.e., link's capacity). The throughputs of each flow averaged over a 10 sec interval under DRR, CSFQ, FRED, FREDL, RED and FIFO, are shown here (the UDP flow is 0 indexed).

2.3. One TCP flow and n-1 UDP flows (See Figure 4)

In this simulation we consider n flows that shares the congested link, where n = 2...32. One of these flows is TCP, while the other ones are UDP. In each case the UDP flows send at twice its fair share of 10/n Mbps. The relative throughput (i.e., the throughput over the fair rate) of the TCP flow, as a function of the number of competing flows is shown here.

Note 1: In the case of FRED we take the best results between regular FRED and FREDL at each instance.

Note 2: The reason for which the performance of DRR drops sharply after 22 flows is because in this case DRR allocates less than 3 buffers per TCP (recall that the buffer size is 64), which has a negative impact on TCP. On the other hand, CSFQ is able to cope with the TCP burstiness due to the rate estimation algorithm.

3. Multiple Congested Links (See Section 3.2)

In this experiment we consider the impact on a session traversing multiple congested link. On every congested link we assume 10 UDP flows sending at twice their fair rate (= 0.909 Mbps).

3.1. UDP flow (See Figure 4.a)

Here we assume an UDP flow traversing up to 5 congested link. This plot shows the relative throughput of the flow (averaged over 10 sec) versus the number of congested links it traverses.

3.2. TCP (See Figure 4.b)

The same experiment as above, but this time the UDP flow is replaced by a TCP flow. The corresponding plot is here.

4. Two TCP and one UDP Flows Sharing the Same Link

This is similar to Experiment 1, except that here we consider a 1.5 Mbps link shared by one UDP sending at 1 Mbps and two TCPs of the same type. We perform four experiments, one for each of the following popular TCP variants: Tahoe (called simply TCP in our simulations), Reno, Newreno, and Sack. The average bandwidth of each flow for each of these cases are plotted here: Tahoe, Reno, Newreno, Sack. The average throughputs are written in TcpUdp-*-csfq.dat files and they are

Tcp Type UDP (Mbps) TCP1 (Mbps) TCP2 (Mbps)
Tahoe 5.7 4.6 4.6
Reno 6 4.4 4.4
Newreno 5.4 4.7 4.7
Sack 5.5 4.7 4.7

Notes:

  1. This experiment is similar to the one described in Section 4.2 in [1] (see Table 5, line 2) with one significant difference: the end-to-end propagation delays of both TCPs are identical and equal to 3 ms. In the experiment presented in the paper we have used the topology from Sally's scripts in which one TCP incurs an additional delay of 6 ms, while the other TCP incurs an additional delay of 8 ms, respectively.
  2. The reason for which Reno does not perform so well is due to the occurrence of retransmission timeouts when multiple packets are dropped during the same window. By reducing the timer granularity one can obtain slightly better results. Below we show the bandwidth obtained by each flow when the timer granularity (TcpTick in ns-2) is set to 10 ms.

    Tcp Type UDP (Mbps) TCP1 (Mbps) TCP2 (Mbps)
    Tahoe 5.6 4.7 4.6
    Reno 5.8 4.6 4.6
    Newreno 5.5 4.6 4.7
    Sack 5.4 4.8 4.7

  3. Finally, it should be noted that due to statistical multiplexing CSFQ performs in general better when the number of flows is large (see 3 above for an example).

References

  1. Ion Stoica, Scott Shenker, Hui Zhang, "Core-Stateless Fair Queueing: A Scalable Architecture to Approximate Fair Bandwidth Allocations in High Speed Networks", SIGCOMM'98 . [.ps.gz] [.pdf]

Ion Stoica
Last modified: Sat Jul 24 10:28:00 EDT 1999
크리에이티브 커먼즈 라이센스
Creative Commons License
And

Implementing a New Manet Unicast Routing Protocol in NS2 Version 0.2

|

Implementing a New Manet Unicast Routing Protocol in NS2 번역입니다.

수정할 내용이 있으면 바로 알려주세요.


NS2에서 새로운 Manet 유니캐스트 라우팅 프로토콜을 구현하기

 

1 소개

작년 동안에, 우리는 ns-유저들 메일링 리스트에서 같은 질문을 요구하는 많은 사람들을 목격해왔다. 내가 NS2에 적합한 내 소유의 프로토콜을 어떻게 개발하는가? 이 문서를 작성함으로써 우리는 NS-2.27에 적합한 manet 라우팅 프로토콜 더할 필요가 있는 그런 연구자들을 돕기를 희망한다. 우리는 우리의 문서를 유니캐스트 프로토콜에 초점을 맞춘다. 그 문서는 ns-2에서 시뮬레이션들을 수행하는 것에 그럭저럭 잘 아는 그런 사람들을 목표로 하고, 그들은 그들 소유의 프로토콜들을 구현하기 위해서 한 단계 앞으로 가기를 원한다. 이 문장에서 우리가 기술한 모든 것은 NS2 2.27 버전과 관련이 있지만, 그것은 다른 버전들에서도 마찬가지로 유용할 것이다.

우리는 독자가 NS2 기본들을 잘 안다고 가정한다. 그것은 “Marc Greis’ Tutorial” [1]을 읽었고 적어도 대부분 이해했다는 것을 의미한다. 만약 네가 “The ns Manual” [2], 특히 3-5, 10-12, 15-16, 23, 25 그리고 29장을 또한 훑어본다면 매우 유용할 것이다. 우리는 이런 문장 처음부터 끝까지 여러 번 그것들을 언급할 것이고 네가 그것들을 읽도록 격려할 것이다. 너의 소유의 라우팅 프로토콜을 코딩하기 전에, 너는 다른 구현된 프로토콜들로 시뮬레이션들을 어떻게 수행하는 지 알아야 하고 너는 시뮬레이터를 잘 알고 편안하게 느낄 것으로 예상된다. 이것은 이 문서를 읽는 동안에 많은 오해들과 의심들을 피할 것이다.

게다가 이 tutorial은 프로그래밍에 관한 것이다. 너는 C++ (조금) Tcl 프로그래밍에 대한 지식이 필요하다. 만약 네가 이런 프로그래밍 언어들을 충분히 경험되지 않았다면, 너는 인터넷에서 자유로이 이용 가능한 그것들에 관한 임의의 훌륭한 자원들을 첫째로 읽어야 한다.

 

2 시작하면서

우리는 protoname으로 불리는 새로운 manet 라우팅 프로토콜을 단계별로 구현할 예정이다. 이 프로토콜은 유용하지 않지만, 다른 라우팅 프로토콜들과 함께 몇 개의 공통점들을 가지기엔 충분히 일반적이다. 너도 알 것이기 때문에 (만약 아니라면, 우리가 말했던 것을 첫째로 읽어라!) 우리는 C++를 사용해서 라우팅 프로토콜을 구현할 예정이고 그리고 나서 우리는 Tcl 스트립트들로 시나리오들을 기술하는 시뮬레이션들을 할 것이다.

우리의 코드를 할당하기 위해서 우리는 너의 NS2 기반 디렉토리 안쪽에 protoname으로 불리는 새로운 디렉토리를 첫째로 생성할 것이다. 우리는 거기에 5 개의 파일들을 생성할 것이다:

 

protoname.h 이것은 (만약 있다면) 모든 필요한 타이머들이 정의될 것인 헤더 파일이고 프로토콜의 기능성을 수행하는 라우팅 에이전트이다.

protoname.cc 이 파일 안에는 모든 타이머들, 라우팅 에이전트 그리고 Tcl 후크들이 실제로 구현된다.

protoname_pkt.h 여기에는 MANET에서 노드들 사이에 교환할 필요가 있는 모든 패킷들 protoname 프로토콜이 선언된다.

protoname_rtable.h 우리 소유의 라우팅 표가 선언되는 헤더 파일

protoname_rtable.cc 라우팅 표 구현

 

너는 원하는 대로 너의 코드를 조직화할 수 있다. , 너는 그런 이름들 또는 다른 것들과 함께 파일들을 더 또는 덜 생성할 수 있다; 그것은 단지 힌트이다. 우리의 조언은 그런 파일들을 적어도 사용하는 것이고 그것들이 필요한 만큼 더 생성하는 것이다.

이제 우리는 우리의 물리적인구조 (파일들)를 가지고 있기 때문에, “논리적인 (클래스들)을 계속하자. NS2에서 라우팅 프로토콜을 구현하기 위해서 너는 에이전트 클래스로부터 물려받음으로써 에이전트를 생성해야 한다. 10 [2]의 맨 처음에서 우리는 에이전트들은 네트워크-레이어 패킷들이 구성되거나 소비되는 엔드포인트들을 의미하고, 다양한 레이어들에서 프로토콜들의 구현 안에 사용된다를 읽을 수 있다. 네가 이해할 수 있는 대로, 이것은 우리가 우리의 라우팅 프로토콜을 구현하기 위해서 코드를 해야할 것인 주된 클래스이다. 게다가, 이 클래스는 Tcl 인터페이스와 연계를 제공하고, 그래서 우리는 Tcl 안에 쓰여진 시뮬레이션 스크립트들을 통해서 우리의 라우팅 프로토콜을 제어할 수 있을 것이다.

우리의 라우팅 에이전트는 내부의 상태와 (항상 필요하지는 않는) 라우팅 표를 유지할 것이다. 내부의 상태는 새로운 클래스로써 또는 라우팅 에이전트 안쪽에 속성들의 집합으로써 나타낼 수 있다. 우리는 새로운 클래스, protoname_rtable로써 라우팅 표를 다룰 수 있을 것이다.

또한 우리의 새로운 프로토콜은 자신의 제어 패킷들의 형식을 의미할 것인 적어도 하나의 새로운 패킷 유형을 정의해야 한다. 우리가 말했던 대로 이런 패킷 유형들은 protoname/protoname_pkt.h에 정의된다. 그 프로토콜이 주기적으로 또는 이벤트의 발생으로부터 약간의 시간 후에 패킷들을 보낼 필요가 있을 때, 타이머 클래스에 의지하는 것은 매우 유용하다. 우리는 규칙적인 간격들에서 이런 패킷들을 보내기 위해서 우리 소유의 타이머들을 코드하는 예를 보여준다. 타이머들은 많은 다른 경우들에서도 또한 유용하다. 정해진 시간에서 지워져야 하는 내부 정보의 몇 가지 종류들을 저장할 필요가 있는 protoname을 상상해라. 가장 좋은 해결책은 그런 일을 할 수 있는 맞춤의 타이머를 생성하는 것이다. 타이머는 라우팅 표 안에서 입력의 시간수명을 명시하기 위해서 또한 사용되어야 한다. 일반적으로, 우리가 주어진 시간에서 업무를 스케쥴해야할 때는 언제나 타이머를 사용할 것이다.

세부적인 것들로 가기전에 우리가 알아야 하는 또 다른 중요한 클래스가 있다. 트레이스 클래스는 시뮬레이션 동안에 무엇이 일어 났는지에 대한 정보를 가진 로그 파일들을 쓰기 위한 기반이다.

그리고 마지막 힌트: 네가 너의 코드 안에 디버그 메시지를 프린트 하기를 원할 때, 25 [2]에서 제안되는 것처럼 디버그() 함수를 사용하는 것은 도움이 된다. 이것은 네가 너의 시뮬레이션 스크립트들로부터 디버깅을 켜거나 끄도록 허락하고 다른 프로그래머들이 읽는 것이 쉽다.

 

3 패킷 유형들

이제 네가 기초들을 벌써 알기 때문에, 새로운 파일을 생성하고 그것을 protoname/protoname_pkt.h 이라고 부르자. 여기에서 우리는 우리의 새로운 패킷() 유형과 관련된 모든 데이터 구조들, 상수들, 매크로들을 넣을 예정이다. 다음 예제를 보자.

 

protoname/protoname_pkt.h

1: #ifndef __protoname_pkt_h__

2: #define __protoname_pkt_h__

3:

4: #include <packet.h>

5:

6: #define HDR_PROTONAME_PKT(p) hdr_protoname_pkt::access(p)

7:

8: struct hdr_protoname_pkt {

9:

10: nsaddr_t pkt_src_; // Node which originated this packet

11: u_int16_t pkt_len_; // Packet length (in bytes)

12: u_int8_t pkt_seq_num_; // Packet sequence number

13:

14: inline nsaddr_t& pkt_src() { return pkt_src_; }

15: inline u_int16_t& pkt_len() { return pkt_len_; }

16: inline u_int8_t& pkt_seq_num() { return pkt_seq_num_; }

17:

18: static int offset_;

19: inline static int& offset() { return offset_; }

20: inline static hdr_protoname_pkt* access(const Packet* p) {

21: return (hdr_protoname_pkt*)p->access(offset_);

22: }

23:

24: };

25:

26: #endif

 

줄들 8-24는 우리가 정의하는 중인 새로운 패킷 유형을 의미하는 hdr_protoname_pkt를 선언한다. 줄들 10-12에서 우리는 우리의 패킷이 가지는 3가지 처리되지 않은 속성들을 볼 수 있다. 그것들은 다음의 유형들이다:

 

nsaddr_t 네가 NS2에서 네트워크 주소를 선언하기를 원할 때마다 매번 너는 이 유형을 사용해야 한다.

u_int16_t 16 비트들 부호 없는 정수

u_int8_t 8 비트들 부호 없는 정수

이런 모든 유형들과 더 많은 것들은 헤더 파일 config.h 안에 정의된다. 우리는 독자가 이런 파일을 훑어보고 거기에 정의된 유형들을 사용하는 것을 격려한다. 다른 변수들과 그것들을 구별하기 위한 밑줄과 함께 끝내기로 예상되는 처리 되지 않은 속성들의 이름들은 또한 언급할 만한 가치가 있다. (25 [2]를 보라)

줄들 14-16은 정의된 속성들을 위한 멤버 함수들이다. 이것은 의무적이지는 않지만 12 [2]에서 제안되는 좋은 습관이다 (그리고 우리는 그것을 실제로 지원한다!)

4패킷 클래스를 정의하는 파일 common/packet.h를 포함한다. (12 [2]를 보라) 패킷들은 시뮬레이션 안에서 오브젝트들 사이에 정보를 교환하기 위해서 사용되고, 우리의 목표는 우리의 새로운 struct hdr_protoname_pkt를 그것들에 더하는 것이다. 우리의 제어 패킷들을 그렇게 하는 것은 시뮬레이션 안에서 노드들에 의해서 보내지고 받아들여 질 수 있을 것이다. 우리가 그것을 어떻게 할 수 있는가? 이런 질문에 답하기 위해서 우리는 모든 패킷 헤더들이 패킷 클래스에 의해서 어떻게 저장되는 지를 알아야 하고, 그 답은 패킷들의 필드들이 보관되는 부호 없는 케릭터들의 배열 (비트들의 가방)을 사용하는 것이다. 구체적인 패킷 헤더에 접속하기 위해서 그것이 위치되는 오프셋을 제공할 필요가 있다. 그리고 그것은 정확하게 우리가 줄들 18-22를 통해서 하는 것이다. 우리는 고정된 (모든 hdr_protoname_pkt 구조들에 공통적인) 오프셋, 그것에 접속하기 위한 하나의 멤버 함수 그리고 하나의 패킷이 주어진 hdr_protoname_pkt을 되돌려보내는 함수를 정의한다. 게다가, 6에서 우리는 이런 마지막 함수를 사용하기 위한 매크로를 생성한다.

하나의 업무가 남아있다: 우리의 패킷 헤더를 Tcl 인터페이스에 묶는 것. 우리는 다음의 코드로 protoname/protoname.cc 안에 그렇게 할 것이다. 우리가 볼 수 있듯이 우리는 우리의 패킷 헤더의 오프셋을 Tcl을 통해서 접근하기 쉽게 하는 중이다.

 

protoname/protoname.cc

1: int protoname_pkt::offset_;

2: static class ProtonameHeaderClass : public PacketHeaderClass {

3: public:

4: ProtonameHeaderClass() : PacketHeaderClass("PacketHeader/Protoname",

5: sizeof(hdr_protoname_pkt)) {

6: bind_offset(&hdr_protoname_pkt::offset_);

7: }

8: } class_rtProtoProtoname_hdr;

 

4 라우팅 에이전트

이제 우리는 에이전트 자체의 프로그래밍을 시작한다. protoname/protoname.h 안쪽에 우리는 그것의 작업을 하는데 있어서 프로토콜을 돕도록 요구되는 속성들과 함수들을 포함하는 Protoname이라 불리는 새로운 클래스를 정의한다. 타이머들의 사용을 묘사하기 위해서 우리는 protoname이 몇 개의 제어 패킷들을 주기적으로 발산할 필요가 있는 예방의 라우팅 프로토콜임을 가정한다. 다음 코드는 그런 예제를 보여준다.

 

protoname/protoname.h

1: #ifndef __protoname_h__

2: #define __protoname_h__

3:

4: #include "protoname_pkt.h"

5: #include <agent.h>

6: #include <packet.h>

7: #include <trace.h>

8: #include <timer-handler.h>

9: #include <random.h>

10: #include <classifier-port.h>

11:

12: #define CURRENT_TIME Scheduler::instance().clock()

13: #define JITTER (Random::uniform()*0.5)

14:

15: class Protoname; // forward declaration

16:

17: /* Timers */

18:

19: class Protoname_PktTimer : public TimerHandler {

20: public:

21: Protoname_PktTimer(Protoname* agent) : TimerHandler() {

22: agent_ = agent;

23: }

24: protected:

25: Protoname* agent_;

7

26: virtual void expire(Event* e);

27: };

28:

29: /* Agent */

30:

31: class Protoname : public Agent {

32:

33: /* Friends */

34: friend class Protoname_PktTimer;

35:

36: /* Private members */

37: nsaddr_t ra_addr_;

38: protoname_state state_;

39: protoname_rtable rtable_;

40: int accesible_var_;

41: u_int8_t seq_num_;

42:

43: protected:

44:

45: PortClassifier* dmux_; // For passing packets up to agents.

46: Trace* logtarget_; // For logging.

47: Protoname_PktTimer pkt_timer_; // Timer for sending packets.

48:

49: inline nsaddr_t& ra_addr() { return ra_addr_; }

50: inline protoname_state& state() { return state_; }

51: inline int& accessible_var() { return accessible_var_; }

52:

53: void forward_data(Packet*);

54: void recv_protoname_pkt(Packet*);

55: void send_protoname_pkt();

56:

57: void reset_protoname_pkt_timer();

58:

59: public:

60:

61: Protoname(nsaddr_t);

62: int command(int, const char*const*);

63: void recv(Packet*, Handler*);

64:

65: };

66:

67: #endif

 

줄들 4-10은 우리의 에이전트에 의해서 요구되는 헤더 파일들을 포함하기 위해서 사용된다. 아래에 우리는 그것들이 무엇을 위해 유용한지를 설명한다.

 

protoname/protoname_pkt.h 우리의 패킷 헤더를 정의한다.

common/agent.h 에이전트 기반 클래스를 정의한다.

common/packet.h 패킷 클래스를 정의한다.

common/timer-handler.h TimerHandler 기반 클래스를 정의한다. 우리는 우리의 맞춤 타이머들을 생성하기 위해서 그것을 사용할 것이다.

trace/trace.h 시뮬레이션 결과를 트레이스 파일로 쓰기 위해 사용되는, 트레이스 클래스를 정의한다.

tools/random.h 가짜의-랜덤 숫자들을 발생시키는 데 유용한, 랜덤 클래스를 정의한다. 우리는 그것을 곧 사용할 것이다.

classifier/classifier-port.h 패킷들을 상위 레이어들에게 올려 보내기 위해서 사용되는 포트분류자 클래스를 정의한다.

 

12는 시뮬레이터 시계 안에 현재 시간을 얻기 위한 유용한 매크로를 정의한다. 그것은 스케쥴러 클래스의 단일의 인스턴스에 접속함으로써 실행된다. 이 오브젝트는 시뮬레이션 동안에 생산된 모든 이벤트들과 시뮬레이터의 내부 시계를 관리한다. (4 [2]을 보라)

또 다른 매크로는 줄 13에 있다. 그것은 [0-0.5] 간격 안쪽에 랜덤 숫자를 획득하기 위한 단지 쉬운 방법이다. 이것은 결국에 충돌들을 생산하고 그래서 이런 패킷들을 송신하는 시간에서 딜레이되는, 자신의 이웃들과 함께 노드의 동기화를 피하기 위해 제어 패킷들의 송신을 랜덤화하기 위해서 보통 사용된다.[1]

줄들 19-27은 주기적인 제어 패킷들을 송신하기 위한 우리의 맞춤 타이머를 선언한다. 우리의 Protoname_PktTimer 클래스는 TimerHandler로부터 물려받고 그것을 생성하는 라우팅 에이전트에 관계가 있다. 이것은 라우팅 에이전트에게 새로운 제어 패킷을 전송하고 다음 것을 스케쥴하라고 말하기 위한 콜백으로써 사용된다. 우리는 expire() 방법에 어떻게 과부하가 걸리는 지를 기술할 때, 뒤에 이것을 더 봐야 한다. 이런 콜백들을 실행하기 위해서 라우팅 에이전트는 동료 클래스로써 Protoname_PktTimer를 다룰 필요가 있다. ( 34)

Protoname 클래스는 줄들 31-65 내에 정의된다. 그것은 자신 소유의 주소, 내부의 상태, 라우팅 표, Tcl에서 접근하기 쉬운 변수 그리고 시퀀스 넘버들을 출력 패킷들에 할당하기 위한 카운터 (줄들 37-41)를 캡슐화한다. protoname_state_는 클래스 자체 또는 그것의 작업을 하기 위해서 Protoname 클래스에 의해 요구되는 속성들의 집합일 수 있다. accessible_var_ Tcl 스크립트들 또는 쉘 명령어들로부터 읽혀지고 쓰여지는 것으로 생각된다. 이것은 많은 상황들에서 유용한데 왜냐하면 그것은 유저들이 시뮬레이터의 재-컴파일없이 자신들의 스크립트들을 통해서 시뮬레이션 행동을 바꾸는 것을 허락하기 때문이다.

포트분류자 오브젝트는 줄 45에서 선언된다. 너는 노드의 구조를 이해하기 위해서 5 [2]를 읽어야 한다. 거기서 노드가 주소 분류자와 포트 분류자로 어떻게 구성되는 지 너는 볼 수 있을 것이다. 첫 번째는 적절한 링크로 들어오는 패킷들을 안내하거나 그것들을 상위 레이어 에이전트에게 충당하기 위해서 그것들을 운반할 것인, 포트 분류자에게로 그것들을 통과하기 위해서 사용된다. 그것이 라우팅 에이전트가 포트 분류자를 필요로 하는 이유이다. 그것이 자신으로 예정된 데이터 패킷들을 받을 때 그것은 일치하는 에이전트에게 그것들을 주기 위해서 dmux_를 사용할 것이다.[2] 모바일 노드의 세부적인 아키텍처는 [2] 16장에서 설명된다.

또 다른 중요한 속성은 트레이스 오브젝트이다. ( 46을 보라) 그것은 트레이스 파일 안에 저장되기 위한 로그들을 생산하기 위해서 사용된다. 우리의 예제에서 유저가 Tcl 인터페이스에서 그것을 요청할 때마다 라우팅 표의 내용들을 쓰기 위해서 우리는 그것을 사용한다. 만약 네가 패킷들에 관해서 트레이싱 정보를 쓰는 데만 단지 관심이 있다면 이것이 필수는 아니다. 그 경우에서, 그런 로깅 함수들은 다른 위치에서 구현된다. (우리가 6절에서 봐야 할 것처럼)

47은 우리의 맞춤 타이머를 선언한다. 그리고 줄들 49-51은 몇 개의 내부 속성들에 접속하기 위한 멤버 함수들이다.

53에서 함수는 데이터 패킷들을 그것들의 올바른 목적지로 전송하기 위해서 사용될 것이다. 54에서 함수는 제어 패킷이 받아들여질 때마다 호출될 것이고, 55에서 그것은 제어 패킷을 전송하기 위해서 선포된다. 57은 우리의 맞춤 타이머 기한 만료를 스케쥴하기 위해서 사용되는 함수를 선언한다.

줄들 61-63은 클래스 Protoname의 공개 함수들을 포함한다. 건설자는 라우팅 에이전트의 주소로써 사용되는 식별자를 인수로써 받는다. Protoname은 구현될 필요가 있는 두 개의 메인 함수들을 에이전트 기반 클래스로부터 물려받는다: recv()command(). recv()는 에이전트가 패킷을 받을 때마다 불려진다. 노드 자체 (실제로는 UDP 또는 TCP와 같은 상위 레이어)가 패킷을 발생하는 중이거나 다른 노드로부터 패킷을 받는 중일 때 이것은 일어날 것이다. command() 함수는 3 [2]에서 기술된 것처럼 Tcl로부터 선포된다. 그것은 우리의 Tcl 코드로부터 몇 개의 업무를 하도록 C++ 오브젝트에게 요청하기 위한 방식이다. 한번 우리가 절 4.3을 경험하면 너는 이것을 더 이해할 것이다.

이제 너는 Protoname의 인터페이스가 어떻게 있는 지 알기 때문에, 그것의 구현을 계속할 시간이다. 다음 하위 절들은 protoname/protoname.cc 파일과 관련된다.

 

4.1 Tcl hooks

우리는 우리 소유의 패킷을 Tcl에 묶는 법을 3절에서 보았다. 이제 우리는 우리의 에이전트 클래스를 위해 같은 것을 할 것이다. 그 목적은 Protoname에게 Tcl로부터 예시되도록 하는 것이다. 그렇게 하기 위해서 우리는 다음의 코드에서 묘사된 것처럼 클래스 TclClass로부터 물려받아야 한다.

 

protoname/protoname.cc

1: static class ProtonameClass : public TclClass {

2: public:

3: ProtonameClass() : TclClass("Agent/Protoname") {}

4: TclObject* create(int argc, const char*const* argv) {

5: assert(argc == 5);

6: return (new Protoname((nsaddr_t)Address::instance().str2addr(argv[4])));

7: }

8: } class_rtProtoProtoname;

 

클래스 건설자는 줄 3에 있고 그것은 인수로써 스트링 Agent/Protoname을 가진 기반 클래스를 호출한다. 이것은 본문의 방법에서 이 에이전트를 위한 클래스 계층을 의미한다.

줄들 4-7에서 우리는 TclObject로써 새로운 Protoname 인스턴스를 되돌려주는 create()로 불리는 함수를 구현한다. argv<오브젝트의 이름> <$self> <$클래스> <$proc> <유저 인수>형태이다. (더 많은 정보를 위해서 [2] 3장을 보라) 이런 특별한 경우에서 그것은 <오브젝트의 이름> <$self> Agent/Protoname create-shadow <id>이다. 이것 때문에, 6에서 우리는 argv[4]에서 시작되는 식별자를 가진 새로운 Protoname 오브젝트를 되돌려준다. 우리는 스트링에서 nsaddr_t 유형을 얻기 위해 주소 클래스를 사용한다.

 

4.2 타이머들

우리가 타이머들에 대해 protoname/protoname.cc 안에서 코드해야 하는 모두는 expire() 방법이다. 타이머들은 11 [2]에 세부적으로 되어있다. 이것을 구현하는 것은 매우 쉬운데 왜냐하면 우리는 단지 새로운 제어 패킷을 보내고 타이머 자체를 다시 스케쥴하기를 단지 원하기 때문이다. 우리의 설계 결정들에 따라서 두 개의 업무들은 라우팅 에이전트에 의해서 실행되어야 하고, 그래서 우리는 다음의 예제에서처럼 이런 콜백들을 선포한다.

 

protoname/protoname.cc

1: void

2: Protoname_PktTimer::expire(Event* e) {

3: agent_->send_protoname_pkt();

4: agent_->reset_protoname_pkt_timer();

5: }

 

4.3 에이전트

4.3.1 건설자

건설자 구현과 함께 시작해보자. 아래 줄 1에서 볼 수 있는 것처럼, 우리는 PT_PROTONAME을 통과하는 기반 클래스를 위한 건설자를 인수로써 호출함으로써 시작한다. 이런 상수는 뒤에 정의될 것이고 그것은 이런 라우팅 에이전트에 의해서 보내지고 받아들여지는 제어 패킷들을 식별하기 위해서 사용된다. 같은 줄에서 우리는 우리의 Protoname_PktTimer 오브젝트를 생성한다.

하자마자 우리는 이제 Tcl로부터 읽혀지고 쓰여질 것인 논리 속성으로써 accessible_var_을 묶는다. 정수로써 이 변수를 묶기 위해서, 우리는 bind_bool() 대신에 bind() 함수를 사용해야 한다.

3은 주어진 식별자를 라우팅 에이전트의 주소로 저장한다.

 

protoname/protoname.cc

1: Protoname::Protoname(nsaddr_t id) : Agent(PT_PROTONAME), pkt_timer_(this) {

2: bind_bool("accessible_var_", &accessible_var_);

3: ra_addr_ = id;

4: }

 

Tcl 스크립트들로부터 접근하는 것은 꽤 단순하다. 다음 예제는 accessible_var_의 값을 true로 설정한다.

 

simulation.tcl

1: Agent/Protoname set accesible_var_ true

 

4.3.2 command()

코드의 다음 조각은 조금 더 복잡하다. 그것은 우리의 에이전트가 에이전트 클래스로부터 물려받는 command() 방법의 구현으로 구성된다.

 

protoname/protoname.cc

1: int

2: Protoname::command(int argc, const char*const* argv) {

3: if (argc == 2) {

4: if (strcasecmp(argv[1], "start") == 0) {

5: pkt_timer_.resched(0.0);

6: return TCL_OK;

7: }

8: else if (strcasecmp(argv[1], "print_rtable") == 0) {

9: if (logtarget_ != 0) {

10: sprintf(logtarget_->pt_->buffer(), "P %f _%d_ Routing Table",

11: CURRENT_TIME,

12: ra_addr());

13: logtarget_->pt_->dump();

14: rtable_.print(logtarget_);

15: }

16: else {

17: fprintf(stdout, "%f _%d_ If you want to print this routing table "

18: "you must create a trace file in your tcl script",

19: CURRENT_TIME,

20: ra_addr());

21: }

22: return TCL_OK;

23: }

24: }

25: else if (argc == 3) {

26: // Obtains corresponding dmux to carry packets to upper layers

27: if (strcmp(argv[1], "port-dmux") == 0) {

28: dmux_ = (PortClassifier*)TclObject::lookup(argv[2]);

29: if (dmux_ == 0) {

30: fprintf(stderr, "%s: %s lookup of %s failed\n",

31: __FILE__,

32: argv[1],

33: argv[2]);

34: return TCL_ERROR;

35: }

36: return TCL_OK;

37: }

38: // Obtains corresponding tracer

39: else if (strcmp(argv[1], "log-target") == 0 ||

40: strcmp(argv[1], "tracetarget") == 0) {

41: logtarget_ = (Trace*)TclObject::lookup(argv[2]);

42: if (logtarget_ == 0)

43: return TCL_ERROR;

44: return TCL_OK;

45: }

46: }

47: // Pass the command to the base class

48: return Agent::command(argc, argv);

49: }

 

argv[0]은 선포되는 방법 (항상 “cmd", 3 [2]를 보라)의 이름을 포함하고, argv[1]은 요청되는 동작이며, argv[2..argc-1]은 통과되었던 인수들의 나머지이다. 이런 함수 내에서 우리가 Tcl로부터 접근하기 쉽게 만들기를 원하는 임의의 다른 동작뿐만 아니라 몇 가지 의무적인 동작들을 코드해야 한다. 한 예로써 우리는 라우팅 표의 내용들을 트레이스 파일로 덤프하는 print_rtable로 불리는 동작을 코드할 것이다.

우리는 네가 그것들을 어떻게 처리하는 지 볼 수 있도록, 2개 또는 3개의 인수들을 가진 경우들에서만 우리의 코드의 초점을 맞춘다. 각각의 경우는 TCL_OK (만약 모든 것이 괜찮으면) 또는 TCL_ERROR (만약 임의의 에러가 발생되었다면) 둘 중의 하나를 되돌려주면서 그것의 실행을 끝내야 한다.

줄들 4-7은 우리가 항상 구현해야 하는 의무적인 명령어를 기술한다: start. 이런 명령어의 예상되는 행동은 그것의 실행을 시작하기 위한 에이전트를 구성하는 것이다. 우리의 경우에서 그것은 자신의 패킷 전송 타이머를 시작한다. 우리는 라우팅 에이전트가 자신의 동작을 시작하기 위해서 수행해야 하는 모든 요구되는 행동들을 여기에서 구현해야 한다.

줄들 8-23은 우리의 print_rtable 명령어를 구현한다. 우리는 logtarget_이 초기화되는 지를 첫 번째로 체크한다. ( 9) 그리고 나서 우리는 줄들 10-13에 보여지는 것처럼 표를 트레이스 파일 안으로 덤프한다. 코드의 이런 조각을 이해하기 위해서 네가 trace/trace.h 헤더 파일을 조사하는 것은 유용할 것이다. 트레이스 클래스가 정의되는 곳이 있다. 그것은 BaseTrace 클래스의 pt_와 관계가 있다. 이런 마지막 클래스는 출력이 버퍼되는 변수를 얻기 위해 사용되는 buffer()와 그 버퍼에서 출력 파일로 쏟아져 나오기 위해 사용되는 dump() 함수들 각각 구현한다. 마지막으로, 14는 자신 소유의 내용을 트레이스 파일 안에 쓰기 위한 우리의 라우팅 표의 print() 함수를 호출한다. 아래의 TCL 코드는 시뮬레이션 스크립트로부터 정해진 시간에 print_rtable 동작을 실행하는 법을 보여준다. ns_시뮬레이터의 인스턴스를 포함하고 node_ns_에 의해서 생성된 노드임을 가정한다. 우리는 인수로써 255를 통과시키는 중인데 왜냐하면 이것은 라우팅 에이전트가 부착되는 포트의 숫자이기 때문이다.

 

simulation.tcl

1: $ns_ at 15.0 "[$node_ agent 255] print_rtable"

 

구현하기 위한 또 다른 의무적인 명령어는 port-dmux이다. 그것의 구현은 줄들 27-37에서 제공된다. [2] 3장에서 설명되는 것처럼, NS는 그것의 이름이 주어지는 그것들 각각에 빠른 접속을 제공하기 위해서 해시 표 안에 모든 콤파일된 오브젝트 (C++ 오브젝트)에 대한 참조를 저장한다. 우리는 그것의 이름이 주어지는 포트분류자 오브젝트를 획득하기 위해서 줄 28에서 그 편의를 이용한다.

유사하게, 그것의 이름이 주어지는 트레이스 오브젝트를 단순히 획득하는 tracetarget (우리는 게다가 그것이 log-target으로 불려지도록 허락한다는 것을 주목해라)이라 불리는 또 다른 의무적인 동작이 있다.

만약 우리가 요청되는 명령어를 처리하는 법을 알지 못한다면, 우리가 줄 48에서 실행한 것처럼, 이런 책임을 기반 클래스에 위임한다.

 

4.3.3 recv()

다음 함수는 recv()이고 우리가 아는 것처럼 라우팅 에이전트가 패킷을 받을 때마다 언제나 선포된다. 모든 패킷common/packet.h에서 정의된 hdr_cmn이라 불리는 공통의 헤더를 가진다. 이런 헤더에 접속하기 위해서 우리 소유의 패킷 유형을 위해서 우리가 전에 정의했던 것과 같은 매크로가 있고, 우리는 그것을 줄 3에서 사용한다. 4는 같은 것을 실행하지만 ip.h 안에 기술된, hdr_ip, IP 헤더를 얻기 위한 것이다.

 

1: void

2: Protoname::recv(Packet* p, Handler* h) {

3: struct hdr_cmn* ch = HDR_CMN(p);

4: struct hdr_ip* ih = HDR_IP(p);

5:

6: if (ih->saddr() == ra_addr()) {

7: // If there exists a loop, must drop the packet

8: if (ch->num_forwards() > 0) {

9: drop(p, DROP_RTR_ROUTE_LOOP);

10: return;

11: }

12: // else if this is a packet I am originating, must add IP header

13: else if (ch->num_forwards() == 0)

14: ch->size() += IP_HDR_LEN;

15: }

16:

17: // If it is a protoname packet, must process it

18: if (ch->ptype() == PT_PROTONAME)

19: recv_protoname_pkt(p);

20: // Otherwise, must forward the packet (unless TTL has reached zero)

21: else {

22: ih->ttl_--;

23: if (ih->ttl_ == 0) {

24: drop(p, DROP_RTR_TTL);

25: return;

26: }

27: forward_data(p);

28: }

29: }

 

우리가 해야 하는 첫 번째 것은 우리가 우리 자신에게 전송했던 패킷을 받는 중이 아님을 체크하는 것이다. 만약 그것이 그 경우라면, 우리가 줄들 8-11에서 실행한 것처럼, 패킷을 버려야 하고 되돌려 보내야 한다. 게다가, 만약 그 패킷이 (노드의 상위 레이어들에 의해서) 노드 내에서 발생되었다면 우리는 라우팅 프로토콜이 (바이트 단위로) 더하는 중인 그 오버헤드를 패킷의 길이에 더해야 한다. 줄들 13-14에서 보여지는 것처럼, 우리는 protoname IP 위에서 동작한다고 가정한다.

받아들여진 패킷이 유형 PT_PROTONAME일 때 그때 우리는 그것을 처리하기 위해서 recv_protoname_pkt()를 호출할 것이다. (줄들 18-19) 만약 TTL[3] 0에 도달하지 않았다면, 만약 그것이 데이터 패킷이라면 그때 우리는 그것을 (만약 그것이 다른 노드로 예정된 것이라면) 전송해야 하거나 (만약 그것이 브로드캐스트 패킷이었거나 우리 자신에게로 예정되었던 것이라면) 상위 레이어들에게 그것을 전달하기 위해서 전송해야 한다. 줄들 21-28forward_data() 함수의 사용하는 것을 단지 기술했던 것을 실행한다.

너는 drop() 함수가 패킷을 버리기 위해서 사용된다는 것을 깨달았을 것이다. 그것의 인수들은 패킷 자체로 향하는 포인터이고 그것을 버리기 위한 이유를 주는 상수이다. 이런 상수들이 여러 개가 존재한다. 너는 파일 trace/cmu-trace.h에서 그것들을 볼 수 있다.

 

4.3.4 recv_protoname_pkt()

라우팅 에이전트가 protoname 패킷을 받았고, 선포되기 위한 recv_protoname_pkt()를 만들었다고 가정하자. 이런 함수의 구현은 구체적인 프로토콜에 따라서 많이 변할 것이지만, 우리는 다음 예제에서 일반적인 설계를 볼 수 있다.

줄들 3-4는 평소대로 IP 헤더와 protoname 패킷 헤더를 얻는다. 그 후에 우리는 줄들 8-9에서 소스와 목적지 포트들이 RT_PORT임을 확인한다. 이 상수는 common/packet.h안에 정의되고 그것은 255와 같다. 이 포트는 라우팅 에이전트를 붙이기 위해서 예약된다.

그 후에, protoname 패킷은 우리의 라우팅 프로토콜의 규격에 따라서 처리되어야 한다.

마지막으로 우리는 줄 14에서 우리가 실행한 것처럼 자원들을 해제해야 한다.

 

1: void

2: Protoname::recv_protoname_pkt(Packet* p) {

3: struct hdr_ip* ih = HDR_IP(p);

4: struct hdr_protoname_pkt* ph = HDR_PROTONAME_PKT(p);

5:

6: // All routing messages are sent from and to port RT_PORT,

7: // so we check it.

8: assert(ih->sport() == RT_PORT);

9: assert(ih->dport() == RT_PORT);

10:

11: /* ... processing of protoname packet ... */

12:

13: // Release resources

14: Packet::free(p);

15: }

 

4.3.5 send_protoname_pkt()

우리는 4.2절에서 우리의 맞춤 타이머가 그것이 만료가 될 때마다 send_protoname_pkt() 함수를 어떻게 호출하는 지를 보았다. 우리는 아래에 이런 함수의 견본 구현을 보여준다. 물론 각각의 프로토콜은 무언가를 다르게 요구하고 이것은 단지 예제일 뿐이다.

 

protoname/protoname.cc

1: void

2: Protoname::send_protoname_pkt() {

3: Packet* p = allocpkt();

4: struct hdr_cmn* ch = HDR_CMN(p);

5: struct hdr_ip* ih = HDR_IP(p);

6: struct hdr_protoname_pkt* ph = HDR_PROTONAME_PKT(p);

7:

8: ph->pkt_src() = ra_addr();

9: ph->pkt_len() = 7;

10: ph->pkt_seq_num() = seq_num_++;

11:

12: ch->ptype() = PT_PROTONAME;

15

13: ch->direction() = hdr_cmn::DOWN;

14: ch->size() = IP_HDR_LEN + ph->pkt_len();

15: ch->error() = 0;

16: ch->next_hop() = IP_BROADCAST;

17: ch->addr_type() = NS_AF_INET;

18:

19: ih->saddr() = ra_addr();

20: ih->daddr() = IP_BROADCAST;

21: ih->sport() = RT_PORT;

22: ih->dport() = RT_PORT;

23: ih->ttl() = IP_DEF_TTL;

24:

25: Scheduler::instance().schedule(target_, p, JITTER);

26: }

 

패킷을 보내기 위해서 우리는 첫 번째 그것을 할당할 필요가 있다. 우리는 그것을 위해서 allocpkt() 함수를 사용한다. 이 함수는 모든 에이전트들을 위해서 정의된다. 그리고 나서 우리는 평소대로 공통의, IPprotoname 패킷 헤더들을 얻는다. (줄들 3-6) 우리의 목표는 우리가 원하는 값들로 모든 이런 헤더들을 채우는 것이다.

Protoname 패킷 헤더는 줄들 8-10에서 채워진다. 우리의 단순한 예제에서 우리는 단지 에이전트의 소스 주소, 메시지의 (바이트 단위로) 길이 그리고 시퀀스 넘버를 필요로 한다. 이런 필드들은 protoname의 패킷 규격에 완전히 의존적이다.

NS에서 공통의 헤더는 여러 개의 필드들을 가진다. 우리는 우리가 흥미가 있는 그런 것들에만 초점을 맞춘다. (줄들 12-17) 우리는 패킷 유형을 protoname 패킷으로 설정할 필요가 있다. ( 12) 우리는 줄 13에서 패킷 방향을 또한 할당한다. 우리가 패킷을 보내는 중이기 때문에, 그것은 hdr_cmn::DOWN 상수에 의해서 의미되는, 아래로 가는 중이다. 패킷의 크기는 줄 14에서 주어진다. 그것은 바이트 단위이고 이것은 NS2 계산들을 위해 사용되는 값이다. 우리가 의도하는 것은 너의 hdr_protoname_pkt struct의 실제 크기는 문제가 되지 않는다는 것이다. 전달 딜레이와 같은 것들을 계산하기 위해서 NS2는 네가 여기 안에 넣는 값을 사용할 것이다. 공통의 헤더와 함께 계속해서, 15에서 우리는 전달에서 어떤 에러를 갖지 않는다고 결정한다. 16은 패킷이 보내져야 하는 쪽으로 다음 홉을 할당한다. 이것은 매우 중요한 필드이고, 우리의 프로토콜에서 그것은 IP_BROADCAST로써 설립되는데 왜냐하면 우리는 그 이웃하는 노드들 모두가 이런 제어 패킷을 받기를 원하기 때문이다. 그 상수는 common/ip.h에서 정의되고 너는 다른 매크로들을 위해서 거기서 체크할 수 있다. 우리가 채우는 그 마지막 필드는 주소 유형이다. 그것은 NS_AF_NONE, NS_AF_ILINK 또는 NS_AF_INET이 될 수 있다. (common/packet.h를 보라) 우리는 NS_AF_INET을 선택하는데 왜냐하면 우리는 인터넷 프로토콜을 구현하는 중이기 때문이다.

이제 우리는 IP 헤더의 구성을 계속한다. 그것은 줄들 19-23에서 볼 수 있는 것처럼 매우 단순하다. common/ip.h에서 정의되고 IP 패킷들을 위한 초기 TTL 값을 의미하는 IP_DEF_TTL로 불리는 새로운 상수가 있다. IP 헤더는 IPv6 시뮬레이션들을 위해 사용되는 다른 필드들을 가지지만, 우리는 우리의 예제를 위해서 그것들이 필요하지 않다.

이제 우리는 패킷을 보내는 것을 단지 계속할 수 있다. 패킷들은 이벤트들이고 ([2]12장을 보라) 그래서 그것들은 스케쥴될 필요가 있다. 사실, 패킷을 보내는 것은 그것을 정해진 시간에서 스케쥴하는 것과 동등하다. 25는 몇 개의 지터를 소개하는 패킷을 전송하는 법을 보여준다. 패킷 클래스는 커넥터 클래스로부터 물려받는데, 이것은 target_으로 불리는 TclObject와 관계가 있다. 이것은 그 이벤트를 처리할 것인 핸들러이고 인수로써 schedule() 함수에게로 통과된다.

 

4.3.6 reset_protoname_pkt_timer()

우리의 패킷 전송 타이머는 그것 자체를 다시 스케쥴하기 위한 다른 콜백 (4.2)을 수행한다. 그것은 함수 reset_protoname_pkt_timer()에서 실행된다. 우리는 pkt_timer_ 5초 후에 기한 만료가 되도록 다시 스케쥴되는, 다음 예제에서 그것을 보여준다.

 

protoname/protoname.cc

1: void

2: Protoname::reset_protoname_pkt_timer() {

3: pkt_timer_.resched((double)5.0);

4: }

 

4.3.7 forward_date()

지금까지 우리는 protoname 패킷들에 주로 초점을 맞춰왔지만, 데이터 패킷들을 다룰 시간이다. forward_data() 함수는 패킷이 상위-레이어 에이전트들에게 배달되어야 하는지 또는 다른 노드에게 전송되어야 하는 지를 결정한다. 우리는 줄들 6-10에서 첫 번째 경우를 체크한다. 그것이 안으로 들어오는 패킷이고 목적지 주소가 노드 자체이거나 브로드캐스트라면, 그때 우리는 안으로 들어오는 패킷을 받아들이기 위해서 (만약 우리가 그것이 포트분류자 오브젝트임을 기억한다면) 노드의 dmux_를 사용한다.

만약 그렇지 않으면, 우리는 패킷을 전송해야 한다. 이것은 우리가 줄들 12-28에서 실행한 것처럼 공통의 헤더를 적당히 설정함으로써 달성된다. 만약 그 패킷이 브로드캐스트 패킷이라면, 그때 다음 홉은 적절히 채워질 것이다. 만약 아니라면, 우리는 다음 홉을 알아내기 위해서 우리의 라우팅 표를 사용한다. ( 17) 우리의 구현은 목적지 주소로 향하는 루트가 없을 때 IP_BROADCAST를 되돌려준다. 그런 경우에서 우리는 디버그 메시지를 프린트하고 (줄들 19-22) 패킷을 버린다. ( 23) 만약 모든 것이 괜찮다면 그때 우리는 줄 29에서 실행한 것처럼 패킷을 전송할 것이다.

 

protoname/protoname.cc

1: void

2: Protoname::forward_data(Packet* p) {

3: struct hdr_cmn* ch = HDR_CMN(p);

4: struct hdr_ip* ih = HDR_IP(p);

5:

6: if (ch->direction() == hdr_cmn::UP &&

7: ((u_int32_t)ih->daddr() == IP_BROADCAST || ih->daddr() == ra_addr())) {

8: dmux_->recv(p, 0.0);

9: return;

10: }

11: else {

12: ch->direction() = hdr_cmn::DOWN;

13: ch->addr_type() = NS_AF_INET;

14: if ((u_int32_t)ih->daddr() == IP_BROADCAST)

15: ch->next_hop() = IP_BROADCAST;

16: else {

17: nsaddr_t next_hop = rtable_.lookup(ih->daddr());

18: if (next_hop == IP_BROADCAST) {

19: debug("%f: Agent %d can not forward a packet destined to %d\n",

20: CURRENT_TIME,

21: ra_addr(),

22: ih->daddr());

23: drop(p, DROP_RTR_NO_ROUTE);

24: return;

25: }

26: else

27: ch->next_hop() = next_hop;

28: }

29: Scheduler::instance().schedule(target_, p, 0.0);

30: }

31: }

 

5 라우팅 표

너는 라우팅 표를 필요로 하지 않을지라도, 만약 너의 프로토콜이 그것을 사용한다면 그때 이 절을 읽어라. 우리는 다른 클래스로써 또는 임의의 다른 데이터 구조 (예를 들어, 해쉬 표)로써 라우팅 표를 구현할 수 있다. 우리는 라우팅 표가 가지기로 되어 있는 그 기능성을 캡슐화하는 클래스를 보일 예정이다. 내부의 정보는 프로토콜에서 프로토콜까지 많이 변할 것이다. 라우팅 표에서 각각의 입력을 위해서 목적지 주소들, 다음 홉 주소들, 루트들과 결합된 거리들 또는 비용, 시퀀스 넘버들, 수명들 기타 등등을 저장하기를 원할 것이다. 물론 우리의 예제는 매우 단순한 라우팅 표와 그것을 프린트하기 위한 방법을 묘사한다. 우리가 각각의 입력에 저장할 유일한 정보는 목적지와 다음 홉 주소들이다. 우리는 저장 구조로써 해시 표 (map)을 사용한다. 이런 경우는 너무 단순해서 새로운 클래스를 구현할 수 없지만, 우리는 예제로써 그것을 실행할 것이다. 코드의 다음 조각은 protoname/protoname_rtable.h에 일치한다.

 

protoname/protoname_rtable.h

1: #ifndef __protoname_rtable_h__

2: #define __protoname_rtable_h__

3:

4: #include <trace.h>

5: #include <map>

6:

7: typedef std::map<nsaddr_t, nsaddr_t> rtable_t;

8:

9: class protoname_rtable {

10:

11: rtable_t rt_;

12:

13: public:

14:

15: protoname_rtable();

16: void print(Trace*);

17: void clear();

18: void rm_entry(nsaddr_t);

19: void add_entry(nsaddr_t, nsaddr_t);

20: nsaddr_t lookup(nsaddr_t);

21: u_int32_t size();

22: };

23:

24: #endif

 

이런 함수들의 구현은 매우 쉽다. 사실 그 건설자는 매우 단순해서 그것 안에 실행할 것이 아무것도 없다.

 

protoname/protoname_rtable.cc

1: protoname_rtable::protoname_rtable() { }

 

print() 함수는 노드의 라우팅 표의 내용들을 트레이스 파일로 덤프할 것이다. 그것을 실행하기 위해서 우리는 절 4.3에서 언급했던 트레이스 클래스를 사용한다.

 

protoname/protoname_rtable.cc

1: void

2: protoname_rtable::print(Trace* out) {

3: sprintf(out->pt_->buffer(), "P\tdest\tnext");

4: out->pt_->dump();

5: for (rtable_t::iterator it = rt_.begin(); it != rt_.end(); it++) {

6: sprintf(out->pt_->buffer(), "P\t%d\t%d",

7: (*it).first,

8: (*it).second);

9: out->pt_->dump();

10: }

11: }

 

다음의 함수는 라우팅 표 안에 모든 입력들을 제거한다.

 

protoname/protoname_rtable.cc

1: void

2: protoname_rtable::clear() {

3: rt_.clear();

4: }

 

그것의 목적지 주소가 주어진 입력을 제거하기 위해서 우리는 rm_entry() 함수를 구현한다.

 

protoname/protoname_rtable.cc

1: void

2: protoname_rtable::rm_entry(nsaddr_t dest) {

3: rt_.erase(dest);

4: }

 

아래의 코드는 그것의 목적지와 다음 홉 주소들이 주어진 라우팅 표 안에 새로운 입력을 더하기 위해서 사용된다.

 

protoname/protoname_rtable.cc

1: void

2: protoname_rtable::add_entry(nsaddr_t dest, nsaddr_t next) {

3: rt_[dest] = next;

4: }

 

Lookup()은 그것의 목적지 주소가 주어진 입력의 다음 홉 주소를 되돌려준다. 만약 그런 입력이 존재하지 않는다면, (, 그 목적지를 위한 루트가 없다면) 그 함수는 IP_BROADCAST를 되돌려준다. 물론 이 상수를 사용하기 위해서 common/ip.h를 포함한다.

 

protoname/protoname_rtable.cc

1: nsaddr_t

2: protoname_rtable::lookup(nsaddr_t dest) {

3: rtable_t::iterator it = rt_.find(dest);

4: if (it == rt_.end())

5: return IP_BROADCAST;

6: else

7: return (*it).second;

8: }

 

마지막으로, size()는 라우팅 표 안에 입력들의 수를 되돌려준다.

 

protoname/protoname_rtable.cc

1: u_int32_t

2: protoname_rtable::size() {

3: return rt_.size();

4: }

 

6 요구되는 변화들

우리는 거의 끝냈다. 우리는 NS2 안쪽에 프로토콜 protoname을 위한 라우팅 에이전트를 구현해왔다. 그러나 우리의 코드를 시뮬레이터 안쪽에 통합하기 위해서 우리가 실행할 필요가 있는 몇 가지 변화들이 있다.

 

6.1 패킷 유형 선언

만약 우리가 기억한다면 우리는 우리의 새로운 패킷 유형, PT_PROTONAME을 가리키기 위한 상수를 사용해야 했었다. 우리는 파일 common/packet.h 안쪽에 그것을 정의할 것이다.

모든 패킷 유형들이 리스트된, packet_h 목록을 찾아보자. 우리는 코드의 다음 조각에서 보이는 것처럼 PT_PROTONAME을 이런 목록에 더할 것이다. ( 6)

 

common/packet.h

1: enum packet_t {

2: PT_TCP,

3: PT_UDP,

4: PT_CBR,

5: /* ... much more packet types ... */

6: PT_PROTONAME,

7: PT_NTYPE // This MUST be the LAST one

8: };

 

바로 아래에 같은 파일에 p_info 클래스의 정의가 있다. 건설자 안쪽에 우리는 우리의 패킷 유형을 위한 본문의 이름을 제공할 것이다. ( 6)

 

common/packet.h

1: p_info() {

2: name_[PT_TCP]= "tcp";

3: name_[PT_UDP]= "udp";

4: name_[PT_CBR]= "cbr";

5: /* ... much more names ... */

6: name_[PT_PROTONAME]= "protoname";

7: }

 

6.2 트레이싱 지원

우리가 알다시피 시뮬레이션의 목적은 실행 동안에 무엇이 일어났는지 기술하는 트레이스 파일을 얻는 것이다. 트레이스들에 친밀함을 느끼기 위해서 23 [2]를 읽어봐라. 트레이스 오브젝트는 패킷이 받아들여지거나, 보내지거나, 또는 버려질 때마다 매번 패킷의 요구되는 정보를 쓰기 위해서 사용된다. 우리의 패킷 유형에 관한 정보를 기록하기 위해서 우리는 CMUTrace 클래스 안쪽에 format_protoname() 함수를 구현한다. 무선 시뮬레이션들을 위한 트레이스 지원은 CMUTrace 오브젝트들에 의해서 제공되고 그것은 16 [2]에서 기술된다.

trace/cmu-trace.h 파일을 편집해보자. 우리는 다음의 예제의 줄 수 6 안에서처럼 우리의 새로운 함수를 더해야 한다.

 

trace/cmu-trace.h

1: class CMUTrace : public Trace {

2: /* ... definitions ... */

3: private:

4: /* ... */

5: void format_aodv(Packet *p, int offset);

6: void format_protoname(Packet *p, int offset);

7: };

 

(trace/cmu-trace.cc로부터 추출된) 코드의 다음 조각은 트레이스들의 다른 유형들을 보여준다.

 

trace/cmu-trace.cc

1: #include <protoname/protoname_pkt.h>

2:

3: /* ... */

4:

5: void

6: CMUTrace::format_protoname(Packet *p, int offset)

7: {

8: struct hdr_protoname_pkt* ph = HDR_PROTONAME_PKT(p);

9:

10: if (pt_->tagged()) {

11: sprintf(pt_->buffer() + offset,

12: "-protoname:o %d -protoname:s %d -protoname:l %d ",

13: ph->pkt_src(),

14: ph->pkt_seq_num(),

15: ph->pkt_len());

16: }

17: else if (newtrace_) {

18: sprintf(pt_->buffer() + offset,

19: "-P protoname -Po %d -Ps %d -Pl %d ",

20: ph->pkt_src(),

21: ph->pkt_seq_num(),

22: ph->pkt_len());

23: }

24: else {

25: sprintf(pt_->buffer() + offset,

26: "[protoname %d %d %d] ",

27: ph->pkt_src(),

28: ph->pkt_seq_num(),

29: ph->pkt_len());

30: }

31: }

 

우리는 위의 코드로부터 3가지 다른 트레이스 포맷들이 있다는 것을 추론할 수 있다: 태그가 붙은 트레이스들, 새로운 포맷 트레이스들 그리고 고전적인 트레이스들. 문법에 이어서 각각은, 비록 다르지만, 네가 말할 수 있는 것처럼 매우 쉽고 직관적이다. 태그가 붙은 것과 새로운 트레이스 포맷들 양쪽에는 프린트되는 중인 정보의 각각의 필드를 식별하기 위해서 사용되는 표지들이 존재한다. 우리는 소스 주소로써 “o” (기원), 시퀀스 넘버로써 “s”, 일치하는 패킷의 길이로써 “l”을 사용하기로 결정해왔다.

이것을 최근에 생성된 함수로 호출하기 위해서 우리는 trace/cmu-trace.cc 안에 format()을 바꿔야 한다.

 

trace/cmu-trace.cc

1: void

2: CMUTrace::format(Packet* p, const char *why)

3: {

4: /* ... */

5: case PT_PING:

6: break;

7:

8: case PT_PROTONAME:

9: format_protoname(p, offset);

10: break;

11:

12: default:

13: /* ... */

14: }

 

6.3 Tcl 라이브러리

이제 우리는 Tcl 파일들에서 몇 개의 변화들을 실행할 필요가 있다. 실제로 우리는 우리의 패킷 유형을 더하고, 묶여진 속성들을 위한 초기 값들을 주고 우리의 protoname 라우팅 프로토콜을 실행하는 무선 노드들을 생성하기 위한 요구되는 인프라스트럭처를 제공할 예정이다.

tcl/lib/ns-packet.tcl에서 우리는 다음의 코드의 위치를 정해야 하고 protoname을 목록에 더해야 한다. (우리가 줄 2에서 실행한 것처럼)

 

tcl/lib/ns-packet.tcl

1: foreach prot {

2: Protoname

3: AODV

4: ARP

5: # ...

6: NV

7: } {

8: add-packet-header $prot

9: }

 

묶여진 속성들을 위한 초기 값들은 tcl/lib/ns-packet.tcl 안쪽에 주어져야 한다. 우리는 파일의 끝으로 가야하고 다음의 코드와 같이 무언가를 써야 한다:

 

tcl/lib/ns-default.tcl

1: # ...

2: # Defaults defined for Protoname

3: Agent/Protoname set accessible_var_ true

 

마지막으로 우리는 tcl/lib/ns-lib.tcl을 변경해야 한다. 우리는 노드를 생성하기 위한 프로시저들을 더할 필요가 있다. 우리의 흥미는 라우팅 프로토콜로써 protoname을 가진 무선 노드를 생성하는 것을 중심으로 행해질 것이다.

프로시저 노드create-wireless-node 프로시저에게로 호출한다. 다른 업무들 중에서, 이 마지막 것은, 하나의 노드를 위한 그 라우팅 에이전트를 설정하려는 의도이다. 우리는 우리의 protoname 프로토콜의 인스턴스를 생성하기 위해서 이런 프로시저를 교묘히 바꿀 필요가 있다.

 

tcl/lib/ns-lib.tcl

1: Simulator instproc create-wireless-node args {

2: # ...

3: switch -exact $routingAgent_ {

4: Protoname {

5: set ragent [$self create-protoname-agent $node]

6: }

7: # ...

8: }

9: # ...

10: }

 

그리고 나서 create-protoname-agent는 다음의 예제에서 보여지는 것처럼 아래에 코드될 것이다.

 

tcl/lib/ns-lib.tcl

1: Simulator instproc create-protoname-agent { node } {

2: # Create Protoname routing agent

3: set ragent [new Agent/Protoname [$node node-addr]]

4: $self at 0.0 "$ragent start"

5: $node set ragent_ $ragent

6: return $ragent

7: }

 

3은 노드의 주소를 가진 새로운 protoname 에이전트를 생성한다. 이런 에이전트는 시뮬레이션의 처음에 시작되도록 스케쥴되고, ( 4) 5에서 노드의 라우팅 에이전트로써 할당된다.

 

6.4 우선순위 큐

네가 너의 시뮬레이션들에서 우선순위 큐들을 사용할 것은 매우 적당하다. 이 큐 유형은 높은 우선순위 패킷들로써 라우팅 패킷들을 취급하고, 그것들을 큐의 처음에 삽입한다. 그러나 우리는 protoname 패킷들이 라우팅 패킷들이고 그래서 높은 우선순위로써 취급된다고 PriQueue 클래스에게 말할 필요가 있다.

우리는 queue/priqueue.cc 파일에 recv() 함수를 변경해야 한다. 코드의 다음 조각에서 줄 13은 우리가 실행할 필요가 있는 유일한 변경이다.

 

queue/priqueue.cc

1: void

2: PriQueue::recv(Packet *p, Handler *h)

3: {

4: struct hdr_cmn *ch = HDR_CMN(p);

5:

6: if (Prefer_Routing_Protocols) {

7:

8: switch(ch->ptype()) {

9: case PT_DSR:

10: case PT_MESSAGE:

11: case PT_TORA:

12: case PT_AODV:

13: case PT_PROTONAME:

14: recvHighPriority(p, h);

15: break;

16:

17: default:

18: Queue::recv(p, h);

19: }

20: }

21: else {

22: Queue::recv(p, h);

23: }

24: }

 

6.5 Makefile

이제 모든 것이 구현되고 우리는 단지 그것을 컴파일할 필요가 있다! 그렇게 하기 위해서 우리는 다음의 코드 ( 4)에서처럼 OBJ_CC 변수 안쪽에 우리의 오브젝트 파일들을 더함으로써 Makefile 파일을 편집할 것이다.

 

Makefile

1: OBJ_CC = \

2: tools/random.o tools/rng.o tools/ranvar.o common/misc.o common/timer-handler.o \

3: # ...

4: protoname/protoname.o protoname/protoname_rtable.o \

5: # ...

6: $(OBJ_STL)

 

우리가 common/packet.h를 변경했지만 common/packet.cc는 변경하지 않았기 때문에 우리는 다시 컴파일되는 것을 위해서 이 마지막 파일을 “touch”해야 한다. 그런 후에 우리는 make를 실행할 수 있고 우리 소유의 라우팅 프로토콜을 즐길 수 있다. (또는 아마 모든 편집 문제들을 해결할 수 있다!)

 

[ns-2.27]$ touch common/packet.cc

[ns-2.27]$ make

 

7 레이어-2 프로토콜들로부터 정보를 받기

몇 개의 라우팅 프로토콜들은 패킷이 레이어-2로부터 보내질 수 없을 때 반응에 흥미가 있을 것이다. 이것은 우리가 아래에 설명한 것처럼, 우리의 라우팅 에이전트에 의해서 쉽게 달성될 수 있다.

그것은 어떻게 작동하는가? 패킷의 공통의 헤더는 그 패킷이 그 레이어-2 에이전트에 의해서 보내질 수 없다면 호출될 것인 함수를 네가 명시할 수 있는 필드를 가진다. 그 함수를 protoname_mac_failed_callback()이라 부르자. 우리는 그런 레이어-2 실패에 대해 반응을 담당하는 역할을 하는 라우팅 에이전트 내에 또 다른 것을 호출하는 이런 함수를 사용할 것이다. 우리는 이런 두 번째 함수를 mac_failed()로 부를 것이다. 그래서 우리는 protoname/protoname.h의 줄 9만 단지 변경해야 한다

 

protoname/protoname.h

1: class Protoname : public Agent {

2: /* ... */

3:

4: public:

5:

6: Protoname(nsaddr_t);

7: int command(int, const char*const*);

8: void recv(Packet*, Handler*);

9: void mac_failed(Packet*);

10: };

11: #endif

 

protoname/protoname.cc 파일은 더 많은 변화들을 요구한다. 무엇보다도 우리는 공통의 헤더 안쪽에 등록되는 그 함수를 구현해야 한다. 그 함수는 Protoname 클래스의 mac_failed() 함수로 단순히 호출할 것이다. 너는 아래에 구현을 볼 수 있다.

 

protoname/protoname.cc

1: static void

2: protoname_mac_failed_callback(Packet *p, void *arg) {

3: ((Protoname*)arg)->mac_failed(p);

4: }

 

mac_failed()에 의해서 구현된 기능성은 protoname 규격에 매우 의존한다. 예를 들어, 코드의 다음 조각은 디버그 메시지를 프린트하고 ( 6-9) 패킷을 버린다. ( 11)

 

protoname/protoname.cc

1: void

2: Protoname::mac_failed(Packet* p) {

3: struct hdr_ip* ih = HDR_IP(p);

4: struct hdr_cmn* ch = HDR_CMN(p);

5:

6: debug("%f: Node %d MAC layer cannot send a packet to node %d\n",

7: CURRENT_TIME,

8: ra_addr(),

9: ch->next_hop());

10:

11: drop(p, DROP_RTR_MAC_CALLBACK);

12:

13: /* ... do something ... */

14: }

 

만약 우리가 라우팅 패킷이 레이어-2 프로토콜들에 의해서 보내지지 않을 때를 알기 원한다면 우리는 send_protoname_pkt()를 변경할 필요가 있다. 유사하게 만약 우리가 데이터 패킷들에 주의하기를 원한다면 forward_data()는 가볍게 마찬가지로 변경되어야 한다. 양쪽의 경우들에서 우리는 그 패킷의 공통의 헤더를 업데이트할 때 다음의 두 개의 줄들을 단지 더해야 한다.

 

protoname/protoname.cc

1: ch->xmit_failure_ = protoname_mac_failed_callback;

2: ch->xmit_failure_data_ = (void*)this;

 

protoname_mac_failed_callback()은 무슨 경우들에서 호출될 것인가? NS-2.27에서 우리는 두 개의 다른 상황들을 설립할 수 있다.

 

mac/arp.cc 노드가 목적지 주소를 (ARP를 통해서) 결정하기를 원하지만 재시도들의 최대 숫자가 초과될 때

mac/mac-802_11.cc 두 개의 가능성들이 있다. 첫 번째 것은 RTS가 보내졌지만 대응하는 CTS가 받아들여지지 않았고 재시도의 최대 숫자가 초과될 때 일어날 수 있다. 두 번째 것은 데이터 패킷이 전달되었지만 절대 ACK를 받지 못했고 (받아들여진 ACK가 없다) 재시도들의 최대 숫자가 초과될 때 발생한다.

 

8 유선이-붙은-무선 환경들을 위한 지원

이제까지 우리는 평평한 manet, , 무선-유일한 시나리오들에 대해서만 관련이 있었다. 이번 절에서 우리는 하이브리드 manet (NS2 용어를 따르면, 유선이-붙은-무선 시나리오들)을 다루기 위한 기본적인 개념들을 소개할 것이다. 유선이-붙은-무선 스크립트들은 계층적인 주소를 사용할 필요가 있고, 그래서 너는 주소의 이런 유형에서 필요한 지식을 얻기 위해서 15장과 29 [2]을 읽어야 한다.

최소의 변화들과 함께 우리는 유선이-붙은-무선 시뮬레이션들에서 우리의 프로토콜을 사용할 수 있다. 이런 것들에서는 고정된 노드들, 무선 노드들 그리고 베이스 스테이션들이 있다. 베이스 스테이션은 유선과 무선 도메인들 사이에 게이트웨이이고, 모든 무선 노드는 자신이 어느 베이스 스테이션에 결합되는지를 알 필요가 있다. 우리가 유선이-붙은-무선 지원을 제공하기 위해서 실행할 필요가 있는 모든 것은 각각의 노드를 위해 일치하는 베이스 스테이션을 설정하는 것이다.

유선이-붙은-무선 시나리오들을 기술하는 시뮬레이션 스크립트들은 각각의 모바일 노드에서 시간적으로 앞선 동작을 수행하는데, , 모든 모바일 노드는 베이스 스테이션 (노드 API베이스-스테이션 함수)에 부착된다. 그러나 우리는 여러 개의 베이스 스테이션들이 사용되는 시나리오들에 관심이 있고, 우리는 모바일 노드들이 그들의 결합된 베이스 스테이션들을 동적으로 바꾸기를 원한다는 것을 상상해라. 우리가 다수의 베이스 스테이션들이 허락되는 하이브리드 ad hoc 네트워크들을 지원하는 라우팅 프로토콜을 코드하기를 원한다면 이것은 유용하다. 만약 이것이 너의 경우라면, 그 절을 계속해서 읽어라.

다음의 코드에서 보여지는 것처럼 protoname/protoname.h를 다시 편집하자. 줄들 1 11은 더해지고, 반면에 나머지는 변하지 않는다.

 

protoname/protoname.h

1: #include <mobilenode.h>

2:

3: /* ... */

4:

5: class Protoname : public Agent {

6:

7: /* ... */

8:

9: protected:

10:

11: MobileNode* node_;

12:

13: /* ... */

14: };

 

우리는 라우팅 에이전트가 부착되는 노드를 의미하는 (common/mobilenode.h에서 정의된) 모바일노드 오브젝트에 대한 참조를 더했다. 이런 참조를 얻기 위해서 우리는 Protoname 건설자 안쪽에 다음의 줄 4를 더할 필요가 있다.

 

protoname/protoname.cc

1: Protoname::Protoname(nsaddr_t id) : Agent(PT_PROTONAME), pkt_timer_(this) {

2: bind_bool("accessible_var_", &accessible_var_);

3: ra_addr_ = id;

4: node_ = (MobileNode*)Node::get_node_by_address(id);

5: }

 

모바일노드 클래스는 우리가 관심이 있는 두 개의 함수들을 소유한다. 우선 첫째로 모바일 노드가 부착되는 베이스 스테이션의 식별자를 되돌려주는, base_stn()이다. 두 번째는 그 모바일 노드를 위한 적합한 베이스 스테이션을 설립할 수 있는 set_base_stn()이다. 그래서 우리는 유선이-붙은-무선 시뮬레이션들을 이런 두 개의 함수들을 사용함으로써 다룰 수 있다. 예를 들어, 다음의 코드는 그 모바일 노드 자체가 베이스 스테이션인지를 체크하고; 만약 아니라면 그때 그것은 베이스 스테이션을 할당 받는다.

 

protoname/protoname.cc

1: if (node_->base_stn() == ra_addr()) {

2: // I’m a base station

3: /* ... */

4: }

5: else {

6: // I’m not a base station

7: node_->set_base_stn(base_stn_addr);

8: }

 

앞의 예제는 결합된 베이스 스테이션을 동적으로 바꾸는 법을 보여준다. 이런 바꿈들을 수행하기 위해서 사용되는 접근들은 프로토콜 자체에 의존한다.

 

참조

[1] Marc Greis. Tutorial for the Network Simulator ”ns”.

http://www.isi.edu/nsnam/ns/tutorial/index.html.

[2] The VINT Project. The ns Manual, December 2003.

http://www.isi.edu/nsnam/ns/ns-documentation.html.



[1] 이것은 manet 라우팅 프로토콜들에서 일반적인 특징이지만, 그러나 실제 목적은 랜덤 숫자들을 얻는 예제를 제공하는 것이다.

[2] 실제 이것은 사실이 아니다. 사실 데이터 패킷들은 자신들의 일치하는 에이전트에게 직접 배달되고, 그래서 포트 분류자는 우리의 라우팅 에이전트에서 필요하지 않다. 그러나 우리는 이런 설명을 유지하는데 왜냐하면 NS-2.27은 라우팅 에이전트가 자신의 API의 부분으로써 port-dmux 동작 (4.3.2절을 보라)을 받아들이는 것을 요구하기 때문이다.

[3] IP 헤더에 따라서 패킷의 Time To Live


And

NS2 - 무선환경 시뮬레이션 파라메터 설명

|

유선과 무선 환경의 차이

  • 노드 사이의 명시적인 링크가 존재하지 않음
    • 라디오의 전송 범위 내에 노드들이 위치하면 통신 가능
  • 무선상에서는 MAC 기술이 아주 중요
    • NS2 유선 시뮬레이션 환경은 MAX이 존재하지 않음
  • 무선상에서 노드는 이동성이 존재
  • 라우팅
    • 유선상에서는 특별히 라우팅을 설정하지 않아도 링크 설정관계를 통해서 최단 경로가 선택
    • 무선에는 반드시 라우팅이 존재해야 함
  • 주소
    • 유선에서는 flat address
    • 무선만 존재하면 flat address
    • 유무선 연동인 경우는 hierarchical address

NS2에서의 유선과 무선의 차이

  • C++ 상에서의
    • 유선 : class Node
    • 무선 : class MobileNode
  • 노드 생성은 유무선 모두 $ns node로 하지만 무선에서는 node-config라는 추가 작업이 필요
  • Trace
    • $ns/common 에서
      • 유선 : trace.{h, cc}
      • 무선 : cmu-trace.{h, cc}
  • 이동성
    • 무선에서는 이동성을 지원
    • 위치 정보가 중요
      • 유선에서는 연결 구조가 중요, 노드의 배치는 중요하지 않음
      • 무선에서는 위치에 따라서 전송 범위 내에 있는지 여부가 결정
  • 그 외에는 유무선 모두 같은 네트워크 컴포넌트를 사용

node-config

역활
 * $ns가 앞으로 만들어질 노드에 대한 설정 정보들을 저장, 이후 만들어지는 노드들은 그 설정 정보들로부터 생성됨
 * -adhocRouting 설정 정보로부터 유선 노드와 무선 노드 구별
 * node-config를 이용해서 노드 설정을 바뀌었다면 바뀐 설정은 새로 만들어지는 노드부터 적용
Topography
 * 지형도
 * 영역을 만듦 ->무선 환경에서는 위치가 중요 정보
 * set topo [new Topography]
 * $topo load_flatgrid <x축 크기> <y축 크기>
God 객체
 * Simulater와 마찬가지로 단 하나의 인스턴스만 존재, 없다면 에러
 *  노드간 연결(라우팅 가능한지) 정보 제공
 * 예) set god_ [create-god <모바일 노드 개수>]
 * God 인스턴스 이름은 god_로 해야함 (일부 $god_로 사용)

  • -adhocRouting : 라우팅 프로토콜 설정
    • DSDV, AODV, TORA,, FLOODING,...
  • -llType : 링크 레이어 설정
    • LL
  • -macType : 맥프로토콜 설정
    • Mac/802_11, Mac/SMAC
  • -propType : 경로손실타입
    • Propagation/twoRayGround, Propagation/FreeSpace, Propagation/Shadowing,...
  • -ifqType : 큐타입
    Queue/DropTail/PriQueue,...
    애드혹라우팅 프로토콜이 DSR인 경우 반드시 CMUPriQueue로 해야 함
    -ifqLen : 큐길이
    패킷 개수
    -phyType : 피지컬레이어타입
    Phy/WirelessPhy,...
    -antType : 안테나 타입
    Antenna/OmniAntenna
    -channel : 현재는 없어짐
    Channel/WirelssChannel
    -topoInstance : 전체 영역
    $topo
    -agentTrace : 트레이스 설정
    ON or OFF
    -routerTrace : 트레이스 설정 
    ON or OFF
    -macTrace : 트레이스 설정
    ON or OFF
    -movementTrace : 트레이스 설정
    ON or OFF


  • NS2 무선환경 시뮬레이션 스크립터
set ns [new Simulator]
set trfd [open prac10.tr w]
$ns trace-all $trfd
set namfd [open prac10.nam w]
$ns namtrace-all-wireless $namfd 1000 1000
set topo [new Topography]
$topo load_flatgrid 1000 1000
set god_ [create-god 25]
$ns node-config -adhocRouting AODV \
                -llType LL \
                -macType Mac/802_11 \
                -propType Propagation/TwoRayGround \
                -ifqType Queue/DropTail/PriQueue \
                -ifqLen 50 \
                -phyType Phy/WirelessPhy \
                -antType Antenna/OmniAntenna \
                -channel [new Channel/WirelessChannel] \
                -topoInstance $topo \
                -agentTrace ON \
                -routerTrace ON \
                -macTrace ON \
                -movementTrace ON
for {set i 0} {$i < 25} {incr i} {
        set node_($i) [$ns node]
        $node_($i) set X_ [expr $i % 5 * 200]
        $node_($i) set Y_ [expr $i / 5 * 200]
        $node_($i) set Z_ 0
        $ns initial_node_pos $node_($i) 150
}
set udp [new Agent/UDP]
$ns attach-agent $node_(0) $udp
set cbr [new Application/Traffic/CBR]
$cbr set packetSize_ 500
$cbr set interval_ 0.1
$cbr attach-agent $udp
set null [new Agent/Null]
$ns attach-agent $node_(24) $null
$ns connect $udp $null
set tcp [new Agent/TCP]
$ns attach-agent $node_(20) $tcp
set ftp [new Application/FTP]
$ftp attach-agent $tcp
set tcpSink [new Agent/TCPSink]
$ns attach-agent $node_(4) $tcpSink
$ns connect $tcp $tcpSink
$ns at 0.5 "$cbr start"
$ns at 5.0 "$cbr stop"
$ns at 2.0 "$ftp start"
$ns at 4.0 "$ftp stop"
#$ns at 1.0 "$node_(24) setdest 500 800 20"
$ns at 6.0 "$ns halt"
$ns run


 Max/802_11 Bandwidth

  • basic rate, data rate 두가지 존재
    • basic reate : RTS, CTS, ACK 을 전송하는 속도
    • data rate : 실제 데이터를 전송하는 속도
  • 설정하는 방법
    • Mac/802_11 set basicRate_ 1Mb
    • Mac/802_11 set dataRate_ 1Mb
    • 기본값은 둘다 1Mbps

범위 설정

  • 전송 범위 : 데이터가 성공적으로 수신되는 범위
  • 캐리어 감지 범위 : 데이터를 성공적으로 수신하지는 못하지만 신호만 감지하는 범위 -> CSMA/CA 프로토콜의 CA
  • 전송 범위와 캐리어 감지 범위는 수신 노드에서 받는 전파세기와 쓰레쉬홀드에 의해서 결정
  • $ns/tcl/lib/ns-default.tcl
 Phy/WirelessPhy set CPThresh_ 10.0
 Phy/WirelessPhy set CSThresh_ 1.559e-11  # 수신한 전파의 세기가 이 값 이상이면 감지
 Phy/WirelessPhy set RXThresh_ 3.652e-10 # 수신한 전파의 세기가 이 값 이상이면 성공적인 수신
 Phy/WirelessPhy set bandwidth_ 2e6
 Phy/WirelessPhy set Pt_ 0.28183815
 Phy/WirelessPhy set freq_ 914e+6
 Phy/WirelessPhy set L_ 1.0

  • 다른 설정은 변경하지 않고 위 두개의 threshold만 변화시켜 범위 조절 가능
  • $ns/indep-utils/propagation/threshold.cc
    • 컴파일 후 threshold -m <경로손실모델> <범위> ->threshold 값 리턴
from http://vessel.tistory.com/100 에서 참조
And

network components, ns by example

|
NS2 Simple Simulation Example

2.2 Simple Simulation Example


이 section은 간단한 NS simulation script를 보여주며, 각 line이 무엇을 하는지를 설명한다. 예제3 은 간단한 network 구성을 생성하는 OTcl script이며, 그림4.에 있는 시뮬레이션 scenario를 동작한다. 이 시뮬레이션을 동작하기 위해서 "ns-simple"를 다운받고, shell prompt에서 "ns ns-simple.tcl"을 입력하라.




Example 3. A Simple NS Simulation Script
#Create a simulator object

set ns [new Simulator]


#Define different colors for data flows (for NAM)

$ns color 1 Blue

$ns color 2 Red


#Open the NAM trace file

set nf [open out.nam w]

$ns namtrace-all $nf


#Define a 'finish' procedure

proc finish {} {

global ns nf

$ns flush-trace

#Close the NAM trace file

close $nf

#Execute NAM on the trace file

exec nam out.nam &

exit 0

}


#Create four nodes

set n0 [$ns node]

set n1 [$ns node]

set n2 [$ns node]

set n3 [$ns node]


#Create links between the nodes

$ns duplex-link $n0 $n2 2Mb 10ms DropTail

$ns duplex-link $n1 $n2 2Mb 10ms DropTail

$ns duplex-link $n2 $n3 1.7Mb 20ms DropTail


#Set Queue Size of link (n2-n3) to 10

$ns queue-limit $n2 $n3 10


#Give node position (for NAM)

$ns duplex-link-op $n0 $n2 orient right-down

$ns duplex-link-op $n1 $n2 orient right-up

$ns duplex-link-op $n2 $n3 orient right


#Monitor the queue for link (n2-n3). (for NAM)

$ns duplex-link-op $n2 $n3 queuePos 0.5



#Setup a TCP connection

set tcp [new Agent/TCP]

$tcp set class_ 2

$ns attach-agent $n0 $tcp

set sink [new Agent/TCPSink]

$ns attach-agent $n3 $sink

$ns connect $tcp $sink

$tcp set fid_ 1

#Setup a FTP over TCP connection

set ftp [new Application/FTP]

$ftp attach-agent $tcp

$ftp set type_ FTP



#Setup a UDP connection

set udp [new Agent/UDP]

$ns attach-agent $n1 $udp

set null [new Agent/Null]

$ns attach-agent $n3 $null

$ns connect $udp $null

$udp set fid_ 2


#Setup a CBR over UDP connection

set cbr [new Application/Traffic/CBR]

$cbr attach-agent $udp

$cbr set type_ CBR

$cbr set packet_size_ 1000

$cbr set rate_ 1mb

$cbr set random_ false



#Schedule events for the CBR and FTP agents

$ns at 0.1 "$cbr start"

$ns at 1.0 "$ftp start"

$ns at 4.0 "$ftp stop"

$ns at 4.5 "$cbr stop"


#Detach tcp and sink agents (not really necessary)

$ns at 4.5 "$ns detach-agent $n0 $tcp ; $ns detach-agent $n3 $sink"


#Call the finish procedure after 5 seconds of simulation time

$ns at 5.0 "finish"


#Print CBR packet size and interval

puts "CBR packet size = [$cbr set packet_size_]"

puts "CBR interval = [$cbr set interval_]"


#Run the simulation

$ns run






다음은 위 script에 대한 설명이다. 일반적으로 NS script는 Simulator object instance를 만들어서 시작한다.




○ set ns[new Simulator]:

NS simulator object instance를 생성하며, 변수 ns로 그것을 할당한다.(italics은 이 section에서 변수와 값으로 사용된다.) 이 라인의 역할은 다음과 같다.

-. packet format를 초기화(여기서는 이것을 무시)

-. scheduler를 생성(default는 calendar scheduler임)

-. default address format를 선택(여기서는 이것을 무시)


이 "Simulator" object는 다음의 일을 하는 member 함수를 가진다.


-. node와 link같은 복합 object를 생성(desired later)

-. 생성된 network 구성 object를 연결(ex. attach-agent)

-. network 구성 parameter를 set(mostly for compound object)

-. agent 사이의 연결 생성(ex. "tcp"와 "sink"사이의 연결을 만든다)

-. NAM display option을 규정

-. Etc.


member 함수의 대부분은 simulation setup(Overview section에서 plumbing 함수와 관련된)과 scheduling을 위해서 있다. 그러나, 그들 중 대부분은 NAM display를 위해서 있다. "Simulator" object member 함수 구현은 "ns-2/tcl/lib/ns-lib.tcl" file내에 위치해 있다.




○ $ns color fid color:

flow id(fid)에 의해 규정된 flow에 대한 packet의 color를 set 하는 것. 이 "Simulator" object의 member 함수는 NAM display를 위한 것이다. 그리고 실제적인 시뮬레이션 상에 어떤 효과도 미치지 않는다.




○ $ns namtrace-all file-descriptor:

이 member 함수는 NAM input format에 있는 simulation trace를 기록하기 위한 시뮬레이터를 말한다. 그것은 또한 trace가 $ns flush-trace 명령에 의해 나중에 쓰여질 file name을 준다.




○ proc finish {}:

이 시뮬레이션 뒤에 $ns at 5.0 "finish" 명령에 의해 호출된다. 이 함수에서 post-simulation process가 규정된다.




○ set n0 [$ns node]:

member 함수 node는 node를 생성한다. NS에 있는 node는 address와 port classifier들로 만들어진 복합 object이다.(다음 section에서 기술된다) 사용자는 주소와 port classifier object를 분리하여 생성하고, 그들을 함께 연결함으로써 node를 생성할 수 있다. 그러나, 이 Simulator object의 member 함수는 job를 보다 쉽게 만든다. node가 생성되는 방법을 보기 위해 file "ns-2/tcl/libs/ns-lib.tcl" 과 "ns-2/tcl/ns-node.tcl"을 살펴보라.




○ $ns duplex-link node1 node2 bandwidth delay queue-type:

상술된 bandwidth와 delay의 두 개의 simplex link를 생성하며, 두 개의 상술된 node를 연결한다. NS에서 node의 출력 queue는 link의 부분으로 구현된다. 그러므로, user는 link를 생성할 때 queue type를 상술해야 한다. 위의 시뮬레이션 script에서, DropTail queue가 사용된다. 만일 독자가 RED queue를 사용하기를 원한다면, 간단하게 DropTail을 RED로 대치하면 된다. link의 NS 구현은 다음 section에서 보여진다. node처럼, link도 compound object이며 user는 그것의 sub-object를 생성하고 그들과 node를 연결할 수 있다. Link source code는 "ns-2/tcl/libs/ns-lib.tcl"과 "ns-2/tcl/libs/ns-link.tcl" 파일에서 찾을 수 있다. 주목해야하는 하나는 당신이 lossy link(실제 user는 어떤 network object를 만들고 삽입할 수 있다.) 를 시뮬레이트 하기 위해서 link component 안에서 error module를 삽입할 수 있다. 이것을 어떻게 찾는가 하는 것은 NS document를 참고하라.




○ $ns queue-limit node1 node2 number:

이 라인은 상술된 숫자로 node1고 node2를 연결하는 두 개의 simplex link의 queue limit를 set한다. 이 점에서, 저자는 얼마나 많은 시뮬레이터 object의 member 함수의 종류들이 유용한지, 그리고 그것들이 무엇인지를 알지 못한다. "ns-2/tcl/libs/ns-lib.tcl"과 "ns-2/tcl/libs/ns-link.tcl"을 찾아보거나, 보다 자세한 정보를 찾으려면 NS document를 참고하라.




○ $ns duplex-link-op node1 node2...:

line의 다음 couple는 NAM display를 위해서 사용된다. 이 라인의 효과를 보기 위해서 user는 이 라인을 구성하고 시뮬레이션을 시도할 수 있다.


지금, 기본적인 network setup이 행해졌다. 다음에 해야할 일은 TCP와 UDP 같은 traffic agent, FTP와 CBR같은 traffic source를 setup하는 것이다. 그리고 그것들을 각각 node와 agent로 접속하는 것이다.




○ set tcp[new Agent/TCP]:

이 라인은 TCP agent를 생성하는 방법을 보여준다. 그러나, 일반적으로 user는 이런 방법으로 어떤 agent나 traffic source를 생성할 수 있다. Agent와 traffic source는 사실상 기본적인 object(not compound object)이며, 대부분 C++에서 구현되고 OTcl로 링크된다. 그러므로, 이러한 object instance를 생성하는 특수한 Simulator object member 함수는 없다. Agentsk traffic source를 생성하기 위해서, User는 object class 이름(Agent/TCP, Agent/TCPSink, Application/FTP 등등)을 알아야 한다. 이 정보는 NS document나 이 document에서 부분적으로 찾을 수 있다. 그러나, one shortcut는 "ns-2/tcl/libs/ns-default.tcl" file을 보는 것이다. 이 file은 가용한 network object에 대한 default 구성 parameter 값 setting을 포함한다. 그러므로, 그것은 어떤 종류의 network object가 NS에 유용한가 그리고 구성 parameter가 무엇인가에 대한 훌륭한 indicator로서 동작한다.




○ $ns attach-agent node agent:

attach-agent member 함수는 node object로 생성된 agent object를 접속한다. 실제적으로, 이 함수는 규정된 node의 attach member 함수를 호출한다. 그것은 그 자신으로 주어진 agent를 접속한다. 그러므로, user는 예를 들면, $n0 attach $tcp에 의해 동일한 일을 할 수 있다. 유사하게, 각 agent object는 그 자신으로 traffic source object를 접속하는 member 함수 attach-agent를 가진다.




○ $ns connect agent1 agent2:

서로간에 통신을 할 두 개의 agent가 생성된 후에, 다음 일은 그들 사이에 logical network 연결을 설정하는 것이다. 이 라인은 각기 다른 network와 port address pair로 목적지 address를 setting함으로서 network 연결을 설정한다.


network 구성이 모두 완료되었다고 가정하면, 다음에 해야할 일은 simulation 시나리오(시뮬레이션 scheduling 등)를 쓰는 일이다. Simulator object는 많은 scheduling member 함수를 갖는다. 그러나, 대부분 사용되는 방법은 다음과 같다.




○ $ns at time "string":

Simulator object의 member 함수는 주어진 simulation time에 규정된 string의 실행을 schedule하기 위해 scheduler(scheduler_ 는 script 시작 부분에서 [net scheduler] 명령에 의해 생성된 scheduler object를 지시하는 변수이다.)를 만든다. 예를 들어, $ns at 0.1 "$cbr start"는 data 전송을 위해 CBR을 시작하는 CBR traffic source object의 start member 함수를 호출하는 call를 만들 것이다. NS에서 보통 traffic source가 실제적인 data를 전송하지는 않지만 그것은 전송해야하는 data가 많으며, 전송할 data가 얼마나 많은지 알고 있다는 것을 기초적인 Agent에게 공지하고 packet을 생성하며 그것들을 전송한다.


모든 network 구성 후에, scheduling과 post-simulation 절차 specification이 행해진다. 남아있는 유일한 일은 시뮬레이션을 작동시키는 것이다. 이것은 $ns run으로 실행된다.

And

NS by Example - Network Components

|
사용자 삽입 이미지

Class Hierarchy (Partial)

 



1. Node and Routing

노드는 아래 그림과 같이 node entry object와 classifiers로 구성된 복합 object이다.
NS에는 두가지 타입의 노드가 있다.

[unicast node]
unicast routing과 port classifier의 역할을 하는 address classifier를 갖는다.

[multicast node]
unicast 패킷으로부터 multicast 패킷을 분류하고 classifier와 multicast routing을 수행하는 multicast classfier를 갖는다.

사용자 삽입 이미지

Node (Unicast and Multicast)


NS에서는 unicast node가 디폴트 노드이다. multicast node를 생성하기 위해 사용자는 OTcl 스크립트를 명확히 알아야 하며 scheduler object를 생성한후 모든 노드들은 multicast node로 생성되어야 한다. 노드 타입을 명확히 한후에 사용자는 디폴트로 사용하던것보다 구체적인 routing protocol을 선택할수 있다.

Unicast
- $ns rtproto [type]
- type : Static, Session, DV, cost, multi-path

Multicast
- $ns multicast (right after set $ns [new Scheduler])
- $ns mrtproto [type]
- type : CtrMcast, DM, ST, BST



2. Link

사용자가 simulator object의 멤버함수인 duplex-link를 사용하여 link를 생성하고자 할때, simplex links는 아래와 같이 생성된다.

사용자 삽입 이미지

Link


한가지 주의할 것은 노드의 output queue는 simplex link object의 일부분으로서 수행된다.
패킷은 link delay를 시뮬레이트하는 Delay object로 넘겨진 queue로부터 dequeue된다. 그리고 queue에서 drop된 패킷들은 Agent/Null로 보내지고 그곳에서 버려진다.
TTL object는 수신된 각 패킷을 위해 Time To Live 파라미터를 계산하고, 패킷의 TTL field를 업데이트한다.


Tracing

NS에서 네트워크의 활동은 simplex links 여기저기를 추적하는 일이다.
만약 시뮬레이터에서 네트워크의 행동을 추적하도록 지시되었다면($ns trace-all file or $ns namtrace-all file), links는 아래 그림에서 보듯이 그 명령이 trace object 다음에 온후에 생성된다. 사용자는 create-trace {type file src dst} 명령을 사용하여 주어진 source와 destination 노드간의 type 형태의 trace objects를 생성할수 있다.

사용자 삽입 이미지

Inserting Trace Objects


각 삽입된 trace object (i.e. EnqT, DeqT, DrpT and RecvT)가 패킷을 수신할때, 다른 어떤 시뮬레이션 시간 소비없이 명시된 trace file을 write하고, 패킷을 다음 네트워크 object에서 넘긴다.


Queue Monitor

근본적으로, tracing objects는 자신이 위치한곳에서 패킷의 도착시간을 기록하도록 고안되었다. 비록 사용자가 trace로부터 충분한 정보를 얻더라도, 누군가가 특정 output queue 내부에서 무엇이 진행중인지 알고싶어한다. 예를들어, RED queue행동에 관심있는 사용자가 평균 queue size의 dynamics(?)와 현재 특정 RED queue의 queue size를 측정하기 바랄수있다.
(즉, queue 모니터링이 필요하다) queue 모니터링은 다음그림과 같이 queue monitor objects와 snoop queue objects를 사용하여 이룰수 있다.

사용자 삽입 이미지

Monitoring Queue


패킷이 도착할때 snoop queue object는 이 상황을 queue monitor object에게 통보한다.
이러한 정보를 사용하는 queue monitor는 queue를 모니터한다.
snoop queue object는 그림에 나오진 않았지만, tracing object와 같이 사용할수 있다는걸 명심해라.



♡ Packet Flow Example

The network consist of two nodes (n0 and n1) of which the network addresses are 0 and 1 respectively. A TCP agent attached to n0 using port 0 communicates with a TCP sink object attached to n1 port 0. Finally, an FTP application (or traffic source) is attached to the TCP agent, asking to send some amount of data.

사용자 삽입 이미지

Packet Flow Example



Note that the above figure does not show the exact behavior of a FTP over TCP. It only shows the detailed internals of simulation network setup and a packet flow


And

802.11 MAC code in NS-2 (version 2.28)

|

[ Transport ]

Ns2 에서는 agent 개념으로 설명

패킷 생성, 전송, 수신 구현


1. TCP

지원 프로토콜 (교재 349P 참고/ 10.3)

1.1 이용 방법

  • 에이전트 생성
    • Set tcp [new Agent/TCP]         # 패킷 송신 에이전트를 변수 tcp로 지정
      _ Agent/TCP - a "tahoe" TCP sender
      _ Agent/TCP/Reno - a "Reno" TCP sender
      _ Agent/TCP/Newreno - Reno with a modification
      _ Agent/TCP/Sack1 - TCP with selective repeat (follows RFC2018)
      _ Agent/TCP/Vegas - TCP Vegas
      _ Agent/TCP/Fack - Reno TCP with "forward acknowledgment"
    • Set tcpsink [new Agent/TCPSink]    # 패킷 수신 에이전트를 변수 tcpsink로 지정
      _ Agent/TCPSink - TCP sink with one ACK per packet
      _ Agent/TCPSink/DelAck - TCP sink with configurable delay per ACK
      _ Agent/TCPSink/Sack1 - selective ACK sink (follows RFC2018)
      _ Agent/TCPSink/Sack1/DelAck - Sack1 with DelAck

  • 에이전트와 노드 결합
    • $ns attach-agent $n0 $tcp         #n0 노드에 tcp에이전트 결합
    • $ns attach-agent $n3 $sink        #n3 노드에 sink에이전트 결합

  • 에이전트끼리 연결
    • $ns connect $tcp $sink            #tcp 에이전트와 sink에이전트 연결

* Agent/TCP 와 Agent/TCPSink 한 쌍으로 설정 해야 함(교재 : 373p)


1.2 설정

[방법 1: simple configuration]

  • 에이전트 설정

set ns [new Simulator]

set node1 [$ns node]

set node2 [$ns node]


set tcp1 [$ns create-connection TCP $node1 TCPSink $node2 42]

$tcp set window_ 50


set ftp1 [new Application/FTP]

$ftp1 attach-agent $tcp1


$ns at 0.0 "$ftp start"

* set tcp1 [$ns create-connection TCP $node1 TCPSink $node2 42]

  1. create-connection 함수의 의미
  • TCP : 송신 에이젼트
  • $node1 : 송신 노드 (souce node)
  • TCPSink : 수신 에이젼트
  • $node2 : 수신 노드
  • 42 : flow ID

  • create-connection함수의 동작 순서
    • 두 에이전트 생성, 에이전트의 flow ID필드 설정
    • 송신 에이전트와 수신 에이전트를 알맞은 노드에 결합 시킨다
    • 에이전트 끼리 연결 한다.(송수신의 각 포트, 주소 설정)
    • create-connection 함수는 최종 결과값을 송신 에이전트가 생성될 때 사용한 이름인 tcp1로 반환 한다.
  • TDP 데이터 소스(Tcp Data Source)

    -데이터 생성모듈 ex) FTP, Telnet


[방법 2: 파라미터 직접 작성]

파라미터 (변수)

클래스 변수 : 전체 에이전트 값 변함 (ex: Agent/TCP set window_ 100)

인스턴스변수 : 해당 에이전트만 변함 (ex : $tcp set window_ 2.0 )

Agent/TCP set window_ 20 ;                # max bound on window size
Agent/TCP set windowInit_ 1 ;            # initial/reset value of cwnd
Agent/TCP set windowOption_ 1 ;            # cong avoid algorithm (1: standard)
Agent/TCP set windowConstant_ 4 ;            # used only when windowOption != 1
Agent/TCP set windowThresh_ 0.002 ;        # used in computing averaged window
Agent/TCP set overhead_ 0 ;            # !=0 adds random time between sends
Agent/TCP set ecn_ 0 ;                # TCP should react to ecn bit
Agent/TCP set packetSize_ 1000 ;           #
packet size used by sender (bytes)
Agent/TCP set bugFix_ true ;            # see explanation
Agent/TCP set slow_start_restart_ true ;   #
see explanation

Agent/TCP set tcpTick_ 0.1 ;        #
timer granulatiry in sec (.1 is NONSTANDARD)
Agent/TCP set maxrto_ 64 ;                # bound on RTO (seconds)
Agent/TCP set dupacks_ 0 ;                # duplicate ACK counter
Agent/TCP set ack_ 0 ;                #
highest ACK received

Agent/TCP set cwnd_ 0 ;                #
congestion window (packets)
Agent/TCP set awnd_ 0 ;                # averaged cwnd (experimental)
Agent/TCP set ssthresh_ 0 ;            # slow-stat threshold (packets)
Agent/TCP set rtt_ 0 ;                # rtt sample
Agent/TCP set srtt_ 0 ;                # smoothed (averaged) rtt
Agent/TCP set rttvar_ 0 ;                #
mean deviation of rtt samples
Agent/TCP set backoff_ 0 ;                # current RTO backoff factor
Agent/TCP set maxseq_ 0 ;                # max (packet) seq number sent


# set up TCP-level connections

$sink listen ;                # will figure out who its peer is

$src set window_ 100;

The creation of the FullTcp agent is similar to the other agents, but the sink is placed in a listening state by the listen method. Because a handle to the receiving side is required in order to make this call, the create-connection call used above cannot be used.


TCP 연결 예제 TCL

set tcp [new Agent/TCP] ;                         # create tcp agent
$ns_ attach-agent $node_(s1) $tcp ;           # bind src to node
$tcp set fid_ 0 ;                                         # set flow ID field
set ftp [new Application/FTP] ;                  # create ftp traffic
$ftp attach-agent $tcp ;                             # bind ftp traffic to tcp agent
set sink [new Agent/TCPSink] ;                # create tcpsink agent
$ns_ attach-agent $node_(k1) $sink ;         # bind sink to node
$sink set fid_ 0 ;                                       # set flow ID field
$ns_ connect $ftp $sink ;                          # active connection src to sink
$ns_ at $start-time "$ftp start" ;                  # start ftp flow

2. UDP

Agent/UDP와 agent/NULL을 한 쌍으로 함


[ Application ]

패킷 보내기

파라미터 정보 ./ns/tcl/lib/ns-default.tcl

CBR의 정보 : 교재 413p
(교재 : 배성수, "네트워크 시뮬레이터", 세화,2005)

1. Exponential

Exponential On/Off An Exponential On/Off object is embodied in the OTcl class Application/Traffic/Exponential. The

member variables that parameterize this object are:


packetSize_         the constant size of the packets generated
burst_time_         the average "on" time for the generator
idle_time_         the average "off" time for the generator
rate_             the sending rate during "on" times

Hence a new Exponential On/Off traffic generator can be created and parameterized as follows:

set e [new Application/Traffic/Exponential]
$e set packetSize_ 210
$e set burst_time_ 500ms
$e set idle_time_ 500ms
$e set rate_ 100k

NOTE: The Exponential On/Off generator can be configured to behave as a Poisson process by setting the variable burst_time_ to 0 and the variable rate_ to a very large value. The C++ code guarantees that even if the burst time is zero, at least one packet is sent. Additionally, the next interarrival time is the sum of the assumed packet transmission time (governed by the variable rate_) and the random variate corresponding to idle_time_. Therefore, to make the first term in the sum very small, make the burst rate very large so that the transmission time is negligible compared to the typical idle times.



2. CBR

A CBR object is embodied in the OTcl class Application/Traffic/CBR. The member variables that parameterize this

object are:

rate_         the sending rate
interval_     (Optional) interval between packets
packetSize_     the constant size of the packets generated
random_     flag indicating whether or not to introduce random "noise" in the scheduled departure times (default is off)
maxpkts_     the maximum number of packets to send (default is (228)

Hence a new CBR traffic generator can be created and parameterized as follows:

set e [new Application/Traffic/CBR]
$e set packetSize_ 48
$e set rate_ 64Kb
$e set random_ 1

The setting of a CBR object's rate_ and interval_ are mutually exclusive (the interval between packets is maintained as an interval variable in C++, and some example ns scripts specify an interval rather than a rate). In a simulation, either a rate or an interval (but not both) should be specified for a CBR object.



-예제 ?

set src [new Agent/UDP]
set sink [new Agent/UDP]
$ns_ attach-agent $node_(s1) $src
$ns_ attach-agent $node_(k1) $sink
$ns_ connect $src $sink

set e [new Application/Traffic/Exponential]
$e attach-agent $src
$e set packetSize_ 210
$e set burst_time_ 500ms
$e set idle_time_ 500ms
$e set rate_ 100k
$ns_ at 0.0 "$e start"



[Tips]


tr파일로 그래프 그리기

  raw2xg ?a xxx.tr > xxx.out

  xgraph xxx.out


nam. Tr. 파일 일괄 삭제 하는 스크립트 : Makefile 로 저장하기

[code type=c]

  default: clean

  clean:

      rm -f *~ *.tr *.nam

[/code]

정리 : 2006.12.11 by 임헌정
http://www.4ellene.net

And

802.11 MAC code in NS-2 (version 2.28)

|

802.11 MAC code in NS-2 (version 2.28)
by Joshua Robinson (jpr at rice.edu)
Last update: 4-29-05

Note that this document is no longer maintained, as I have stopped work on NS-2 as of 2005. Please do alert me if you find any errors or more up-to-date descriptions of the code.


There are 4 different paths the code can follow:

Transmitting a packet

Receiving a packet destined for itself

Overhearing a packet not destined for itself

Packets colliding


Or just jump to the listing of functions

or a list of the timers


Transmitting a packet

Roughly takes the following path (when no errors or congestion):

recv() -> send() -> sendDATA() and sendRTS() -> start defer timer

-> deferHandler() -> check_pktRTS() -> transmit()

-> recv() -> receive timer started

-> recv_timer() -> recvCTS() -> tx_resume() -> start defer timer -> rx_resume()

-> deferHandler() -> check_pktTx() -> transmit()

-> recv() -> receive timer started

-> recv_timer() -> recvACK() -> tx_resume() -> callback_ -> rx_resume() -> done!


When the first RTS fails:

recv() -> send() -> sendDATA() and sendRTS() -> start defer timer

-> deferHandler() -> check_pktRTS() -> transmit -> start send timer

-> send_timer() -> RetransmitRTS() -> tx_resume() -> backoff timer started

backoffHandler() -> check_pktRTS() -> transmit

the rest is the same as above


Receiving a packet

Roughly takes the following path (when no errors or congestion):

recv() -> receive timer started

-> recv_timer() -> recvRTS() -> sendCTS() -> tx_resume() -> start defer timer -> rx_resume()

-> deferHandler() -> check_pktCTRL() -> transmit()

-> recv() -> receive timer started

-> recv_timer() -> recvDATA() -> sendACK() -> tx_resume() -> start defer timer -> uptarget_->recv()

-> deferHandler() -> check_pktCTRL() -> transmit() -> start send timer

-> send_timer() -> tx_resume() <- nothing happens, we're done!


Functions

recv() (DOWN) - Like all connectors, which Mac inherits from, the packet to be sent is received by the recv() function. Because the recv() function is also called when a packet comes from the channel, recv() checks the direction field in the packet header. If the direction is DOWN, meaning the packet came from an upper layer, the packet is then passed on to the send() function.

recv() (UP) – The recv() function is called whenever a packet is received from either an upper or lower layer. If the packet is received from a lower layer – the network interface here, then the first check will be skipped. At this point the phy has just received the first bit of the incoming packet, but the MAC can't do anything with the packet until the whole packet is received. If the packet is received while the MAC is currently transmitting another packet, then the received packet would be ignored – meaning the error flag in the packet's header is set. If the MAC is not currently receiving any packets, then the rx_state_ is changed to RECV and checkBackoffTimer is called. Afterwards, the incoming packet is assigned to pktRx_ and the receive timer is started for the txtime() of the packet. If the MAC was already receiving a packet when this packet arrived, it will compare the received power of the new packet with the old packet. If the power of the new packet is smaller than the old packet by at least the capture threshold, the new packet will be ignored (captured) and the capture() function is called. If the power levels of the two packets are too close though, there will be a collision and control will transfer to collision(), which will drop the arriving packet. The original packet won't be dropped until it's reception is complete. Control will return to the MAC whenever the receive timer expires, calling recvHandler(), which in turns goes right to recv_timer().

send() - The send() function first checks the energy model, dropping the packet if the node is currently in sleep mode. It then sets callback_ to the handler passed along with the packet. This is so the handler can be called when the packet's transmission is complete. Next, send() calls sendDATA() and sendRTS which build the MAC header for the data packet and the RTS packet to go along with the data packet – which are stored in pktTx_ and pktRTS_ respectively. The MAC header for the data packet is then assigned a unique sequence number (with respect to the node).
Next, the MAC checks it's backoff timer. If the backoff timer is not currently counting down, then the node checks if the channel (medium) is idle, and if so the node will begin to defer. The node checks this using the function is_idle(). As per the 802.11 specs, the node will defer a difs time plus a randomly chosen amount of time in the interval [0, cw_), where cw_ is the current contention window. If the node is already waiting on it's defer timer, it will just continue waiting (not resetting the timer). If the medium is detected to be busy, then the node starts it's backoff timer. As of this point, the send() function has finished and control will resume when one of the timers expires, calling either deferHandler() or backoffHandler().

sendDATA() - This function builds the MAC header for the data packet. This involves increasing the size of the packet, setting the type as data, and subtype as data. The packet should now have a complete MAC header attached to it. The function then stores the txtime of the packet, which is computed by the txtime() function. By txtime, we basically mean the size of the packet multiplied by the Data rate. You'll notice (in 2.28 at least), that this calculation is done twice – this first time is just a waste. It's calculated again because a different value for the data rate is used if the packet happens to be a broadcast packet. Also, if the packet is not a broadcast packet, the duration field in the MAC header is computed. By duration, we mean the amount of time this communication still needs the channel after the data packet has been transmitted. For the case of a data packet, this corresponds to the amount of time to transmit an ACK plus a short inter-frame spacing. If the packet happens to be broadcast, this field is set to zero (no ACKs for broadcast packets). Now, the MAC has finished building the MAC header for the packet and finally assigns the internal variable pktTx_ to point to the packet we've been working on. This is essentially a way of storing the packet to be transmitted in a local buffer in the MAC. Now, the code returns to the send() function.

sendRTS() – This function is in charge of creating an RTS packet with the specified destination in conjunction with the data packet the MAC is trying to send. The first thing it does is check the size of the packet against the RTSThreshold. If the packet is smaller (or is broadcast) then no RTS is sent before the data is transmitted (the RTS/CTS mechanism is not used). In this case, the function simply returns control back to the send() function. Otherwise, a brand new packet is created (actually done in the first line of the function) and it's fields are set appropriately, i.e. the type is set as a MAC packet. A rts_frame structure is used to fill in the rest of the packet header and the appropriate values are put in the rts fields. The destination field is filled in with the parameter passed to the function and the rf_ta (source?) is filled in with the MAC's address. The duration field is also calculated as the time to transmit a CTS, the data packet (pktTx_) and an ACK (plus 3 sifs). After the RTS has been constructed, the internal state variable pktRTS_ is assigned a pointer to the new RTS. After this, control is returned to the send() function.

sendCTS() - This function is in charge of creating a CTS packet and pointing pktCTRL_ to it. Everything proceeds straightforwardly, with fields being given obvious values. The duration field is set to be the same as was in the RTS, except minus the txtime of a CTS and a sifs_ time, since that amount of time would have already elapsed once another station decoded the packet. After the creation of the CTS packet is done, pktCTRL_ is pointed to the new packet and control returns to recvRTS().

sendACK() - This function is responsible for creating an ACK packet to be sent in response to a data packet. The packet is created and all the fields are filled in with the obvious values. The duration field is set to zero indicating to other nodes that once this ACK has completed, they don't need to defer to another communication. Once the packet has been successfully built, pktCTRL_ is pointed to the new ACK and control returns to recvDATA().

deferHandler() - This function is called when the defer timer has expired. When this happens, this means the node has waited enough time before transmission to lessen the chance of collision and will now attempt to transmit a packet. Accordingly, the first thing the function does is assert that there is either a control, RTS, or data packet waiting to be transmitted. The function then calls check_pktCTRL(), and then makes sure the backoff timer is not currently running. Afterwards, it calls check_pktRTS() and check_pktTx(). If any of these check_ functions returns a value of zero, the defer handler stops, as this indicates that the check_ function has succeeded in transmitting that particular kind of packet. Therefore, the actual packet transmission is handled by one of these check_ functions. At this point, transmission has most likely begun on some kind of packet and control will resume at the expiration of the interface timer, txHandler(), which simply clears the flag tx_active_ to indicate that the phy is not currently transmitting something. Control will resume if another packet is received via recv(): a CTS if an RTS was just sent, a data packet if a CTS was just sent, or an ACK if a data packet was just sent. But control may also resume at the expiration of the send timer, sendHandler(), which immediately calls send_timer().

check_pktCTRL() - This function is responsible for transmitting CTS and ACK packets, which would be pointed to by pktCTRL_ . So the first thing the function does is check to see if this variable points to anything. If not, the function returns -1, indicating nothing was transmitted. The function will also return if the transmission state (tx_state_) indicates the MAC is currently transmitting either a CTS or ACK packet, although I don't know why this would occur. The function then performs a switch based on what kind of control packet is ready to be sent, CTS or ACK.
If it's a CTS, the MAC will check the status of the medium using is_idle(). If the channel is busy, the CTS will simply be dropped and pktCTRL_ set to zero. If the channel is idle, the function will, using macros, set the value of the tx_state_ to indicate the MAC is currently transmitting a CTS and then call the function checkBackoffTimer(). After this, the function calculates the timeout value – which is how long the MAC should wait before it decides the packet it sent wasn't received successfully. In the case that the control packet is an ACK, the MAC proceeds in the same way, except that it doesn't check the medium. Finally, the function transmit() is called with arguments pktCTRL_ and the previously calculated timeout value. At this point, the phy has just begun transmission of the control packet.

check_pktRTS() - This function, like the other two check_ functions, is responsible for transmitting a packet – in this case, an RTS packet. If there is no RTS packet ready to send, i.e. pktRTS_ is null, then the function simply returns with a value of -1, indicating that it did not send a packet. There is an oddly placed switch statement here presumably in order to detect an improperly built RTS packet. Before the RTS is sent, the channel is checked. If it is sensed to be busy, the contention window (cw_) is doubled using the inline function inc_cw() and the backoff timer is started again. The function therefore returns without transmitting a packet if the channel is busy. If the channel is idle, the tx_state_ of the MAC is set to RTS and the function checkBackoffTimer() is invoked. Next, the timeout value is calculated so that the MAC will know how long to wait for a CTS to be returned. Finally the function transmit() is called with arguments of the RTS packet and timeout value. At this point, the phy has begun transmission of the RTS packet.

check_pktTx() - This function, like the other two check_ functions, is responsible for transmitting a packet – in this case, the actual data packet. If there is no data packet waiting to be sent (pktTx_ is null), then the function returns with a value of -1, indicating that nothing was transmitted. Again, an oddly chosen switch statement is used to catch an improperly built data packet. If the channel is sensed to be busy, sendRTS is called. This means that despite the RTS/CTS exchange, another node is using the channel (possibly due to mobility), or RTS is not being used – in which case the sendRTS function will do nothing. Additionally, the contention window (cw_) is doubled using the inline function inc_cw() and then the backoff timer is started so that the MAC will remain idle until the other node has completed transmission. If the channel is idle, the tx_state_ is set to MAC_SEND and the checkBackoffTimer function is invoked. The timeout value is calculated in two ways, depending on whether or not the data packet is broadcast. If not, the timeout is how long the MAC should wait before it decides an ACK wasn't received. If the packet is broadcast, the timeout is simply the transmission time of the packet because no ACKs will be sent in conjunction with a broadcast packet. Finally, the function transmit() is invoked with arguments of the data packet and the calculated timeout value. At this point, the data packet has begun transmission.

checkBackoffTimer() - This inline function performs two checks. First, if the medium is idle and the backoff timer is currently paused, it will resume the timer.
The second check is if the medium is not idle and the backoff timer is currently running (busy and not paused), then it will pause the timer. This corresponds to the fact that the MAC only counts down it's backoff timer while the channel is idle. As per the specs, the timer should not be running while the channel is being used by another node.

transmit() – This function takes two arguments, a packet and a timeout value. It sets a flag variable, tx_active_, to one to indicate that the MAC is currently transmitting a packet. The function then performs a check because if it is an ACK being transmitted then it is possible that the node could be receiving a packet, in which case that packet would be missed. This next block checks if the MAC is currently receiving a packet and that it is an ACK being transmitted, and if so, marks the packet being received as having errors. Next, the packet is actually passed down to the network interface (WirelessPhy class) which is pointed to by downtarget_. Actually, only a copy of the packet is sent down in case there needs to be a retransmission. Finally, two timers are started – the send timer is started with the timeout value, which will alert the MAC that the transmission probably failed. Also, the interface timer(mhIF_) is started with the txtime() of the packet – when this timer expires, the MAC will know that the phy has completed the transmission of the packet.

send_timer() - This function is called at the expiration of the TxTimer, mhSend_. This timer expires after amount of time calculated as timeout in the corresponding check_ function – the expiration of this timer means slightly different things depending on which kind of packet was sent. In a switch statement, the MAC checks the value of tx_state_ to find out the kind of packet that was most recently sent and then handles each packet differently. If the last packet sent was an RTS, the expiration of the timer means a CTS wasn't received, presumably because the RTS collided or the receiving node is deferring. The MAC responds by attempting to retransmit the RTS in the function RetransmitRTS().
If the last packet sent was a CTS packet, the expiration of the timer means that no data packet was received. This is an infrequent event occurring if the CTS packet collided or if the data packet was in error. The MAC handles this by simply resetting itself to an idle state. This involves freeing the CTS packet stored in pktCTRL_.
If the last packet sent was a data packet, the expiration of the timer means that an ACK was not received. The MAC handles this situation by calling RetransmitDATA().
Finally, if the last packet sent was an ACK, the expiration of the timer simply means that the ACK has been transmitted, as no response is expected from an ACK. The MAC frees the ACK packet pointed to by pktCTRL_ .
After each case has been handled and a packet has possibly been prepared for retransmission, the function tx_resume() is given control. If a packet is going to be retransmitted, the backoff timer has already been started with an increased contention window.

RetransmitRTS() - This function is called in response to a CTS not being received after an RTS was sent. First, the function does some stat collecting, recording this as a failed RTS, and the short retry count (ssrc_) is incremented. The short retry count is maintained so the MAC knows when to give up on this packet and drop it, which happens when ssrc_ reaches the value of ShortRetryLimit in the MAC MIB. The drop is handled by called the discard() function on the RTS packet and resetting the pktRTS_ pointer to zero. Then the data packet is also dropped by calling the same discard() function. The ssrc_ is reset to zero and the contention window is reset to it's initial value. Otherwise, the same RTS pointed to by pktRTS_ is kept, but a retry field in the RTS is incremented. Because of the contention avoidance mechanism, the contention window is doubled and then the backoff timer is started using this new contention window. This means control will eventually return to backoffHandler().

RetransmitDATA() - This function is called when an ACK is not received in response to a data packet being sent. If the data packet was a broadcast packet, an ACK shouldn't be expected and so the data packet is treated as being successfully transmitted and so is freed and the congestion window reset. The backoff counter is started though, I'm not really sure why. Two separate retry counts are maintained depending on whether or not an RTS is being used for this data packet. If an RTS is not being used, the short retry limit is used, otherwise the long retry limit is used as a threshold. If the retry count has exceeded the threshold, then the data packet is discarded using the discard() function and the retry count and congestion window are reset. If the retry count has not been exceeded, the data packet is prepared for retransmission by incrementing a retry field in the mac header, doubling the congestion window, and then starting the backoff timer. This means control will eventually return to backoffHandler().

tx_resume() - This function is called when the MAC is getting ready to send a packet but needs to set some timers. If a control packet (CTS or ACK) is waiting to be sent, this function simply starts the defer time for a sifs_ amount of time. This is because a node is supposed to wait a brief period of time before transmitting. If an RTS packet is waiting to be sent, then the MAC makes sure the backoff timer isn't currently busy – if it is, then the MAC will wait to start the defer timer. If the backoff timer isn't busy the defer timer is started for a random time in the interval [0,cw_) plus a difs_ time. If a data packet is next to be sent, and MAC isn't currently backing off, then the defer timer is started for the data packet. If an RTS wasn't used for this packet, then the defer timer is set for a random value in the interval [0,cw_] plus a difs_ time, but if an RTS was used, the MAC will only defer for a sifs_ time. This is because if an RTS was used, then the channel has already been reserved for this MAC and it shouldn't need to worry about collisions.
If there are no packets waiting to be sent, but the callback_ is defined, then it is handled, corresponding to a successfully completed packet transmission. Finally, the tx_state_ is set to idle. Control will return back to the MAC when the defer timer has expired, deferHandler() - or back to the function that called it, like one of the recvP functions.

capture() - This function is called when a second packet is received while the MAC is currently receiving another packet, but the second packet is weak enough so that the phy can ignore it. The important thing this function does is update the NAV so that carrier sense will know that the channel is still busy after it has finished receiving it's packet. Capture also discards the captured packet.

collision() - The collision handler first checks the rx_state_ variable and sets it to MAC_COLL in case this is the first collision during the current packet. If a third packet collides, rx_state_ will already be MAC_COLL. Then, the MAC calculates how much longer the new packet will last and how much longer the old packet will last. If the new packet will last longer, then the MAC makes the new packet pktRx_ and resets the receive timer, mhRecv_. In this case the old packet is discarded here, but if the old packet will last longer then the new packet is simply discarded and pktTx_ doesn't change. So at the end of this function, the colliding packet that would have completed first has been discarded and rx_state_ is set to MAC_COLL.

recv_timer() - This is the receive timer handler, called when mhRecv_ expires (though indirectly though RecvHandler). The expiration of the receive timer means that a packet has been fully received and can now be acted upon. First, the MAC checks to see if it's currently transmitting a packet by checking the flag, tx_active_. If so, the MAC wouldn't not have even heard the packet so it is just discarded (without updating NAV). Next, the rx_state_ is checked to see if there was a collision during this packet, i.e. rx_state_ equals MAC_COLL. If so, then pktRx_ is the colliding packet that lasted longest and now needs to be discarded. The NAV is also set for an eifs_ time, which is the amount of time the MAC must wait after a collision. The MAC then checks the packet for errors, and discards the packet if any were detected. Again, the NAV is set for eifs_ time after the error packet is finished being received. The next check the MAC performs is if the packet is actually destined for itself – if not, the MAC updates the NAV for the value in the duration field in the MAC header (not necessary just the txtime of the packet). This is of course so that the MAC doesn't attempt to transmit while other nodes are using the channel. The next check consists of sending the packet to any taps if it is a data packet – essentially sending the packet to anybody wanting to listen in promiscuous mode. The next check involves the adaptive fidelity algorithm and basically keeping track of the nodes within radio range of the node. And finally, the last check performed is address filtering, where all packets that are not destined for the current node are discarded. The NAV would have already been updated so there's no need to do anything else with the packet.
Now the MAC decides what to do based on what kind of packet it just received. If the packet is of MAC_Type_Management, it's simply dropped. If it's an RTS packet, recvRTS() is called, if CTS or ACK, then recvCTS() or recvACK() is called. And not surprisingly, if it's a data packet, then recvDATA() is called. After this, pktRx_ is set to zero and control to given to rx_resume().

recvRTS() - This function is called by recv_timer after a full RTS packet has been received. If the tx_state_ is not idle, then the packet wouldn't have been heard, so it's simply discarded. Also, if the MAC is currently responding to another node (pktCTRL_ is nonzero) then the RTS will be ignored. This happens for example, if the node already heard an RTS from another node is waiting to send a CTS back. Otherwise, the MAC is in a state such that it can receive a packet, so it prepares to send a CTS by calling sendCTS(). Next, the MAC stops the defer time and calls tx_resume() - which will restart the defer timer for the appropriate amount of time. Control then returns to recv_timer().

recvCTS() - This function is called by the recv_timer after a full CTS packet has been received, meaning the MAC can now send it's data. Since the MAC has no use for the RTS packet it just transmitted, it's freed and pktRTS_ is set to zero. The send timer is stopped, although I'm not exactly sure why it would be running. Control then goes straight to tx_resume(), which sets the defer timer, and then control finally returns back to recv_timer().

recvACK() - This function is called by the recv_timer after a full ACK packet has been received, indicating a successful data transmission. First, the MAC checks that it really did just sent a data packet (tx_state == MAC_SEND) and discards the ACK if it didn't. The MAC now knows that it just succesfully transmitted it's data packet, so it frees pktTx_ and sets it to zero. The send timer is also stopped, but again, I don't know why. The MAC then resets the appropriate retry count, short if an RTS wasn't used, long if it was. Also, the congestion window is reset and the MAC starts its backoff timer so it won't just immediately send again. Control then goes to tx_resume() and then back to recv_timer(). In tx_resume(), since there are no packets ready to send, the callback will be invoked, effectively telling the interface queue to send down another packet for transmission.

recvDATA() - This function is called by the recv_timer after a full data packet has been received, indicating that this node just successfully received a data packet. First, the MAC strips the MAC header from the packet, getting it ready to be sent to the upper layers. If the data packet wasn't broadcast, RTS packets are being used, and tx_state_ indicates that the last packet the MAC sent was a CTS, then that CTS (pktCTRL_) is cleaned up (freed and pktCTRL_ set to zero). And again, the send timer is stopped. If the MAC didn't just send a CTS when it should have, the data packet is dropped because events didn't happen in the right order and the function returns. Otherwise, the data packet was received correctly and the MAC prepares to send an ACK by calling sendACK() and then tx_resume() to start the defer timer appropriately. If a CTS was not sent (because there was no corresponding RTS), then the MAC checks pktCTRL_. If there is a control packet there, the MAC will drop the data packet because there is no room to buffer an ACK packet (the ACK would go in pktCTRL_). Otherwise, sendACK() is called to create an ACK packet to send. In this case, if the send timer isn't currently counting down, tx_resume() is called to start the defer timer.
Next, the MAC updates it's sequence number cache – if the packet is unicast only. The packet is checked to make sure the source node will fit in the cache – it is possible for the cache to have been configured with an incorrect size, i.e. less than the total number of nodes in the system. Then the sequence number of the packet just received is compared with the most recently received sequence number and if they match, the data packet is discarded as it is a duplicate (same packet received twice). If the source node is not in the cache (cache is too small), some warnings are printed out.
The data packet is then passed to the uptarget_ - the layer above the MAC (usually link-layer). This means the data packet has been fully received by the node and it's on it's way up the protocol stack.

rx_resume() - This simple function is called after recv_timer has completed. All it does is set the rx_state_ to idle and then invoke checkBackoffTimer().

backoffHandler() - This function is called whenever the backoff timer expires. This function first checks to see whether there is a control packet (CTS or ACK) waiting to be sent. If so, it makes sure that the MAC is either sending the packet or deferring before sending the packet. If there was no control packet, check_pktRTS() is called. If there was no RTS packet, then check_pktTx() is called. This means, that at the expiration of the backoff timer, an RTS or a data packet will be transmitted if either is waiting. I think this should only happen on RTS or data retransmissions.

txHandler() - Handler for IFTimer that simply clears a flag in the MAC to indicate that the radio is no longer active.

command() - The command() function is a TCL hook for all the classes in ns-2 which allows C++ functions to be called from a TCL script. The arguments for command() are effectively the same as for the main function is basic C programs, with argc and argv containing the command given to the object. In the 802.11 MAC, this function is not often used, at least in my experience. If none of the commands match those specific to the 802.11 MAC, then the command() function of the parent class is called.



Timers
The timers are defined in the files mac/mac-timers.h/cc while the handlers (functions called when the timer expires) are in mac-802_11.cc.

IFTimer – The interface timer keeps tracks of how long the interface will be in transmit mode. This is only the time when the interface is actively transmitting bits into the air. The handler for this timer is txHandler(). Probably the simplest timer used by the MAC layer.

NavTimer &ndash Started at the reception of a packet for the length of time indicated in the duration field of the MAC header. Calls navHandler() on expiration.

RxTimer &ndash Started when the first bit of a packet is received and set for the length of time the packet will require to be completely received. This timer is needed because in simulation the entire packet is available as soon as the first bit arrives, but the MAC should not access the packet until it would have been completely received in reality. In the case of a packet collision, the receive timer is reset to expire at the end of the last colliding packet. The timer indirectly calls recv_timer() on expiration by calling recvHandler() first.

TxTimer &ndash Indicates the time by which an ACK/CTS should have been received. The TxTimer (mhSend_) is started when a packet is transmitted by the transmit() function. Each type of packet has an expected response, for example, an RTS packet expects a CTS packet to follow. The timer is therefore stopped when a CTS, data, or ACK packet is received. The timer is not started on transmission of an ACK packet as there is no response expected. On expiration, send_timer() is called indirectly by first calling the (ahem, worthless) function sendHandler().

DeferTimer &ndash

BackoffTimer &ndash

BeaconTimer &ndash Not used.


And
prev | 1 | ··· | 3 | 4 | 5 | 6 | next