From 80f774ac6be4e895466488065ca0bdeed7404b7a Mon Sep 17 00:00:00 2001 From: Hanoh Haim Date: Wed, 2 Mar 2016 08:12:45 +0200 Subject: [PATCH] more stateless doc --- draft_trex_stateless.asciidoc | 509 +++++++++++++++++++++++++++++++++++++++--- images/Thumbs.db | Bin 518144 -> 552960 bytes images/stl_tut_pcap_file1.png | Bin 0 -> 3800 bytes 3 files changed, 479 insertions(+), 30 deletions(-) create mode 100644 images/stl_tut_pcap_file1.png diff --git a/draft_trex_stateless.asciidoc b/draft_trex_stateless.asciidoc index a9195d46..7a202c34 100644 --- a/draft_trex_stateless.asciidoc +++ b/draft_trex_stateless.asciidoc @@ -7,7 +7,7 @@ TRex :numbered: :web_server_url: http://trex-tgn.cisco.com/trex :local_web_server_url: csi-wiki-01:8181/trex -:toclevels: 4 +:toclevels: 6 == Stateless support @@ -19,7 +19,7 @@ TRex * Interface can configured with multi traffic profiles * Profile can support multi streams. Scale to 10K streams in parallel * Each Stream -** Packet template - ability to build any packet using Scapy (e.g. MPLS/Ipv4/Ipv6/GRE/VXLAN/NSH) +** Packet template - ability to build any packet using Scapy (e.g. MPLS/IPv4/Ipv6/GRE/VXLAN/NSH) ** Field engine program *** Ability to change any field inside the packet, for example src_ip = 10.0.0.1-10.0.0.255 *** Ability to change the packet size (e.g. Random packet size 64-9K) @@ -97,7 +97,7 @@ image::images/stateless_objects.png[title="TRex Objects ",align="left",width=600 This tutorial will walk you through basic but complete TRex Stateless use cases that will show you common concepts as well as slightly more advanced ones. -==== Tutorial 1: Simple Ipv4/UDP packet - Simulator +==== Tutorial 1: Simple IPv4/UDP packet - Simulator The following example demonstrates the most basic use case using our simulator. @@ -305,7 +305,7 @@ $./stl-sim -f stl/udp_1pkt_simple.py --pkt 0030 78 78 78 78 xxxx ---- -==== Tutorial 2: Simple Ipv4/UDP packet - TRex +==== Tutorial 2: Simple IPv4/UDP packet - TRex ===== Run TRex as a server mode @@ -446,7 +446,7 @@ Port Statistics ---- -==== Tutorial 3: Simple Ipv4/UDP packet +==== Tutorial 3: Simple IPv4/UDP packet The following example demonstrates @@ -666,14 +666,14 @@ file: `stl/imix.py` The following example demonstrates changing packet fields. The Field Engine (FE) has limited number of instructions/operation for supporting most use cases. There is a plan to add LuaJIT to get 100% flexiable in the cost of performance. -The FE can allocate variable in Stream context. Write a variable to a packet offset, change packet size etc. +The FE can allocate stream variable in Stream context. Write a stream variable to a packet offset, change packet size etc. *Some examples for what can be done:* * Change ipv4.tos 1-10 * Change packet size to be random in range 64-9K * Create range of flows (change src_ip,dest_ip,src_port,dest_port) -* Update Ipv4 checksum +* Update IPv4 checksum for more info see link:trex_rpc_server_spec.html#_object_type_em_vm_em_a_id_vm_obj_a[here] @@ -718,10 +718,10 @@ file: `stl/syn_attack.py` mode = STLTXCont()) ---- <1> Create SYN packet using Scapy -<2> Define variable name=ip_src, 4 bytes size for IPv4. -<3> Define variable name=src_port, 2 bytes size for port. +<2> Define stream variable name=ip_src, 4 bytes size for IPv4. +<3> Define stream variable name=src_port, 2 bytes size for port. <4> Write ip_src var into `IP.src` packet offset. Scapy calculate the offset. We could gave `IP:1.src" for second IP header in the packet -<5> Fix Ipv4 checksum. here we provide the header name `IP` we could gave `IP:1` for second IP +<5> Fix IPv4 checksum. here we provide the header name `IP` we could gave `IP:1` for second IP <6> Update TCP src port- TCP checksum is not updated here WARNING: Original Scapy does not have the capability to calculate offset for a header/field by name. This offset capability won't work for all the cases because there could be complex cases that Scapy rebuild the header. In such cases put offset as a number @@ -729,22 +729,22 @@ WARNING: Original Scapy does not have the capability to calculate offset for a h The output pcap file field can be seen here .Pcap file output -[format="csv",cols="1^,2^,1^", options="header"] +[format="csv",cols="1^,2^,2^", options="header",width="40%"] |================= pkt,Client IPv4,Client Port - 1 , 17.152.71.218 , 5814 - 2 , 17.7.6.30 , 26810 - 3 , 17.3.32.200 , 1810 + 1 , 17.152.71.218 , 5814 + 2 , 17.7.6.30 , 26810 + 3 , 17.3.32.200 , 1810 4 , 17.135.236.168 , 55810 - 5 , 17.46.240.12 , 1078 - 6 , 16.133.91.247, 2323 + 5 , 17.46.240.12 , 1078 + 6 , 16.133.91.247 , 2323 |================= ==== Tutorial 8: Field Engine, Tuple Generator The following example demonstrates creating multiply flow from the same packet template. -The TupleGenerator instructions are used to create two variables with IP, port +The TupleGenerator instructions are used to create two stream variables with IP, port file: `stl/udp_1pkt_tuple_gen.py` @@ -770,12 +770,12 @@ file: `stl/udp_1pkt_tuple_gen.py` vm = vm) ---- <1> Define struct with two dependent varibles tuple.ip tuple.port -<2> Write tuple.ip to Ipv4 src field offset +<2> Write tuple.ip to IPv4 src field offset <3> Write tuple.port to UDP header. You should set UDP.checksum to zero .Pcap file output -[format="csv",cols="1^,2^,1^", options="header"] +[format="csv",cols="1^,2^,1^", options="header",width="40%"] |================= pkt,Client IPv4,Client Port 1 , 16.0.0.1 , 1025 @@ -788,11 +788,11 @@ pkt,Client IPv4,Client Port * Number of clients are two 16.0.0.1 and 16.0.0.2 * Number of flows is limited to 129020 (2*65535-1025) -* The variable size should match the size of the FlowVarWr instruction +* The stream variable size should match the size of the FlowVarWr instruction ==== Tutorial 9: Field Engine, write to a bit-field packet -The following example demonstrates a way to write a variable to a bit field packet variables. +The following example demonstrates a way to write a stream variable to a bit field packet variables. In this example MPLS label field will be changed. .MPLS header @@ -832,7 +832,7 @@ file: `stl/udp_1pkt_mpls_vm.py` ---- <1> Define varible size of 2 bytes -<2> Write the variable label with a shift of 12 bits and with 20bit MSB mask. Cast the variables of 2 bytes to 4 bytes +<2> Write the stream variable label with a shift of 12 bits and with 20bit MSB mask. Cast the stream variables of 2 bytes to 4 bytes <3> Second MPLS header should be changed @@ -882,7 +882,7 @@ file: `stl/udp_rand_len_9k.py` ] ) ---- -<1> Define a random variable with maximum size of the packet +<1> Define a random stream variable with maximum size of the packet <2> Trim the packet size to the fv_rand value <3> fix ip.len <4> fix udp.len @@ -969,15 +969,17 @@ To send gratuitous ARP from TRex server side for this server (58.0.0.1) [source,python] ---- - def create_stream (self): # create a base packet and pad it to size - base_pkt = Ether(src="00:00:dd:dd:00:01",dst="ff:ff:ff:ff:ff:ff")/ARP(psrc="58.0.0.1",hwsrc="00:00:dd:dd:00:01", hwdst="00:00:dd:dd:00:01", pdst="58.0.0.1") - + base_pkt = Ether(src="00:00:dd:dd:00:01", + dst="ff:ff:ff:ff:ff:ff")/ + ARP(psrc="58.0.0.1", + hwsrc="00:00:dd:dd:00:01", + hwdst="00:00:dd:dd:00:01", + pdst="58.0.0.1") ---- -Then we can send the clients traffic from A->C - +Then traffic can be sent from client side A->C file: `stl/udp_1pkt_range_clients_split.py` @@ -1014,8 +1016,455 @@ class STLS1(object): return STLStream(packet = STLPktBuilder(pkt = base_pkt/pad,vm = vm), mode = STLTXCont( pps=10 )) ---- -<1> Write the variable mac_src with offset of 10 (last 2 bytes of src_mac field) -<2> Write the variable mac_src with `offset_fixup` of 2. beacuse we write it with offset +<1> Write the stream variable mac_src with offset of 10 (last 2 bytes of src_mac field) +<2> Write the stream variable mac_src with `offset_fixup` of 2. beacuse we write it with offset + + +==== Tutorial 12: Field Engine, Split to core + +The following example demonstrates a way to split generated traffic to a number of threads. +Using this feature, there is a way to specify by each field to split the traffic to threads. +Without this feature the traffic is duplicated and all the threads transmits the same traffic. + +===== Without Split + +Let's assume we have two transmitters DP threads + +[source,python] +---- + def create_stream (self): + + # TCP SYN + base_pkt = Ether()/IP(dst="48.0.0.1")/TCP(dport=80,flags="S") + + + # vm + vm = CTRexScRaw( [ STLVmFlowVar(name="ip_src", + min_value="16.0.0.0", + max_value="16.0.0.254", + size=4, op="inc"), <1> + + + STLVmWrFlowVar(fv_name="ip_src", pkt_offset= "IP.src" ), <2> + + STLVmFixIpv4(offset = "IP"), # fix checksum + ] + + ) + +---- +<1> Stream variable +<2> write it to IPv4.src + + +.Variable per thread +[format="csv",cols="1^,3^,3^", options="header",width="40%"] +|================= +pkt, thread-0 ip_src,thread-1 ip_src + 1 , 16.0.0.1 , 16.0.0.1 + 2 , 16.0.0.2 , 16.0.0.2 + 3 , 16.0.0.3 , 16.0.0.3 + 4 , 16.0.0.4 , 16.0.0.4 + 5 , 16.0.0.5 , 16.0.0.5 + 6 , 16.0.0.6, 16.0.0.6 +|================= + +* In this case all the threads transmit the same packets + + +===== With Split feature + +Let's assume we have two transmitters DP threads + +[source,python] +---- + def create_stream (self): + + # TCP SYN + base_pkt = Ether()/IP(dst="48.0.0.1")/TCP(dport=80,flags="S") + + + # vm + vm = CTRexScRaw( [ STLVmFlowVar(name="ip_src", + min_value="16.0.0.0", + max_value="16.0.0.254", + size=4, op="inc"), + + + STLVmWrFlowVar(fv_name="ip_src", pkt_offset= "IP.src" ), + + STLVmFixIpv4(offset = "IP"), # fix checksum + ] + ,split_by_field = "ip_src" <1> + ) + +---- +<1> The same example but now we with split by `ip_src` stream variable + +.Variable per thread +[format="csv",cols="1^,3^,3^", options="header",width="40%"] +|================= +pkt, thread-0 ip_src ,thread-1 ip_src + 1 , 16.0.0.1 , 16.0.0.128 + 2 , 16.0.0.2 , 16.0.0.129 + 3 , 16.0.0.3 , 16.0.0.130 + 4 , 16.0.0.4 , 16.0.0.131 + 5 , 16.0.0.5 , 16.0.0.132 + 6 , 16.0.0.6, 16.0.0.133 +|================= + +* In this case the stream variable is split + +To simulate it you can run the following command, let's take the file `stl/udp_1pkt_range_clients_split.py` and simulate it + +[source,bash] +---- +$./stl-sim -f stl/udp_1pkt_range_clients_split.py -o a.pcap -c 2 -l 10 #<1> +---- +<1> simulate 2 threads -c 2 + + +.Variable per thread +[format="csv",cols="1^,3^,3^", options="header",width="40%"] +|================= +pkt, thread-0 ip_src,thread-1 ip_src + 1 , 55.55.0.1 , 55.55.58.153 + 2 , 55.55.0.2 , 55.55.58.154 + 3 , 55.55.0.3 , 55.55.58.155 + 4 , 55.55.0.4 , 55.55.58.156 + 5 , 55.55.0.5 , 55.55.58.157 + 6 , 55.55.0.6 , 55.55.58.158 +|================= + + + +===== Some rules about Split stream varibles and burst/multi-burst + +* In case of burst/multi-burst the number of packets are split to number of threads in *default* there is no need an explict split +* When the number of packets in a burst is smaller than the number of threads only one thread will do the work. +* In case there is stream with burst of *1* packet, only the first DP thread will do the work. + + +==== Tutorial 13: Pcap file to *one* stream + +There is a way to load *one* packet data into a stream. There is an assumption that this pcap. only the first packet from this pcap is taken. + +file: `stl/udp_1pkt_pcap.py` + +[source,python] +---- + + def get_streams (self, direction = 0): + return [STLStream(packet = + STLPktBuilder(pkt ="stl/yaml/udp_64B_no_crc.pcap"), # path relative to pwd <1> + mode = STLTXCont(pps=10)) ] + +---- +<1> packet is taken from pcap file relative to pwd of the script you run + + +file: `stl/udp_1pkt_pcap_relative_path.py` + + +[source,python] +---- + + def get_streams (self, direction = 0): + return [STLStream(packet = STLPktBuilder(pkt ="yaml/udp_64B_no_crc.pcap", + path_relative_to_profile = True), <1> + mode = STLTXCont(pps=10)) ] + +---- +<1> packet is taken from pcap file relative to *profile* file location + +==== Tutorial 14: Pcap file to many streams + +The following example demonstrates a way to load pcap with *number* of packets and for each packet create a stream with burst of 1. + +file: `stl/pcap.py` + +[source,python] +---- + def get_streams (self, + ipg_usec = 10.0, <1> + loop_count = 1): <2> + + profile = STLProfile.load_pcap(self.pcap_file, <3> + ipg_usec = ipg_usec, + loop_count = loop_count) +---- +<1> The inter stream gap in usec +<2> How many times to loop +<3> the pcap file + + +image::images/stl_tut_pcap_file1.png[title="pcap file",align="left",width=300, link="images/stl_tut_pcap_file1.png"] + +This figure illustrates how the streams look like for pcap file with 3 packet. +* Each stream is configured to burst with one packet +* Each stream point to the next stream. +* The last stream point to the first with action_loop=loop_count in case it was asked (>1) +The profile will run on only one DP thread because it has burst with one packet (see Split example) + +Running this example + +[source,bash] +---- +./stl-sim -f stl/pcap.py --yaml +---- + +will give this + +[source,python] +---- +csi-kiwi-02]> ./stl-sim -f stl/pcap.py --yaml +- name: 1 + next: 2 <1> + stream: + action_count: 0 + enabled: true + flags: 0 + isg: 10.0 + mode: + percentage: 100 + total_pkts: 1 + type: single_burst + packet: + meta: '' + rx_stats: + enabled: false + self_start: true + vm: + instructions: [] + split_by_var: '' +- name: 2 + next: 3 + stream: + action_count: 0 + enabled: true + flags: 0 + isg: 10.0 + mode: + percentage: 100 + total_pkts: 1 + type: single_burst + packet: + meta: '' + rx_stats: + enabled: false + self_start: false + vm: + instructions: [] + split_by_var: '' +- name: 3 + next: 4 + stream: + action_count: 0 + enabled: true + flags: 0 + isg: 10.0 + mode: + percentage: 100 + total_pkts: 1 + type: single_burst + packet: + meta: '' + rx_stats: + enabled: false + self_start: false + vm: + instructions: [] + split_by_var: '' +- name: 4 + next: 5 + stream: + action_count: 0 + enabled: true + flags: 0 + isg: 10.0 + mode: + percentage: 100 + total_pkts: 1 + type: single_burst + packet: + meta: '' + rx_stats: + enabled: false + self_start: false + vm: + instructions: [] + split_by_var: '' +- name: 5 + next: 1 <2> + stream: + action_count: 1 <3> + enabled: true + flags: 0 + isg: 10.0 + mode: + percentage: 100 + total_pkts: 1 + type: single_burst + packet: + meta: '' + rx_stats: + enabled: false + self_start: false <4> + vm: + instructions: [] + split_by_var: '' +---- +<1> each stream point to the next stream +<2> last point to the first +<3> the number of loop is given in `action_count: 1` +<4> self_start is disabled for all the streams except the first one + + +==== Tutorial 15: Pcap file to many streams and Field Engine + +The following example demonstrates a way to load pcap file to many stream and attach to each stream a Field Engine program. +For example change the IP.src of all the streams to a random number + +file: `stl/pcap_with_vm.py` + +[source,python] +---- + + def create_vm (self, ip_src_range, ip_dst_range): + if not ip_src_range and not ip_dst_range: + return None + + # until the feature of offsets will be fixed for PCAP use hard coded offsets + + vm = [] + + if ip_src_range: + vm += [STLVmFlowVar(name="src", + min_value = ip_src_range['start'], + max_value = ip_src_range['end'], + size = 4, op = "inc"), + #STLVmWrFlowVar(fv_name="src",pkt_offset= "IP.src") + STLVmWrFlowVar(fv_name="src",pkt_offset = 26) + ] + + if ip_dst_range: + vm += [STLVmFlowVar(name="dst", + min_value = ip_dst_range['start'], + max_value = ip_dst_range['end'], + size = 4, op = "inc"), + + #STLVmWrFlowVar(fv_name="dst",pkt_offset= "IP.dst") + STLVmWrFlowVar(fv_name="dst",pkt_offset = 30) + ] + + vm += [#STLVmFixIpv4(offset = "IP") + STLVmFixIpv4(offset = 14) + ] + + return vm + + + def get_streams (self, + ipg_usec = 10.0, + loop_count = 5, + ip_src_range = None, + ip_dst_range = {'start' : '10.0.0.1', + 'end': '10.0.0.254'}): + + vm = self.create_vm(ip_src_range, ip_dst_range) <1> + profile = STLProfile.load_pcap(self.pcap_file, + ipg_usec = ipg_usec, + loop_count = loop_count, + vm = vm) <2> + + return profile.get_streams() +---- +<1> Create Field Engine program, +<2> Apply to all the packets -> convert to streams + +.Output +[format="csv",cols="1^,2^,1^", options="header",width="40%"] +|================= +pkt, IPv4 , flow + 1 , 10.0.0.1, 1 + 2 , 10.0.0.1, 1 + 3 , 10.0.0.1, 1 + 4 , 10.0.0.1, 1 + 5 , 10.0.0.1, 1 + 6 , 10.0.0.1, 1 + 7 , 10.0.0.2, 2 + 8 , 10.0.0.2, 2 + 9 , 10.0.0.2, 2 + 10 , 10.0.0.2,2 + 11 , 10.0.0.2,2 + 12 , 10.0.0.2,2 +|================= + + +==== Tutorial 16: Source and Destination MAC address + +Each TRex port has a source MAC configure and destination MAC (DUT) configured in /etc/trex_cfg.yaml +By default those MAC (source and destination) is taken +In case a user configures a source or destination MAC explicitly this MAC will override + + +.MAC addrees +[format="csv",cols="2^,2^,2^", options="header",width="40%"] +|================= +Scapy , Source MAC,Destination MAC +Ether() , trex_cfg,trex_cfg +Ether(src="00:bb:12:34:56:01"),"00:bb:12:34:56:01",trex_cfg +Ether(dst="00:bb:12:34:56:01"),trex_cfg,"00:bb:12:34:56:01" +|================= + +For example + +file: `stl/udp_1pkt_1mac_override.py` + + +[source,python] +---- + def create_stream (self): + + base_pkt = Ether(src="00:bb:12:34:56:01")/ <1> + IP(src="16.0.0.1",dst="48.0.0.1")/ + UDP(dport=12,sport=1025) +---- +<1> Don't take TRex port src interface MAC + +==== Tutorial 17: Teredo tunnel (IPv6 over IPv4) + +The following example demonstrates creating IPv6 packet inside IPv4 packet and create a range of IPs + +file: `stl/udp_1pkt_ipv6_in_ipv4.py` + +[source,python] +---- + def create_stream (self): + # Teredo Ipv6 over Ipv4 + pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/ + UDP(dport=3797,sport=3544)/ + IPv6(dst="2001:0:4137:9350:8000:f12a:b9c8:2815", + src="2001:4860:0:2001::68")/ + UDP(dport=12,sport=1025)/ICMPv6Unknown() + + vm = CTRexScRaw( [ + # tuple gen for inner Ipv6 + STLVmTupleGen ( ip_min="16.0.0.1", ip_max="16.0.0.2", + port_min=1025, port_max=65535, + name="tuple"), <1> + + STLVmWrFlowVar (fv_name="tuple.ip", + pkt_offset= "IPv6.src", + offset_fixup=12 ), <2> + STLVmWrFlowVar (fv_name="tuple.port", + pkt_offset= "UDP:1.sport" ) <3> + ] + ) +---- +<1> Define stream struct name tuple. it has tuple.ip, tuple.port variables +<2> Write stream tuple.ip variable into IPv6.src offset and fixup with 12 bytes (only 4 LSB) +<3> Write stream tuple.port variable into the second UDP header diff --git a/images/Thumbs.db b/images/Thumbs.db index 309dd91ecc18de00145e160526619541eeb15728..175b366f912404630c18dbeef4de623da0dbfbcf 100755 GIT binary patch delta 16790 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 literal 0 HcmV?d00001 -- 2.16.6