def get_streams(pps, duration, ip_tot_len, ip_src, ip_dst): udp_data_len = ip_tot_len - IPv4_HDR_LEN - UDP_HDR_LEN if udp_data_len < 16: raise RuntimeError("The minimal payload size is 16 bytes.") print(f"UDP payload size: {udp_data_len}") udp_payload = "Z" * udp_data_len pkt = STLPktBuilder(pkt=Ether() / IP(src=ip_src, dst=ip_dst) / UDP(dport=8888, sport=9999, chksum=0) / udp_payload) streams = [ STLStream( name="s0", packet=pkt, flow_stats=STLFlowLatencyStats(pg_id=0), mode=STLTXSingleBurst(pps=pps, total_pkts=int(duration * pps)), ) ] return streams
def configure_traffic_stream(self, traffic_flows, nr_of_flows, packet_size, **kwargs): flow_percentage = float(kwargs.pop("percentage", 1000000)) / 10000 trex_dst_mac = kwargs.pop("traffic_dst_mac", '00:00:02:00:00:00') trex_src_mac = kwargs.pop("traffic_src_mac", '00:00:01:00:00:00') l2_macs = kwargs.pop("l2_macs", 1) # # The packet size passed here assumes it includes the checksum, however # the TRex packet size does not. Adjust the size to correct this. # packet_size -= 4 if traffic_flows == TrafficFlowType.none or \ self.__traffic_flows != TrafficFlowType.none: # # We need either a cleanup, or a cleanup before we configure # a new traffic flow type # self._delete_traffic_stream_config() if traffic_flows == TrafficFlowType.l2_mac or \ traffic_flows == TrafficFlowType.l3_ipv4 or \ traffic_flows == TrafficFlowType.nfv_mobile: # # Max flows due to IPv4 address limit, and addresses used for tests # if nr_of_flows > 0x00ffffff: raise ValueError( "To many flows requested, max {} supported!".format( 0x00ffffff)) L2 = Ether(src=trex_src_mac, dst=trex_dst_mac) L3 = IP(src="1.0.0.0", dst="2.0.0.0") L4 = UDP(chksum=0) if (len(L2 / L3 / L4) + 4) > packet_size: # +4 for Ethernet CRC raise ValueError("Packet size ({} bytes) too small for" "requested packet ({} bytes)!".format( packet_size, len(L2 / L3 / L4) + 4)) if traffic_flows == TrafficFlowType.l2_mac: src_base = self._mac_2_int(trex_src_mac) & 0xff000000 dst_base = self._mac_2_int(trex_dst_mac) & 0xff000000 vm = [ # Source MAC address STLVmFlowVar(name="src", min_value=src_base, max_value=src_base + nr_of_flows - 1, size=4, op="inc"), STLVmWrFlowVar(fv_name="src", pkt_offset=8), # Destination MAC address STLVmFlowVar(name="dst", min_value=dst_base, max_value=dst_base + nr_of_flows - 1, size=4, op="inc"), STLVmWrFlowVar(fv_name="dst", pkt_offset=2) ] elif traffic_flows == TrafficFlowType.l3_ipv4: src_end = str( netaddr.IPAddress( int(netaddr.IPAddress('1.0.0.0')) + nr_of_flows - 1)) dst_end = str( netaddr.IPAddress( int(netaddr.IPAddress('2.0.0.0')) + nr_of_flows - 1)) vm = [ # Source IPv4 address STLVmFlowVar(name="src", min_value="1.0.0.0", max_value=src_end, size=4, op="inc"), STLVmWrFlowVar(fv_name="src", pkt_offset="IP.src"), # Destination IPv4 address STLVmFlowVar(name="dst", min_value="2.0.0.0", max_value=dst_end, size=4, op="inc"), STLVmWrFlowVar(fv_name="dst", pkt_offset="IP.dst"), # Checksum STLVmFixIpv4(offset="IP") ] elif traffic_flows == TrafficFlowType.nfv_mobile: src_end = str( netaddr.IPAddress( int(netaddr.IPAddress('1.0.0.0')) + nr_of_flows - 1)) dst_end = str( netaddr.IPAddress( int(netaddr.IPAddress('2.0.0.0')) + nr_of_flows - 1)) vm = [ # Source MAC address STLVmFlowVar(name="srcm", min_value=0x01000001, max_value=0x01000001 + l2_macs - 1, size=4, op="inc"), STLVmWrFlowVar(fv_name="srcm", pkt_offset=8), # Destination MAC address STLVmFlowVar(name="dstm", min_value=0x02000000, max_value=0x02000000 + l2_macs - 1, size=4, op="inc"), STLVmWrFlowVar(fv_name="dstm", pkt_offset=2), # Source IPv4 address STLVmFlowVar(name="src", min_value="1.0.0.0", max_value=src_end, size=4, op="inc"), STLVmWrFlowVar(fv_name="src", pkt_offset="IP.src"), # Destination IPv4 address STLVmFlowVar(name="dst", min_value="2.0.0.0", max_value=dst_end, size=4, op="inc"), STLVmWrFlowVar(fv_name="dst", pkt_offset="IP.dst"), # Checksum STLVmFixIpv4(offset="IP") ] else: raise ValueError( "Unsupported traffic type for T-Rex tester!!!") if traffic_flows == TrafficFlowType.nfv_mobile: stream_percentage = flow_percentage / 2 else: stream_percentage = flow_percentage headers = L2 / L3 / L4 padding = max(0, (packet_size - len(headers))) * 'e' packet = headers / padding trex_packet = STLPktBuilder(pkt=packet, vm=vm) trex_stream = STLStream( packet=trex_packet, mode=STLTXCont(percentage=stream_percentage)) self.__trex_client.add_streams(trex_stream, ports=[self.__trex_port]) # # For nfv_mobile we still need to setup the alternating streams. # if traffic_flows == TrafficFlowType.nfv_mobile: alternate_flows = kwargs.pop("alternate_flows", 200000) stream_percentage = flow_percentage / 2 self.__active_alternate_stream = 0 # # Keep the flows the same as for the Xena version, so the # traffic scripts using this do not have to differentiate # between traffic generator types. # # The Xena uses streams and every stream can generate 64K # flows. To Find the flow start we need the number of base # flows rounded of the next 64K (stream) and use the next one. # # For the individual iterations of the flow set they also # need to start at a 64K boundary. # start_stream_id = self._div_round_up(nr_of_flows, 0x10000) + 1 for alternate_flow_sets in range(0, 3): flow_start = start_stream_id * 0x10000 src_start = str( netaddr.IPAddress( int(netaddr.IPAddress('1.0.0.0')) + flow_start)) src_end = str( netaddr.IPAddress( int(netaddr.IPAddress('1.0.0.0')) + flow_start + alternate_flows - 1)) dst_start = str( netaddr.IPAddress( int(netaddr.IPAddress('2.0.0.0')) + flow_start)) dst_end = str( netaddr.IPAddress( int(netaddr.IPAddress('2.0.0.0')) + flow_start + alternate_flows - 1)) vm = [ # Source MAC address STLVmFlowVar(name="srcm", min_value=0x01000001, max_value=0x01000001 + l2_macs - 1, size=4, op="inc"), STLVmWrFlowVar(fv_name="srcm", pkt_offset=8), # Destination MAC address STLVmFlowVar(name="dstm", min_value=0x02000000, max_value=0x02000000 + l2_macs - 1, size=4, op="inc"), STLVmWrFlowVar(fv_name="dstm", pkt_offset=2), # Source IPv4 address STLVmFlowVar(name="src", min_value=src_start, max_value=src_end, size=4, op="inc"), STLVmWrFlowVar(fv_name="src", pkt_offset="IP.src"), # Destination IPv4 address STLVmFlowVar(name="dst", min_value=dst_start, max_value=dst_end, size=4, op="inc"), STLVmWrFlowVar(fv_name="dst", pkt_offset="IP.dst"), # Checksum STLVmFixIpv4(offset="IP") ] trex_packet = STLPktBuilder(pkt=packet, vm=vm) stream = STLStream( packet=trex_packet, mode=STLTXCont(percentage=stream_percentage), start_paused=False if alternate_flow_sets == 0 else True) self.__alternate_stream_sets.append( self.__trex_client.add_streams( stream, ports=[self.__trex_port])) start_stream_id += self._div_round_up( alternate_flows, 0x10000) self.__traffic_flows = traffic_flows return True elif traffic_flows == TrafficFlowType.none: self.__traffic_flows = traffic_flows return True else: raise ValueError( "Unsupported traffic flow passed for T-Rex tester!") self.__traffic_flows = TrafficFlowType.none return False
if __name__ == "__main__": # Create a client for stateless tests. clt = STLClient() passed = True try: udp_payload = "A" * 50 pkt = STLPktBuilder( pkt=Ether() / IP(src="192.168.17.1", dst="192.168.17.2") / UDP(dport=8888, sport=9999, chksum=0) / udp_payload) st = STLStream( name="udp_single_burst", packet=pkt, # Packet group id flow_stats=STLFlowLatencyStats(pg_id=PG_ID), mode=STLTXSingleBurst(total_pkts=TOTAL_PKTS, pps=PPS), ) clt.connect() all_ports = clt.get_all_ports() print("All ports: {}".format(",".join(map(str, all_ports)))) tx_port, rx_port = all_ports print(f"TX port: {tx_port}, RX port: {rx_port}") tx_port_attr = clt.get_port_attr(tx_port) rx_port_attr = clt.get_port_attr(rx_port) assert tx_port_attr["src_ipv4"] == "192.168.17.1" assert rx_port_attr["src_ipv4"] == "192.168.18.1" clt.reset(ports=all_ports) clt.add_streams([st], ports=[tx_port])
def generate_streams(self, port, chain_id, stream_cfg, l2frame, latency=True, e2e=False): """Create a list of streams corresponding to a given chain and stream config. port: port where the streams originate (0 or 1) chain_id: the chain to which the streams are associated to stream_cfg: stream configuration l2frame: L2 frame size (including 4-byte FCS) or 'IMIX' latency: if True also create a latency stream e2e: True if performing "end to end" connectivity check """ streams = [] pg_id, lat_pg_id = self.get_pg_id(port, chain_id) if l2frame == 'IMIX': for ratio, l2_frame_size in zip(IMIX_RATIOS, IMIX_L2_SIZES): pkt = self._create_pkt(stream_cfg, l2_frame_size) if e2e: streams.append(STLStream(packet=pkt, mode=STLTXCont(pps=ratio))) else: if stream_cfg['vxlan'] is True: streams.append(STLStream(packet=pkt, flow_stats=STLFlowStats(pg_id=pg_id, vxlan=True), mode=STLTXCont(pps=ratio))) else: streams.append(STLStream(packet=pkt, flow_stats=STLFlowStats(pg_id=pg_id), mode=STLTXCont(pps=ratio))) if latency: # for IMIX, the latency packets have the average IMIX packet size pkt = self._create_pkt(stream_cfg, IMIX_AVG_L2_FRAME_SIZE) else: l2frame_size = int(l2frame) pkt = self._create_pkt(stream_cfg, l2frame_size) if e2e: streams.append(STLStream(packet=pkt, mode=STLTXCont())) else: if stream_cfg['vxlan'] is True: streams.append(STLStream(packet=pkt, flow_stats=STLFlowStats(pg_id=pg_id, vxlan=True), mode=STLTXCont())) else: streams.append(STLStream(packet=pkt, flow_stats=STLFlowStats(pg_id=pg_id), mode=STLTXCont())) # for the latency stream, the minimum payload is 16 bytes even in case of vlan tagging # without vlan, the min l2 frame size is 64 # with vlan it is 68 # This only applies to the latency stream if latency and stream_cfg['vlan_tag'] and l2frame_size < 68: pkt = self._create_pkt(stream_cfg, 68) if latency: # TRex limitation: VXLAN skip is not supported for latency stream streams.append(STLStream(packet=pkt, flow_stats=STLFlowLatencyStats(pg_id=lat_pg_id), mode=STLTXCont(pps=self.LATENCY_PPS))) return streams
def get_streams( pps: float, burst_num: int, model: str, src_num, tot_pkts_burst, l3_data, test: bool ) -> list: """ Utilize the single burst stream profile in STL to build the model-based traffic. The ONLY focus here is the inter-arrival times of bursts. """ if src_num > 1: raise RuntimeError("Currently not implemented!") pps = pps * 10 ** 6 pprint.pp(pps) if pps < LATENCY_FLOW_PPS: raise RuntimeError( f"The minimal PPS {LATENCY_FLOW_PPS} is required for accuracy." ) # Limit the search symbol table to current module. func_params = globals().get(f"create_stream_params_{model}", None) if not func_params: raise RuntimeError(f"Unknown model: {model}!") stream_params, flow_duration = func_params(pps, burst_num, src_num, tot_pkts_burst) if test: print("* Stream parameters: ") pprint.pp(stream_params) print(f"* Flow duration: {flow_duration} seconds.") streams = list() for index, param in enumerate(stream_params): self_start = False next_name = f"s{index+1}" if index == 0: self_start = True elif index == len(stream_params) - 1: next_name = None udp_payload_len = param["ip_tot_len"] - IPv4_HDR_LEN - UDP_HDR_LEN if udp_payload_len < 16: raise RuntimeError("The minimal payload size is 16 bytes.") udp_payload = "Z" * udp_payload_len # UDP checksum is disabled. pkt = STLPktBuilder( pkt=Ether() / IP(src=l3_data["ip_src"], dst=l3_data["ip_dst"]) / UDP(dport=8888, sport=9999, chksum=0) / udp_payload ) streams.append( STLStream( name=f"s{index}", isg=param["isg"] * 10 ** 6, # TRex interprets them as µs packet=pkt, flow_stats=STLFlowLatencyStats(pg_id=index), mode=STLTXSingleBurst( pps=param["pps"], total_pkts=param["tot_pkts_burst"] ), next=next_name, self_start=self_start, ) ) return (streams, flow_duration)
def create_streams_with_second_flow(stream_params: dict, second_stream_params: dict, ip_src: str, ip_dst: str) -> list: """Create a list of STLStream objects with the second flow.""" spoofed_eth_srcs = ["0c:42:a1:51:41:d8", "ab:ab:ab:ab:ab:02"] udp_payload_size = IP_TOT_LEN - IPv4_HDR_LEN - UDP_HDR_LEN if udp_payload_size < 16: raise RuntimeError("The minimal payload size is 16 bytes.") print(f"UDP payload size: {udp_payload_size}") udp_payload = ["Z" * udp_payload_size, "Z" * (udp_payload_size - 1)] # UDP checksum is disabled. pkts = list() pkts.append( STLPktBuilder(pkt=Ether(src=spoofed_eth_srcs[0]) / IP(src=ip_src, dst=ip_dst) / UDP(dport=8888, sport=9999, chksum=0) / udp_payload[0])) pkts.append( STLPktBuilder(pkt=Ether(src=spoofed_eth_srcs[0]) / IP(src=ip_src, dst=ip_dst) / UDP(dport=8888, sport=9999, chksum=0) / udp_payload[1])) streams = list() pg_id = 0 for prefix, params in enumerate([stream_params, second_stream_params]): print("Prefix: ", prefix) for index, stp in enumerate(params): next_st_name = None next_st_w_name = None self_start = False if index != len(params) - 1: next_st_name = f"s{prefix+1}{index+1}" next_st_w_name = f"sw{prefix+1}{index+1}" if index == 0: self_start = True # Add extra workload stream. workload_stream_pps = stp["pps"] - LATENCY_FLOW_PPS streams.append( STLStream( name=f"sw{prefix+1}{index}", isg=stp["isg"], packet=pkts[prefix], mode=STLTXSingleBurst( pps=workload_stream_pps, total_pkts=int(workload_stream_pps * stp["on_time"]), ), next=next_st_w_name, self_start=self_start, )) # Add latency monitoring flow with a fixed PPS. streams.append( STLStream( name=f"s{prefix+1}{index}", isg=stp["isg"], packet=pkts[prefix], flow_stats=STLFlowLatencyStats(pg_id=pg_id), # index), mode=STLTXSingleBurst( pps=LATENCY_FLOW_PPS, total_pkts=int(LATENCY_FLOW_PPS * stp["on_time"]), ), next=next_st_name, self_start=self_start, )) pg_id += 1 return streams
def create_streams(stream_params: dict, ip_src: str, ip_dst: str) -> list: """Create a list of STLStream objects.""" udp_payload_size = IP_TOT_LEN - IPv4_HDR_LEN - UDP_HDR_LEN if udp_payload_size < 16: raise RuntimeError("The minimal payload size is 16 bytes.") print(f"UDP payload size: {udp_payload_size}") udp_payload = "Z" * udp_payload_size # UDP checksum is disabled. pkt = STLPktBuilder(pkt=Ether() / IP(src=ip_src, dst=ip_dst) / UDP(dport=8888, sport=9999, chksum=0) / udp_payload) streams = list() # Ref: https://trex-tgn.cisco.com/trex/doc/trex_stateless.html#_tutorial_per_stream_latency_jitter_packet_errors # Latency streams are handled fully by software and do not support at full # line rate like normal streams. Typically, it is sufficient to have a # low-rate latency stream alongside with the real workload stream. # In order to have a latency resolution of 1usec, it is not necessary to # send a latency stream at a speed higher than 1 MPPS. It is suggested not # make the total rate of latency streams higher than 5 MPPS. for index, stp in enumerate(stream_params): next_st_name = None next_st_w_name = None self_start = False if index != len(stream_params) - 1: next_st_name = f"s{index+1}" next_st_w_name = f"sw{index+1}" if index == 0: self_start = True # Add extra workload stream. workload_stream_pps = stp["pps"] - LATENCY_FLOW_PPS streams.append( STLStream( name=f"sw{index}", isg=stp["isg"], packet=pkt, mode=STLTXSingleBurst( pps=workload_stream_pps, total_pkts=int(workload_stream_pps * stp["on_time"]), ), next=next_st_w_name, self_start=self_start, )) # Add latency monitoring flow with a fixed PPS. streams.append( STLStream( name=f"s{index}", isg=stp["isg"], packet=pkt, flow_stats=STLFlowLatencyStats(pg_id=index), mode=STLTXSingleBurst( pps=LATENCY_FLOW_PPS, total_pkts=int(LATENCY_FLOW_PPS * stp["on_time"]), ), next=next_st_name, self_start=self_start, )) return streams
def _load_traffic_profiles(self, test_config): """Load traffic profiles for all ports based on loaded configuration.""" test_name = test_config["name"] logger.debug(f"{test_name}: loading traffic profiles") for tx_config in test_config["transmit"]: tx_server_name, tx_port_id = tx_config["from"].split(":") rx_server_name, rx_port_id = tx_config["to"].split(":") tx_port_ip = self.get_port_by_id(tx_server_name, tx_port_id)["ip"] rx_port_ip = self.get_port_by_id(rx_server_name, rx_port_id)["ip"] tunables = { **{ "src_ip": tx_port_ip, "dst_ip": rx_port_ip }, **tx_config.get("tunables", {}), } profile_file = tx_config.get("profile_file") if not profile_file: logger.info( f"{test_name}: profile file for {tx_server_name} port {tx_port_id} is not defined. Using default profile" ) profile_file = os.path.join( os.path.dirname(os.path.abspath(__file__)), "default_profile.py", ) client = self.get_server_by_name(tx_server_name)["client"] profile = STLProfile.load(profile_file, port_id=tx_port_id, **tunables) stream_ids = client.add_streams(profile.get_streams(), tx_port_id) stream_ids = stream_ids if isinstance(stream_ids, list) else [stream_ids] logger.debug( f"Added {len(stream_ids)} streams to {tx_server_name} port {tx_port_id}" ) # to measure stats we need to attach to the receiver # a stream with pg_id of transmitter's stats stream, because # (see: https://trex-tgn.cisco.com/trex/doc/trex_faq.html # section 1.5.15.: "latency streams are handled by rx software") flow_stats_type = tunables.get("flow_stats") if flow_stats_type: if flow_stats_type not in ("stats", "latency"): raise Exception( 'Unknown stats type. Valid values: "stats", "latency"') pg_id = tunables.get("flow_stats_pg_id", tunables.get("flow_stats_pg_id")) if not pg_id: raise Exception( "Streams with flow stats must have defined pg_id") if rx_server_name == tx_server_name: continue if flow_stats_type == "latency": flow_stats = STLFlowLatencyStats(pg_id=pg_id) else: flow_stats = STLFlowStats(pg_id=pg_id) flow_stats_stream = STLStream(flow_stats=flow_stats, start_paused=True) flow_stats_profile = STLProfile(flow_stats_stream) rx_server_client = self.get_server_by_name( rx_server_name)["client"] stream_ids = rx_server_client.add_streams( flow_stats_profile.get_streams(), rx_port_id) stream_ids = (stream_ids if isinstance(stream_ids, list) else [stream_ids]) logger.debug( f"{test_name}: Added {len(stream_ids)} streams to {rx_server_name} port {rx_port_id}" ) logger.debug(f"{test_name}: traffic profiles succesfully loaded")