From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from dpdk.org (dpdk.org [92.243.14.124]) by inbox.dpdk.org (Postfix) with ESMTP id B8468A2EFC for ; Fri, 20 Sep 2019 05:19:21 +0200 (CEST) Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 6689E1F212; Fri, 20 Sep 2019 05:19:21 +0200 (CEST) Received: from mga02.intel.com (mga02.intel.com [134.134.136.20]) by dpdk.org (Postfix) with ESMTP id D198C1F211 for ; Fri, 20 Sep 2019 05:19:19 +0200 (CEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga008.jf.intel.com ([10.7.209.65]) by orsmga101.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 19 Sep 2019 20:19:18 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.64,527,1559545200"; d="scan'208";a="181682404" Received: from fmsmsx106.amr.corp.intel.com ([10.18.124.204]) by orsmga008.jf.intel.com with ESMTP; 19 Sep 2019 20:19:17 -0700 Received: from fmsmsx601.amr.corp.intel.com (10.18.126.81) by FMSMSX106.amr.corp.intel.com (10.18.124.204) with Microsoft SMTP Server (TLS) id 14.3.439.0; Thu, 19 Sep 2019 20:19:17 -0700 Received: from fmsmsx601.amr.corp.intel.com (10.18.126.81) by fmsmsx601.amr.corp.intel.com (10.18.126.81) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.1713.5; Thu, 19 Sep 2019 20:19:16 -0700 Received: from shsmsx101.ccr.corp.intel.com (10.239.4.153) by fmsmsx601.amr.corp.intel.com (10.18.126.81) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256) id 15.1.1713.5 via Frontend Transport; Thu, 19 Sep 2019 20:19:16 -0700 Received: from shsmsx104.ccr.corp.intel.com ([169.254.5.32]) by SHSMSX101.ccr.corp.intel.com ([169.254.1.92]) with mapi id 14.03.0439.000; Fri, 20 Sep 2019 11:19:14 +0800 From: "Lin, Xueqin" To: "Mo, YufengX" , "dts@dpdk.org" , "Tu, Lijuan" , "Yu, PingX" Thread-Topic: [dts][PATCH V4 1/1] [examples/flow_classify]: upload automation test script Thread-Index: AQHVb1n7jnCihrSkvkGtYySbAfjDGacz5ZbA Date: Fri, 20 Sep 2019 03:19:15 +0000 Message-ID: <0D300480287911409D9FF92C1FA2A3355B56F260@SHSMSX104.ccr.corp.intel.com> References: <20190920022151.11817-1-yufengx.mo@intel.com> <20190920022151.11817-2-yufengx.mo@intel.com> In-Reply-To: <20190920022151.11817-2-yufengx.mo@intel.com> Accept-Language: zh-CN, en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-ctpclassification: CTP_NT x-titus-metadata-40: eyJDYXRlZ29yeUxhYmVscyI6IiIsIk1ldGFkYXRhIjp7Im5zIjoiaHR0cDpcL1wvd3d3LnRpdHVzLmNvbVwvbnNcL0ludGVsMyIsImlkIjoiMjUzMzk0YzQtYTZiYy00Y2NlLTkzMmYtYjVmMjE3ODc4NDQ0IiwicHJvcHMiOlt7Im4iOiJDVFBDbGFzc2lmaWNhdGlvbiIsInZhbHMiOlt7InZhbHVlIjoiQ1RQX05UIn1dfV19LCJTdWJqZWN0TGFiZWxzIjpbXSwiVE1DVmVyc2lvbiI6IjE3LjIuNS4xOCIsIlRydXN0ZWRMYWJlbEhhc2giOiJzc053ZnpidkJzVFZIN0htSW1VWDIwZXdHc2pRekdLRTgxMlhXdEE5c0Y1XC9zZE9ZRU1oNzdLZmprZDBTR1UwaiJ9 dlp-product: dlpe-windows dlp-version: 11.2.0.6 dlp-reaction: no-action x-originating-ip: [10.239.127.40] Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Subject: Re: [dts] [PATCH V4 1/1] [examples/flow_classify]: upload automation test script X-BeenThere: dts@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: test suite reviews and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dts-bounces@dpdk.org Sender: "dts" Acked-by: Lin Xueqin -----Original Message----- From: Mo, YufengX=20 Sent: Friday, September 20, 2019 10:22 AM To: dts@dpdk.org; Tu, Lijuan ; Lin, Xueqin ; Yu, PingX Cc: Mo, YufengX Subject: [dts][PATCH V4 1/1] [examples/flow_classify]: upload automation te= st script This automation test script is for flow classify feature. flow_classify is the tool to call flow_classify lib for group of packets, j= ust after receiving them or before transmitting them. Signed-off-by: yufengmx --- tests/TestSuite_flow_classify.py | 509 +++++++++++++++++++++++++++++++ 1 file changed, 509 insertions(+) create mode 100644 tests/TestSuite_flow_classify.py diff --git a/tests/TestSuite_flow_classify.py b/tests/TestSuite_flow_classi= fy.py new file mode 100644 index 0000000..ffa6136 --- /dev/null +++ b/tests/TestSuite_flow_classify.py @@ -0,0 +1,509 @@ +# BSD LICENSE +# +# Copyright(c) 2010-2019 Intel Corporation. All rights reserved. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without #=20 +modification, are permitted provided that the following conditions #=20 +are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of Intel Corporation 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 #=20 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT #=20 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR #=20 +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT #=20 +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, #=20 +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT #=20 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, #=20 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY #=20 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT #=20 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE #=20 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import os +import time +import re +from collections import Counter +from datetime import datetime + +from packet import Packet +from scapy.sendrecv import sendp + +from utils import create_mask as dts_create_mask from test_case import=20 +TestCase from exception import VerifyFailure from settings import=20 +HEADER_SIZE + + +class TestFlowClassify(TestCase): + + def get_cores_mask(self, config=3D'all'): + sockets =3D [self.dut.get_numa_id(index) for index in self.dut_por= ts] + socket_count =3D Counter(sockets) + port_socket =3D socket_count.keys()[0] if len(socket_count) =3D=3D= 1 else -1 + mask =3D dts_create_mask(self.dut.get_core_list(config, + socket=3Dport_socket= )) + return mask + + @property + def output_path(self): + suiteName =3D self.__class__.__name__[4:].lower() + if self.logger.log_path.startswith(os.sep): + output_path =3D os.path.join(self.logger.log_path, suiteName) + else: + cur_path =3D os.path.dirname( + os.path.dirname(os.path.realpath(__file__))) + output_path =3D os.path.join( + cur_path, self.logger.log_path, suiteName) + if not os.path.exists(output_path): + os.makedirs(output_path) + + return output_path + + def get_ixia_peer_port(self): + for cnt in self.dut_ports: + if self.tester.get_local_port_type(cnt) !=3D 'ixia': + continue + tester_port =3D self.tester.get_local_port(cnt) + return tester_port + + def d_console(self, cmds): + return self.execute_cmds(cmds, con_name=3D'dut') + + def d_a_console(self, cmds): + return self.execute_cmds(cmds, con_name=3D'dut_alt') + + def get_console(self, name): + if name =3D=3D 'dut': + console =3D self.dut.send_expect + msg_pipe =3D self.dut.get_session_output + elif name =3D=3D 'dut_alt': + console =3D self.dut.alt_session.send_expect + msg_pipe =3D self.dut.alt_session.session.get_output_all + else: + msg =3D 'not support <{}> session'.format(name) + raise VerifyFailure(msg) + return console, msg_pipe + + def execute_cmds(self, cmds, con_name): + console, msg_pipe =3D self.get_console(con_name) + if len(cmds) =3D=3D 0: + return + if isinstance(cmds, (str, unicode)): + cmds =3D [cmds, '# ', 5] + if not isinstance(cmds[0], list): + cmds =3D [cmds] + outputs =3D [] if len(cmds) > 1 else '' + for item in cmds: + expected_items =3D item[1] + expected_str =3D expected_items or '# ' + try: + if len(item) =3D=3D 3: + timeout =3D int(item[2]) + output =3D console(item[0], expected_str, timeout) + output =3D msg_pipe() if not output else output + else: + # timeout =3D 5 + output =3D console(item[0], expected_str) + output =3D msg_pipe() if not output else output + except Exception as e: + # self.check_process_status() + msg =3D "execute '{0}' timeout".format(item[0]) + raise Exception(msg) + time.sleep(1) + if len(cmds) > 1: + outputs.append(output) + else: + outputs =3D output + return outputs + + def get_pkt_len(self, pkt_type): + # packet size + frame_size =3D 256 + headers_size =3D sum( + map(lambda x: 132 if x =3D=3D 'sctp' else HEADER_SIZE[x], + ['eth', 'ip', pkt_type])) + pktlen =3D frame_size - headers_size + return pktlen + + def set_stream(self, stm_names=3DNone): + ''' set streams for traffic ''' + pkt_configs =3D { + # UDP_1: + # Frame Data/Protocols: Ethernet 2 0800, IPv4,UDP/IP, Fixed= 64. + # IPv4 Header Page: Dest Address: 2.2.2.7 Src Address: 2.2= .2.3 + # UDP Header: Src Port: 32 Dest Port: 33 + # + # Stream Control: Stop after this Stream, Packet Count 32. + # + 'UDP_1': { + 'type': 'UDP', + 'pkt_layers': { + 'ipv4': {'src': '2.2.2.3', 'dst': '2.2.2.7'}, + 'udp': {'src': 32, 'dst': 33}, + 'raw': {'payload': ['58'] * self.get_pkt_len('udp')}}}= , + # UDP_2: + # Frame Data/Protocols: Ethernet 2 0800, IPv4,UDP/IP, Fixed= 64. + # IPv4 Header Page: Dest Address: 9.9.9.7 Src Address: 9.9= .9.3 + # UDP Header: Src Port: 32 Dest Port: 33 + # + # Stream Control: Stop after this Stream, Packet Count 32. + # + 'UDP_2': { + 'type': 'UDP', + 'pkt_layers': { + 'ipv4': {'src': '9.9.9.3', 'dst': '9.9.9.7'}, + 'udp': {'src': 32, 'dst': 33}, + 'raw': {'payload': ['58'] * self.get_pkt_len('udp')}}}= , + 'invalid_UDP': { + 'type': 'UDP', + 'pkt_layers': { + 'ipv4': {'src': '9.8.7.6', 'dst': '192.168.0.36'}, + 'udp': {'src': 10, 'dst': 11}, + 'raw': {'payload': ['58'] * self.get_pkt_len('udp')}}}= , + # TCP_1: + # Frame Data/Protocols: Ethernet 2 0800, IPv4,TCP/IP, Fixed= 64. + # IPv4 Header Page: Dest Address: 9.9.9.7 Src Address: 9.9= .9.3 + # TCP Header: Src Port: 32 Dest Port: 33 + # + # Stream Control: Stop after this Stream, Packet Count 32. + # + 'TCP_1': { + 'type': 'TCP', + 'pkt_layers': { + 'ipv4': {'src': '9.9.9.3', 'dst': '9.9.9.7'}, + 'tcp': {'src': 32, 'dst': 33}, + 'raw': {'payload': ['58'] * self.get_pkt_len('tcp')}}}= , + # TCP_2: + # Frame Data/Protocols: Ethernet 2 0800, IPv4,TCP/IP, Fixed= 64. + # IPv4 Header Page: Dest Address: 9.9.8.7 Src Address: 9.9= .8.3 + # TCP Header: Src Port: 32 Dest Port: 33 + # + # Stream Control: Stop after this Stream, Packet Count 32. + # + 'TCP_2': { + 'type': 'TCP', + 'pkt_layers': { + 'ipv4': {'src': '9.9.8.3', 'dst': '9.9.8.7'}, + 'tcp': {'src': 32, 'dst': 33}, + 'raw': {'payload': ['58'] * self.get_pkt_len('tcp')}}}= , + 'invalid_TCP': { + 'type': 'TCP', + 'pkt_layers': { + 'ipv4': {'src': '9.8.7.6', 'dst': '192.168.0.36'}, + 'tcp': {'src': 10, 'dst': 11}, + 'raw': {'payload': ['58'] * self.get_pkt_len('tcp')}}}= , + # SCTP_1: + # Frame Data/Protocols: Ethernet 2 0800, IPv4, None, Fixed = 256. + # IPv4 Header Page: Dest Address: 2.3.4.5 Src Address: 6.7= .8.9 + # Protocol: 132-SCTP + # Stream Control: Stop after this Stream, Packet Count 32. + # + 'SCTP_1': { + 'type': 'SCTP', + 'pkt_layers': { + 'ipv4': {'src': '6.7.8.9', 'dst': '2.3.4.5'}, + 'sctp': {'src': 32, 'dst': 33}, + 'raw': {'payload': ['58'] * self.get_pkt_len('sctp')}}= }, + 'invalid_SCTP': { + 'type': 'SCTP', + 'pkt_layers': { + 'ipv4': {'src': '9.8.7.6', 'dst': '192.168.0.36'}, + 'sctp': {'src': 10, 'dst': 11}, + 'raw': {'payload': ['58'] * self.get_pkt_len('sctp')}}= }, + } + + # create packet for send + streams =3D [] + for stm_name in stm_names: + if stm_name not in pkt_configs.keys(): + continue + values =3D pkt_configs[stm_name] + savePath =3D os.sep.join([self.output_path, + "pkt_{0}.pcap".format(stm_name)]) + pkt_type =3D values.get('type') + pkt_layers =3D values.get('pkt_layers') + pkt =3D Packet(pkt_type=3Dpkt_type) + for layer in pkt_layers.keys(): + pkt.config_layer(layer, pkt_layers[layer]) + pkt.pktgen.pkt.show() + streams.append(pkt.pktgen.pkt) + + return streams + + def send_packet_by_scapy(self, config): + tx_iface =3D config.get('tx_intf') + cmd =3D "ifconfig {0} up".format(tx_iface) + self.tester.send_expect(cmd, '# ', 30) + pkts =3D config.get('stream') + # stream config + stream_configs =3D config.get('stream configs') + frame_config =3D stream_configs.get('frame config') + gapUnit =3D frame_config.get('gapUnit') + if gapUnit =3D=3D 'gapMilliSeconds': + time_unit =3D 10e-4 + elif gapUnit =3D=3D 'gapMicroSeconds': + time_unit =3D 10e-7 + else: + time_unit =3D 1 + time_unit =3D 10e-4 + ifg =3D frame_config.get('ifg') + count =3D stream_configs.get('count') + interval =3D ifg * time_unit + # run traffic + sendp(pkts, iface=3Dtx_iface, inter=3Dinterval, verbose=3DFalse,=20 + count=3Dcount) + + @property + def target_dir(self): + ''' get absolute directory of target source code ''' + target_dir =3D '/root' + self.dut.base_dir[1:] \ + if self.dut.base_dir.startswith('~') else \ + self.dut.base_dir + return target_dir + + @property + def target_name(self): + return self.dut.target + + def prepare_binary(self, name): + example_dir =3D "examples/" + name + out =3D self.dut.build_dpdk_apps('./' + example_dir) + self.verify("Error" not in out, "Compilation error") + self.verify("No such" not in out, "Compilation error") + binary_dir =3D os.path.join(self.target_dir, example_dir, 'build') + cmd =3D ["ls -F {0} | grep '*'".format(binary_dir), '# ', 5] + exec_file =3D self.d_a_console(cmd) + binary_file =3D os.path.join(binary_dir, exec_file[:-1]) + return binary_file + + def start_flow_classify(self): + ''' boot up flow_classify ''' + rule_config =3D os.sep.join([self.target_dir, + 'examples', + 'flow_classify', + 'ipv4_rules_file.txt']) + if not os.path.exists(rule_config): + raise VerifyFailure("rules file doesn't existed") + core =3D "1S/1C/1T" + option =3D r" -c {0} -n 4 --file-prefix=3Dtest -- --rule_ipv4=3D{1= }".format( + self.get_cores_mask(core), rule_config) + prompt =3D 'table_entry_delete succeeded' + cmd =3D [' '.join([self.flow_classify, option]), prompt, 30] + output =3D self.d_console(cmd) + return output + + def close_flow_classify(self): + output =3D self.dut.get_session_output() + dt =3D datetime.now() + timestamp =3D dt.strftime('%Y-%m-%d_%H%M%S') + self.test_data =3D '{0}/{1}_{2}.log'.format( + self.output_path, 'flow_classify', timestamp) + with open(self.test_data, 'wb') as fp: + fp.write(output) + cmds =3D ['killall flow_classify', '# ', 10] + self.d_a_console(cmds) + + def get_stream_rule_priority(self, stream_type): + stream_types =3D { + 'UDP_1': 0, + 'UDP_2': 1, + 'TCP_1': 2, + 'TCP_2': 3, + 'SCTP_1': 4} + return stream_types.get(stream_type, None) + + def run_traffic(self, config): + stm_types =3D config.get('stm_types') + total_packets =3D config.get('total_packets') + gap =3D config.get('gap') + flow_type =3D config.get('flow_type') + # set traffic topology + pktgen_name =3D 'ixia' if self._enable_perf else 'scapy' + tx_port =3D self.get_ixia_peer_port() if pktgen_name =3D=3D 'ixia'= else \ + self.tester.get_interface(self.tester.get_local_port(0)) + # set traffic configuration + ports_topo =3D { + 'tx_intf': tx_port, + 'rx_intf': 0, + 'stream': self.set_stream(stm_types), + 'stream configs': { + 'count': total_packets, + 'frame config': { + 'gapUnit': 'gapMilliSeconds', + 'ifg': gap}, + 'flow_type': flow_type, + }, } + # begin traffic checking + self.logger.info("begin traffic ... ") + method_name =3D 'send_packet_by_' + pktgen_name + pkt_gen_func =3D getattr(self, 'send_packet_by_' + pktgen_name) + if pkt_gen_func: + result =3D pkt_gen_func(ports_topo) + else: + msg =3D 'not support {}'.format(method_name) + raise VerifyFailure(msg) + # end traffic + self.logger.info("complete transmission") + + def check_filter_pkts(self, log, rule_priority): + pat =3D "rule\[{0}\] count=3D(\d+)".format(rule_priority) \ + if rule_priority is not None else \ + "rule\[\d+\] count=3D(\d+)" + with open(log, 'rb') as fp: + content =3D fp.read() + if content: + grp =3D re.findall(pat, content, re.M) + total =3D reduce(lambda x, y: x + y, [int(i) for i in grp]) \ + if grp and len(grp) else 0 + return total + + def check_test_result(self, config): + stm_types =3D config.get('stm_types') + total_packets =3D config.get('total_packets') + flow_type =3D config.get('flow_type') + self.logger.info(stm_types) + check_results =3D [] + for stm_type in stm_types: + rule_priority =3D self.get_stream_rule_priority(stm_type) + captured_pkts =3D self.check_filter_pkts(self.test_data, + rule_priority) + self.logger.info("%s %d %d" % (stm_type, rule_priority or 0, + captured_pkts or 0)) + msg =3D None + if flow_type =3D=3D 'multi_stream': + # check if packets are multiple rules' pkts + # ignore invalid rule + if rule_priority and captured_pkts % total_packets !=3D 0: + msg =3D ("captured packets are not multiples of " + "rules' {0} packets".format(total_packets)) + else: + continue + elif flow_type =3D=3D 'single_stream': + if rule_priority is None and captured_pkts !=3D 0: + msg =3D "invalid stream hasn't been filtered out" + elif rule_priority is None and captured_pkts !=3D total_pa= ckets: + msg =3D "expect {0} ".format(total_packets) + \ + "captured {0}".format(captured_pkts) + else: + continue + else: + continue + if msg: + check_results.append(msg) + + if check_results: + self.logger.error(os.linesep.join(check_results)) + raise VerifyFailure("test result fail") + + def init_params(self): + self.test_data =3D None + + def verify_traffic(self, stm_types=3DNone, gap=3D10, + flow_type=3D"single_stream"): + self.logger.info('begin to check ...... ') + info =3D { + 'stm_types': stm_types, + 'flow_type': flow_type, + 'total_packets': 32, + 'gap': gap, } + + try: + self.init_params() + # preset test environment + self.start_flow_classify() + # run traffic + self.run_traffic(info) + # close flow_classify + self.close_flow_classify() + except Exception as e: + # close flow_classify + self.close_flow_classify() + msg =3D 'failed to run traffic' + self.verify(False, msg) + # analysis test result + self.check_test_result(info) + + def verify_multiple_rules(self): + stream_list =3D [ + 'UDP_1', 'UDP_2', 'invalid_UDP', + 'TCP_1', 'TCP_2', 'invalid_TCP', + 'SCTP_1', 'invalid_SCTP'] + self.verify_traffic(stm_types=3Dstream_list,=20 + flow_type=3D"multi_stream") + + def verify_supported_nic(self): + supported_drivers =3D ['i40e', 'ixgbe'] + result =3D all([self.dut.ports_info[index]['port'].default_driver = in + supported_drivers + for index in self.dut_ports]) + msg =3D "current nic is not supported" + self.verify(result, msg) + # + # Test cases. + # + + def set_up_all(self): + """ + Run before each test suite + """ + # initialize ports topology + self.dut_ports =3D self.dut.get_ports() + self.verify(len(self.dut_ports) >=3D 2, "Insufficient ports") + # set binary process setting + self.flow_classify =3D self.prepare_binary('flow_classify') + self.verify_supported_nic() + + def set_up(self): + """ + Run before each test case. + """ + pass + + def tear_down(self): + """ + Run after each test case. + """ + pass + + def tear_down_all(self): + """ + Run after each test suite. + """ + pass + + def test_udp_valid_rule(self): + stream_list =3D ['UDP_1', 'UDP_2'] + for stm_type in stream_list: + self.verify_traffic([stm_type]) + + def test_udp_invalid_rule(self): + stream_list =3D ['invalid_UDP'] + self.verify_traffic(stream_list) + + def test_tcp_valid_rule(self): + stream_list =3D ['TCP_1', 'TCP_2'] + for stm_type in stream_list: + self.verify_traffic([stm_type]) + + def test_tcp_invalid_rule(self): + stream_list =3D ['invalid_TCP'] + self.verify_traffic(stream_list) + + def test_sctp_valid_rule(self): + stream_list =3D ['SCTP_1'] + self.verify_traffic(stream_list) + + def test_sctp_invalid_rule(self): + stream_list =3D ['invalid_SCTP'] + self.verify_traffic(stream_list) + + def test_multiple_rules(self): + self.verify_multiple_rules() -- 2.21.0