Merge "test_spec: Clarify LTD.Throughput.RFC2544.SystemRecoveryTime"
[vswitchperf.git] / tools / pkt_gen / ixia / pass_fail.tcl
1 #!/usr/bin/env tclsh
2
3 # Copyright (c) 2014, Ixia
4 # Copyright (c) 2015, 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, time, 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 time != 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 time                    [expr {[dict get $flowSpec time] * 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_vlan        [dict get $trafficSpec vlan]
250
251     set frameSize               [dict get $trafficSpec_l2 framesize]
252     set srcMac                  [dict get $trafficSpec_l2 srcmac]
253     set dstMac                  [dict get $trafficSpec_l2 dstmac]
254     set srcPort                 [dict get $trafficSpec_l2 srcport]
255     set dstPort                 [dict get $trafficSpec_l2 dstport]
256
257     set proto                   [dict get $trafficSpec_l3 proto]
258     set srcIp                   [dict get $trafficSpec_l3 srcip]
259     set dstIp                   [dict get $trafficSpec_l3 dstip]
260
261     set vlanEnabled             [dict get $trafficSpec_vlan enabled]
262     if {$vlanEnabled == 1 } {
263         # these keys won't exist if vlan wasn't enabled
264         set vlanId                  [dict get $trafficSpec_vlan id]
265         set vlanUserPrio            [dict get $trafficSpec_vlan priority]
266         set vlanCfi                 [dict get $trafficSpec_vlan cfi]
267     }
268
269     ##################################################
270     ##################### Streams ####################
271     ##################################################
272
273     streamRegion get $::chassis $::card $::port1
274     if {[streamRegion enableGenerateWarningList $::chassis $::card $::port1 false]} {
275         errorMsg "Error calling streamRegion enableGenerateWarningList  $::chassis $::card $::port1 false"
276     }
277
278     set streamId 1
279
280     stream setDefault
281     stream config -ifg                                9.6
282     stream config -ifgMIN                             19.2
283     stream config -ifgMAX                             25.6
284     stream config -ibg                                9.6
285     stream config -isg                                9.6
286     stream config -rateMode                           streamRateModePercentRate
287     stream config -percentPacketRate                  $frameRate
288     stream config -framesize                          $frameSize
289     stream config -frameType                          "08 00"
290     stream config -sa                                 $srcMac
291     stream config -da                                 $dstMac
292     stream config -numSA                              16
293     stream config -numDA                              16
294     stream config -asyncIntEnable                     true
295     stream config -dma                                $streamType
296     stream config -numBursts                          1
297     stream config -numFrames                          $numPkts
298     stream config -patternType                        incrByte
299     stream config -dataPattern                        x00010203
300     stream config -pattern                            "00 01 02 03"
301
302     protocol setDefault
303     protocol config -name                             ipV4
304     protocol config -ethernetType                     ethernetII
305     if {$vlanEnabled == 1} {
306         protocol config -enable802dot1qTag            vlanSingle
307     }
308
309     ip setDefault
310     ip config -ipProtocol                             ipV4Protocol[string totitle $proto]
311     ip config -checksum                               "f6 75"
312     ip config -sourceIpAddr                           $srcIp
313     ip config -sourceIpAddrRepeatCount                10
314     ip config -sourceClass                            classA
315     ip config -destIpAddr                             $dstIp
316     ip config -destIpAddrRepeatCount                  10
317     ip config -destClass                              classA
318     ip config -destMacAddr                            $dstMac
319     ip config -destDutIpAddr                          0.0.0.0
320     ip config -ttl                                    64
321     if {[ip set $::chassis $::card $::port1]} {
322         errorMsg "Error calling ip set $::chassis $::card $::port1"
323     }
324
325     "$proto" setDefault
326     "$proto" config -checksum                         "25 81"
327     if {["$proto" set $::chassis $::card $::port1]} {
328         errorMsg "Error calling $proto set $::chassis $::card $::port"
329     }
330
331     if {$vlanEnabled == 1 } {
332         vlan setDefault
333         vlan config -vlanID                               $vlanId
334         vlan config -userPriority                         $vlanUserPrio
335         vlan config -cfi                                  $vlanCfi
336         vlan config -mode                                 vIdle
337         vlan config -repeat                               10
338         vlan config -step                                 1
339         vlan config -maskval                              "0000XXXXXXXXXXXX"
340         vlan config -protocolTagId                        vlanProtocolTag8100
341     }
342
343     if {[vlan set $::chassis $::card $::port1]} {
344         errorMsg "Error calling vlan set $::chassis $::card $::port1"
345     }
346
347     if {[port isValidFeature $::chassis $::card $::port1 $::portFeatureTableUdf]} {
348         tableUdf setDefault
349         tableUdf clearColumns
350         if {[tableUdf set $::chassis $::card $::port1]} {
351             errorMsg "Error calling tableUdf set $::chassis $::card $::port1"
352         }
353     }
354
355     if {[port isValidFeature $::chassis $::card $::port1 $::portFeatureRandomFrameSizeWeightedPair]} {
356         weightedRandomFramesize setDefault
357         if {[weightedRandomFramesize set $::chassis $::card $::port1]} {
358             errorMsg "Error calling weightedRandomFramesize set $::chassis $::card $::port1"
359         }
360     }
361
362     if {$proto == "tcp"} {
363         tcp setDefault
364         tcp config -sourcePort                            $srcPort
365         tcp config -destPort                              $dstPort
366         if {[tcp set $::chassis $::card $::port1 ]} {
367             errorMsg "Error setting tcp on port $::chassis.$::card.$::port1"
368         }
369
370         if {$vlanEnabled != 1} {
371             udf setDefault
372             udf config -repeat                                1
373             udf config -continuousCount                       true
374             udf config -initval                               {00 00 00 01}
375             udf config -updown                                uuuu
376             udf config -cascadeType                           udfCascadeNone
377             udf config -step                                  1
378
379             packetGroup setDefault
380             packetGroup config -insertSequenceSignature       true
381             packetGroup config -sequenceNumberOffset          38
382             packetGroup config -signatureOffset               42
383             packetGroup config -signature                     "08 71 18 05"
384             packetGroup config -groupIdOffset                 52
385             packetGroup config -groupId                       $streamId
386             packetGroup config -allocateUdf                   true
387             if {[packetGroup setTx $::chassis $::card $::port1 $streamId]} {
388                 errorMsg "Error calling packetGroup setTx $::chassis $::card $::port1 $streamId"
389             }
390         }
391     } elseif {$proto == "udp"} {
392         udp setDefault
393         udp config -sourcePort                            $srcPort
394         udp config -destPort                              $dstPort
395         if {[udp set $::chassis $::card $::port1]} {
396             errorMsg "Error setting udp on port $::chassis.$::card.$::port1"
397         }
398     }
399
400     if {[stream set $::chassis $::card $::port1 $streamId]} {
401         errorMsg "Error calling stream set $::chassis $::card $::port1 $streamId"
402     }
403
404     incr streamId
405     streamRegion generateWarningList $::chassis $::card $::port1
406     ixWriteConfigToHardware portList -noProtocolServer
407
408     if {[packetGroup getRx $::chassis $::card $::port2]} {
409         errorMsg "Error calling packetGroup getRx $::chassis $::card $::port2"
410     }
411
412     ##################################################
413     ######### Traffic Transmit and Results ###########
414     ##################################################
415
416     # Transmit traffic
417
418     logMsg "Clearing stats for all ports"
419     ixClearStats portList
420
421     logMsg "Starting packet groups on port $::port2"
422     ixStartPortPacketGroups $::chassis $::card $::port2
423
424     logMsg "Starting Capture on port $::port2"
425     ixStartPortCapture $::chassis $::card $::port2
426
427     logMsg "Starting transmit on port $::port1"
428     ixStartPortTransmit $::chassis $::card $::port1
429
430     # If time=0 is passed, exit after starting transmit
431
432     if {$time == 0} {
433         logMsg "Sending traffic until interrupted"
434         return
435     }
436
437     logMsg "Waiting for $time ms"
438
439     # Wait for time - 1 second to get traffic rate
440
441     after [expr "$time - 1"]
442
443     # Get result
444
445     set result                                        [stopTraffic]
446
447     if {$streamType == "contPacket"} {
448         return $result
449     } elseif {$streamType == "stopStream"} {
450         set payError 0
451         set seqError 0
452         set captureLimit 3000
453
454         # explode results from 'stopTraffic' for ease of use later
455         set framesSent [lindex $result 0]
456         set framesRecv [lindex $result 1]
457         set bytesSent [lindex $result 2]
458         set bytesRecv [lindex $result 3]
459
460         if {$framesSent <= $captureLimit} {
461             captureBuffer get $::chassis $::card $::port2 1 $framesSent
462             set capturedFrames [captureBuffer cget -numFrames]
463
464             set notCaptured [expr "$framesRecv - $capturedFrames"]
465             if {$notCaptured != 0} {
466                 errorMsg "'$notCaptured' frames were not captured"
467             }
468
469             if {$proto == "tcp"} {
470                 for {set z 1} {$z <= $capturedFrames} {incr z} {
471                     captureBuffer getframe $z
472                     set capFrame [captureBuffer cget -frame]
473                     regsub -all " " $capFrame "" frameNoSpaces
474                     set frameNoSpaces
475
476                     set startPayload 108
477                     set endPayload [expr "[expr "$frameSize * 2"] - 9"]
478                     set payload [string range $frameNoSpaces $startPayload $endPayload]
479
480                     if {$vlanEnabled != 1} {
481                         set startSequence 76
482                         set endSequence 83
483                         set sequence [string range $frameNoSpaces $startSequence $endSequence]
484                         scan $sequence %x seqDecimal
485                         set seqDecimal
486                         if {"$payload" != $::payloadLookup($frameSize)} {
487                             errorMsg "frame '$z' payload: invalid payload"
488                             incr payError
489                         }
490                         # variable z increments from 1 to total number of packets
491                         # captured TCP sequence numbers start at 0, not 1. When
492                         # comparing sequence numbers for captured frames, reduce
493                         # variable z by 1 i.e. frame 1 with sequence 0 is compared
494                         # to expected sequence 0.
495                         if {$seqDecimal != $z-1} {
496                             errorMsg "frame '$z' sequence number: Found '$seqDecimal'. Expected '$z'"
497                             incr seqError
498                         }
499                     }
500                 }
501             }
502             logMsg "Sequence Errors: $seqError"
503             logMsg "Payload Errors:  $payError\n"
504         } else {
505             errorMsg "Too many packets for capture."
506         }
507
508         set result [list $framesSent $framesRecv $bytesSent $bytesRecv $payError $seqError]
509         return $result
510     } else {
511         errorMsg "streamtype is not supported: '$streamType'"
512     }
513 }
514
515 proc stopTraffic {} {
516     # Stop sending traffic from IXIA.
517     #
518     # Stops Transmit of traffic from Rx port.
519     #
520     # Returns:
521     #   Output from Rx end of Ixia.
522
523     ##################################################
524     ################# Initialisation #################
525     ##################################################
526
527     # Configure global variables. See documentation on 'global' for more
528     # information on why this is necessary
529     #   https://www.tcl.tk/man/tcl8.5/tutorial/Tcl13.html
530     global portList
531
532     ##################################################
533     ####### Stop Traffic Transmit and Results ########
534     ##################################################
535
536      # Read frame rate of transmission
537
538     if {[stat getRate statAllStats $::chassis $::card $::port1]} {
539         errorMsg "Error reading stat rate on port $::chassis $::card $::port1"
540         return $::TCL_ERROR
541     }
542
543     set sendRate [stat cget -framesSent]
544     set sendRateBytes [stat cget -bytesSent]
545
546     if {[stat getRate statAllStats $::chassis $::card $::port2]} {
547         errorMsg "Error reading stat rate on port $::chassis $::card $::port2"
548         return $::TCL_ERROR
549     }
550
551     set recvRate [stat cget -framesReceived]
552     set recvRateBytes [stat cget -bytesReceived]
553
554     # Wait for a second, else we get funny framerate statistics
555     after 1
556
557     # Stop transmission of traffic
558     ixStopTransmit portList
559
560     if {[ixCheckTransmitDone portList] == $::TCL_ERROR} {
561         return -code error
562     } else {
563         logMsg "Transmission is complete.\n"
564     }
565
566     ixStopPacketGroups portList
567     ixStopCapture portList
568
569     # Get statistics
570
571     if {[stat get statAllStats $::chassis $::card $::port1]} {
572         errorMsg "Error reading stat on port $::chassis $::card $::port1"
573         return $::TCL_ERROR
574     }
575
576     set bytesSent [stat cget -bytesSent]
577     set framesSent [stat cget -framesSent]
578
579     if {[stat get statAllStats $::chassis $::card $::port2]} {
580         errorMsg "Error reading stat on port $::chassis $::card $::port2"
581         return $::TCL_ERROR
582     }
583
584     set bytesRecv [stat cget -bytesReceived]
585     set framesRecv [stat cget -framesReceived]
586
587     set bytesDropped [expr "$bytesSent - $bytesRecv"]
588     set framesDropped [expr "$framesSent - $framesRecv"]
589
590     logMsg "Frames Sent:       $framesSent"
591     logMsg "Frames Recv:       $framesRecv"
592     logMsg "Frames Dropped:    $framesDropped\n"
593
594     logMsg "Bytes Sent:        $bytesSent"
595     logMsg "Bytes Recv:        $bytesRecv"
596     logMsg "Bytes Dropped:     $bytesDropped\n"
597
598     logMsg "Frame Rate Sent:   $sendRate"
599     logMsg "Frame Rate Recv:   $recvRate\n"
600
601     set result [list $framesSent $framesRecv $bytesSent $bytesRecv $sendRate $recvRate $sendRateBytes $recvRateBytes]
602
603     return $result
604 }
605
606 proc rfcThroughputTest { testSpec trafficSpec } {
607     # Execute RFC tests from IXIA.
608     #
609     # Wraps the sendTraffic proc, repeatedly calling it, storing the result and
610     # performing an iterative binary search to find the highest possible RFC
611     # transmission rate. Abides by the specification of RFC2544 as given by the
612     # IETF:
613     #
614     #   https://www.ietf.org/rfc/rfc2544.txt
615     #
616     # Parameters:
617     #   testSpec - a dict detailing how the test should be run. Should be
618     #     of format:
619     #         {numtrials, duration, lossrate}
620     #   trafficSpec - a dict describing the packet to be sent. Should be
621     #     of format:
622     #         { l2, l3}
623     #     where each item is in turn a dict detailing the configuration of each
624     #     layer of the packet
625     # Returns:
626     #   Highest rate with acceptable packet loss.
627
628     ##################################################
629     ################# Initialisation #################
630     ##################################################
631
632     # Configure global variables. See documentation on 'global' for more
633     # information on why this is necessary
634     #   https://www.tcl.tk/man/tcl8.5/tutorial/Tcl13.html
635     global portList
636
637     # Extract the provided dictionaries to local variables to simplify the
638     # rest of the script
639
640     # testSpec
641
642     set numTrials               [dict get $testSpec trials]  ;# we don't use this yet
643     set duration                [dict get $testSpec duration]
644     set lossRate                [dict get $testSpec lossrate]
645     set multipleStream          [dict get $testSpec multipleStreams]  ;# we don't use this yet
646
647     # variables used for binary search of results
648     set min 1
649     set max 100
650     set diff [expr "$max - $min"]
651
652     set result [list 0 0 0 0 0 0 0 0]  ;# best result found so far
653     set percentRate 100  ;# starting throughput percentage rate
654
655     ##################################################
656     ######### Traffic Transmit and Results ###########
657     ##################################################
658
659     # iterate a maximum of 20 times, sending packets at a set  rate to
660     # find fastest possible rate with acceptable packetloss
661     #
662     # As a reminder, the binary search works something like this:
663     #
664     #   percentRate < idealValue --> min = percentRate
665     #   percentRate > idealValue --> max = percentRate
666     #   percentRate = idealValue --> max = min = percentRate
667     #
668     for {set i 0} {$i < 20} {incr i} {
669         dict set flowSpec type                        "contPacket"
670         dict set flowSpec numpkts                     100 ;# this can be bypassed
671         dict set flowSpec time                        $duration
672         dict set flowSpec framerate                   $percentRate
673
674         set flowStats [sendTraffic $flowSpec $trafficSpec]
675
676         # explode results from 'sendTraffic' for ease of use later
677         set framesSent [lindex $flowStats 0]
678         set framesRecv [lindex $flowStats 1]
679         set sendRate [lindex $flowStats 4]
680
681         set framesDropped [expr "$framesSent - $framesRecv"]
682         if {$framesSent > 0} {
683             set framesDroppedRate [expr "double($framesDropped) / $framesSent"]
684         } else {
685             set framesDroppedRate 100
686         }
687
688         # check if we've already found the rate before 10 iterations, i.e.
689         # 'percentRate = idealValue'. This is as accurate as we can get with
690         # integer values.
691         if {[expr "$max - $min"] <= 0.5 } {
692             break
693         }
694
695         # handle 'percentRate <= idealValue' case
696         if {$framesDroppedRate <= $lossRate} {
697             logMsg "Frame sendRate of '$sendRate' pps succeeded ('$framesDropped' frames dropped)"
698
699             set result $flowStats
700             set min $percentRate
701
702             set percentRate [expr "$percentRate + ([expr "$max - $min"] * 0.5)"]
703         # handle the 'percentRate > idealValue' case
704         } else {
705             if {$framesDropped == $framesSent} {
706                 errorMsg "Dropped all frames!"
707             }
708
709             errorMsg "Frame sendRate of '$sendRate' pps failed ('$framesDropped' frames dropped)"
710
711             set max $percentRate
712             set percentRate [expr "$percentRate - ([expr "$max - $min"] * 0.5)"]
713         }
714     }
715
716     set bestRate [lindex $result 4]
717
718     logMsg "$lossRate% packet loss rate: $bestRate"
719
720     return $result
721 }