Merge "Tools: Improve Stability."
[vswitchperf.git] / 3rd_party / ixia / pass_fail.tcl
1 #!/usr/bin/env tclsh
2
3 # Copyright (c) 2014, Ixia
4 # Copyright (c) 2015-2016, Intel Corporation
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions
9 # are met:
10 #
11 # 1. Redistributions of source code must retain the above copyright
12 # notice, this list of conditions and the following disclaimer.
13 #
14 # 2. Redistributions in binary form must reproduce the above copyright
15 # notice, this list of conditions and the following disclaimer in the
16 # documentation and/or other materials provided with the distribution.
17 #
18 # 3. Neither the name of the copyright holder nor the names of its
19 # contributors may be used to endorse or promote products derived
20 # from this software without specific prior written permission.
21 #
22 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 # POSSIBILITY OF SUCH DAMAGE.
34
35 # This file is a modified version of a script generated by Ixia
36 # IxExplorer.
37
38 lappend auto_path [list $lib_path]
39
40 package req IxTclHal
41
42 ###################################################################
43 ########################## Configuration ##########################
44 ###################################################################
45
46 # Verify that the IXIA chassis spec is given
47
48 set reqVars [list "host" "card" "port1" "port2"]
49
50 foreach var $reqVars {
51     set var_ns [namespace which -variable "$var"]
52     if { [string compare $var_ns ""] == 0 } {
53         errorMsg "The '$var' variable is undefined. Did you set it?"
54         return -1
55     }
56 }
57
58 # constants
59
60 set fullHex                             "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF"
61 set hexToC5                             "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5"
62
63 #set payloadLookup(64)                   [string range $fullHex 0 11]
64 set payloadLookup(64)                   "000102030405"
65 set payloadLookup(128)                  "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445"
66 set payloadLookup(256)                  "$hexToC5"
67 set payloadLookup(512)                  "$fullHex$hexToC5"
68 set payloadLookup(1024)                 "$fullHex$fullHex$fullHex$hexToC5"
69
70 ###################################################################
71 ###################### Chassis Configuration ######################
72 ###################################################################
73
74 if {[isUNIX]} {
75     if {[ixConnectToTclServer $host]} {
76         errorMsg "Error connecting to Tcl Server $host"
77         return $::TCL_ERROR
78     }
79 }
80
81 ######### Chassis #########
82
83 # Now connect to the chassis
84 if [ixConnectToChassis $host] {
85     ixPuts $::ixErrorInfo
86     return 1
87 }
88
89 # Get the chassis ID to use in port lists
90 set chassis [ixGetChassisID $host]
91
92 #########  Ports #########
93
94 set portList [list [list $chassis $card $port1] \
95                    [list $chassis $card $port2]]
96
97 # Clear ownership of the ports we’ll use
98 if [ixClearOwnership $portList force] {
99     ixPuts $::ixErrorInfo
100     return 1
101 }
102
103 # Take ownership of the ports we’ll use
104 if [ixTakeOwnership $portList] {
105     ixPuts $::ixErrorInfo
106     return 1
107 }
108
109 foreach portElem $portList {
110     set chasNum [lindex $portElem 0]
111     set cardNum [lindex $portElem 1]
112     set portNum [lindex $portElem 2]
113
114     port setFactoryDefaults $chasNum $cardNum $portNum
115     port config -speed                                10000
116     port config -flowControl                          true
117     port config -transmitMode                         portTxModeAdvancedScheduler
118     port config -receiveMode                          [expr $::portCapture|$::portRxModeWidePacketGroup]
119     port config -advertise100FullDuplex               false
120     port config -advertise100HalfDuplex               false
121     port config -advertise10FullDuplex                false
122     port config -advertise10HalfDuplex                false
123     port config -portMode                             port10GigLanMode
124     port config -enableTxRxSyncStatsMode              true
125     port config -txRxSyncInterval                     2000
126     port config -enableTransparentDynamicRateChange   true
127     port config -enableDynamicMPLSMode                true
128     if {[port set $chasNum $cardNum $portNum]} {
129         errorMsg "Error calling port set $chasNum $cardNum $portNum"
130     }
131
132     packetGroup setDefault
133     packetGroup config -numTimeBins                   1
134     if {[packetGroup setRx $chasNum $cardNum $portNum]} {
135         errorMsg "Error calling packetGroup setRx $chasNum $cardNum $portNum"
136     }
137
138     sfpPlus setDefault
139     sfpPlus config -enableAutomaticDetect             false
140     sfpPlus config -txPreTapControlValue              1
141     sfpPlus config -txMainTapControlValue             63
142     sfpPlus config -txPostTapControlValue             2
143     sfpPlus config -rxEqualizerControlValue           0
144     if {[sfpPlus set $chasNum $cardNum $portNum]} {
145         errorMsg "Error calling sfpPlus set $chasNum $cardNum $portNum"
146     }
147
148     filter setDefault
149     filter config -captureTriggerFrameSizeFrom        48
150     filter config -captureTriggerFrameSizeTo          48
151     filter config -captureFilterFrameSizeFrom         48
152     filter config -captureFilterFrameSizeTo           48
153     filter config -userDefinedStat1FrameSizeFrom      48
154     filter config -userDefinedStat1FrameSizeTo        48
155     filter config -userDefinedStat2FrameSizeFrom      48
156     filter config -userDefinedStat2FrameSizeTo        48
157     filter config -asyncTrigger1FrameSizeFrom         48
158     filter config -asyncTrigger1FrameSizeTo           48
159     filter config -asyncTrigger2FrameSizeFrom         48
160     filter config -asyncTrigger2FrameSizeTo           48
161     filter config -userDefinedStat1Enable             true
162     filter config -userDefinedStat2Enable             true
163     filter config -asyncTrigger1Enable                true
164     filter config -asyncTrigger2Enable                true
165     if {[filter set $chasNum $cardNum $portNum]} {
166         errorMsg "Error calling filter set $chasNum $cardNum $portNum"
167     }
168
169     filterPallette setDefault
170     filterPallette config -pattern1                   00
171     filterPallette config -patternMask1               00
172     filterPallette config -patternOffset1             20
173     filterPallette config -patternOffset2             20
174     if {[filterPallette set $chasNum $cardNum $portNum]} {
175         errorMsg "Error calling filterPallette set $chasNum $cardNum $portNum"
176     }
177
178     capture setDefault
179     capture config -sliceSize                         65536
180     if {[capture set $chasNum $cardNum $portNum]} {
181         errorMsg "Error calling capture set $chasNum $cardNum $portNum"
182     }
183
184     if {[interfaceTable select $chasNum $cardNum $portNum]} {
185         errorMsg "Error calling interfaceTable select $chasNum $cardNum $portNum"
186     }
187
188     interfaceTable setDefault
189     if {[interfaceTable set]} {
190         errorMsg "Error calling interfaceTable set"
191     }
192
193     interfaceTable clearAllInterfaces
194     if {[interfaceTable write]} {
195         errorMsg "Error calling interfaceTable write"
196     }
197
198     ixEnablePortIntrinsicLatencyAdjustment $chasNum $cardNum $portNum true
199 }
200
201 ixWritePortsToHardware portList
202
203 if {[ixCheckLinkState $portList] != 0} {
204     errorMsg "One or more port links are down"
205 }
206
207 proc sendTraffic { flowSpec trafficSpec } {
208     # Send traffic from IXIA.
209     #
210     # Transmits traffic from Rx port (port1), and captures traffic at
211     # Tx port (port2).
212     #
213     # Parameters:
214     #   flowSpec - a dict detailing how the packet should be sent. Should be
215     #     of format:
216     #         {type, numpkts, duration, framerate}
217     #   trafficSpec - a dict describing the packet to be sent. Should be
218     #     of format:
219     #         { l2, vlan, l3}
220     #     where each item is in turn a dict detailing the configuration of each
221     #     layer of the packet
222     # Returns:
223     #   Output from Rx end of Ixia if duration != 0, else 0
224
225     ##################################################
226     ################# Initialisation #################
227     ##################################################
228
229     # Configure global variables. See documentation on 'global' for more
230     # information on why this is necessary
231     #   https://www.tcl.tk/man/tcl8.5/tutorial/Tcl13.html
232     global portList
233
234     # Extract the provided dictionaries to local variables to simplify the
235     # rest of the script
236
237     # flow spec
238
239     set streamType              [dict get $flowSpec type]
240     set numPkts                 [dict get $flowSpec numpkts]
241     set duration                [expr {[dict get $flowSpec duration] * 1000}]
242     set frameRate               [dict get $flowSpec framerate]
243
244     # traffic spec
245
246     # extract nested dictionaries
247     set trafficSpec_l2          [dict get $trafficSpec l2]
248     set trafficSpec_l3          [dict get $trafficSpec l3]
249     set trafficSpec_l4          [dict get $trafficSpec l4]
250     set trafficSpec_vlan        [dict get $trafficSpec vlan]
251
252     set frameSize               [dict get $trafficSpec_l2 framesize]
253     set srcMac                  [dict get $trafficSpec_l2 srcmac]
254     set dstMac                  [dict get $trafficSpec_l2 dstmac]
255     set srcPort                 [dict get $trafficSpec_l4 srcport]
256     set dstPort                 [dict get $trafficSpec_l4 dstport]
257
258     set proto                   [dict get $trafficSpec_l3 proto]
259     set srcIp                   [dict get $trafficSpec_l3 srcip]
260     set dstIp                   [dict get $trafficSpec_l3 dstip]
261
262     if {[dict exists $trafficSpec_l3 protocolpadbytes]} {
263         set protocolPad             [dict get $trafficSpec_l4 protocolpad]
264         set protocolPadBytes        [dict get $trafficSpec_l4 protocolpadbytes]
265     }
266
267     set vlanEnabled             [dict get $trafficSpec_vlan enabled]
268     if {$vlanEnabled == 1 } {
269         # these keys won't exist if vlan wasn't enabled
270         set vlanId                  [dict get $trafficSpec_vlan id]
271         set vlanUserPrio            [dict get $trafficSpec_vlan priority]
272         set vlanCfi                 [dict get $trafficSpec_vlan cfi]
273     }
274
275     ##################################################
276     ##################### Streams ####################
277     ##################################################
278
279     streamRegion get $::chassis $::card $::port1
280     if {[streamRegion enableGenerateWarningList $::chassis $::card $::port1 false]} {
281         errorMsg "Error calling streamRegion enableGenerateWarningList  $::chassis $::card $::port1 false"
282     }
283
284     set streamId 1
285
286     stream setDefault
287     stream config -ifg                                9.6
288     stream config -ifgMIN                             19.2
289     stream config -ifgMAX                             25.6
290     stream config -ibg                                9.6
291     stream config -isg                                9.6
292     stream config -rateMode                           streamRateModePercentRate
293     stream config -percentPacketRate                  $frameRate
294     stream config -framesize                          $frameSize
295     stream config -frameType                          "08 00"
296     stream config -sa                                 $srcMac
297     stream config -da                                 $dstMac
298     stream config -numSA                              16
299     stream config -numDA                              16
300     stream config -asyncIntEnable                     true
301     stream config -dma                                $streamType
302     stream config -numBursts                          1
303     stream config -numFrames                          $numPkts
304     stream config -patternType                        incrByte
305     stream config -dataPattern                        x00010203
306     stream config -pattern                            "00 01 02 03"
307
308     protocol setDefault
309     protocol config -name                             ipV4
310     protocol config -ethernetType                     ethernetII
311     if {$vlanEnabled == 1} {
312         protocol config -enable802dot1qTag            vlanSingle
313     }
314
315     if {[info exists protocolPad]} {
316         protocol config -enableProtocolPad                $protocolPad
317     }
318
319     ip setDefault
320     ip config -ipProtocol                             ipV4Protocol[string totitle $proto]
321     ip config -checksum                               "f6 75"
322     ip config -sourceIpAddr                           $srcIp
323     ip config -sourceIpAddrRepeatCount                10
324     ip config -sourceClass                            classA
325     ip config -destIpAddr                             $dstIp
326     ip config -destIpAddrRepeatCount                  10
327     ip config -destClass                              classA
328     ip config -destMacAddr                            $dstMac
329     ip config -destDutIpAddr                          0.0.0.0
330     ip config -ttl                                    64
331     if {[ip set $::chassis $::card $::port1]} {
332         errorMsg "Error calling ip set $::chassis $::card $::port1"
333     }
334
335     "$proto" setDefault
336     "$proto" config -checksum                         "25 81"
337     if {["$proto" set $::chassis $::card $::port1]} {
338         errorMsg "Error calling $proto set $::chassis $::card $::port"
339     }
340
341     if {[info exists protocolPad]} {
342         protocolPad setDefault
343         # VxLAN header with VNI 99 (0x63)
344         # Inner SRC 01:02:03:04:05:06
345         # Inner DST 06:05:04:03:02:01
346         # IP SRC 192.168.0.2
347         # IP DST 192.168.240.9
348         # SRC port 3000 (0x0BB8)
349         # DST port 3001 (0x0BB9)
350         # length 26
351         # UDP Checksum 0x2E93
352
353         # From encap case capture
354         protocolPad config -dataBytes "$protocolPadBytes"
355         if {[protocolPad set $::chassis $::card $::port1]} {
356             errorMsg "Error calling protocolPad set $::chassis $::card $::port"
357             set retCode $::TCL_ERROR
358         }
359     }
360
361     if {$vlanEnabled == 1 } {
362         vlan setDefault
363         vlan config -vlanID                               $vlanId
364         vlan config -userPriority                         $vlanUserPrio
365         vlan config -cfi                                  $vlanCfi
366         vlan config -mode                                 vIdle
367         vlan config -repeat                               10
368         vlan config -step                                 1
369         vlan config -maskval                              "0000XXXXXXXXXXXX"
370         vlan config -protocolTagId                        vlanProtocolTag8100
371     }
372
373     if {[vlan set $::chassis $::card $::port1]} {
374         errorMsg "Error calling vlan set $::chassis $::card $::port1"
375     }
376
377     if {[port isValidFeature $::chassis $::card $::port1 $::portFeatureTableUdf]} {
378         tableUdf setDefault
379         tableUdf clearColumns
380         if {[tableUdf set $::chassis $::card $::port1]} {
381             errorMsg "Error calling tableUdf set $::chassis $::card $::port1"
382         }
383     }
384
385     if {[port isValidFeature $::chassis $::card $::port1 $::portFeatureRandomFrameSizeWeightedPair]} {
386         weightedRandomFramesize setDefault
387         if {[weightedRandomFramesize set $::chassis $::card $::port1]} {
388             errorMsg "Error calling weightedRandomFramesize set $::chassis $::card $::port1"
389         }
390     }
391
392     if {$proto == "tcp"} {
393         tcp setDefault
394         tcp config -sourcePort                            $srcPort
395         tcp config -destPort                              $dstPort
396         if {[tcp set $::chassis $::card $::port1 ]} {
397             errorMsg "Error setting tcp on port $::chassis.$::card.$::port1"
398         }
399
400         if {$vlanEnabled != 1} {
401             udf setDefault
402             udf config -repeat                                1
403             udf config -continuousCount                       true
404             udf config -initval                               {00 00 00 01}
405             udf config -updown                                uuuu
406             udf config -cascadeType                           udfCascadeNone
407             udf config -step                                  1
408
409             packetGroup setDefault
410             packetGroup config -insertSequenceSignature       true
411             packetGroup config -sequenceNumberOffset          38
412             packetGroup config -signatureOffset               42
413             packetGroup config -signature                     "08 71 18 05"
414             packetGroup config -groupIdOffset                 52
415             packetGroup config -groupId                       $streamId
416             packetGroup config -allocateUdf                   true
417             if {[packetGroup setTx $::chassis $::card $::port1 $streamId]} {
418                 errorMsg "Error calling packetGroup setTx $::chassis $::card $::port1 $streamId"
419             }
420         }
421     } elseif {$proto == "udp"} {
422         udp setDefault
423         udp config -sourcePort                            $srcPort
424         udp config -destPort                              $dstPort
425         if {[dict exists $trafficSpec_l3 packetsize]} {
426             set packetSize               [dict get $trafficSpec_l3 packetsize]
427         } else {
428             set packetSize               $frameSize
429         }
430         stream config -framesize                          $packetSize
431         if {[udp set $::chassis $::card $::port1]} {
432             errorMsg "Error setting udp on port $::chassis.$::card.$::port1"
433         }
434         errorMsg "frameSize: $frameSize, packetSize: $packetSize, srcMac: $srcMac, dstMac: $dstMac, srcPort: $srcPort, dstPort: $dstPort, framerate: $frameRate %"
435         if {[info exists protocolPad]} {
436             errorMsg "protocolPad: $protocolPad, protocolPadBytes: $protocolPadBytes"
437         }
438     }
439
440     if {[stream set $::chassis $::card $::port1 $streamId]} {
441         errorMsg "Error calling stream set $::chassis $::card $::port1 $streamId"
442     }
443
444     incr streamId
445     streamRegion generateWarningList $::chassis $::card $::port1
446     ixWriteConfigToHardware portList -noProtocolServer
447
448     if {[packetGroup getRx $::chassis $::card $::port2]} {
449         errorMsg "Error calling packetGroup getRx $::chassis $::card $::port2"
450     }
451
452     ##################################################
453     ######### Traffic Transmit and Results ###########
454     ##################################################
455
456     # Transmit traffic
457
458     logMsg "Clearing stats for all ports"
459     ixClearStats portList
460
461     logMsg "Starting packet groups on port $::port2"
462     ixStartPortPacketGroups $::chassis $::card $::port2
463
464     logMsg "Starting Capture on port $::port2"
465     ixStartPortCapture $::chassis $::card $::port2
466
467     logMsg "Starting transmit on port $::port1"
468     ixStartPortTransmit $::chassis $::card $::port1
469
470     # If duration=0 is passed, exit after starting transmit
471
472     if {$duration == 0} {
473         logMsg "Sending traffic until interrupted"
474         return
475     }
476
477     logMsg "Waiting for $duration ms"
478
479     # Wait for duration - 1 second to get traffic rate
480
481     after [expr "$duration - 1"]
482
483     # Get result
484
485     set result                                        [stopTraffic]
486
487     if {$streamType == "contPacket"} {
488         return $result
489     } elseif {$streamType == "stopStream"} {
490         set payError 0
491         set seqError 0
492         set captureLimit 3000
493
494         # explode results from 'stopTraffic' for ease of use later
495         set framesSent [lindex $result 0]
496         set framesRecv [lindex $result 1]
497         set bytesSent [lindex $result 2]
498         set bytesRecv [lindex $result 3]
499
500         if {$framesSent <= $captureLimit} {
501             captureBuffer get $::chassis $::card $::port2 1 $framesSent
502             set capturedFrames [captureBuffer cget -numFrames]
503
504             set notCaptured [expr "$framesRecv - $capturedFrames"]
505             if {$notCaptured != 0} {
506                 errorMsg "'$notCaptured' frames were not captured"
507             }
508
509             if {$proto == "tcp"} {
510                 for {set z 1} {$z <= $capturedFrames} {incr z} {
511                     captureBuffer getframe $z
512                     set capFrame [captureBuffer cget -frame]
513                     regsub -all " " $capFrame "" frameNoSpaces
514                     set frameNoSpaces
515
516                     set startPayload 108
517                     set endPayload [expr "[expr "$frameSize * 2"] - 9"]
518                     set payload [string range $frameNoSpaces $startPayload $endPayload]
519
520                     if {$vlanEnabled != 1} {
521                         set startSequence 76
522                         set endSequence 83
523                         set sequence [string range $frameNoSpaces $startSequence $endSequence]
524                         scan $sequence %x seqDecimal
525                         set seqDecimal
526                         if {"$payload" != $::payloadLookup($frameSize)} {
527                             errorMsg "frame '$z' payload: invalid payload"
528                             incr payError
529                         }
530                         # variable z increments from 1 to total number of packets
531                         # captured TCP sequence numbers start at 0, not 1. When
532                         # comparing sequence numbers for captured frames, reduce
533                         # variable z by 1 i.e. frame 1 with sequence 0 is compared
534                         # to expected sequence 0.
535                         if {$seqDecimal != $z-1} {
536                             errorMsg "frame '$z' sequence number: Found '$seqDecimal'. Expected '$z'"
537                             incr seqError
538                         }
539                     }
540                 }
541             }
542             logMsg "Sequence Errors: $seqError"
543             logMsg "Payload Errors:  $payError\n"
544         } else {
545             errorMsg "Too many packets for capture."
546         }
547         lappend result $payError
548         lappend result $seqError
549         return $result
550     } else {
551         errorMsg "streamtype is not supported: '$streamType'"
552     }
553 }
554
555 proc stopTraffic {} {
556     # Stop sending traffic from IXIA.
557     #
558     # Stops Transmit of traffic from Rx port.
559     #
560     # Returns:
561     #   Output from Rx end of Ixia.
562
563     ##################################################
564     ################# Initialisation #################
565     ##################################################
566
567     # Configure global variables. See documentation on 'global' for more
568     # information on why this is necessary
569     #   https://www.tcl.tk/man/tcl8.5/tutorial/Tcl13.html
570     global portList
571
572     ##################################################
573     ####### Stop Traffic Transmit and Results ########
574     ##################################################
575
576      # Read frame rate of transmission
577
578     if {[stat getRate statAllStats $::chassis $::card $::port1]} {
579         errorMsg "Error reading stat rate on port $::chassis $::card $::port1"
580         return $::TCL_ERROR
581     }
582
583     set sendRate [stat cget -framesSent]
584     set sendRateBytes [stat cget -bytesSent]
585
586     if {[stat getRate statAllStats $::chassis $::card $::port2]} {
587         errorMsg "Error reading stat rate on port $::chassis $::card $::port2"
588         return $::TCL_ERROR
589     }
590
591     set recvRate [stat cget -framesReceived]
592     set recvRateBytes [stat cget -bytesReceived]
593
594     # Wait for a second, else we get funny framerate statistics
595     after 1
596
597     # Stop transmission of traffic
598     ixStopTransmit portList
599
600     if {[ixCheckTransmitDone portList] == $::TCL_ERROR} {
601         return -code error
602     } else {
603         logMsg "Transmission is complete.\n"
604     }
605
606     ixStopPacketGroups portList
607     ixStopCapture portList
608
609     # Get statistics
610
611     if {[stat get statAllStats $::chassis $::card $::port1]} {
612         errorMsg "Error reading stat on port $::chassis $::card $::port1"
613         return $::TCL_ERROR
614     }
615
616     set bytesSent [stat cget -bytesSent]
617     set framesSent [stat cget -framesSent]
618
619     if {[stat get statAllStats $::chassis $::card $::port2]} {
620         errorMsg "Error reading stat on port $::chassis $::card $::port2"
621         return $::TCL_ERROR
622     }
623
624     set bytesRecv [stat cget -bytesReceived]
625     set framesRecv [stat cget -framesReceived]
626
627     set bytesDropped [expr "$bytesSent - $bytesRecv"]
628     set framesDropped [expr "$framesSent - $framesRecv"]
629
630     logMsg "Frames Sent:       $framesSent"
631     logMsg "Frames Recv:       $framesRecv"
632     logMsg "Frames Dropped:    $framesDropped\n"
633
634     logMsg "Bytes Sent:        $bytesSent"
635     logMsg "Bytes Recv:        $bytesRecv"
636     logMsg "Bytes Dropped:     $bytesDropped\n"
637
638     logMsg "Frame Rate Sent:   $sendRate"
639     logMsg "Frame Rate Recv:   $recvRate\n"
640
641     logMsg "Bytes Rate Sent:   $sendRateBytes"
642     logMsg "Bytes Rate Recv:   $recvRateBytes\n"
643
644     set result [list $framesSent $framesRecv $bytesSent $bytesRecv $sendRate $recvRate $sendRateBytes $recvRateBytes]
645
646     return $result
647 }
648
649 proc rfcThroughputTest { testSpec trafficSpec } {
650     # Execute RFC tests from IXIA.
651     #
652     # Wraps the sendTraffic proc, repeatedly calling it, storing the result and
653     # performing an iterative binary search to find the highest possible RFC
654     # transmission rate. Abides by the specification of RFC2544 as given by the
655     # IETF:
656     #
657     #   https://www.ietf.org/rfc/rfc2544.txt
658     #
659     # Parameters:
660     #   testSpec - a dict detailing how the test should be run. Should be
661     #     of format:
662     #         {numtrials, duration, lossrate}
663     #   trafficSpec - a dict describing the packet to be sent. Should be
664     #     of format:
665     #         { l2, l3}
666     #     where each item is in turn a dict detailing the configuration of each
667     #     layer of the packet
668     # Returns:
669     #   Highest rate with acceptable packet loss.
670
671     ##################################################
672     ################# Initialisation #################
673     ##################################################
674
675     # Configure global variables. See documentation on 'global' for more
676     # information on why this is necessary
677     #   https://www.tcl.tk/man/tcl8.5/tutorial/Tcl13.html
678     global portList
679
680     # Extract the provided dictionaries to local variables to simplify the
681     # rest of the script
682
683     # testSpec
684
685     # RFC2544 to IXIA terminology mapping (it affects Ixia configuration below):
686     # Test    => Trial
687     # Trial   => Iteration
688     set numTrials               [dict get $testSpec tests]  ;# we don't use this yet
689     set duration                [dict get $testSpec duration]
690     set lossRate                [dict get $testSpec lossrate]
691     set multipleStream          [dict get $testSpec multipleStreams]  ;# we don't use this yet
692
693     # variables used for binary search of results
694     set min 1
695     set max 100
696     set diff [expr "$max - $min"]
697
698     set result [list 0 0 0 0 0 0 0 0]  ;# best result found so far
699     set percentRate 100  ;# starting throughput percentage rate
700
701     ##################################################
702     ######### Traffic Transmit and Results ###########
703     ##################################################
704
705     # iterate a maximum of 20 times, sending packets at a set  rate to
706     # find fastest possible rate with acceptable packetloss
707     #
708     # As a reminder, the binary search works something like this:
709     #
710     #   percentRate < idealValue --> min = percentRate
711     #   percentRate > idealValue --> max = percentRate
712     #   percentRate = idealValue --> max = min = percentRate
713     #
714     for {set i 0} {$i < 20} {incr i} {
715         dict set flowSpec type                        "contPacket"
716         dict set flowSpec numpkts                     100 ;# this can be bypassed
717         dict set flowSpec duration                    $duration
718         dict set flowSpec framerate                   $percentRate
719
720         set flowStats [sendTraffic $flowSpec $trafficSpec]
721
722         # explode results from 'sendTraffic' for ease of use later
723         set framesSent [lindex $flowStats 0]
724         set framesRecv [lindex $flowStats 1]
725         set sendRate [lindex $flowStats 4]
726
727         set framesDropped [expr "$framesSent - $framesRecv"]
728         if {$framesSent > 0} {
729             set framesDroppedRate [expr "double($framesDropped) / $framesSent"]
730         } else {
731             set framesDroppedRate 100
732         }
733
734         # handle 'percentRate <= idealValue' case
735         if {$framesDroppedRate <= $lossRate} {
736             logMsg "Frame sendRate of '$sendRate' pps succeeded ('$framesDropped' frames dropped)"
737
738             set result $flowStats
739             set min $percentRate
740
741             set percentRate [expr "$percentRate + ([expr "$max - $min"] * 0.5)"]
742         # handle the 'percentRate > idealValue' case
743         } else {
744             if {$framesDropped == $framesSent} {
745                 errorMsg "Dropped all frames!"
746             }
747
748             errorMsg "Frame sendRate of '$sendRate' pps failed ('$framesDropped' frames dropped)"
749
750             set max $percentRate
751             set percentRate [expr "$percentRate - ([expr "$max - $min"] * 0.5)"]
752         }
753
754         # check if we've already found the rate before 10 iterations, i.e.
755         # 'percentRate = idealValue'. This is as accurate as we can get with
756         # integer values.
757         if {[expr "$max - $min"] <= 0.5 } {
758             logMsg "End of search condition for framerate is met: $max % - $min % <= 0.5 %"
759             break
760         }
761
762         logMsg "Waiting 2000 ms"
763         # wait to process delayed frames
764         after 2000
765     }
766
767     set bestRate [lindex $result 4]
768
769     logMsg "$lossRate% packet loss rate: $bestRate"
770
771     return $result
772 }