core-extra/gui/traffic.tcl
2013-08-29 14:21:13 +00:00

617 lines
20 KiB
Tcl

#
# Copyright 2011-2013 the Boeing Company.
# See the LICENSE file included in this distribution.
#
set g_traffic_start_opt 0
set g_traffic_flows ""
# preset MGEN traffic pattern parameters
# fields are name [list dst params]
array set traffic_presets {
"00 1 kbps" {"" "PERIODIC \[1.0 125\]"}
"01 10 kbps" {"" "PERIODIC \[10.0 125\]"}
"02 100 kbps" {"" "PERIODIC \[10.0 1250\]"}
"03 512 kbps" {"" "PERIODIC \[50.0 1280\]"}
"04 POISSON 10 kbps" {"" "POISSON \[10.0 125\]"}
"05 POISSON 100 kbps" {"" "POISSON \[10.0 1250\]"}
"06 POISSON 512 kbps" {"" "POISSON \[50.0 1280\]"}
"07 3s fixed BURST 75kbps every 15s" {"" "BURST \[REGULAR 15.0 PERIODIC \[10.0 938\] FIXED 3.0\]"}
"08 0-5s random BURST 100kbps randomly every 10s" {"" "BURST \[RANDOM 10.0 PERIODIC \[10.0 1250\] EXP 5.0\]"}
"09 3s fixed BURST 256kbps randomly every 5s" {"" "BURST \[RANDOM 5.0 PERIODIC \[25.0 1280\] FIXED 3.0\]"}
"10 JITTER 10 kbps 0.05-0.15s" {"" "JITTER \[10.0 125 0.5\]"}
"11 JITTER 75 kbps 0.08-0.12s" {"" "JITTER \[10.0 938 0.2\]"}
"12 CLONE pcap file" {"" "CLONE \[tcpdump /tmp/source.pcap \[0\]\]"}
"13 CLONE pcap file continuous" {"" "CLONE \[tcpdump /tmp/source.pcap \[-1\]\]"}
"14 100 kbps multicast" {"224.225.1.1" "PERIODIC \[10.0 1250\]"}
"15 POISSON 384 kbps multicast" {"224.225.2.1" "POISSON \[40.0 1200\]"}
}
proc popupTrafficDialog {} {
global plugin_img_add plugin_img_edit plugin_img_del g_traffic_start_opt
global oper_mode
set wi .traffic1
catch {destroy $wi}
toplevel $wi
wm transient $wi .
wm resizable $wi 0 0
wm title $wi "CORE traffic flows"
if { ![info exists g_traffic_start_opt] } { set g_traffic_start_opt 0 }
labelframe $wi.f -text "Traffic flows"
listbox $wi.f.flows -selectmode extended -width 50 -exportselection 0 \
-yscrollcommand "$wi.f.flows_scroll set"
scrollbar $wi.f.flows_scroll -command "$wi.f.flows yview"
pack $wi.f.flows $wi.f.flows_scroll -pady 4 -fill both -side left
pack $wi.f -padx 4 -pady 4 -fill both -side top
bind $wi.f.flows <Double-Button-1> "trafficHelper $wi edit"
bind $wi.f.flows <<ListboxSelect>> "trafficHelper $wi sel"
frame $wi.bbar
button $wi.bbar.new -image $plugin_img_add -command "trafficHelper $wi new"
button $wi.bbar.save -image $plugin_img_edit \
-command "trafficHelper $wi edit"
button $wi.bbar.del -image $plugin_img_del -command "trafficHelper $wi del"
label $wi.bbar.help -text \
"Press the new button to define a new traffic flow."
pack $wi.bbar.new $wi.bbar.save $wi.bbar.del -side left
pack $wi.bbar.help -padx 8 -side left
pack $wi.bbar -padx 4 -pady 4 -fill both -side top
labelframe $wi.traf -text "Traffic options"
frame $wi.traf.opt
radiobutton $wi.traf.opt.opt1 -variable g_traffic_start_opt -value 0 \
-text "Do not start traffic flows automatically"
radiobutton $wi.traf.opt.opt2 -variable g_traffic_start_opt -value 1 \
-text "Start traffic flows after all nodes have booted"
radiobutton $wi.traf.opt.opt3 -variable g_traffic_start_opt -value 2 \
-text "Start traffic flows after routing has converged" -state disabled
pack $wi.traf.opt.opt1 $wi.traf.opt.opt2 $wi.traf.opt.opt3 \
-side top -anchor w
pack $wi.traf.opt -side top
frame $wi.traf.btn
set buttonstate disabled
if { $oper_mode == "exec" } { set buttonstate normal }
button $wi.traf.btn.start -text "Start all flows" \
-command startTrafficScripts -state $buttonstate
button $wi.traf.btn.stop -text "Stop all flows" \
-command stopTrafficScripts -state $buttonstate
button $wi.traf.btn.startsel -text "Start selected" \
-command "trafficHelper $wi start" -state $buttonstate
button $wi.traf.btn.stopsel -text "Stop selected" \
-command "trafficHelper $wi stop" -state $buttonstate
pack $wi.traf.btn.start $wi.traf.btn.stop $wi.traf.btn.startsel \
$wi.traf.btn.stopsel -side left
pack $wi.traf.btn -side top -fill x
pack $wi.traf -side top -fill x
# save/cancel buttons
frame $wi.b -borderwidth 4
button $wi.b.close -text "Close" -command \
".c delete withtag twonode; destroy $wi"
pack $wi.b.close -side bottom
pack $wi.b -side bottom
refreshTrafficList $wi
}
# helper for start, stop, delete selected, or add/modify flows
proc trafficHelper { wi cmd } {
set selected [$wi.f.flows curselection]
# listbox select
if { $cmd == "sel" } { .c delete withtag "twonode" }
# start/stop/delete selected
if { $cmd == "del" || $cmd == "sel" || $cmd == "start" || $cmd == "stop" } {
foreach i $selected {
set name [$wi.f.flows get $i]
if { $cmd == "del" } {
removeTrafficFlow $name
refreshTrafficList $wi
} elseif { $cmd == "sel" } {
set flow [getTrafficFlow $name]
set srcnode [lindex $flow 4]
if { $srcnode != "" } {
set tags "twonode $srcnode"
drawNodeCircle $srcnode 30 green $tags ""
}
set dstnode [lindex $flow 5]
if { $dstnode != "" } {
set tags "twonode $dstnode"
drawNodeCircle $dstnode 30 red $tags ""
}
} else {
set flow [getTrafficFlow $name]
if { $flow == "" } { continue }
startstopTrafficScript $flow $cmd
}
}
return
}
set selected [lindex $selected 0]
if { $selected == "" } {
set name ""
} else {
set name [$wi.f.flows get $selected]
}
if { $cmd == "edit" && $name == "" } { return }
if { $cmd == "new" } {
# TODO: find the next flow number and set the name
set name ""
}
popupTraffic2Dialog $name
}
proc refreshTrafficList { wi } {
global g_traffic_flows
$wi.f.flows delete 0 end
if { ![info exists g_traffic_flows] } { set g_traffic_flows "" }
foreach flow $g_traffic_flows {
set name [lindex $flow 0]
$wi.f.flows insert end $name
}
}
proc popupTraffic2Dialog { name } {
global g_twoNodeSelect g_twoNodeSelectCallback activetool
global traffic_flow_protocol traffic_presets
global g_traffic_pat
set wi .traffic
catch {destroy $wi}
toplevel $wi
set activetool select
wm transient $wi
wm resizable $wi 0 0
wm title $wi "Edit traffic flow"
set g_twoNodeSelect ""
set g_twoNodeSelectCallback selectTwoNodeTrafficCallback
labelframe $wi.e -text "Flow configuration"
frame $wi.e.flow -borderwidth 4
label $wi.e.flow.namel -text "name"
entry $wi.e.flow.name -bg white -width 20
label $wi.e.flow.numl -text "flow number"
entry $wi.e.flow.num -bg white -width 4
label $wi.e.flow.timestartl -text "start time"
entry $wi.e.flow.timestart -bg white -width 4
label $wi.e.flow.timestopl -text "stop time"
entry $wi.e.flow.timestop -bg white -width 4
pack $wi.e.flow.namel $wi.e.flow.name \
$wi.e.flow.numl $wi.e.flow.num $wi.e.flow.timestartl \
$wi.e.flow.timestart $wi.e.flow.timestopl $wi.e.flow.timestop -side left
pack $wi.e.flow -side top -fill both
# sending node
frame $wi.e.nodes -borderwidth 4
set sf $wi.e.nodes.src
labelframe $sf -text "source"
frame $sf.n -borderwidth 4
label $sf.n.srcl -text "source node"
radiobutton $sf.n.src -text " (none) " -variable g_twoNodeSelect \
-value "$sf.n.src" -indicatoron off -activebackground green \
-selectcolor green -padx 4 -pady 4
pack $sf.n.srcl $sf.n.src -side left -padx 4 -pady 4
pack $sf.n -side top -anchor w
frame $sf.ipp
button $sf.ipp.ipl -text "IP" -command \
"popupAddressPicker $sf.n.src $sf.ipp.ip"
entry $sf.ipp.ip -bg white -width 15
label $sf.ipp.pl -text "port"
entry $sf.ipp.p -bg white -width 5
pack $sf.ipp.ipl $sf.ipp.ip $sf.ipp.pl $sf.ipp.p -side left -padx 4 -pady 4
pack $sf.ipp -side top -anchor w
frame $sf.traff -borderwidth 4
label $sf.traff.protol -text "protocol"
tk_optionMenu $sf.traff.proto traffic_flow_protocol "TCP" "UDP" "SINK"
label $sf.traff.tosl -text "TOS"
entry $sf.traff.tos -bg white -width 5
pack $sf.traff.protol $sf.traff.proto $sf.traff.tosl \
$sf.traff.tos -side left
pack $sf.traff -side top
frame $sf.patt -borderwidth 4
label $sf.patt.patl -text "pattern"
set trafficpresetsMenu [tk_optionMenu $sf.patt.pat g_traffic_pat presets]
entry $sf.patt.p -bg white -width 20
pack $sf.patt.patl $sf.patt.pat $sf.patt.p -side left
pack $sf.patt -side top
frame $sf.log -borderwidth 4
label $sf.log.l -text "log file"
entry $sf.log.file -bg white -width 30
pack $sf.log.l $sf.log.file -side left
pack $sf.log -side top
# receiving node
set df $wi.e.nodes.dst
labelframe $df -text "destination"
frame $df.n -borderwidth 4
label $df.n.dstl -text "destination node"
radiobutton $df.n.dst -text " (none) " -variable g_twoNodeSelect \
-value "$df.n.dst" -indicatoron off -activebackground red \
-selectcolor red -padx 4 -pady 4
button $df.n.clear -text "clear" -command "$df.n.dst configure -text \" (none) \""
pack $df.n.dstl $df.n.dst $df.n.clear -side left -padx 4 -pady 4
pack $df.n -side top -anchor w
frame $df.ipp
button $df.ipp.ipl -text "IP" -command \
"popupAddressPicker $df.n.dst $df.ipp.ip"
entry $df.ipp.ip -bg white -width 15
label $df.ipp.pl -text "port"
entry $df.ipp.p -bg white -width 5
pack $df.ipp.ipl $df.ipp.ip $df.ipp.pl $df.ipp.p -side left -padx 4 -pady 4
pack $df.ipp -side top -anchor w
frame $df.log -borderwidth 4
label $df.log.l -text "log file"
entry $df.log.file -bg white -width 30
pack $df.log.l $df.log.file -side left
pack $df.log -side top
pack $sf $df -side left -fill both
pack $wi.e.nodes -side top -fill x
frame $wi.e.extra -borderwidth 4
label $wi.e.extra.lab -text "additional MGEN parameters"
entry $wi.e.extra.txt -bg white -width 40
pack $wi.e.extra.lab $wi.e.extra.txt -side left
pack $wi.e.extra -side top -fill x
pack $wi.e -side top
set g_traffic_pat presets
# save/cancel buttons
frame $wi.b -borderwidth 4
button $wi.b.apply -text "Apply" \
-command "popdownTrafficDialog $wi save \"$name\""
button $wi.b.cancel -text "Cancel" \
-command "popdownTrafficDialog $wi cancel \"$name\""
pack $wi.b.cancel $wi.b.apply -side right
pack $wi.b -side bottom
# populate traffic presets
$trafficpresetsMenu delete 0
foreach p [lsort -dictionary [array names traffic_presets]] {
set txt [string range $p 3 end]
$trafficpresetsMenu add radiobutton -label $txt -value $txt \
-variable g_traffic_pay \
-command "trafficPresets $wi \"$p\""
}
# default values
.c delete withtag "twonode"
set flow ""
if {$name != "" } { set flow [getTrafficFlow $name] }
if { $flow == "" } {
set flownum [nextTrafficFlowNumber]
$wi.e.flow.name insert 0 $name
$wi.e.flow.num insert 0 $flownum
$wi.e.flow.timestart insert 0 "0.0"
set traffic_flow_protocol "UDP"
$sf.ipp.p insert 0 "5000"
$df.ipp.p insert 0 "5000"
$df.log.file insert 0 "/var/log/mgen$flownum.log"
# load values from flow list
} else {
$wi.e.flow.name insert 0 [lindex $flow 0]
$wi.e.flow.num insert 0 [lindex $flow 1]
$wi.e.flow.timestart insert 0 [lindex $flow 2]
$wi.e.flow.timestop insert 0 [lindex $flow 3]
set srcnode [lindex $flow 4]
if { $srcnode != "" } {
set tags "twonode $srcnode twonode$sf.n.src"
$wi.e.nodes.src.n.src configure -text " $srcnode "
drawNodeCircle $srcnode 30 green $tags ""
}
set dstnode [lindex $flow 5]
if { $dstnode != "" } {
set tags "twonode $dstnode twonode$df.n.dst"
$wi.e.nodes.dst.n.dst configure -text " $dstnode "
drawNodeCircle $dstnode 30 red $tags ""
}
$wi.e.nodes.src.ipp.ip insert 0 [lindex $flow 6]
$wi.e.nodes.src.ipp.p insert 0 [lindex $flow 7]
$wi.e.nodes.src.log.file insert 0 [lindex $flow 8]
$wi.e.nodes.dst.ipp.ip insert 0 [lindex $flow 9]
$wi.e.nodes.dst.ipp.p insert 0 [lindex $flow 10]
$wi.e.nodes.dst.log.file insert 0 [lindex $flow 11]
set traffic_flow_protocol [lindex $flow 12]
$wi.e.nodes.src.traff.tos insert 0 [lindex $flow 13]
$wi.e.nodes.src.patt.p insert 0 [lindex $flow 14]
$wi.e.extra.txt insert 0 [lindex $flow 15]
}
}
# fill in traffic pattern field when preset has been selected from menu
proc trafficPresets { wi preset } {
global traffic_presets
set patterndata $traffic_presets($preset)
set dst [lindex $patterndata 0]
set params [lindex $patterndata 1]
set sf $wi.e.nodes.src
$sf.patt.p delete 0 end
$sf.patt.p insert end $params
set df $wi.e.nodes.dst
if { $dst != "" } {
$df.ipp.ip delete 0 end
$df.ipp.ip insert end $dst
}
}
# close the traffic window
proc popdownTrafficDialog { wi cmd oldname } {
global g_traffic_flows traffic_flow_protocol oper_mode
if { $cmd == "cancel" } {
.c delete withtag "twonode"
destroy $wi
return
}
# apply button was pressed
set num [$wi.e.flow.num get]
set name [$wi.e.flow.name get]
set start [$wi.e.flow.timestart get]
if { $start == "" || $start < 0.0 } { set start 0.0 }
set stop [$wi.e.flow.timestop get]
foreach n [list src dst] {
set ${n}node [string trim [$wi.e.nodes.$n.n.$n cget -text]]
set ${n}ip [$wi.e.nodes.$n.ipp.ip get]
set ${n}port [$wi.e.nodes.$n.ipp.p get]
set ${n}log [$wi.e.nodes.$n.log.file get]
}
set proto $traffic_flow_protocol
set tos [$wi.e.nodes.src.traff.tos get]
set pat [$wi.e.nodes.src.patt.p get]
set extra [$wi.e.extra.txt get]
if { $oldname != "" } { removeTrafficFlow $oldname }
set trafficentry [list "$name" $num "$start" "$stop" "$srcnode" "$dstnode" \
"$srcip" "$srcport" "$srclog" \
"$dstip" "$dstport" "$dstlog" \
"$proto" "$tos" "$pat" "$extra"]
lappend g_traffic_flows $trafficentry
refreshTrafficList .traffic1
.c delete withtag "twonode"
destroy $wi
if { $oper_mode == "exec" } {
set sock [lindex [getEmulPlugin $srcnode] 2]
sendTrafficScript $trafficentry $sock
}
}
# called from editor.tcl:button1 when user clicks on a node
# search for IP address and populate
proc selectTwoNodeTrafficCallback { } {
set wi .traffic
if { ![winfo exists $wi] } { return }; # user has closed window
foreach n [list src dst] {
set node$n [string trim [$wi.e.nodes.$n.n.$n cget -text]]
if { [set node$n] == "(none)" } { set node$n "" }
$wi.e.nodes.$n.ipp.ip delete 0 end
}
$wi.e.nodes.src.ipp.ip insert 0 [getDestinationAddress $nodedst $nodesrc]
$wi.e.nodes.dst.ipp.ip insert 0 [getDestinationAddress $nodesrc $nodedst]
if { [$wi.e.flow.name get] == "" && $nodesrc != "" && $nodedst != "" } {
set num [$wi.e.flow.num get]
$wi.e.flow.name insert 0 "flow $num from $nodesrc to $nodedst"
}
}
proc popupAddressPicker { ctl dstentry } {
set wi .addressPicker
set node [string trim [$ctl cget -text]]
catch {destroy $wi}
toplevel $wi
wm transient $wi
wm resizable $wi 0 0
wm title $wi "Select IP"
# listbox
labelframe $wi.a -text "IP addresses"
listbox $wi.a.addrs -selectmode single -width 16 -exportselection 0 \
-yscrollcommand "$wi.a.addrs_scroll set"
scrollbar $wi.a.addrs_scroll -command "$wi.a.addrs yview"
pack $wi.a.addrs $wi.a.addrs_scroll -pady 4 -fill both -side left
pack $wi.a -padx 4 -pady 4 -fill both -side top
bind $wi.a.addrs <Double-Button-1> "popdownAddressPicker $wi $dstentry"
# OK button
frame $wi.b -borderwidth 4
button $wi.b.ok -text "OK" -command "popdownAddressPicker $wi $dstentry"
pack $wi.b.ok $wi.b -side bottom
# populate the address list
if { $node == "" || $node == "(none)" } { return }
set dst [$dstentry get]
set i 0; set selected 0
foreach ifc [ifcList $node] {
set addr [lindex [split [getIfcIPv4addr $node $ifc] /] 0]
$wi.a.addrs insert end $addr
if { $addr == $dst } { set selected $i }
incr i
}
$wi.a.addrs selection set $selected
}
proc popdownAddressPicker { wi dstentry } {
$dstentry delete 0 end
set sel [$wi.a.addrs curselection]
if { $sel != "" } { $dstentry insert 0 [$wi.a.addrs get $sel] }
destroy $wi
}
proc nextTrafficFlowNumber { } {
global g_traffic_flows
set max 0
foreach flow $g_traffic_flows {
set n [lindex $flow 1]
if { $n > $max } { set max $n }
}
set next [expr {$max + 1}]
return $next
}
proc getTrafficFlow { name } {
global g_traffic_flows
foreach flow $g_traffic_flows {
if { [lindex $flow 0] == $name } { return $flow }
}
return ""
}
proc removeTrafficFlow { name } {
global g_traffic_flows
for { set i 0 } { $i < [llength $g_traffic_flows] } { incr i } {
set flow [lindex $g_traffic_flows $i]
if { [lindex $flow 0] == $name } {
set g_traffic_flows [lreplace $g_traffic_flows $i $i]
return true
}
}
return false
}
# returns a list of node numbers and MGEN scripts for a particular flow
proc getTrafficScripts { flow } {
if { $flow == "" } { return [list "" "" "" ""] }
set name [lindex $flow 0]
set num [lindex $flow 1]
set start [lindex $flow 2]
set stop [lindex $flow 3]
set srcnode [lindex $flow 4]
set dstnode [lindex $flow 5]
set srcip "" ;#[lindex $flow 6] source IP is currently ignored
set srcport [lindex $flow 7]
set srclog [lindex $flow 8]
set dstip [lindex $flow 9]
set dstport [lindex $flow 10]
set dstlog [lindex $flow 11]
set proto [lindex $flow 12]
set tos [lindex $flow 13]
set patt [lindex $flow 14]
set extra [lindex $flow 15]
foreach n [list src dst] {
if { [set ${n}ip] != "" } {
if { [set ${n}port] != "" } {
set n${n} "[set ${n}ip]/[set ${n}port]"
} else {
set n${n} [set ${n}ip]
}
} else { set n${n} [set ${n}port] }
}
if { $tos != "" } { set tos "TOS $tos " }
set s ""; set d ""
if { $srcnode != "" } {
set s "# CORE MGEN script: $name"
set s "$s\n$start ON $num $proto SRC $nsrc DST $ndst $patt $tos$extra"
}
if { $dstnode != "" } {
set d "# CORE MGEN script: $name"
if { [isMulticast $dstip] } {
set d "$d\n$start JOIN $dstip"
if { $dstport != "" } { set d "$d PORT $dstport" }
}
set d "$d\n$start LISTEN $proto $dstport"
}
if { $stop != "" } {
if { $s != "" } { set s "$s\n$stop OFF $num" }
if { $d != "" } {
set d "$d\n$stop IGNORE $proto $dstport"
if { [isMulticast $dstip] } {
set d "$d\n$stop LEAVE $dstip"
if { $dstport != "" } { set d "$d PORT $dstport" }
}
}
}
return [list $srcnode $dstnode "$s\n" "$d\n"]
}
# send File API messages to create traffic scripts on nodes
proc sendTrafficScripts { sock } {
global g_traffic_flows
foreach flow $g_traffic_flows { sendTrafficScript $flow $sock }
}
proc sendTrafficScript { flow sock } {
set scripts [getTrafficScripts $flow]
set srcnode [lindex $scripts 0]
set dstnode [lindex $scripts 1]
set flownum [lindex $flow 1]
set name "flow$flownum.mgn"
if { $srcnode != "" } {
set data [lindex $scripts 2]
set data_len [string length $data]
sendFileMessage $sock $srcnode "" $name "" $data $data_len
}
if { $dstnode != "" } {
set data [lindex $scripts 3]
set data_len [string length $data]
sendFileMessage $sock $dstnode "" $name "" $data $data_len
}
}
proc startTrafficScripts { } {
global g_traffic_flows
foreach flow $g_traffic_flows { startstopTrafficScript $flow start }
}
proc stopTrafficScripts { } {
global g_traffic_flows
foreach flow $g_traffic_flows { startstopTrafficScript $flow stop }
}
proc startstopTrafficScript { flow startstop } {
set flownum [lindex $flow 1]
set srcnode [lindex $flow 4]
set dstnode [lindex $flow 5]
set srclog [lindex $flow 8]
set dstlog [lindex $flow 11]
set name "flow$flownum.mgn"
set mgen "mgen" ;# assume mgen is in PATH, otherwise change this
# start receiving script first, then sender
foreach n [list dst src] {
set sock [lindex [getEmulPlugin [set ${n}node]] 2]
if { [set ${n}node] == "" || $sock == -1 } { continue }
if { $startstop == "start" } {
set cmd "$mgen instance [set ${n}node]$name input $name"
if { [set ${n}log] != "" } { set cmd "$cmd output [set ${n}log]" }
} else {
set cmd "$mgen instance [set ${n}node]$name stop"
}
sendExecMessage $sock [set ${n}node] $cmd 0 0
}
}