nseinput.tcl.in 14.9 KB
Newer Older
1
#!/usr/local/etc/emulab/nse
2

Leigh Stoller's avatar
Leigh Stoller committed
3
#
4
# Copyright (c) 2000-2004, 2006 University of Utah and the Flux Group.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
# 
# {{{EMULAB-LICENSE
# 
# This file is part of the Emulab network testbed software.
# 
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
# 
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
# License for more details.
# 
# You should have received a copy of the GNU Affero General Public License
# along with this file.  If not, see <http://www.gnu.org/licenses/>.
# 
# }}}
Leigh Stoller's avatar
Leigh Stoller committed
24 25
#

26
global CLIENTVARDIR
27
global CLIENTBINDIR
28
set CLIENTVARDIR @CLIENT_VARDIR@
29
set CLIENTBINDIR @CLIENT_BINDIR@
30
set EVENTSERVER  @EVENTSERVER@
31
set PROJDIR	 @PROJROOT_DIR@
32

33 34 35 36
# consults tmcc hostnames database to translate hostname to IP
# returns ip address of name
proc getipaddr {name} {

37 38 39 40 41 42 43
    set resolver [new TbResolver]
    set ip [$resolver lookup $name]
    delete $resolver
    if { $ip != {} } {
	return $ip	
    }

44 45
    set hostnamelist [split [exec tmcc hostnames] "\n"]
    foreach hostname $hostnamelist {
46 47 48 49 50 51 52 53 54 55 56
	if { $hostname == {} } {
	    continue
	}
	set ret1 [regexp -- {NAME=([-\w\.]+) } $hostname Matchvar hname]
	set ret2 [regexp -- {IP=([0-9\.]*) } $hostname Matchvar ip]
	set ret3 [regexp -- {ALIASES='([-\w\. ]*)'} $hostname Matchvar aliases]
	if { $ret == 0 || $ret1 == 0 || $ret2 == 0 } {
	    puts stderr "NSE: tmcc hostnames format has changed."
	    puts stderr "NSE: Contact testbed operations to fix this."
	    exit -1
	}
57 58 59 60 61
	set aliaslist [split $aliases " "]
	foreach alias $aliaslist {
	    if { $alias == $name } {
		return $ip
	    }
62 63 64 65 66 67
	}
    }
    puts stderr "NSE: Could not find ipaddress for $name"
    return ""
}

68 69 70 71 72
# Reads tmcc ifconfig output and constructs an array of
# IP address to MAC address mappings
proc readifconfig {} {
    global CLIENTVARDIR
    set tmccifconfig [open $CLIENTVARDIR/boot/tmcc.ifconfig r]
73 74
    set ifconf [read $tmccifconfig]
    close $tmccifconfig
75 76 77

    global tbiptomac
    global tbiptortabid
78
    set ifconfiglist [split $ifconf "\n"]
79
    foreach ifconfig $ifconfiglist {
80 81
	if { $ifconfig == {} } {
	    continue
82
	}
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
	set ret [regexp -- {IFACETYPE=(\w*) } $ifconfig Matchvar ifacetype]
	if { $ret == 0 } {
            puts stderr "NSE: tmcc ifconfig format has changed."
            puts stderr "NSE: Contact testbed operations to fix this."
	    exit -1
	}
	if { $ifacetype == "veth" } {
	    set ret [regexp -- {VMAC=(\w*) } $ifconfig Matchvar mac]
	} else {
	    set ret [regexp -- {MAC=(\w*) } $ifconfig Matchvar mac]
	}
	set ret1 [regexp -- {INET=([0-9\.]*) } $ifconfig Matchvar inet]
	set ret2 [regexp -- {MASK=([0-9\.]*) } $ifconfig Matchvar mask]
	set ret3 [regexp -- {RTABID=(\d*)} $ifconfig Matchvar rtabid]
	if { $ret == 0 || $ret1 == 0 || $ret2 == 0 || $ret3 == 0  } {
	    puts stderr "NSE: tmcc ifconfig format has changed."
	    puts stderr "NSE: Contact testbed operations to fix this."
	    exit -1
	}
	set tbiptomac($inet) $mac
	set tbiptortabid($inet) $rtabid
104 105 106
    }
}

