From 309ceb0d58739b910d5474d1462cde2a6791e6fa Mon Sep 17 00:00:00 2001 From: Peter Mikus Date: Mon, 18 Nov 2024 09:32:32 +0100 Subject: feat(core): IMIX stream dynamic Signed-off-by: Peter Mikus Change-Id: Ib1f68a853012ee08688115e1c021756a18f5cb04 --- GPL/tools/trex/trex_stl_profile.py | 13 +- .../trex/TrafficStreamsJsonClass.py | 106 ++++++++++++ GPL/traffic_profiles/trex/trex-stl-json-geneve.py | 179 +++++++++++++++++++++ 3 files changed, 294 insertions(+), 4 deletions(-) create mode 100644 GPL/traffic_profiles/trex/TrafficStreamsJsonClass.py create mode 100644 GPL/traffic_profiles/trex/trex-stl-json-geneve.py (limited to 'GPL') diff --git a/GPL/tools/trex/trex_stl_profile.py b/GPL/tools/trex/trex_stl_profile.py index e3a3c8c0f9..772c656a07 100644 --- a/GPL/tools/trex/trex_stl_profile.py +++ b/GPL/tools/trex/trex_stl_profile.py @@ -160,14 +160,19 @@ def simple_burst( client.add_streams(streams=[i], ports=[j]) elif isinstance(framesize, str): mark = 0 + strp = len(streams) // len(ports) for i in ports[::2]: - client.add_streams(streams=streams[mark:mark+3], ports=[i]) - mark = mark + 3 + client.add_streams( + streams=streams[mark:mark+strp], ports=[i] + ) + mark = mark + strp if traffic_directions > 1: mark = len(streams) // 2 for i in ports[1::2]: - client.add_streams(streams=streams[mark:mark+3], ports=[i]) - mark = mark + 3 + client.add_streams( + streams=streams[mark:mark+strp], ports=[i] + ) + mark = mark + strp if latency: try: if isinstance(framesize, int): diff --git a/GPL/traffic_profiles/trex/TrafficStreamsJsonClass.py b/GPL/traffic_profiles/trex/TrafficStreamsJsonClass.py new file mode 100644 index 0000000000..9ba3c31658 --- /dev/null +++ b/GPL/traffic_profiles/trex/TrafficStreamsJsonClass.py @@ -0,0 +1,106 @@ +# Copyright (c) 2024 Cisco and/or its affiliates. +# +# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +# +# Licensed under the Apache License 2.0 or +# GNU General Public License v2.0 or later; you may not use this file +# except in compliance with one of these Licenses. You +# may obtain a copy of the Licenses at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html +# +# Note: If this file is linked with Scapy, which is GPLv2+, your use of it +# must be under GPLv2+. If at any point in the future it is no longer linked +# with Scapy (or other GPLv2+ licensed software), you are free to choose +# Apache 2. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Base class for stream profiles for T-rex traffic generator. +""" + +import json +import os + +from trex.stl.api import * + +CP = os.path.join(os.path.abspath(os.sep), "etc") + + +class TrafficStreamsJsonClass: + """Base class for stream profiles for T-rex traffic generator.""" + + def __init__(self): + # Default value of frame size, it will be overwritten by the value of + # "framesize" parameter of "get_streams" method. + self.framesize = 64 + + def define_packets(self): + """Define the packets to be sent from the traffic generator. + + This method MUST return: + + return pkt, vm + + :returns: Packets to be sent from the traffic generator. + :rtype: tuple + """ + raise NotImplementedError + + def create_streams(self): + """Create traffic streams. + + Implement your own traffic streams. + + :returns: Traffic streams. + :rtype: list + """ + pkt_streams = [] + lat_streams = [] + + with open(os.path.join(CP, "packet-profile.json")) as packets_json: + packets_data = json.load(packets_json) + + for profile in packets_data["profiles"]: + for i, stream in enumerate(profile["streams"]): + self.stream_data = stream + pkt, vm = self.define_packets() + packet = STLPktBuilder(pkt=pkt, vm=vm) + pkt_streams.append( + STLStream( + packet=packet, + mode=STLTXCont(pps=9000) + ) + ) + lat_streams.append( + STLStream( + packet=packet, + flow_stats=STLFlowLatencyStats(pg_id=i), + mode=STLTXCont(pps=9000) + ) + ) + + streams = [] + streams.extend(pkt_streams) + streams.extend(lat_streams) + return streams + + def get_streams(self, **kwargs): + """Get traffic streams created by "create_streams" method. + + If needed, add your own parameters. + + :param kwargs: Key-value pairs used by "create_streams" method while + creating streams. + :returns: Traffic streams. + :rtype: list + """ + self.framesize = kwargs["framesize"] + self.rate = kwargs["rate"] + + return self.create_streams() diff --git a/GPL/traffic_profiles/trex/trex-stl-json-geneve.py b/GPL/traffic_profiles/trex/trex-stl-json-geneve.py new file mode 100644 index 0000000000..10d41d15da --- /dev/null +++ b/GPL/traffic_profiles/trex/trex-stl-json-geneve.py @@ -0,0 +1,179 @@ +# Copyright (c) 2024 Cisco and/or its affiliates. +# +# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +# +# Licensed under the Apache License 2.0 or +# GNU General Public License v2.0 or later; you may not use this file +# except in compliance with one of these Licenses. You +# may obtain a copy of the Licenses at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html +# +# Note: If this file is linked with Scapy, which is GPLv2+, your use of it +# must be under GPLv2+. If at any point in the future it is no longer linked +# with Scapy (or other GPLv2+ licensed software), you are free to choose +# Apache 2. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Stream profile for T-Rex traffic generator. + +Stream profile: + - Packet: ETH / IP / UDP / GENEVE +""" + +import ipaddress + +from scapy.all import ( + Packet, BitField, XShortField, ThreeBytesField, XByteField, Raw +) +from scapy.contrib.geneve import GENEVE +from scapy.layers.l2 import Ether +from scapy.layers.inet import IP, UDP +from scapy.layers.inet6 import IPv6 + +from trex.stl.api import * +from TrafficStreamsJsonClass import TrafficStreamsJsonClass + + +class Geneve(Packet): + """Defines custom Geneve Class.""" + name = "Geneve" + fields_desc = [ + BitField("version", 0, 2), + BitField("optionlen", 0, 6), + BitField("oam", 0, 1), + BitField("critical", 0, 1), + BitField("reserved", 0, 6), + XShortField("proto", 0x6558), + ThreeBytesField("vni", 0), + XByteField("reserved2", 0x00) + ] + + +class TrafficStreams(TrafficStreamsJsonClass): + """Stream profile.""" + + def __init__(self): + """Initialization and setting of streams' parameters.""" + + super(TrafficStreamsJsonClass, self).__init__() + + def new_packet_header(self, key): + """Define the packet modifications. + + :returns: List of VMs to be sent from the traffic generator. + :rtype: list + """ + header_data = self.stream_data[key] + vm = [] + for vm_entry in header_data: + value_list, size, offset = ( + vm_entry["value-list"], + vm_entry["size"], + vm_entry["offset"] + ) + # reserved mac address size is 8 bytes so fill the rest with zeros + if "mac" in vm_entry["name"]: + for i, value in enumerate(vm_entry["value-list"]): + value += ":00:00" + value_list[i] = int(value.lower().replace(':', ''), 16) + # replace only the second part of the IPv6 address + if "ip" in vm_entry["name"] and vm_entry["size"] == 16: + for i, value in enumerate(vm_entry["value-list"]): + value_list[i] = int.from_bytes( + ipaddress.IPv6Address(value).packed[8:], + byteorder="big" + ) + size, offset = 8, offset + 8 + vm.append(STLVmFlowVar( + name=vm_entry["name"], + value_list=value_list, + size=size, + op=vm_entry["op"]), + ) + vm.append(STLVmWrFlowVar( + fv_name=vm_entry["name"], + pkt_offset=offset), + ) + return vm + + def define_packets(self): + """Defines the packets to be sent from the traffic generator. + + :returns: Base packets to be sent and transformation function. + :rtype: tuple + """ + base_pkt_data = self.stream_data["base-packet"] + base_pkt = ( + Ether( + src=base_pkt_data["outer-eth"]["src"], + dst=base_pkt_data["outer-eth"]["dst"], + ) / + IP( + src=base_pkt_data["outer-ip"]["src"], + dst=base_pkt_data["outer-ip"]["dst"], + ) / + UDP( + sport=int(base_pkt_data["outer-udp"]["src"]), + dport=int(base_pkt_data["outer-udp"]["dst"]), + ) / + GENEVE( + # proto = 0x86dd, + vni=base_pkt_data["encap-vni"], + ) + ) + if "inner-eth" in base_pkt_data: + base_pkt = base_pkt / ( + Ether( + src=base_pkt_data["inner-eth"]["src"], + dst=base_pkt_data["inner-eth"]["dst"], + ) + ) + + if ipaddress.ip_address(base_pkt_data["inner-ip"]["src"]).version == 6: + base_pkt = base_pkt / ( + IPv6( + src=base_pkt_data["inner-ip"]["src"], + dst=base_pkt_data["inner-ip"]["dst"], + ) + ) + else: + base_pkt = base_pkt / ( + IP( + src=base_pkt_data["inner-ip"]["src"], + dst=base_pkt_data["inner-ip"]["dst"], + ) + ) + base_pkt = base_pkt / ( + UDP( + sport=int(base_pkt_data["inner-udp"]["src"]), + dport=int(base_pkt_data["inner-udp"]["dst"]), + ) + ) + length = self.stream_data["base-packet"]["length"] + pkt = base_pkt / Raw(load="X" * (length - len(base_pkt))) + + outer_header_data = self.new_packet_header("outer-header") + if "inner-l2-header" in self.stream_data: + inner_header_data = self.new_packet_header("inner-l2-header") + else: + inner_header_data = self.new_packet_header("inner-l3-header") + vm = STLScVmRaw(outer_header_data + inner_header_data) + return pkt, vm + + +def register(): + """Register this traffic profile to T-Rex. + + Do not change this function. + + :return: Traffic streams. + :rtype: Object + """ + return TrafficStreams() -- cgit