Commit 2c06544a authored by Shashi Guruprasad's avatar Shashi Guruprasad

Initial checkin of NSE traffic generator (for FullTcp models with FTP and

Telnet sources)
parent b326f05e
For NSE based TCP traffic generation, we need the following files
as part of the FBSD disk image or it should be part of
/usr/testbed/sup/sup/FBSD43-STD/root*/etc/testbed on the boss node.
a) nseinput.tcl and startnse present in this directory
b) NSE executable file. To build it, do a "cvs co ns-2" in this
directory or elsewhere. The main branch of the ns-2 cvs tree
will contain the latest files to build an NSE suitable for
emulab.
cvs co ns-2
cd ns-2
./install
cp ns-2.1b8a/nse <sup_directory>
The current NSE trafgen has the following caveats:
0) The Telnet and FTP traffic generators will automatically start
30 seconds after "$ns run" in nseinput.tcl has been called. This just
arbitrary and will be fixed in the event system version of NSE.
1) nseinput.tcl does tmcc ready barrier synchronization which is not
sufficient across reboots. Again, this will be fixed
2) It comes with all the bugs and idiosyncrasies of the FullTcp, FTP and
Telnet models in NS.
Here is an example scenario:
source tb_compat.tcl
set ns [new Simulator]
#Create four nodes
set nodeA [$ns node]
set nodeB [$ns node]
$ns duplex-link $nodeA $nodeB 100Mb 0ms DropTail
##### ftp0
set tcp0 [new Agent/TCP/FullTcp/Tahoe]
$ns attach-agent $nodeA $tcp0
set ftp0 [new Application/FTP]
$ftp0 attach-agent $tcp0
set tcp1 [new Agent/TCP/FullTcp/Sack]
$ns attach-agent $nodeB $tcp1
$tcp1 listen
#Connect the traffic sources with the traffic sink
$ns connect $tcp0 $tcp1
##### telnet0
set tcp2 [new Agent/TCP/FullTcp/Newreno]
$ns attach-agent $nodeB $tcp2
set telnet0 [new Application/Telnet]
$telnet0 attach-agent $tcp2
set tcp3 [new Agent/TCP/FullTcp]
$tcp3 listen
$ns attach-agent $nodeA $tcp3
$ns connect $tcp2 $tcp3
#Run the simulation
$ns run
#!/etc/testbed/nse
# consults tmcc hostnames database to translate hostname to IP
# returns ip address of name
proc getipaddr {name} {
set hostnamelist [split [exec tmcc hostnames] "\n"]
foreach hostname $hostnamelist {
scan $hostname "NAME=%s LINK=%u IP=%s " hname linknum ip
set hnametomatch "$hname-$linknum"
if { $hnametomatch == $name } {
return $ip
}
}
puts stderr "NSE: Could not find ipaddress for $name"
return ""
}
# consults tmcc ifconfig and findif to find the interface name
# returns the interface name for ipaddr
proc getmac {ipaddr} {
set ifconfiglist [split [exec tmcc ifconfig] "\n"]
foreach ifconfig $ifconfiglist {
scan $ifconfig "INTERFACE=%s INET=%s MASK=%s MAC=%s " iface inet mask mac
if { $inet == $ipaddr } {
return [exec findif $mac]
}
}
puts stderr "NSE: Could not find the interface name for $ipaddr"
return ""
}
set ns [new Simulator]
$ns use-scheduler RealTime
set n0 [$ns node]
set n1 [$ns node]
# required hack for now or FullTcp timer cancels cause aborts
$ns duplex-link $n0 $n1 100Mb 0ms DropTail
# assume PATH properly set by wrapper script
set nseconfig [exec tmcc nseconfigs]
# If there is no nseconfig associated with this
# node, then we just give up
if { $nseconfig == {} } {
exit 0
}
eval $nseconfig
# set sysctl tcp blackhole to 2
exec sysctl -w net.inet.tcp.blackhole=2
lappend tcpclasses "Agent/TCP/FullTcp"
foreach tcpsubclass [Agent/TCP/FullTcp info subclass] {
lappend tcpclasses $tcpsubclass
}
foreach tcpclass $tcpclasses {
set tcpobjs [$tcpclass info instances]
foreach tcpobj $tcpobjs {
$ns attach-agent $n0 $tcpobj
}
}
# for each entry in `tmcc trafgens` that has NSE as the generator
# configure that object to connect to a newly created
# TCPTap along with the required Live and RAW IP objects. Set the filter and interface
# after learning it from tmcc commands and other scripts in /etc/testbed
set trafgenlist [split [exec tmcc trafgens] "\n"]
set formatstr {TRAFGEN=%s MYNAME=%s MYPORT=%u PEERNAME=%s PEERPORT=%u PROTO=%s ROLE=%s GENERATOR=%s}
foreach trafgen $trafgenlist {
variable i 0
scan $trafgen $formatstr trafgen myname myport peername peerport proto role gen
if { $gen != "NSE" || $proto != "tcp" } {
continue
}
# if the variable in $trafgen exists, we create a bunch of taps
# first do the tap part
if { [info exists $trafgen] == 1 } {
# convert myname and peername to corresponding ipaddresses
# using the getipaddr helper subroutine
set myipaddr [getipaddr $myname]
set peeripaddr [getipaddr $peername]
# find interface name with a helper subroutine
set interface [getmac $myipaddr]
# one TCPTap object per TCP class that we have instantiated
set tcptap($i) [new Agent/TCPTap]
$tcptap($i) nsipaddr $myipaddr
$tcptap($i) nsport $myport
$tcptap($i) extipaddr $peeripaddr
$tcptap($i) extport $peerport
# open the bpf, set the filter for capturing incoming packets towards
# the current tcp object
set bpf($i) [new Network/Pcap/Live]
set dev($i) [$bpf($i) open readonly $interface]
$bpf($i) filter "tcp and dst $myipaddr and dst port $myport and src $peeripaddr and src port $peerport"
# open RAW IP interface for outgoing packets
set ipnet($i) [new Network/IP]
$ipnet($i) open writeonly
# associate the 2 network objects in the TCPTap object
$tcptap($i) network-incoming $bpf($i)
$tcptap($i) network-outgoing $ipnet($i)
# attach the TCPTap agent to node n1
$ns attach-agent $n1 $tcptap($i)
# connect this tap and the particular tcp agent
set tcpagent($i) [expr \$$trafgen]
$ns connect $tcpagent($i) $tcptap($i)
incr i
}
}
exec tmcc ready
while { 1 } {
set readycount [exec tmcc readycount]
scan $readycount "READY=%u TOTAL=%u" numready total
if { $numready == $total } {
break;
}
exec sleep 1
}
$ns run
#!/etc/testbed/nse
# consults tmcc hostnames database to translate hostname to IP
# returns ip address of name
proc getipaddr {name} {
set hostnamelist [split [exec tmcc hostnames] "\n"]
foreach hostname $hostnamelist {
scan $hostname "NAME=%s LINK=%u IP=%s " hname linknum ip
set hnametomatch "$hname-$linknum"
if { $hnametomatch == $name } {
return $ip
}
}
puts stderr "NSE: Could not find ipaddress for $name"
return ""
}
# consults tmcc ifconfig and findif to find the interface name
# returns the interface name for ipaddr
proc getmac {ipaddr} {
set ifconfiglist [split [exec tmcc ifconfig] "\n"]
foreach ifconfig $ifconfiglist {
scan $ifconfig "INTERFACE=%s INET=%s MASK=%s MAC=%s " iface inet mask mac
if { $inet == $ipaddr } {
return [exec findif $mac]
}
}
puts stderr "NSE: Could not find the interface name for $ipaddr"
return ""
}
set ns [new Simulator]
$ns use-scheduler RealTime
set n0 [$ns node]
set n1 [$ns node]
# required hack for now or FullTcp timer cancels cause aborts
$ns duplex-link $n0 $n1 100Mb 0ms DropTail
# assume PATH properly set by wrapper script
set nseconfig [exec tmcc nseconfigs]
# If there is no nseconfig associated with this
# node, then we just give up
if { $nseconfig == {} } {
exit 0
}
eval $nseconfig
# set sysctl tcp blackhole to 2
exec sysctl -w net.inet.tcp.blackhole=2
lappend tcpclasses "Agent/TCP/FullTcp"
foreach tcpsubclass [Agent/TCP/FullTcp info subclass] {
lappend tcpclasses $tcpsubclass
}
foreach tcpclass $tcpclasses {
set tcpobjs [$tcpclass info instances]
foreach tcpobj $tcpobjs {
$ns attach-agent $n0 $tcpobj
}
}
# for each entry in `tmcc trafgens` that has NSE as the generator
# configure that object to connect to a newly created
# TCPTap along with the required Live and RAW IP objects. Set the filter and interface
# after learning it from tmcc commands and other scripts in /etc/testbed
set trafgenlist [split [exec tmcc trafgens] "\n"]
set formatstr {TRAFGEN=%s MYNAME=%s MYPORT=%u PEERNAME=%s PEERPORT=%u PROTO=%s ROLE=%s GENERATOR=%s}
foreach trafgen $trafgenlist {
variable i 0
scan $trafgen $formatstr trafgen myname myport peername peerport proto role gen
if { $gen != "NSE" || $proto != "tcp" } {
continue
}
# if the variable in $trafgen exists, we create a bunch of taps
# first do the tap part
if { [info exists $trafgen] == 1 } {
# convert myname and peername to corresponding ipaddresses
# using the getipaddr helper subroutine
set myipaddr [getipaddr $myname]
set peeripaddr [getipaddr $peername]
# find interface name with a helper subroutine
set interface [getmac $myipaddr]
# one TCPTap object per TCP class that we have instantiated
set tcptap($i) [new Agent/TCPTap]
$tcptap($i) nsipaddr $myipaddr
$tcptap($i) nsport $myport
$tcptap($i) extipaddr $peeripaddr
$tcptap($i) extport $peerport
# open the bpf, set the filter for capturing incoming packets towards
# the current tcp object
set bpf($i) [new Network/Pcap/Live]
set dev($i) [$bpf($i) open readonly $interface]
$bpf($i) filter "tcp and dst $myipaddr and dst port $myport and src $peeripaddr and src port $peerport"
# open RAW IP interface for outgoing packets
set ipnet($i) [new Network/IP]
$ipnet($i) open writeonly
# associate the 2 network objects in the TCPTap object
$tcptap($i) network-incoming $bpf($i)
$tcptap($i) network-outgoing $ipnet($i)
# attach the TCPTap agent to node n1
$ns attach-agent $n1 $tcptap($i)
# connect this tap and the particular tcp agent
set tcpagent($i) [expr \$$trafgen]
$ns connect $tcpagent($i) $tcptap($i)
incr i
}
}
exec tmcc ready
while { 1 } {
set readycount [exec tmcc readycount]
scan $readycount "READY=%u TOTAL=%u" numready total
if { $numready == $total } {
break;
}
exec sleep 1
}
$ns run
#!/usr/bin/perl -w
use English;
#
# Setup the files and environment required for NSE and start it
#
# usage: startnse &
#
#
# Untaint path
#
$ENV{'PATH'} = '/bin:/sbin:/usr/bin:/usr/local/bin:/etc/testbed';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
#
# Load the OS independent support library. It will load the OS dependent
# library and initialize itself.
#
use lib "/etc/testbed";
use libsetup;
#
# We use the tmcc to figure out where the Testbed Event Daemon is running.
# Don't worry about the port for now.
#
#my ($bossname, $bossipaddr) = split(" ", `tmcc bossinfo`);
# 1. sysctl tcp blackhole to 2. Done in nseinput.tcl
# 2. create lots of BPF devices
chdir "/etc/testbed";
$id = `id`;
if( $id =~ /uid=(\d+)/ ) {
if( $1 ne "0" ) {
system("sudo nse nseinput.tcl");
} else {
system("nse nseinput.tcl");
}
}
......@@ -108,9 +108,14 @@ Node instproc updatedb {DB} {
incr i
}
set nseconfig ""
foreach agent $agentlist {
$agent updatedb $DB
append nseconfig [$agent get_nseconfig]
}
# update the per-node nseconfigs table in the DB
sql exec $DB "insert into nseconfigs (pid,eid,vname,nseconfig) values ('$pid','$eid','$self','$nseconfig')";
# Update the DB
sql exec $DB "insert into virt_nodes (pid,eid,vname,type,ips,osname,cmd_line,rpms,deltas,startupcmd,tarfiles,failureaction,routertype,fixed) values (\"$pid\",\"$eid\",\"$self\",\"$type\",\"$ipraw\",\"$osid\",\"$cmdline\",\"$rpms\",\"$deltas\",\"$startup\",\"$tarfiles\",\"$failureaction\",\"$default_ip_routing_type\",\"$fixed\")";
......
......@@ -5,6 +5,11 @@
# This defines the various agents and applications needed to support
# traffic generation. Specifically it defines Agent/UDP, Agent/Null,
# and Application/Traffic/CBR.
#
# Added: TCP traffic generation using NSE. Defines
# Agent/TCP/FullTcp, Agent/TCP/FullTcp/Reno, Agent/TCP/FullTcp/Newreno,
# Agent/TCP/FullTcp/Tahoe, Agent/TCP/FullTcp/Sack,
# Application/FTP and Application/Telnet
######################################################################
Class Agent -superclass NSObject
......@@ -12,16 +17,30 @@ Class Agent/UDP -superclass Agent
Class Agent/TCP -superclass Agent
Class Agent/Null -superclass Agent
Class Agent/TCPSINK -superclass Agent
Class Agent/TCP/FullTcp -superclass Agent
Class Agent/TCP/FullTcp/Reno -superclass Agent/TCP/FullTcp
Class Agent/TCP/FullTcp/Newreno -superclass Agent/TCP/FullTcp
Class Agent/TCP/FullTcp/Tahoe -superclass Agent/TCP/FullTcp
Class Agent/TCP/FullTcp/Sack -superclass Agent/TCP/FullTcp
Class Application -superclass NSObject
Class Application/Traffic/CBR -superclass Application
Class Application/FTP -superclass Application
Class Application/Telnet -superclass Application
namespace eval GLOBALS {
set new_classes(Agent/UDP) {}
set new_classes(Agent/Null) {}
set new_classes(Agent/TCP) {}
set new_classes(Agent/TCPSINK) {}
set new_classes(Agent/TCP/FullTcp) {}
set new_classes(Agent/TCP/FullTcp/Reno) {}
set new_classes(Agent/TCP/FullTcp/Newreno) {}
set new_classes(Agent/TCP/FullTcp/Tahoe) {}
set new_classes(Agent/TCP/FullTcp/Sack) {}
set new_classes(Application/Traffic/CBR) {}
set new_classes(Application/FTP) {}
set new_classes(Application/Telnet) {}
}
# Agent
......@@ -98,6 +117,13 @@ Agent instproc updatedb {DB} {
# Update the DB
sql exec $DB "insert into virt_trafgens (pid,eid,vnode,vname,role,proto,port,target_vnode,target_port,generator) values ('$pid','$eid','$node','$application','$role','$proto', $port,'$target_vnode',$target_port,'$generator')";
}
# get_nseconfig is only defined for subclasses that will be simulated by NSE
# Will be called from Node updatedb
Agent instproc get_nseconfig {} {
return ""
}
# Agent/UDP
......@@ -162,6 +188,159 @@ Agent/TCP instproc connect {dst} {
$node set osid "FBSD-STD"
}
# Agent/TCP/FullTcp
Agent/TCP/FullTcp instproc init args {
eval $self next $args
$self set simulated 1
$self set tcptype ""
$self set segsize_ 1460
$self set role "source"
$self set generator "NSE"
}
# Agent/TCP/FullTcp/Newreno
Agent/TCP/FullTcp/Newreno instproc init args {
eval $self next $args
$self set tcptype "Newreno"
}
# Agent/TCP/FullTcp/Tahoe
Agent/TCP/FullTcp/Tahoe instproc init args {
eval $self next $args
$self set tcptype "Tahoe"
}
# Agent/TCP/FullTcp/Sack
Agent/TCP/FullTcp/Sack instproc init args {
eval $self next $args
$self set tcptype "Sack"
}
# Agent/TCP/FullTcp
Agent/TCP/FullTcp instproc listen {} {
$self set role "sink"
}
# updatedb DB
# This adds rows to the virt_trafgens table corresponding to this agent.
# We do things differently from the base class version. For NSE, the
# vname in the table is the FullTcp agent object and not the application
# object connected to it. The important thing is using "$ns at", we can
# send events and control any of these objects
Agent/TCP/FullTcp instproc updatedb {DB} {
var_import ::GLOBALS::pid
var_import ::GLOBALS::eid
$self instvar application
$self instvar destination
$self instvar node
$self instvar proto
$self instvar role
$self instvar generator
$self instvar port
if {$role == {}} {
perror "\[updatedb] $self has no role."
return
}
if {$destination == {}} {
perror "\[updatedb] $self has no destination."
return
}
set target_vnode [$destination set node]
set target_port [$destination set port]
set vname $self
# Update the DB
sql exec $DB "insert into virt_trafgens (pid,eid,vnode,vname,role,proto,port,target_vnode,target_port,generator) values ('$pid','$eid','$node','$vname','$role','$proto', $port,'$target_vnode',$target_port,'$generator')";
}
# Agent/TCP/FullTcp
Agent/TCP/FullTcp instproc get_nseconfig {} {
$self instvar tcptype
$self instvar role
$self instvar simulated
$self instvar application
set nseconfig ""
# This agent could possibly be a real one in which case
# we don't do NSE traffic generation
if { $simulated != 1 } {
return $nseconfig
}
if { ($tcptype == "") || ($tcptype == "Reno") } {
set nseconfig "set $self \[new Agent/TCP/FullTcp]\n"
} else {
set nseconfig "set $self \[new Agent/TCP/FullTcp/$tcptype]\n"
}
if { $role == "sink" } {
append nseconfig "\$$self listen\n\n"
}
# We end up including variables present only in TB parser. However
# that does not matter coz all NS variables end with _ character
# Pruning can be done later
foreach var [$self info vars] {
if { [$self set $var] != {} } {
append nseconfig "\$$self set $var [$self set $var]\n"
}
}
if { $application != {} } {
append nseconfig [$application get_nseconfig]
}
return $nseconfig
}
# Agent/TCP/FullTcp
Agent/TCP/FullTcp instproc connect {dst} {
$self next $dst
$self instvar node
$self instvar application
$self instvar destination
$self instvar role
set error 0
if {$node == {}} {
perror "\[connect] $self is not attached to a node."
set error 1
}
if { ($role == "source") && ($application == {}) } {
perror "\[connect] $self does not have an attached application."
set error 1
}
set dest [$destination set node]
if {$dest == {}} {
perror "\[connect] $destination is not attached to a node."
set error 1
}
# This was an artifact of SEND-CONSUME CBR model. Now that we run freebsd on the
# traffic generator nodes, all we need to do is either enable OSPF routing by
# default or setup static routes
if {[llength [$node set portlist]] != 1} {
perror "\[connect] $node must have exactly one link to be a traffic generator."
set error 1
}
if {$error} {return}
$self set proto "tcp"
$dst set proto "tcp"
$node set osid "FBSD-STD"
$dest set osid "FBSD-STD"
}
# Agent/Null
Agent/Null instproc connect {dst} {
$self next $dst
......@@ -267,3 +446,60 @@ Application/Traffic/CBR instproc get_params {} {
}
return $param
}
# Application/FTP
Application/FTP instproc init {} {
$self next
$self set role "source"
}
# Application/FTP
Application/FTP instproc get_nseconfig {} {
$self instvar agent
set nseconfig "set $self \[new Application/FTP]\n"
if { $agent != {} } {
append nseconfig "\$$self attach-agent \$$agent\n\n"
}
# XXX temporary untill event system changes get in
append nseconfig "\[Simulator instance] at 30.0 \"\$$self start\"\n\n"
return $nseconfig
}
# Application/Telnet
Application/Telnet instproc init {} {
$self next
$self instvar role
$self instvar interval_
$self set role "source"
# NS's default is 1.0 and therefore would result in inter-packet times
# to be chosen from the exponential distribution. If it is 0, it would
# be chosen based on the tcplib distribution. I differ from NS in my
# default because of the realism of tcplib distribution
$self set interval_ 0.0
}
# Application/Telnet
Application/Telnet instproc get_nseconfig {} {
$self instvar agent
$self instvar interval_
set nseconfig "set $self \[new Application/Telnet]\n"
append nseconfig "\$$self set interval_ $interval_\n"
if { $agent != {} } {
append nseconfig "\$$self attach-agent \$$agent\n\n"
}
# XXX temporary untill event system changes get in
append nseconfig "\[Simulator instance] at 30.0 \"\$$self start\"\n\n"
return $nseconfig
}
......@@ -83,6 +83,8 @@ DBQueryWarn("DELETE from virt_lans where pid='$pid' and eid='$eid'") or
$errors++;
DBQueryWarn("DELETE from virt_trafgens where pid='$pid' and eid='$eid'") or
$errors++;
DBQueryWarn("DELETE from nseconfigs where pid='$pid' and eid='$eid'") or
$errors++;
DBQueryWarn("DELETE from eventlist where pid='$pid' and eid='$eid'") or
$errors++;
......
......@@ -66,6 +66,7 @@ sub cleanup {
DBQueryWarn("DELETE from virt_nodes where pid='$pid' and eid='$eid'");
DBQueryWarn("DELETE from virt_lans where pid='$pid' and eid='$eid'");
DBQueryWarn("DELETE from virt_trafgens where pid='$pid' and eid='$eid'");
DBQueryWarn("DELETE from nseconfigs where pid='$pid' and eid='$eid'");
DBQueryWarn("DELETE from eventlist where pid='$pid' and eid='$eid'");
SetExpState($pid, $eid, EXPTSTATE_NEW);
}
......
......@@ -749,6 +749,7 @@ sub dotrafficconfig()
my $pat;
my $TM;
my $boss;
my $startnse = 0;
$TM = OPENTMCC(TMCCCMD_BOSSINFO);
($boss) = split(" ", <$TM>);
......@@ -781,6 +782,13 @@ sub dotrafficconfig()
# Skip if not specified as a TG generator. At some point
# work in Shashi's NSE work.
if ($generator ne "TG") {
$startnse = 1;
if (! $didopen) {
open(RC, ">" . TMTRAFFICCONFIG)
or die("Could not open " . TMTRAFFICCONFIG . ": $!");
print RC "#!/bin/sh\n";
$didopen = 1;
}
next;
}
......@@ -803,6 +811,11 @@ sub dotrafficconfig()
warn "*** WARNING: Bad traffic line: $_";
}
}
if( $startnse ) {
print RC "$SETUPDIR/startnse &\n";
}
close($TM);
if ($didopen) {
close(RC);
......
......@@ -749,6 +749,7 @@ sub dotrafficconfig()
my $pat;
my $TM;
my $boss;
my $startnse = 0;
$TM = OPENTMCC(TMCCCMD_BOSSINFO);
($boss) = split(" ", <$TM>);
......@@ -781,6 +782,13 @@ sub dotrafficconfig()
# Skip if not specified as a TG generator. At some point
# work in Shashi's NSE work.
if ($generator ne "TG") {
$startnse = 1;
if (! $didopen) {
open(RC, ">" . TMTRAFFICCONFIG)
or die("Could not open " . TMTRAFFICCONFIG . ": $!");
print RC "#!/bin/sh\n";
$didopen = 1;
}
next;
}
......@@ -803,6 +811,11 @@ sub dotrafficconfig()
warn "*** WARNING: Bad traffic line: $_";
}
}