107

108 109 110 111 112 113 114
# consults info from tmcc ifconfig and findif to find the interface name
# returns the interface name for ipaddr
proc getif {ipaddr} {
    global tbiptomac

    if { [info exists tbiptomac($ipaddr)] } {
	return [exec findif $tbiptomac($ipaddr)]
115 116
    }

117
    puts stderr "NSE: getif: Could not find the interface name for $ipaddr"
118 119 120
    return ""
}

121 122 123 124 125 126 127 128
# consults info from tmcc ifconfig and findif to find the interface name
# returns the interface name for ipaddr
proc getrtabid {ipaddr} {
    global tbiptortabid

    if { [info exists tbiptortabid($ipaddr)] } {
	return $tbiptortabid($ipaddr)
    }
129

130 131 132
    puts stderr "NSE: getrtabid: Could not find the rtabid for $ipaddr"
    return ""
}
133

134 135 136 137 138 139 140 141 142 143
proc getmac {ipaddr} {
    global tbiptomac

    if { [info exists tbiptomac($ipaddr)] } {
	set mac $tbiptomac($ipaddr)
	set macaddrchars [split $mac ""]
	set i 0
	while { $i < [llength $macaddrchars] } {
	    lappend mac2chars "[lindex $macaddrchars $i][lindex $macaddrchars [expr $i + 1]]"
	    set i [expr $i + 2]
144
	}
145
	return [join $mac2chars ":"]
146
    }
147 148 149

    puts stderr "NSE: getmac: Could not find the interface name for $ipaddr"
    return ""
150 151 152
}

