aboutsummaryrefslogtreecommitdiffstats
path: root/tools/pkt_gen/ixia/pass_fail.tcl
diff options
context:
space:
mode:
authorBilly O'Mahony <billy.o.mahony@intel.com>2015-05-29 15:24:03 +0100
committerBilly O'Mahony <billy.o.mahony@intel.com>2015-06-08 13:55:35 +0000
commit8d6777df09c3dc441013a31f21cc50ab3b0f42a3 (patch)
treed00f189e00631c33385122012727dd3c6438f406 /tools/pkt_gen/ixia/pass_fail.tcl
parentacd2499310f81565c6b1eb11d18528f7372894f5 (diff)
framework: Add reworked framework to repo
This commit adds the vSwitch Integration Test Framework whose design, based off TOIT, is outlined in the HLD previously made availiable to the community for review. The design of this framework allows developers to add different implementations of components, specifically vSwitches, Traffic Generators, Metrics Collectors and VNFs, easily. The goal of this design is that all testcases should run regardless of what is "under the hood". This commit adds support for running the framework for a phy to phy RFC2544 testcase only. More testcases will be added by the community. vSwitches supported at this time: * Intel DPDK (r) accelerated OpenvSwitch Traffic Generators supported at this time: * IxNet - IxNetwork Implementation * Ixia - IxExplorer Implementation * Dummy - Manual Implementation Metrics Collectors supported at this time: * Linux Metrics No VNFs are supported at this time but the framework outlines how they should be integrated and provides APIs for them to adhere to. JIRA: VSPERF-27 Change-Id: I312e1a1199487ffee8f824be06cd97d4f793eee0 Signed-off-by: Stephen Finucane <Stephen.Finucane@intel.com> Signed-off-by: Meghan Halton <Meghan.Halton@intel.com> Signed-off-by: Christopher Nolan <Christopher.Nolan@intel.com> Signed-off-by: Maryam Tahhan <Maryam.Tahhan@intel.com> Signed-off-by: Ciara Loftus <Ciara.Loftus@intel.com> Signed-off-by: Mark Kavanagh <Mark.B.Kavanagh@intel.com> Signed-off-by: Cian Ferriter <Cian.Ferriter@intel.com> Signed-off-by: Timo Puha <TimoX.Puha@intel.com> Signed-off-by: Billy O'Mahony <billy.o.mahony@intel.com> Signed-off-by: Michal Weglicki <MichalX.Weglicki@intel.com> Signed-off-by: Rory Sexton <Rory.Sexton@intel.com> Signed-off-by: Ian Stokes <Ian.Stokes@intel.com> Signed-off-by: Kevin Traynor <Kevin.Traynor@intel.com> Signed-off-by: Dino Simeon Madarang <dino.simeonx.madarang@intel.com> Reviewed-by: Eugene Snider <Eugene.Snider@huawei.com> Reviewed-by: Aihua Li <aihua.li@huawei.com>
Diffstat (limited to 'tools/pkt_gen/ixia/pass_fail.tcl')
-rwxr-xr-xtools/pkt_gen/ixia/pass_fail.tcl721
1 files changed, 721 insertions, 0 deletions
diff --git a/tools/pkt_gen/ixia/pass_fail.tcl b/tools/pkt_gen/ixia/pass_fail.tcl
new file mode 100755
index 00000000..63d4d914
--- /dev/null
+++ b/tools/pkt_gen/ixia/pass_fail.tcl
@@ -0,0 +1,721 @@
+#!/usr/bin/env tclsh
+
+# Copyright (c) 2014, Ixia
+# Copyright (c) 2015, Intel Corporation
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+# This file is a modified version of a script generated by Ixia
+# IxExplorer.
+
+lappend auto_path [list $lib_path]
+
+package req IxTclHal
+
+###################################################################
+########################## Configuration ##########################
+###################################################################
+
+# Verify that the IXIA chassis spec is given
+
+set reqVars [list "host" "card" "port1" "port2"]
+
+foreach var $reqVars {
+ set var_ns [namespace which -variable "$var"]
+ if { [string compare $var_ns ""] == 0 } {
+ errorMsg "The '$var' variable is undefined. Did you set it?"
+ return -1
+ }
+}
+
+# constants
+
+set fullHex "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF"
+set hexToC5 "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5"
+
+#set payloadLookup(64) [string range $fullHex 0 11]
+set payloadLookup(64) "000102030405"
+set payloadLookup(128) "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445"
+set payloadLookup(256) "$hexToC5"
+set payloadLookup(512) "$fullHex$hexToC5"
+set payloadLookup(1024) "$fullHex$fullHex$fullHex$hexToC5"
+
+###################################################################
+###################### Chassis Configuration ######################
+###################################################################
+
+if {[isUNIX]} {
+ if {[ixConnectToTclServer $host]} {
+ errorMsg "Error connecting to Tcl Server $host"
+ return $::TCL_ERROR
+ }
+}
+
+######### Chassis #########
+
+# Now connect to the chassis
+if [ixConnectToChassis $host] {
+ ixPuts $::ixErrorInfo
+ return 1
+}
+
+# Get the chassis ID to use in port lists
+set chassis [ixGetChassisID $host]
+
+######### Ports #########
+
+set portList [list [list $chassis $card $port1] \
+ [list $chassis $card $port2]]
+
+# Clear ownership of the ports we’ll use
+if [ixClearOwnership $portList force] {
+ ixPuts $::ixErrorInfo
+ return 1
+}
+
+# Take ownership of the ports we’ll use
+if [ixTakeOwnership $portList] {
+ ixPuts $::ixErrorInfo
+ return 1
+}
+
+foreach portElem $portList {
+ set chasNum [lindex $portElem 0]
+ set cardNum [lindex $portElem 1]
+ set portNum [lindex $portElem 2]
+
+ port setFactoryDefaults $chasNum $cardNum $portNum
+ port config -speed 10000
+ port config -flowControl true
+ port config -transmitMode portTxModeAdvancedScheduler
+ port config -receiveMode [expr $::portCapture|$::portRxModeWidePacketGroup]
+ port config -advertise100FullDuplex false
+ port config -advertise100HalfDuplex false
+ port config -advertise10FullDuplex false
+ port config -advertise10HalfDuplex false
+ port config -portMode port10GigLanMode
+ port config -enableTxRxSyncStatsMode true
+ port config -txRxSyncInterval 2000
+ port config -enableTransparentDynamicRateChange true
+ port config -enableDynamicMPLSMode true
+ if {[port set $chasNum $cardNum $portNum]} {
+ errorMsg "Error calling port set $chasNum $cardNum $portNum"
+ }
+
+ packetGroup setDefault
+ packetGroup config -numTimeBins 1
+ if {[packetGroup setRx $chasNum $cardNum $portNum]} {
+ errorMsg "Error calling packetGroup setRx $chasNum $cardNum $portNum"
+ }
+
+ sfpPlus setDefault
+ sfpPlus config -enableAutomaticDetect false
+ sfpPlus config -txPreTapControlValue 1
+ sfpPlus config -txMainTapControlValue 63
+ sfpPlus config -txPostTapControlValue 2
+ sfpPlus config -rxEqualizerControlValue 0
+ if {[sfpPlus set $chasNum $cardNum $portNum]} {
+ errorMsg "Error calling sfpPlus set $chasNum $cardNum $portNum"
+ }
+
+ filter setDefault
+ filter config -captureTriggerFrameSizeFrom 48
+ filter config -captureTriggerFrameSizeTo 48
+ filter config -captureFilterFrameSizeFrom 48
+ filter config -captureFilterFrameSizeTo 48
+ filter config -userDefinedStat1FrameSizeFrom 48
+ filter config -userDefinedStat1FrameSizeTo 48
+ filter config -userDefinedStat2FrameSizeFrom 48
+ filter config -userDefinedStat2FrameSizeTo 48
+ filter config -asyncTrigger1FrameSizeFrom 48
+ filter config -asyncTrigger1FrameSizeTo 48
+ filter config -asyncTrigger2FrameSizeFrom 48
+ filter config -asyncTrigger2FrameSizeTo 48
+ filter config -userDefinedStat1Enable true
+ filter config -userDefinedStat2Enable true
+ filter config -asyncTrigger1Enable true
+ filter config -asyncTrigger2Enable true
+ if {[filter set $chasNum $cardNum $portNum]} {
+ errorMsg "Error calling filter set $chasNum $cardNum $portNum"
+ }
+
+ filterPallette setDefault
+ filterPallette config -pattern1 00
+ filterPallette config -patternMask1 00
+ filterPallette config -patternOffset1 20
+ filterPallette config -patternOffset2 20
+ if {[filterPallette set $chasNum $cardNum $portNum]} {
+ errorMsg "Error calling filterPallette set $chasNum $cardNum $portNum"
+ }
+
+ capture setDefault
+ capture config -sliceSize 65536
+ if {[capture set $chasNum $cardNum $portNum]} {
+ errorMsg "Error calling capture set $chasNum $cardNum $portNum"
+ }
+
+ if {[interfaceTable select $chasNum $cardNum $portNum]} {
+ errorMsg "Error calling interfaceTable select $chasNum $cardNum $portNum"
+ }
+
+ interfaceTable setDefault
+ if {[interfaceTable set]} {
+ errorMsg "Error calling interfaceTable set"
+ }
+
+ interfaceTable clearAllInterfaces
+ if {[interfaceTable write]} {
+ errorMsg "Error calling interfaceTable write"
+ }
+
+ ixEnablePortIntrinsicLatencyAdjustment $chasNum $cardNum $portNum true
+}
+
+ixWritePortsToHardware portList
+
+if {[ixCheckLinkState $portList] != 0} {
+ errorMsg "One or more port links are down"
+}
+
+proc sendTraffic { flowSpec trafficSpec } {
+ # Send traffic from IXIA.
+ #
+ # Transmits traffic from Rx port (port1), and captures traffic at
+ # Tx port (port2).
+ #
+ # Parameters:
+ # flowSpec - a dict detailing how the packet should be sent. Should be
+ # of format:
+ # {type, numpkts, time, framerate}
+ # trafficSpec - a dict describing the packet to be sent. Should be
+ # of format:
+ # { l2, vlan, l3}
+ # where each item is in turn a dict detailing the configuration of each
+ # layer of the packet
+ # Returns:
+ # Output from Rx end of Ixia if time != 0, else 0
+
+ ##################################################
+ ################# Initialisation #################
+ ##################################################
+
+ # Configure global variables. See documentation on 'global' for more
+ # information on why this is necessary
+ # https://www.tcl.tk/man/tcl8.5/tutorial/Tcl13.html
+ global portList
+
+ # Extract the provided dictionaries to local variables to simplify the
+ # rest of the script
+
+ # flow spec
+
+ set streamType [dict get $flowSpec type]
+ set numPkts [dict get $flowSpec numpkts]
+ set time [expr {[dict get $flowSpec time] * 1000}]
+ set frameRate [dict get $flowSpec framerate]
+
+ # traffic spec
+
+ # extract nested dictionaries
+ set trafficSpec_l2 [dict get $trafficSpec l2]
+ set trafficSpec_l3 [dict get $trafficSpec l3]
+ set trafficSpec_vlan [dict get $trafficSpec vlan]
+
+ set frameSize [dict get $trafficSpec_l2 framesize]
+ set srcMac [dict get $trafficSpec_l2 srcmac]
+ set dstMac [dict get $trafficSpec_l2 dstmac]
+ set srcPort [dict get $trafficSpec_l2 srcport]
+ set dstPort [dict get $trafficSpec_l2 dstport]
+
+ set proto [dict get $trafficSpec_l3 proto]
+ set srcIp [dict get $trafficSpec_l3 srcip]
+ set dstIp [dict get $trafficSpec_l3 dstip]
+
+ set vlanEnabled [dict get $trafficSpec_vlan enabled]
+ if {$vlanEnabled == 1 } {
+ # these keys won't exist if vlan wasn't enabled
+ set vlanId [dict get $trafficSpec_vlan id]
+ set vlanUserPrio [dict get $trafficSpec_vlan priority]
+ set vlanCfi [dict get $trafficSpec_vlan cfi]
+ }
+
+ ##################################################
+ ##################### Streams ####################
+ ##################################################
+
+ streamRegion get $::chassis $::card $::port1
+ if {[streamRegion enableGenerateWarningList $::chassis $::card $::port1 false]} {
+ errorMsg "Error calling streamRegion enableGenerateWarningList $::chassis $::card $::port1 false"
+ }
+
+ set streamId 1
+
+ stream setDefault
+ stream config -ifg 9.6
+ stream config -ifgMIN 19.2
+ stream config -ifgMAX 25.6
+ stream config -ibg 9.6
+ stream config -isg 9.6
+ stream config -rateMode streamRateModePercentRate
+ stream config -percentPacketRate $frameRate
+ stream config -framesize $frameSize
+ stream config -frameType "08 00"
+ stream config -sa $srcMac
+ stream config -da $dstMac
+ stream config -numSA 16
+ stream config -numDA 16
+ stream config -asyncIntEnable true
+ stream config -dma $streamType
+ stream config -numBursts 1
+ stream config -numFrames $numPkts
+ stream config -patternType incrByte
+ stream config -dataPattern x00010203
+ stream config -pattern "00 01 02 03"
+
+ protocol setDefault
+ protocol config -name ipV4
+ protocol config -ethernetType ethernetII
+ if {$vlanEnabled == 1} {
+ protocol config -enable802dot1qTag vlanSingle
+ }
+
+ ip setDefault
+ ip config -ipProtocol ipV4Protocol[string totitle $proto]
+ ip config -checksum "f6 75"
+ ip config -sourceIpAddr $srcIp
+ ip config -sourceIpAddrRepeatCount 10
+ ip config -sourceClass classA
+ ip config -destIpAddr $dstIp
+ ip config -destIpAddrRepeatCount 10
+ ip config -destClass classA
+ ip config -destMacAddr $dstMac
+ ip config -destDutIpAddr 0.0.0.0
+ ip config -ttl 64
+ if {[ip set $::chassis $::card $::port1]} {
+ errorMsg "Error calling ip set $::chassis $::card $::port1"
+ }
+
+ "$proto" setDefault
+ "$proto" config -checksum "25 81"
+ if {["$proto" set $::chassis $::card $::port1]} {
+ errorMsg "Error calling $proto set $::chassis $::card $::port"
+ }
+
+ if {$vlanEnabled == 1 } {
+ vlan setDefault
+ vlan config -vlanID $vlanId
+ vlan config -userPriority $vlanUserPrio
+ vlan config -cfi $vlanCfi
+ vlan config -mode vIdle
+ vlan config -repeat 10
+ vlan config -step 1
+ vlan config -maskval "0000XXXXXXXXXXXX"
+ vlan config -protocolTagId vlanProtocolTag8100
+ }
+
+ if {[vlan set $::chassis $::card $::port1]} {
+ errorMsg "Error calling vlan set $::chassis $::card $::port1"
+ }
+
+ if {[port isValidFeature $::chassis $::card $::port1 $::portFeatureTableUdf]} {
+ tableUdf setDefault
+ tableUdf clearColumns
+ if {[tableUdf set $::chassis $::card $::port1]} {
+ errorMsg "Error calling tableUdf set $::chassis $::card $::port1"
+ }
+ }
+
+ if {[port isValidFeature $::chassis $::card $::port1 $::portFeatureRandomFrameSizeWeightedPair]} {
+ weightedRandomFramesize setDefault
+ if {[weightedRandomFramesize set $::chassis $::card $::port1]} {
+ errorMsg "Error calling weightedRandomFramesize set $::chassis $::card $::port1"
+ }
+ }
+
+ if {$proto == "tcp"} {
+ tcp setDefault
+ tcp config -sourcePort $srcPort
+ tcp config -destPort $dstPort
+ if {[tcp set $::chassis $::card $::port1 ]} {
+ errorMsg "Error setting tcp on port $::chassis.$::card.$::port1"
+ }
+
+ if {$vlanEnabled != 1} {
+ udf setDefault
+ udf config -repeat 1
+ udf config -continuousCount true
+ udf config -initval {00 00 00 01}
+ udf config -updown uuuu
+ udf config -cascadeType udfCascadeNone
+ udf config -step 1
+
+ packetGroup setDefault
+ packetGroup config -insertSequenceSignature true
+ packetGroup config -sequenceNumberOffset 38
+ packetGroup config -signatureOffset 42
+ packetGroup config -signature "08 71 18 05"
+ packetGroup config -groupIdOffset 52
+ packetGroup config -groupId $streamId
+ packetGroup config -allocateUdf true
+ if {[packetGroup setTx $::chassis $::card $::port1 $streamId]} {
+ errorMsg "Error calling packetGroup setTx $::chassis $::card $::port1 $streamId"
+ }
+ }
+ } elseif {$proto == "udp"} {
+ udp setDefault
+ udp config -sourcePort $srcPort
+ udp config -destPort $dstPort
+ if {[udp set $::chassis $::card $::port1]} {
+ errorMsg "Error setting udp on port $::chassis.$::card.$::port1"
+ }
+ }
+
+ if {[stream set $::chassis $::card $::port1 $streamId]} {
+ errorMsg "Error calling stream set $::chassis $::card $::port1 $streamId"
+ }
+
+ incr streamId
+ streamRegion generateWarningList $::chassis $::card $::port1
+ ixWriteConfigToHardware portList -noProtocolServer
+
+ if {[packetGroup getRx $::chassis $::card $::port2]} {
+ errorMsg "Error calling packetGroup getRx $::chassis $::card $::port2"
+ }
+
+ ##################################################
+ ######### Traffic Transmit and Results ###########
+ ##################################################
+
+ # Transmit traffic
+
+ logMsg "Clearing stats for all ports"
+ ixClearStats portList
+
+ logMsg "Starting packet groups on port $::port2"
+ ixStartPortPacketGroups $::chassis $::card $::port2
+
+ logMsg "Starting Capture on port $::port2"
+ ixStartPortCapture $::chassis $::card $::port2
+
+ logMsg "Starting transmit on port $::port1"
+ ixStartPortTransmit $::chassis $::card $::port1
+
+ # If time=0 is passed, exit after starting transmit
+
+ if {$time == 0} {
+ logMsg "Sending traffic until interrupted"
+ return
+ }
+
+ logMsg "Waiting for $time ms"
+
+ # Wait for time - 1 second to get traffic rate
+
+ after [expr "$time - 1"]
+
+ # Get result
+
+ set result [stopTraffic]
+
+ if {$streamType == "contPacket"} {
+ return $result
+ } elseif {$streamType == "stopStream"} {
+ set payError 0
+ set seqError 0
+ set captureLimit 3000
+
+ # explode results from 'stopTraffic' for ease of use later
+ set framesSent [lindex $result 0]
+ set framesRecv [lindex $result 1]
+ set bytesSent [lindex $result 2]
+ set bytesRecv [lindex $result 3]
+
+ if {$framesSent <= $captureLimit} {
+ captureBuffer get $::chassis $::card $::port2 1 $framesSent
+ set capturedFrames [captureBuffer cget -numFrames]
+
+ set notCaptured [expr "$framesRecv - $capturedFrames"]
+ if {$notCaptured != 0} {
+ errorMsg "'$notCaptured' frames were not captured"
+ }
+
+ if {$proto == "tcp"} {
+ for {set z 1} {$z <= $capturedFrames} {incr z} {
+ captureBuffer getframe $z
+ set capFrame [captureBuffer cget -frame]
+ regsub -all " " $capFrame "" frameNoSpaces
+ set frameNoSpaces
+
+ set startPayload 108
+ set endPayload [expr "[expr "$frameSize * 2"] - 9"]
+ set payload [string range $frameNoSpaces $startPayload $endPayload]
+
+ if {$vlanEnabled != 1} {
+ set startSequence 76
+ set endSequence 83
+ set sequence [string range $frameNoSpaces $startSequence $endSequence]
+ scan $sequence %x seqDecimal
+ set seqDecimal
+ if {"$payload" != $::payloadLookup($frameSize)} {
+ errorMsg "frame '$z' payload: invalid payload"
+ incr payError
+ }
+ # variable z increments from 1 to total number of packets
+ # captured TCP sequence numbers start at 0, not 1. When
+ # comparing sequence numbers for captured frames, reduce
+ # variable z by 1 i.e. frame 1 with sequence 0 is compared
+ # to expected sequence 0.
+ if {$seqDecimal != $z-1} {
+ errorMsg "frame '$z' sequence number: Found '$seqDecimal'. Expected '$z'"
+ incr seqError
+ }
+ }
+ }
+ }
+ logMsg "Sequence Errors: $seqError"
+ logMsg "Payload Errors: $payError\n"
+ } else {
+ errorMsg "Too many packets for capture."
+ }
+
+ set result [list $framesSent $framesRecv $bytesSent $bytesRecv $payError $seqError]
+ return $result
+ } else {
+ errorMsg "streamtype is not supported: '$streamType'"
+ }
+}
+
+proc stopTraffic {} {
+ # Stop sending traffic from IXIA.
+ #
+ # Stops Transmit of traffic from Rx port.
+ #
+ # Returns:
+ # Output from Rx end of Ixia.
+
+ ##################################################
+ ################# Initialisation #################
+ ##################################################
+
+ # Configure global variables. See documentation on 'global' for more
+ # information on why this is necessary
+ # https://www.tcl.tk/man/tcl8.5/tutorial/Tcl13.html
+ global portList
+
+ ##################################################
+ ####### Stop Traffic Transmit and Results ########
+ ##################################################
+
+ # Read frame rate of transmission
+
+ if {[stat getRate statAllStats $::chassis $::card $::port1]} {
+ errorMsg "Error reading stat rate on port $::chassis $::card $::port1"
+ return $::TCL_ERROR
+ }
+
+ set sendRate [stat cget -framesSent]
+ set sendRateBytes [stat cget -bytesSent]
+
+ if {[stat getRate statAllStats $::chassis $::card $::port2]} {
+ errorMsg "Error reading stat rate on port $::chassis $::card $::port2"
+ return $::TCL_ERROR
+ }
+
+ set recvRate [stat cget -framesReceived]
+ set recvRateBytes [stat cget -bytesReceived]
+
+ # Wait for a second, else we get funny framerate statistics
+ after 1
+
+ # Stop transmission of traffic
+ ixStopTransmit portList
+
+ if {[ixCheckTransmitDone portList] == $::TCL_ERROR} {
+ return -code error
+ } else {
+ logMsg "Transmission is complete.\n"
+ }
+
+ ixStopPacketGroups portList
+ ixStopCapture portList
+
+ # Get statistics
+
+ if {[stat get statAllStats $::chassis $::card $::port1]} {
+ errorMsg "Error reading stat on port $::chassis $::card $::port1"
+ return $::TCL_ERROR
+ }
+
+ set bytesSent [stat cget -bytesSent]
+ set framesSent [stat cget -framesSent]
+
+ if {[stat get statAllStats $::chassis $::card $::port2]} {
+ errorMsg "Error reading stat on port $::chassis $::card $::port2"
+ return $::TCL_ERROR
+ }
+
+ set bytesRecv [stat cget -bytesReceived]
+ set framesRecv [stat cget -framesReceived]
+
+ set bytesDropped [expr "$bytesSent - $bytesRecv"]
+ set framesDropped [expr "$framesSent - $framesRecv"]
+
+ logMsg "Frames Sent: $framesSent"
+ logMsg "Frames Recv: $framesRecv"
+ logMsg "Frames Dropped: $framesDropped\n"
+
+ logMsg "Bytes Sent: $bytesSent"
+ logMsg "Bytes Recv: $bytesRecv"
+ logMsg "Bytes Dropped: $bytesDropped\n"
+
+ logMsg "Frame Rate Sent: $sendRate"
+ logMsg "Frame Rate Recv: $recvRate\n"
+
+ set result [list $framesSent $framesRecv $bytesSent $bytesRecv $sendRate $recvRate $sendRateBytes $recvRateBytes]
+
+ return $result
+}
+
+proc rfcThroughputTest { testSpec trafficSpec } {
+ # Execute RFC tests from IXIA.
+ #
+ # Wraps the sendTraffic proc, repeatedly calling it, storing the result and
+ # performing an iterative binary search to find the highest possible RFC
+ # transmission rate. Abides by the specification of RFC2544 as given by the
+ # IETF:
+ #
+ # https://www.ietf.org/rfc/rfc2544.txt
+ #
+ # Parameters:
+ # testSpec - a dict detailing how the test should be run. Should be
+ # of format:
+ # {numtrials, duration, lossrate}
+ # trafficSpec - a dict describing the packet to be sent. Should be
+ # of format:
+ # { l2, l3}
+ # where each item is in turn a dict detailing the configuration of each
+ # layer of the packet
+ # Returns:
+ # Highest rate with acceptable packet loss.
+
+ ##################################################
+ ################# Initialisation #################
+ ##################################################
+
+ # Configure global variables. See documentation on 'global' for more
+ # information on why this is necessary
+ # https://www.tcl.tk/man/tcl8.5/tutorial/Tcl13.html
+ global portList
+
+ # Extract the provided dictionaries to local variables to simplify the
+ # rest of the script
+
+ # testSpec
+
+ set numTrials [dict get $testSpec trials] ;# we don't use this yet
+ set duration [dict get $testSpec duration]
+ set lossRate [dict get $testSpec lossrate]
+ set multipleStream [dict get $testSpec multipleStreams] ;# we don't use this yet
+
+ # variables used for binary search of results
+ set min 1
+ set max 100
+ set diff [expr "$max - $min"]
+
+ set result [list 0 0 0 0 0 0 0 0] ;# best result found so far
+ set percentRate 100 ;# starting throughput percentage rate
+
+ ##################################################
+ ######### Traffic Transmit and Results ###########
+ ##################################################
+
+ # iterate a maximum of 20 times, sending packets at a set rate to
+ # find fastest possible rate with acceptable packetloss
+ #
+ # As a reminder, the binary search works something like this:
+ #
+ # percentRate < idealValue --> min = percentRate
+ # percentRate > idealValue --> max = percentRate
+ # percentRate = idealValue --> max = min = percentRate
+ #
+ for {set i 0} {$i < 20} {incr i} {
+ dict set flowSpec type "contPacket"
+ dict set flowSpec numpkts 100 ;# this can be bypassed
+ dict set flowSpec time $duration
+ dict set flowSpec framerate $percentRate
+
+ set flowStats [sendTraffic $flowSpec $trafficSpec]
+
+ # explode results from 'sendTraffic' for ease of use later
+ set framesSent [lindex $flowStats 0]
+ set framesRecv [lindex $flowStats 1]
+ set sendRate [lindex $flowStats 4]
+
+ set framesDropped [expr "$framesSent - $framesRecv"]
+ if {$framesSent > 0} {
+ set framesDroppedRate [expr "double($framesDropped) / $framesSent"]
+ } else {
+ set framesDroppedRate 100
+ }
+
+ # check if we've already found the rate before 10 iterations, i.e.
+ # 'percentRate = idealValue'. This is as accurate as we can get with
+ # integer values.
+ if {[expr "$max - $min"] <= 0.5 } {
+ break
+ }
+
+ # handle 'percentRate <= idealValue' case
+ if {$framesDroppedRate <= $lossRate} {
+ logMsg "Frame sendRate of '$sendRate' pps succeeded ('$framesDropped' frames dropped)"
+
+ set result $flowStats
+ set min $percentRate
+
+ set percentRate [expr "$percentRate + ([expr "$max - $min"] * 0.5)"]
+ # handle the 'percentRate > idealValue' case
+ } else {
+ if {$framesDropped == $framesSent} {
+ errorMsg "Dropped all frames!"
+ }
+
+ errorMsg "Frame sendRate of '$sendRate' pps failed ('$framesDropped' frames dropped)"
+
+ set max $percentRate
+ set percentRate [expr "$percentRate - ([expr "$max - $min"] * 0.5)"]
+ }
+ }
+
+ set bestRate [lindex $result 4]
+
+ logMsg "$lossRate% packet loss rate: $bestRate"
+
+ return $result
+}