-
Notifications
You must be signed in to change notification settings - Fork 1k
IPv4 decapsulation test #128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
5e62043
75ea6f4
fd8f7fa
d41c699
6648d02
18f8019
76d7239
1478c4c
e5f198a
af1588e
9628d7e
e903bc0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,194 @@ | ||
| ''' | ||
| Owner: Dor Marcus <Dorm@mellanox.com> | ||
| Created on: 12/09/2017 | ||
| Description: This file contains the Decapasulation test for SONIC, to test Decapsulation of IPv4 with double and triple encapsulated packets | ||
|
|
||
| Design is available in https://github.com/Azure/SONiC/wiki/IPv4-Decapsulation-test | ||
|
|
||
| Precondition: Before the test start, all routes need to be defined as in the route_info.txt file, in addition to the decap rule that need to be set as the dspc_mode | ||
| topology: The test need to run on non-lag systems with at least 31 active ports | ||
|
|
||
| Usage: Examples of how to start the test | ||
| ptf --test-dir /root/dor/ ip_decap_test_red --platform remote -t "verbose=True;route_info='/tmp/route_info.txt';lo_ip='10.1.0.32';router_mac='00:02:03:04:05:00';dscp_mode='pipe'" --log-dir /tmp/logs --verbose | ||
| Parameters: route_info - The route_info file location | ||
| lo_ip - The loop_back IP that is configured in the decap rule | ||
| router_mac - The mac of the router_mac | ||
| dscp_mode - The rule for the dscp parameter in the decap packet that is configured in the JSON file ('pipe' for inner and 'uniform' for outer) | ||
|
|
||
| ''' | ||
|
|
||
| #--------------------------------------------------------------------- | ||
| # Global imports | ||
| #--------------------------------------------------------------------- | ||
| import random | ||
| import time | ||
| import logging | ||
| import ptf.packet as scapy | ||
| import socket | ||
| import ptf.dataplane as dataplane | ||
|
|
||
| from ptf.testutils import * | ||
| from ptf.mask import Mask | ||
| import ipaddress | ||
|
|
||
| import os | ||
| import unittest | ||
|
|
||
| import ptf | ||
| from ptf.base_tests import BaseTest | ||
| from ptf import config | ||
| import ptf.dataplane as dataplane | ||
| import ptf.testutils as testutils | ||
|
|
||
| import pprint | ||
| from router_utils import * | ||
|
|
||
| #TODO: please make sure all imports are required. those that are not should be removed. | ||
|
|
||
| #--------------------------------------------------------------------- | ||
| # Global variables | ||
| #--------------------------------------------------------------------- | ||
| PREFIX_AND_PORT_SPLITTER=" " | ||
| PORT_LIST_SPLITTER="," | ||
| PORT_COUNT = 31 | ||
|
|
||
| class DecapPacketTest(BaseTest, RouterUtility): | ||
| def __init__(self): | ||
| BaseTest.__init__(self) | ||
| self.test_params = testutils.test_params_get() | ||
| #----------------------------------------------------------------- | ||
| def setUp(self): | ||
| ''' | ||
| @summary: Setup for the test | ||
| ''' | ||
| self.dataplane = ptf.dataplane_instance | ||
| self.router_mac = self.test_params['router_mac'] | ||
| #----------------------------------------------------------------- | ||
|
|
||
| def send_and_verify(self, dst_ip, expected_ports, src_port, triple_encap = False): | ||
| ''' | ||
| @summary: This function builds encap packet, send and verify their arrival, When a packet will not arrived as expected an exeption will be throwen | ||
| @dst_ip: the destination ip for the inner IP header | ||
| @expected_ports: list of ports that a packet can arrived from | ||
| @src_port: the physical port that the packet will be sent from | ||
| @triple_encap: Bool if to send triple encapsulated packet | ||
| ''' | ||
| #setting parameters | ||
| src_mac = self.dataplane.get_mac(0, 0) | ||
| dst_mac = '00:11:22:33:44:55' | ||
| outer_src_ip = '1.1.1.1' | ||
| inner_src_ip = '2.2.2.2' | ||
| router_mac = self.test_params['router_mac'] | ||
| dscp_in = random.randint(0, 32) | ||
| tos_in = dscp_in << 2 | ||
| dscp_out = random.randint(0, 32) | ||
| tos_out = dscp_out << 2 | ||
| outer_ttl = random.randint(2, 63) | ||
| sport=1234 | ||
| dport=80 | ||
| if ("pipe" == self.test_params['dscp_mode']): | ||
| exp_tos = tos_in | ||
| elif("uniform" == self.test_params['dscp_mode']): | ||
| exp_tos = tos_out | ||
| else: | ||
| print("ERROR: no dscp is configured") | ||
| exit() | ||
|
|
||
| #building the packets and the expected packets | ||
| if (not triple_encap): | ||
| #for the double encap packet we will use IP header with TCP header without mac | ||
| inner_pkt = simple_ip_only_packet(ip_dst=dst_ip, ip_src=inner_src_ip, ip_ttl=64) | ||
| #after the decap process the retuning packet will be normal tcp packet, The TTL is taked from the inner layer and redused by one | ||
| exp_pkt = simple_tcp_packet(pktlen=114, | ||
| eth_dst=dst_mac, | ||
| eth_src=router_mac, | ||
| ip_dst=dst_ip, | ||
| ip_src=inner_src_ip, | ||
| ip_tos=exp_tos, | ||
| ip_ttl=63) | ||
| else: | ||
| #Building triple encap packet with SCAPY, because there is no PTF function for it, I use the defualt values for the TCP header | ||
| tcp_hdr = scapy.TCP(sport=sport, dport=dport, flags="S", chksum=0) | ||
| inner_pkt2 = scapy.IP(src='4.4.4.4', dst='3.3.3.3', tos=0, ttl=64, id=1, ihl=None) / tcp_hdr | ||
| inner_pkt = scapy.IP(src=inner_src_ip, dst=dst_ip, tos=tos_in, ttl=64, id=1, ihl=None,proto =4) / inner_pkt2 | ||
| inner_pkt = inner_pkt/("".join([chr(x) for x in xrange(100 - len(inner_pkt))])) | ||
| #The expected packet is also built by scapy, and the TTL is taked from the inner layer and redused by one | ||
| exp_pkt = scapy.Ether(dst=dst_mac, src=router_mac)/inner_pkt | ||
| exp_pkt['IP'].tos = exp_tos #this parameter is taken by the decap rule configuration | ||
| exp_pkt['IP'].ttl = 63 | ||
|
|
||
| pkt = simple_ipv4ip_packet( | ||
| eth_dst=router_mac, | ||
| eth_src=src_mac, | ||
| ip_src=outer_src_ip, | ||
| ip_dst=self.test_params['lo_ip'], | ||
| ip_tos=tos_out, | ||
| ip_ttl=outer_ttl, | ||
| inner_frame=inner_pkt) | ||
|
|
||
| #send and verify the return packets | ||
| masked_exp_pkt = Mask(exp_pkt) | ||
| masked_exp_pkt.set_do_not_care_scapy(scapy.Ether, "dst") | ||
| masked_exp_pkt.set_do_not_care_scapy(scapy.Ether, "src") | ||
| send_packet(self, src_port, pkt) | ||
| (match_index, rcv_pkt) = verify_packet_any_port(self, masked_exp_pkt, expected_ports) | ||
| #----------------------------------------------------------------- | ||
|
|
||
| def runTest(self): | ||
| test_result = True | ||
| random.seed(1) | ||
| self.load_route_info(self.test_params["route_info"]) | ||
| default_route_ports =[] | ||
| unicast_ip = 'none' | ||
| unicast_dst_port = [] | ||
| print self.route_info.iteritems() | ||
| #running on the routes_info file and extractin ECMP route and unicast route | ||
| for prefix, port_index_list in self.route_info.iteritems(): | ||
| dest_ip_addr = prefix.split("/")[0] | ||
|
|
||
| if (self.is_ipv6_address(dest_ip_addr)): | ||
| continue | ||
|
|
||
| if (len(port_index_list) > 1): | ||
| for port_index in port_index_list: | ||
| if (len(port_index)> 0): | ||
| default_route_ports.append(int(port_index)) | ||
| default_route_dst_ip = dest_ip_addr | ||
| elif (len(port_index_list) == 1): | ||
| unicast_ip = dest_ip_addr | ||
| unicast_dst_port = [int(port_index_list[0])] | ||
| #when found unicast and ECMP routes stop | ||
| if ((unicast_ip != 'none') and (len(default_route_ports) != 0)): | ||
| break | ||
|
|
||
| #Sending double and triple encapsulated packets from all ports with unicast and ECMP IP routes | ||
| for src_port in range(PORT_COUNT): | ||
|
|
||
| try: | ||
| self.send_and_verify(default_route_dst_ip, default_route_ports, src_port) | ||
| except: | ||
| print("ERROR: failed to send encap packet with default route from port: " + str(src_port)) | ||
| test_result = False | ||
|
|
||
| try: | ||
| self.send_and_verify(default_route_dst_ip, default_route_ports, src_port, True) | ||
| except: | ||
| print("ERROR: failed to send triple encap packet with default route from port: " + str(src_port)) | ||
| test_result = False | ||
|
|
||
| try: | ||
| self.send_and_verify(unicast_ip, unicast_dst_port, src_port) | ||
| except: | ||
| print("ERROR: failed to send encap packet with unicast route from port: " + str(src_port)) | ||
| test_result = False | ||
|
|
||
| try: | ||
| self.send_and_verify(unicast_ip, unicast_dst_port, src_port, True) | ||
| except: | ||
| print("ERROR: faield to send triple encap packet with unicast route from port: " + str(src_port)) | ||
| test_result = False | ||
|
|
||
| assert(test_result) | ||
| #--------------------------------------------------------------------- | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| #----------------------------------------- | ||
| # Run Decap test and Perform log analysis. | ||
| #----------------------------------------- | ||
|
|
||
| - fail: msg="information about testbed missing." | ||
| when: (testbed_type is not defined) or | ||
| (lo_ip is not defined) or | ||
| (dscp_mode is not defined) | ||
|
|
||
| - fail: msg="Invalid testbed_type value '{{testbed_type}}'" | ||
| when: testbed_type not in [ 'fib'] | ||
|
|
||
| - fail: msg="Invalid testbed_type value '{{dscp_mode}}'" | ||
| when: dscp_mode not in [ 'pipe','uniform'] | ||
|
|
||
| # Gather minigraph facts | ||
| - name: Gathering minigraph facts about the device | ||
| minigraph_facts: host={{ inventory_hostname }} | ||
| become: no | ||
| connection: local | ||
|
|
||
| - name: Print neighbors in minigraph | ||
| debug: msg="{{ minigraph_neighbors }}" | ||
|
|
||
| - name: Read port reverse alias mapping | ||
| set_fact: | ||
| alias_reverse_map: "{{ lookup('file', 'roles/sonicv2/files/ssw/{{ sonic_hwsku }}/alias_reverse_map.json') | from_json }}" | ||
|
|
||
| - name: Print alias reverse mapping | ||
| debug: msg="{{ alias_reverse_map }}" | ||
|
|
||
| # Generate file with BGP routes information | ||
| - template: src=fib.j2 dest=/tmp/fib.txt | ||
| connection: local | ||
|
|
||
| - template: src=decap_conf.j2 dest=/tmp/decap_conf.json | ||
| connection: local | ||
|
|
||
| - name: Copy conf file to the switch | ||
| copy: src="/tmp/decap_conf.json" dest="/tmp/decap_conf.json" | ||
|
|
||
| - name: Copy route into file to the PTF host | ||
| copy: src="/tmp/fib.txt" dest="/tmp/route_info.txt" | ||
| delegate_to: "{{ ptf_host }}" | ||
|
|
||
| - set_fact: | ||
| testname: decap | ||
| run_dir: /tmp | ||
| out_dir: /tmp/ansible-loganalyzer-results | ||
| test_match_file: decap_match_messages.txt | ||
| test_ignore_file: decap_ignore_messages.txt | ||
| test_expect_file: decap_expect_messages.txt | ||
| match_file: loganalyzer_common_match.txt | ||
| ignore_file: loganalyzer_common_ignore.txt | ||
| tests_location: "{{ 'roles/test/tasks' }}" | ||
|
|
||
| # Separate set_fact is required to be able to use 'testname' fact. | ||
| - set_fact: | ||
| testname_unique: "{{ testname }}.{{ ansible_date_time.date}}.{{ ansible_date_time.hour}}-{{ ansible_date_time.minute}}-{{ ansible_date_time.second}}" | ||
|
|
||
| # Separate set_fact is required to be able to use 'testname_unique' fact. | ||
| - set_fact: | ||
| test_out_dir: "{{ out_dir }}/{{testname_unique}}" | ||
| match_file_list: "{{ run_dir }}/{{test_match_file}},{{ run_dir }}/{{match_file}}" | ||
| ignore_file_list: "{{ run_dir }}/{{test_ignore_file}},{{ run_dir }}/{{ignore_file}}" | ||
|
|
||
| - debug: msg="output directory for current test run {{ test_out_dir }}" | ||
| - debug: msg="generated run id:{{testname_unique}}" | ||
|
|
||
| - include: roles/test/files/tools/loganalyzer/loganalyzer_init.yml | ||
|
|
||
| - debug : msg="INVOKE FIB TEST" | ||
|
||
|
|
||
| - block: | ||
|
|
||
| - name: Copy JSON configs from switch into docker filesystem | ||
| command: docker cp /tmp/decap_conf.json swss:/tmp/decap_conf.json | ||
|
|
||
| - name: Load decap JSON config. | ||
| command: docker exec -i swss swssconfig /tmp/decap_conf.json | ||
| register: valid_config_upload | ||
| failed_when: valid_config_upload.rc != 0 | ||
|
|
||
| - name: copy the test to ptf container | ||
| copy: src=roles/test/files/acstests dest=/root | ||
| delegate_to: "{{ ptf_host }}" | ||
|
|
||
| - name: "Running test {{ testname }}" | ||
| #shell: ptf --test-dir acstests IP_decap_test --platform remote -t "verbose=True; router_mac='{{ ansible_Ethernet0['macaddress'] }}';lo_ip='{{ ansible_lo['ipv4']['address'] }}';dscp_mode='uniform'; route_info='/tmp/route_info.txt'" --relax | ||
| shell: ptf --test-dir acstests IP_decap_test --platform remote -t "verbose=True; router_mac='{{ ansible_Ethernet0['macaddress'] }}';lo_ip='{{lo_ip}}';dscp_mode='{{dscp_mode}}'; route_info='/tmp/route_info.txt'" --relax | ||
| args: | ||
| chdir: /root | ||
| delegate_to: "{{ ptf_host }}" | ||
| register: out | ||
|
|
||
| - debug: var=out.stdout_lines | ||
| when: out.rc != 0 | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. configuration is not removed from the switch after test finishes/fails
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
| always: | ||
| - include: roles/test/files/tools/loganalyzer/loganalyzer_analyze.yml | ||
|
|
||
| # Output content of result files to ansible console | ||
| - shell: cat {{ test_out_dir }}/* | ||
| register: out | ||
| - debug: var=out.stdout_lines | ||
|
|
||
| - include: roles/test/files/tools/loganalyzer/loganalyzer_end.yml | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| [ | ||
| { | ||
| "TUNNEL_DECAP_TABLE:NETBOUNCER" : { | ||
| "tunnel_type":"IPINIP", | ||
| "dst_ip":"{{lo_ip}}", | ||
| "src_ip":"1.1.1.1", | ||
| "dscp_mode":"{{dscp_mode}}", | ||
| "ecn_mode":"standard", | ||
| "ttl_mode":"pipe" | ||
| }, | ||
| "OP": "SET" | ||
| } | ||
| ] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please put values from lines 77 to 87 into simple_tcp_packet() and simple_ipv4ip_packet()?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some fields only filled once as some parameters only for the inner (end with *_in) and some for only the outer
in addition, simple_ipv4ip_packet there isn't all of the fields like simple_tcp_packet
its only another layer of L2 and L3
But in simple_tcp_packet I added tcp ports for more detailed code
Because it's the default values, so it will not change anything