proc findcpuspeed {} {
153
    if { [catch {set speed [exec sysctl -n machdep.tsc_freq]}] == 0 && $speed != {} } {
154 155
         return $speed
    }
156 157 158 159
    set dmesgfd [open /var/run/dmesg.boot r]
    set dmesg [read $dmesgfd]
    close $dmesgfd

160
    set tscregret [regexp {TSC[\" \t]*frequency[ \t]*(\d+)[ \t]*Hz} $speed]
161 162
    set regret [regexp {CPU:\D*(\d+\.?\d*)-([MmGg][Hh][zZ])} $dmesg matchstr speed mghz]

163 164 165
    if { $tscregret == 1 } {
         return $speed
    } elseif { $regret == 1 } {
166 167 168 169 170 171 172 173 174 175 176 177 178 179
	
	if { [regexp -nocase {mhz} $mghz] == 1 } {
	    return [expr $speed * 1000000]
	} elseif { [regexp -nocase {ghz} $mghz] == 1 } {
	    return [expr $speed * 1000000000]
	} else {
	    return -1
	}

    } else {
	return -1
    }
}

180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
proc readroutes {} {
    global CLIENTVARDIR
    set tmccroutelist [open $CLIENTVARDIR/boot/tmcc.routelist r]
    set routeliststr [read $tmccroutelist]
    close $tmccroutelist

    global tbroutes
    
    set routelist [split $routeliststr \n]
    unset routeliststr
    foreach route $routelist {
	if { $route == {} } {
	    continue
	}
	set ret [scan $route "ROUTE NODE=%s SRC=%s DEST=%s DESTTYPE=%s DESTMASK=%s NEXTHOP=%s COST=%s" \
		              node src dst dsttype dstmask nexthop cost]
	# we ensure that by expecting all 7 conversions in scan to happen for correct lines
	# probably a ROUTERTYPE line if the conversion fails
	if { $ret == 7 } {
	    lappend tbroutes($node) "$dst:$dstmask:$nexthop"
	}
    }
}

204 205 206 207 208 209 210
proc readtrafgens {} {
    global CLIENTVARDIR
    global tbtrafgens

    set tmcctrafgens [open $CLIENTVARDIR/boot/tmcc.trafgens r]
    set tmcctraf [read $tmcctrafgens]
    close $tmcctrafgens
211

212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
    set trafgenlist [split $tmcctraf "\n"]
    set formatstr {TRAFGEN=%s MYNAME=%s MYPORT=%u PEERNAME=%s PEERPORT=%u PROTO=%s ROLE=%s GENERATOR=%s}
    foreach trafgen $trafgenlist {
	if { $trafgen == {} } {
	    continue
	}
	
	scan $trafgen $formatstr traf myname myport peername peerport proto role gen
	if { $gen != "NSE" || $proto != "tcp" } {
	    continue
	}
	set tbtrafgens($traf) "$myname:$myport:$peername:$peerport"
    }
}

227 228 229 230 231
# call it after evaluating nseconfigs
# This will parse tmcc routelist and
# store a list of routes for all source nodes that are
# in this simulation

232
set tmccnseconfigs [open $CLIENTVARDIR/boot/tmcc.nseconfigs r]
233 234
set nseconfig [read $tmccnseconfigs]
close $tmccnseconfigs
235 236 237 238 239 240 241

# If there is no nseconfig associated with this
# node, then we just give up
if { $nseconfig == {} } {
   exit 0
}

242 243
set nsetrafgen_present 0
set simcode_present 0
244

245
# since we ran the original script through NSE (without running the simulation),
246 247
# we can ignore all sorts of errors.
# XXX: Hmm not true anymore. Need to fix this later.
248 249
if { [catch {eval $nseconfig} errMsg] == 1 } {
    puts stderr "NSE: syntax error evaluating script: $errMsg"
250 251
}

252 253 254 255 256
# ifconfig
readifconfig

# Routes
readroutes
257

258 259 260
# Traffic Generators used only if NSE based traffic generators are present
readtrafgens

261 262 263
# the name of the simulator instance variable might not
# always be 'ns', coming from the TB parser
set ns [Simulator instance]
264 265

# we only need 1 RAW IP socket for introducing packets into the network
266 267
set ipnetcommon [new Network/IP]
$ipnetcommon open writeonly
268

269 270
# configuring NSE FullTcp traffic generators
if { $nsetrafgen_present == 1 } {
271

272 273
    # The following nodes are present 
    set n0_FullTcp [$ns node]
274

275 276
    # set sysctl tcp blackhole to 2
    exec sysctl -w net.inet.tcp.blackhole=2
277

278 279 280 281 282
    lappend tcpclasses "Agent/TCP/FullTcp"
    foreach tcpsubclass [Agent/TCP/FullTcp info subclass] {
	lappend tcpclasses $tcpsubclass
    }
    
283 284 285 286
    # 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 /var/emulab
287
    
288
    set i 0
289 290 291 292 293 294 295 296 297 298 299
    foreach tcpclass $tcpclasses {
	set tcpobjs [$tcpclass info instances]
	
	foreach tcpobj $tcpobjs {
	    
	    # objname is a instance variable that was put
	    # by TB parser on objects instantiated by it.
	    # we are concerned only with those. the rest
	    # of the FullTcp objects could be created as
	    # as a result of a combined simulation and
	    # emulation scenario
300 301
	    set tcptbname [$tcpobj set tbname]
	    if { $tcptbname != {} } {
302
		$ns attach-agent $n0_FullTcp $tcpobj
303

304 305 306 307 308 309 310 311 312 313
		set trafrecord [split $tbtrafgens($tcptbname) ":"]
		set myname [lindex $trafrecord 0]
		set myport [lindex $trafrecord 1]
		set peername [lindex $trafrecord 2]
		set peerport [lindex $trafrecord 3]

		# convert myname and peername to corresponding ipaddresses
		# using the getipaddr helper subroutine
		set myipaddr [getipaddr $myname]
		set peeripaddr [getipaddr $peername]
314
	    
315 316 317 318 319 320 321 322 323
		# find interface name with a helper subroutine
		set interface [getif $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
324
	    
325 326 327 328 329
		# open the bpf, set the filter for capturing incoming packets towards
		# the current tcp object
		set bpf_tcp($i) [new Network/Pcap/Live]
		set dev_tcp($i) [$bpf_tcp($i) open readonly $interface]
		$bpf_tcp($i) filter "tcp and dst $myipaddr and dst port $myport and src $peeripaddr and src port $peerport"
330
	    
331 332 333
		# associate the 2 network objects in the TCPTap object
		$tcptap($i) network-incoming $bpf_tcp($i)
		$tcptap($i) network-outgoing $ipnetcommon
334
	    
335 336
		# attach the TCPTap agent to node n1_FullTcp
		$ns attach-agent $n0_FullTcp $tcptap($i)
337
	    
338 339 340 341 342
		# connect this tap and the particular tcp agent
		$ns connect $tcpobj $tcptap($i)

		incr i
	    }
343
	}
344 345 346
    }
}

347 348 349 350
if { $simcode_present == 1 } {

    exec sysctl -w net.inet.ip.forwarding=0 net.inet.ip.fastforwarding=0
    
351 352
    # Disabling and Enabling interfaces so that routes are all
    # flushed and we can start cleanly
353 354 355 356
    if { [file exists $CLIENTVARDIR/boot/rc.ifc] } { 
    	exec $CLIENTVARDIR/boot/rc.ifc disable
	exec $CLIENTVARDIR/boot/rc.ifc enable
    }
357
    
358 359 360
    # Now, we configure IPTaps for links between real and simulated nodes
    set i 0    
    
361
    foreach nodeinst [concat [Node info instances] [Node/MobileNode info instances]] {
362

363 364 365 366 367 368 369 370 371 372 373 374 375 376
	if { [$nodeinst info vars tbname] != {} } {
	    set tbnodename [$nodeinst set tbname]

	    if { [info exists tbroutes($tbnodename)] } {
		foreach route $tbroutes($tbnodename) {
		    set rt [split $route ":"]
		    # format is dst:dstmask:nexthop
		    # add-route-to-ip ip nhopip mask
		    $nodeinst add-route-to-ip [lindex $rt 0] [lindex $rt 2] [lindex $rt 1]
		    # We dont really consider the case where different routes
		    # have different masks. A complete longest prefix match
		    # implementation that is also efficient for nse may
		    # have to be done in the future
		}
377
	    }
378 379
	}
    }
380

381 382
    # Perhaps create the veths right here
    foreach rlink [Rlink info instances] {
383 384 385
	if { $rlink == {} } {
	    continue
	}
386 387 388
	set iptap [$rlink target]
	set ip [$rlink srcipaddr]
	set iface [getif $ip] 
389 390
	set rtabid [getrtabid $ip]
	$iptap ipaddr $ip
391 392 393

	# except for the current host itself
	set bpf_ip [new Network/Pcap/Live]
394
	set devname [$bpf_ip open readonly $iface]
395 396 397 398 399 400
	set mac [getmac $ip]
	if { $mac != {} } {
	    $bpf_ip filter "ip and not ether src $mac"
	} else {
	    $bpf_ip filter "ip"
	}
401 402
	# associate the 2 network objects in the IPTap object
	$iptap network-incoming $bpf_ip
403

404
	set srcnode [$rlink src]
405
	if { [info exists ipnet($srcnode)] } {
406
	    $iptap network-outgoing $ipnet($srcnode)
407
	    $iptap icmpagent $icmpagt($srcnode)
408 409 410
	} else {
	    set ipnet($srcnode) [new Network/IP]
	    $ipnet($srcnode) open writeonly	    
411
	    $ipnet($srcnode) setrtabid $rtabid
412 413
	    $iptap network-outgoing $ipnet($srcnode)

414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
	    set icmpagt($srcnode) [new Agent/IcmpAgent]
	    $ns attach-agent $srcnode $icmpagt($srcnode)
	    $iptap icmpagent $icmpagt($srcnode)

	    if { [$srcnode info vars tbname] != {} } {
		set tbnodename [$srcnode set tbname]

		if { [info exists tbroutes($tbnodename)] } {
		    foreach route $tbroutes($tbnodename) {
			set rt [split $route ":"]
			set ip [lindex $rt 0]
			set nhopip [lindex $rt 2]
			set mask [lindex $rt 1]
			# Need to add routes for nodes that have
			# rlinks so that packets that leave the
			# pnode will be routed by the kernel.
			# We will use the "route" command to
			# add routes. Since we are already
			# running as root, no need to run sudo.
			# Also, if a route add fails, it is probably
			# due to the nexthop being a sim node on the
			# this pnode for which the nexthop IP is not
			# attached to any interface and the kernel does
			# not know how to route to it. But that's ok
			# coz we have added a route already in the sim
			if { $rtabid > 0 } {
			    catch {exec route add -rtabid $rtabid $ip $nhopip $mask}
			} else {
			    catch {exec route add $ip $nhopip $mask}
			}
		    }
		}
	    }
	}
448 449 450

	# No need to attach the iptap to the node coz it was
	# done when an rlink got created
451 452 453
    }
}

454
# get some params to configure the event system interface
455 456 457 458 459

set pideidlist [split [exec hostname] "."]
set vnode [lindex $pideidlist 0]
set eid [lindex $pideidlist 1]
set pid [lindex $pideidlist 2]
460
set logpath "$PROJDIR/$pid/exp/$eid/logs/nse-$vnode.log"
461
set simobjname [$ns set tbname]
462
set nseswap_cmdline "$CLIENTBINDIR/tevc -s $EVENTSERVER -e $pid/$eid now $simobjname NSESWAP SIMHOST=$vnode"
463

464
set pktrate_logpath "$PROJDIR/$pid/exp/$eid/logs/nse-vnodepktrate-$vnode.log"
465

466 467
# Configuring the Scheduler to monitor the event system
set evsink [new TbEventSink]
468
$evsink event-server "elvin://$EVENTSERVER"
469
$evsink nseswap_cmdline $nseswap_cmdline
470 471
$evsink logfile $logpath
[$ns set scheduler_] tbevent-sink $evsink
472

473 474 475 476 477
set cpuspeed [findcpuspeed]
if { $cpuspeed != -1 } {
    [$ns set scheduler_] cpuspeed $cpuspeed
}

478 479 480 481 482 483
# Releasing memory that is not needed so that
# the simulation run might get them
if { [info exists tbroutes] } {
    unset tbroutes
}

484 485 486 487 488 489 490 491 492
# Adding an event to send an ISUP event to the testbed
# Note that the actual events specified by the user will
# come from the event system after a little while. So, this
# first event at 0.0 will ensure that the simulator scheduler
# has subscribed to the event system and is processing the
# first event
$ns at 0.0 {exec echo "Informing the testbed that we're up and running ..." > /dev/console}
$ns at 0.0 "exec $CLIENTBINDIR/tmcc state ISUP"

493 494 495 496 497 498 499 500 501 502 503 504 505 506 507
proc write-vnode-pktrate {} {
    global pktrate_logpath
    set f [open $pktrate_logpath w]
    foreach nodeinst [concat [Node info instances] [Node/MobileNode info instances]] {

	if { [$nodeinst info vars tbname] == {} } {
	    continue
	}
	set tbnodename [$nodeinst set tbname]
	set pktrate [$nodeinst get-pktrate]
	puts $f "$tbnodename=$pktrate"
    }
    close $f
}

508
$ns run