LAB 5 Adding New Components • High level scripting in otcl • Linking otcl and C++ • Low level in C++ NS2Work 1 Adding New Component using otcl • Additional <new_stuff>.tcl file • source <new_stuff>.tcl • Adding new files – change Makefile (NS_TCL_LIB), tcl/lib/nslib.tcl – recompile NS2Work 2 Example: Agent/Message C n2 cross traffic n0 128Kb, 50ms 10Mb, 1ms S n4 C n5 R n1 10Mb, 1ms n3 msg agent http://nslab.ee.ntu.edu.tw/courses/ns-tutorial/ftw-tutorial.html NS2Work 3 Agent/Message Receiver-side processing string message S • • • • R A UDP agent (without UDP header) Up to 64 bytes user message Good for fast prototyping a simple idea Usage requires extending ns functionality NS2Work 4 Agent/Message: Step 1 • Define sender class Sender –superclass Agent/Message # Message format: “Addr Op SeqNo” Sender instproc send-next {} { $self instvar seq_ agent_addr_ $self send “$agent_addr_ send $seq_” incr seq_ global ns $ns at [expr [$ns now]+0.1] "$self send-next" } NS2Work 5 Agent/Message: Step 2 • Define sender packet processing Sender instproc recv msg { $self instvar agent_addr_ set sdr [lindex $msg 0] set seq [lindex $msg 2] puts "Sender gets ack $seq from $sdr" } NS2Work 6 Agent/Message: Step 3 • Define receiver packet processing Class Receiver –superclass Agent/Message Receiver instproc recv msg { $self instvar agent_addr_ set sdr [lindex $msg 0] set seq [lindex $msg 2] puts “Receiver gets seq $seq from $sdr” $self send “$addr_ ack $seq” } NS2Work 7 Agent/Message: Step 4 • Scheduler and tracing # Create scheduler set ns [new Simulator] # Turn on Tracing set fd [new “message.nam” w] $ns namtrace-all $fd NS2Work 8 Agent/Message: Step 5 • Topology for {set i 0} {$i < 6} {incr i} { set n($i) [$ns node] } $ns duplex-link $n(0) $n(1) 128kb 50ms DropTail $ns duplex-link $n(1) $n(4) 10Mb 1ms DropTail $ns duplex-link $n(1) $n(5) 10Mb 1ms DropTail $ns duplex-link $n(0) $n(2) 10Mb 1ms DropTail $ns duplex-link $n(0) $n(3) 10Mb 1ms DropTail $ns queue-limit $n(0) $n(1) 5 $ns queue-limit $n(1) $n(0) 5 NS2Work 9 Agent/Message: Step 6 • Routing # Packet loss produced by queueing # Routing protocol: let’s run distance vector $ns rtproto DV NS2Work 10 Agent/Message: Step 7 • Cross traffic set $ns set $ns $ns udp0 [new Agent/UDP] attach-agent $n(2) $udp0 null0 [new Agent/NULL] attach-agent $n(4) $null0 connect $udp0 $null0 set exp0 [new Application/Traffic/Exponential] $exp0 set rate_ 128k $exp0 attach-agent $udp0 $ns at 1.0 “$exp0 start” NS2Work 11 Agent/Message: Step 8 • Message agents set sdr [new Sender] $sdr set packetSize_ 1000 set rcvr [new Receiver] $rcvr set packetSize_ 40 $ns $ns $ns $ns $ns attach $n(3) $sdr attach $n(5) $rcvr connect $sdr $rcvr connect $rcvr $sdr at 1.1 “$sdr send-next” NS2Work 12 Agent/Message: Step 9 • End-of-simulation wrapper (as usual) $ns at 2.0 finish proc finish {} { global ns fd $ns flush-trace close $fd exit 0 } NS2Work 13 Agent/Message: Result • Example output > ./ns msg.tcl Receiver gets seq Sender gets ack 0 Receiver gets seq Sender gets ack 1 Receiver gets seq Sender gets ack 2 Receiver gets seq Sender gets ack 3 Receiver gets seq Sender gets ack 4 Receiver gets seq 0 from from 1 1 from from 1 2 from from 1 3 from from 1 4 from from 1 5 from 0 0 0 0 0 0 NS2Work 14 Add Your Change into ns • tcl/lib/ns-lib.tcl Class Simulator … source ../mysrc/msg.tcl • Makefile NS_TCL_LIB = \ tcl/mysrc/msg.tcl \ … – Or: change Makefile.in, make distclean, then ./configure --enable-debug NS2Work 15 Extending ns in C++ • Adding code in <new_stuff>.{cc,h} files – Change Makefile – make depend – recompile NS2Work 16 Guidelines • Decide position in class hierarchy – i.e., which class to derive from? • • • • • Create new packet header (if necessary) Create C++ class, fill in methods Define otcl linkage Write otcl code (if required) Build NS2Work 17 Class Hierarchy TclObject NsObject Connector Queue Delay Agent DropTail RED TCP Reno Classifier Trace AddrClassifier McastClasifier Enq Deq Drop SACK NS2Work 18 C++/otcl Linkage Root of ns-2 object hierarchy TclObject bind(): link variable values between C++ and OTcl command(): link OTcl methods to C++ implementations TclClass Tcl Create and initialize TclObject’s C++ methods to access Tcl interpreter TclCommand Standalone global commands EmbeddedTcl ns script initialization NS2Work 19 TclObject: Hierarchy and Shadowing C++ class hierarchy TclObject otcl class hierarchy TclObject Agent Agent Agent/TCP TcpAgent _o123 *tcp Agent/TCP otcl shadow object Agent/TCP C++ object NS2Work 20 TclObject::bind() • Link C++ member variables to otcl object variables • C++ TcpAgent::TcpAgent() { bind(“window_”, &wnd_); … … } – bind_time(), bind_bool(), bind_bw() • otcl set tcp [new Agent/TCP] $tcp set window_ 200 NS2Work 21 Initialization of Bound Variables • Initialization through otcl class variables Agent/TCP set window_ 50 • Do all initialization of bound variables in ~ns/lib/ns-default.tcl – Otherwise a warning will be issued when the shadow object is created NS2Work 22 TclObject::command() • Implement otcl methods in C++ • Trap point: otcl method cmd{} • Send all arguments after cmd{} call to TclObject::command() NS2Work 23 TclObject::command() $tcp send OTcl space no such procedure TclObject::unknown{} $tcp cmd send C++ space TcpAgent::command() Yes match “send”? No Invoke parent: return Agent::command() process and return NS2Work 24 TclObject::command() • otcl set tcp [new Agent/TCP] $tcp advance 10 • C++ int TcpAgent::command(int argc, const char*const* argv) { if (argc == 3) { if (strcmp(argv[1], “advance”) == 0) { int newseq = atoi(argv[2]); …… return(TCL_OK); } } return (Agent::command(argc, argv); } NS2Work 25 TclClass C++ TclObject NsObject Agent TcpAgent mirroring OTcl Static class TcpClass : public TclClass { public: TclObject TcpClass() : TclClass(“Agent/TCP”) {} TclObject* create(int, const char*const*) { return (new TcpAgent()); ?? } } class_tcp; Agent Agent/TCP NS2Work 26 Class Tcl • Singleton class with a handle to Tcl interpreter • Usage – Invoke otcl procedure – Obtain otcl evaluation results – Pass a result string to otcl – Return success/failure code to otcl NS2Work 27 Class Tcl Tcl& tcl = Tcl::instance(); if (argc == 2) { if (strcmp(argv[1], “now”) == 0) { tcl.resultf(“%g”, clock()); return TCL_OK; } tcl.error(“command not found”); return TCL_ERROR; } else if (argc == 3) { tcl.eval(argv[2]); clock_ = atof(tcl.result()); return TCL_OK; } NS2Work 28 Creating New Components • new agent, no headers • new agent, new packet header NS2Work 29 DtopTail Round Robin (DTRR) – Step 1 TclObject NsObject Connector Queue Delay Agent DropTail RED DTRR TCP Reno Classifier Trace AddrClassifier McastClasifier Enq Deq Drop SACK NS2Work 30 Adding New Queue (DropTail Round Robin) using C++ //dtrr-queue.h class DtRrQueue : public Queue { public: DtRrQueue() { q1_ = new PacketQueue; q2_ = new PacketQueue; pq_ = q1_; deq_turn_ = 1; } protected: void enque(Packet*); Packet* deque(); PacketQueue *q1_; PacketQueue *q2_; int deq_turn_; // First FIFO queue // Second FIFO queue // 1 for First queue 2 for Second }; NS2Work 31 Creating Object //dtrr-queue.cc #include "dtrr-queue.h" static class DtRrQueueClass : public TclClass { public: DtRrQueueClass() : TclClass("Queue/DTRR") {} TclObject* create(int, const char*const*) { return (new DtRrQueue); } } class_dropt_tail_round_robin; void DtRrQueue::enque(Packet* p) { … } Packet* DtRrQueue::deque(){ … } NS2Work 32 New Agent, New Header for Example MANET Unicast Routing (EMUR) • Example: Agent/Message – New packet header for 64-byte message – New transport agent to process this new header NS2Work 33 New Packet Header • Create new header structure • Enable tracing support of new header • Create static class for otcl linkage (packet.h) • Enable new header in otcl (tcl/lib/nspacket.tcl) • This does not apply when you add a new field into an existing header! NS2Work 34 How Packet Header Works Packet PacketHeader/Common next_ hdrlen_ bits_ size determined at simulator startup time (PacketHeaderManager) size determined hdr_cmn at compile time PacketHeader/IP size determined at compile time size determined at compile time …… NS2Work hdr_ip PacketHeader/TCP hdr_tcp 35 EMUR Header – Step 1 //emur_pkt.h struct hdr_emur_pkt { nsaddr_t pkt_src_; // Node which originated this packet u_int16_t pkt_len_; // Packet length (in bytes) u_int8_t pkt_seq_num_; // Packet sequence number inline nsaddr_t& pkt_src() { return pkt_src_; } inline u_int16_t& pkt_len() { return pkt_len_; } inline u_int8_t& pkt_seq_num() { return pkt_seq_num_; } static int offset_; inline static int& offset() { return offset_; } inline static hdr_emur_pkt* access(const Packet* p) { return (hdr_emur_pkt*)p->access(offset_); } }; #define HDR_EMUR_PKT(p) hdr_emur_pkt::access(p) NS2Work 36 EMUR Header – Step 2 //emur.cc #include <emur_pkt.h> int Emur_pkt::offset_; static class EmurHeaderClass : public HeaderClass { public: EmurHeaderClass() : PacketHeaderClass("PacketHeader/EMUR", sizeof(hdr_emur_pkt)) { bind_offset(&hdr_emur_pkt::offset_); } NS2Work } class_rtEMUR_hdr; 37 EMUR Header – Step 3 • Enable tracing (packet.h): enum packet_t { PT_TCP, …, PT_EMUR, PT_NTYPE // This MUST be the LAST one }; class p_info { …… name_[PT_EMUR] = “EMUR”; name_[PT_NTYPE]= "undefined"; …… }; NS2Work 38 EMUR Timer – Step 1 //emur.h class Emur; // forward declaration /* Timers */ class Emur_PktTimer : public TimerHandler { public: Emur_PktTimer(Emur* agent) : TimerHandler() { agent_ = agent; } protected: Emur* agent_; virtual void expire(Event* e); }; NS2Work 39 EMUR Class – Step 2 //emur.h class Emur : public Agent { /* Friends */ friend class Emur_PktTimer; /* Private members */ … Emur_PktTimer pkt_timer_; // Timer for sending packets. public: Emur(nsaddr_t); int command(int, const char*const*); void recv(Packet*, Handler*); NS2Work 40 }; EMUR Binding – Step 3 //Emur/Emur.cc static class EmurClass : public TclClass { public: EmurClass() : TclClass("Agent/EMUR") {} TclObject* create(int argc, const char*const* argv) { assert(argc == 5); return (new Emur((nsaddr_t)Address::instance().str2add r(argv[4]))); } } class_rtEmur; NS2Work 41 EMUR Constructor – Step 4 //Emur/Emur.cc Emur::Emur(nsaddr_t id) : Agent(PT_EMUR), pkt_timer_(this) { bind(“tcl_var_", &cc_var_); ra_addr_ = id; } In Simulation script Agent/Emur set tcl_var_ 100 NS2Work 42 EMUR Command – Step 5 Int Emur::command(int argc, const char*const* argv) { if (argc == 2) { if (strcasecmp(argv[1], "start") == 0) { Start(); //pkt_timer_.resched(0.0); return TCL_OK; } else if (strcasecmp(argv[1], “some_other_func") == 0) { … } } else if (argc == 3) { // Obtains corresponding dmux to carry packets to upper layers if (strcmp(argv[1], “some_var") == 0) { loc_var_ = atoi (strcmp(argv[2]) return TCL_OK; } … // Pass the command to the base class return Agent::command(argc, argv); } NS2Work 43 Emur Packet Receive – Step 6 Void Emur::recv(Packet* p, Handler* h) { struct hdr_cmn* ch = HDR_CMN(p); struct hdr_ip* ih = HDR_IP(p); if (ih->saddr() == ra_addr()) { // If there exists a loop, must drop the packet if (ch->num_forwards() > 0) { drop(p, DROP_RTR_ROUTE_LOOP); return; } // else if this is a packet I am originating, must add IP header else if (ch->num_forwards() == 0) ch->size() += IP_HDR_LEN; } // If it is a Emur packet, must process it if (ch->ptype() == PT_EMUR) recv_emur_pkt(p); // Otherwise, must forward the packet (unless TTL has reached zero) else { ih->ttl_--; if (ih->ttl_ == 0) { drop(p, DROP_RTR_TTL); return; NS2Work 44 } Emur Send Packet – Step - 7 Void Emur::send_emur_pkt() { Packet* p = allocpkt(); struct hdr_cmn* ch = HDR_CMN(p); struct hdr_ip* ih = HDR_IP(p); struct hdr_Emur_pkt* ph = HDR_Emur_PKT(p); ph->pkt_src() = ra_addr(); ph->pkt_len() = 7; ph->pkt_seq_num() = seq_num_++; ch->ptype() = PT_Emur; ch->direction() = hdr_cmn::DOWN; ch->size() = IP_HDR_LEN + ph->pkt_len(); ch->error() = 0; ch->next_hop() = IP_BROADCAST; ch->addr_type() = NS_AF_INET; ih->saddr() = ra_addr(); ih->daddr() = IP_BROADCAST; ih->sport() = RT_PORT; ih->dport() = RT_PORT; ih->ttl() = IP_DEF_TTL; Scheduler::instance().schedule(target_, p, JITTER); } NS2Work 45 Emur Scheduling Packets – Step 8 void Emur_PktTimer::expire(Event* e) { agent_->send_emur_pkt(); resched((double)5.0); } NS2Work 46 Emur Tracing – Step 9 //trace/cmu-trace.h trace/cmu-trace.h class CMUTrace : public Trace { /* ... definitions ... */ private: /* ... */ void format_aodv(Packet *p, int offset); void format_emur(Packet *p, int offset); }; //trace/cmu-trace.cc Void CMUTrace::format_emur(Packet *p, int offset) { struct hdr_emur_pkt* ph = HDR_EMUR_PKT(p); … NS2Work } 47 Add Method to TCL library – Step 10 //tcl/lib/ns-lib.tcl Simulator instproc create-Emur-agent { node } { # Create Emur routing agent set ragent [new Agent/Emur [$node nodeaddr]] $self at 0.0 "$ragent start" $node set ragent_ $ragent return $ragent } NS2Work 48 EMUR Final Step • Change Makefile.in to add your source files • Run Configure • Run make NS2Work 49 Summary • Internals of NS2 • How to add new component in ns2 – Queue – Routing NS2Work 50