summaryrefslogtreecommitdiffstats
path: root/3rd_party/ixia/pass_fail.tcl
blob: bf1fb5565dd7bd9931b11e6781f1fd575ab8f281 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #75715e } /* Generic.Subheading */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
.highlight .kn { color: #f92672 } /* Keyword.Namespace */
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
.highlight .kt { color: #66d9ef } /* Keyword.Type */
.highlight .ld { color: #e6db74 } /* Literal.Date */
.highlight .m { color: #ae81ff } /* Literal.Number */
.highlight .s { color: #e6db74 } /* Literal.String */
.highlight .na { color: #a6e22e } /* Name.Attribute */
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
.highlight .nc { color: #a6e22e } /* Name.Class */
.highlight .no { color: #66d9ef } /* Name.Constant */
.highlight .nd { color: #a6e22e } /* Name.Decorator */
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
.highlight .ne { color: #a6e22e } /* Name.Exception */
.highlight .nf { color: #a6e22e } /* Name.Function */
.highlight .nl { color: #f8f8f2 } /* Name.Label */
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
.highlight .nx { color: #a6e22e } /* Name.Other */
.highlight .py { color: #f8f8f2 } /* Name.Property */
.highlight .nt { color: #f92672 } /* Name.Tag */
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
.highlight .ow { color: #f92672 } /* Operator.Word */
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
}
@media (prefers-color-scheme: light) {
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
}
heat_template_version: pike

description: >
  Memcached service configured with Puppet

parameters:
  ServiceNetMap:
    default: {}
    description: Mapping of service_name -> network name. Typically set
                 via parameter_defaults in the resource registry.  This
                 mapping overrides those in ServiceNetMapDefaults.
    type: json
  DefaultPasswords:
    default: {}
    type: json
  RoleName:
    default: ''
    description: Role name on which the service is applied
    type: string
  RoleParameters:
    default: {}
    description: Parameters specific to the role
    type: json
  EndpointMap:
    default: {}
    description: Mapping of service endpoint -> protocol. Typically set
                 via parameter_defaults in the resource registry.
    type: json
  MemcachedMaxMemory:
    default: '50%'
    description: The maximum amount of memory for memcached to be configured
                 to use when installed. This can be either a percentage ('50%')
                 or a fixed value ('2048').
    type: string
  MonitoringSubscriptionMemcached:
    default: 'overcloud-memcached'
    type: string

outputs:
  role_data:
    description: Role data for the Memcached role.
    value:
      service_name: memcached
      monitoring_subscription: {get_param: MonitoringSubscriptionMemcached}
      config_settings:
        # NOTE: bind IP is found in Heat replacing the network name with the local node IP
        # for the given network; replacement examples (eg. for internal_api):
        # internal_api -> IP
        # internal_api_uri -> [IP]
        # internal_api_subnet - > IP/CIDR
        memcached::listen_ip: {get_param: [ServiceNetMap, MemcachedNetwork]}
        memcached::max_memory: {get_param: MemcachedMaxMemory}
        tripleo.memcached.firewall_rules:
          '121 memcached':
            dport: 11211
      step_config: |
        include ::tripleo::profile::base::memcached
      service_config_settings:
        collectd:
            tripleo.collectd.plugins.memcached:
              - memcached
            collectd::plugin::memcached::instances:
              local:
                host: "%{hiera('memcached::listen_ip')}"
                port: 11211
#n560'>560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772
#!/usr/bin/env tclsh

# Copyright (c) 2014, Ixia
# Copyright (c) 2015-2016, 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, duration, 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 duration != 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 duration                [expr {[dict get $flowSpec duration] * 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_l4          [dict get $trafficSpec l4]
    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_l4 srcport]
    set dstPort                 [dict get $trafficSpec_l4 dstport]

    set proto                   [dict get $trafficSpec_l3 proto]
    set srcIp                   [dict get $trafficSpec_l3 srcip]
    set dstIp                   [dict get $trafficSpec_l3 dstip]

    if {[dict exists $trafficSpec_l3 protocolpadbytes]} {
        set protocolPad             [dict get $trafficSpec_l4 protocolpad]
        set protocolPadBytes        [dict get $trafficSpec_l4 protocolpadbytes]
    }

    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
    }

    if {[info exists protocolPad]} {
        protocol config -enableProtocolPad                $protocolPad
    }

    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 {[info exists protocolPad]} {
        protocolPad setDefault
        # VxLAN header with VNI 99 (0x63)
        # Inner SRC 01:02:03:04:05:06
        # Inner DST 06:05:04:03:02:01
        # IP SRC 192.168.0.2
        # IP DST 192.168.240.9
        # SRC port 3000 (0x0BB8)
        # DST port 3001 (0x0BB9)
        # length 26
        # UDP Checksum 0x2E93

        # From encap case capture
        protocolPad config -dataBytes "$protocolPadBytes"
        if {[protocolPad set $::chassis $::card $::port1]} {
            errorMsg "Error calling protocolPad set $::chassis $::card $::port"
            set retCode $::TCL_ERROR
        }
    }

    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 {[dict exists $trafficSpec_l3 packetsize]} {
            set packetSize               [dict get $trafficSpec_l3 packetsize]
        } else {
            set packetSize               $frameSize
        }
        stream config -framesize                          $packetSize
        if {[udp set $::chassis $::card $::port1]} {
            errorMsg "Error setting udp on port $::chassis.$::card.$::port1"
        }
        errorMsg "frameSize: $frameSize, packetSize: $packetSize, srcMac: $srcMac, dstMac: $dstMac, srcPort: $srcPort, dstPort: $dstPort, framerate: $frameRate %"
        if {[info exists protocolPad]} {
            errorMsg "protocolPad: $protocolPad, protocolPadBytes: $protocolPadBytes"
        }
    }

    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 duration=0 is passed, exit after starting transmit

    if {$duration == 0} {
        logMsg "Sending traffic until interrupted"
        return
    }

    logMsg "Waiting for $duration ms"

    # Wait for duration - 1 second to get traffic rate

    after [expr "$duration - 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."
        }
        lappend result $payError
        lappend result $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"

    logMsg "Bytes Rate Sent:   $sendRateBytes"
    logMsg "Bytes Rate Recv:   $recvRateBytes\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

    # RFC2544 to IXIA terminology mapping (it affects Ixia configuration below):
    # Test    => Trial
    # Trial   => Iteration
    set numTrials               [dict get $testSpec tests]  ;# 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 duration                    $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
        }

        # 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)"]
        }

        # 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 } {
            logMsg "End of search condition for framerate is met: $max % - $min % <= 0.5 %"
            break
        }

        logMsg "Waiting 2000 ms"
        # wait to process delayed frames
        after 2000
    }

    set bestRate [lindex $result 4]

    logMsg "$lossRate% packet loss rate: $bestRate"

    return $result
}