diff --git a/ansible/roles/test/files/ptftests/IP_decap_test.py b/ansible/roles/test/files/ptftests/IP_decap_test.py index 971529ec273..8b8b67a4173 100644 --- a/ansible/roles/test/files/ptftests/IP_decap_test.py +++ b/ansible/roles/test/files/ptftests/IP_decap_test.py @@ -1,24 +1,31 @@ ''' -Description: This file contains the Decapasulation test for SONIC, to test Decapsulation of IPv4 with double and triple encapsulated packets - +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 fib_info.txt file, in addition to the decap rule that need to be set as the dspc_mode -topology: SUpports t1, t1-lag, t0-116 and t0 topology - -Usage: Examples of how to start the test - ptf --test-dir /root/dor/ ip_decap_test_red --platform remote -t "verbose=True;fib_info='/root/fib_info.txt';lo_ip='10.1.0.32';router_mac='00:02:03:04:05:00';dscp_mode='pipe'; testbed_type='t1'" --log-dir /tmp/logs --verbose -Parameters: fib_info - The fib_info file location + +Precondition: Before the test start, all routes need to be defined as in the fib_info.txt file, in addition to the + decap rule that need to be set as the dspc_mode + +topology: Supports t1, t1-lag, t0-116 and t0 topology + +Usage: Examples of how to start the test + ptf --test-dir /root/dor/ ip_decap_test_red --platform remote -t "verbose=True;fib_info='/root/fib_info.txt';lo_ip='10.1.0.32';router_mac='00:02:03:04:05:00';dscp_mode='pipe';ttl_mode='pipe';testbed_type='t1'" --log-dir /tmp/logs --verbose + +Parameters: fib_info - The fib_info file location lo_ip - The loop_back IP that is configured in the decap rule lo_ipv6 - The loop_back IP v6that is configured in the decap rule router_mac - The mac of the router_mac testbed_type - The type of testbed topology - 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) + 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) + ttl_mode - The rule for the ttl parameter in the decap packet that is configured in the JSON file + ('pipe' for inner and 'uniform' for outer) inner_ipv4 - Test IPv4 encap packets inner_ipv6 - Test IPv6 encap packets outer_ipv4 - Test packets encapsulated in IPv4 outer_ipv6 - Test packets encapsulated in IPv6 - + ''' #--------------------------------------------------------------------- @@ -64,6 +71,10 @@ class DecapPacketTest(BaseTest): DEFAULT_INNER2_V4_PKT_DST_IP = '3.3.3.3' DEFAULT_INNER2_V6_PKT_DST_IP = '3::3' + # Allowed DSCP and TTL values + DSCP_RANGE = list(range(0, 33)) + TTL_RANGE = list(range(2, 65)) + def __init__(self): ''' @summary: constructor @@ -95,6 +106,12 @@ def setUp(self): self.test_inner_ipv4 = self.test_params.get('inner_ipv4', True) self.test_inner_ipv6 = self.test_params.get('inner_ipv6', True) + # Index of current DSCP and TTL value in allowed DSCP_RANGE and TTL_RANGE + self.dscp_in_idx = 0 # DSCP of inner layer. + self.dscp_out_idx = len(self.DSCP_RANGE) / 2 # DSCP of outer layer. Set different initial dscp_in and dscp_out + self.ttl_in_idx = 0 # TTL of inner layer. + self.ttl_out_idx = len(self.TTL_RANGE) / 2 # TTL of outer layer. Set different initial ttl_in and ttl_out + self.summary = {} #----------------------------------------------------------------- @@ -110,7 +127,7 @@ def print_summary(self): sys.stdout.flush() - def create_ipv4_inner_pkt_only(self, src_ip, dst_ip, tos, encap=False): + def create_ipv4_inner_pkt_only(self, src_ip, dst_ip, tos, encap=False, ttl=64): """Creates an IP only packet for the test @param src_ip: source ip @param dst_ip: destination ip @@ -118,9 +135,10 @@ def create_ipv4_inner_pkt_only(self, src_ip, dst_ip, tos, encap=False): @param encap: build encapsulated packet. If @encap is True the return packet would be: IP(@src_ip, @dst_ip, @tos) / IP(dst_ip=4.4.4.4, src_ip=3.3.3.3) / TCP() + @param ttl: ttl field """ - inner_pkt = simple_ip_only_packet(ip_dst=dst_ip, ip_src=src_ip, ip_ttl=64, ip_tos=tos) + inner_pkt = simple_ip_only_packet(ip_dst=dst_ip, ip_src=src_ip, ip_ttl=ttl, ip_tos=tos) if encap: inner_pkt2 = self.create_ipv4_inner_pkt_only(self.DEFAULT_INNER2_V4_PKT_SRC_IP, self.DEFAULT_INNER2_V4_PKT_DST_IP, @@ -128,14 +146,14 @@ def create_ipv4_inner_pkt_only(self, src_ip, dst_ip, tos, encap=False): inner_pkt = simple_ipv4ip_packet(ip_src=src_ip, ip_dst=dst_ip, ip_tos=tos, - ip_ttl=64, + ip_ttl=ttl, inner_frame=inner_pkt2).getlayer(scapy.IP) # get only the IP layer return inner_pkt #----------------------------------------------------------------- - def create_ipv6_inner_pkt_only(self, src_ip, dst_ip, tc, encap=False): + def create_ipv6_inner_pkt_only(self, src_ip, dst_ip, tc, encap=False, hlim=64): """Creates an IPv6 only packet for the test @param src_ip: source ip @param dst_ip: destination ip @@ -148,7 +166,8 @@ def create_ipv6_inner_pkt_only(self, src_ip, dst_ip, tc, encap=False): # no ptf function to build simple ipv6 only packet # so use simple_tcpv6_packet function which builds the same packet # with TCP header as simple_ip_only_packet but extract away Ethernet - inner_pkt = simple_tcpv6_packet(ipv6_dst=dst_ip, ipv6_src=src_ip, ipv6_hlim=64, ipv6_tc=tc).getlayer(scapy.IPv6) + + inner_pkt = simple_tcpv6_packet(ipv6_dst=dst_ip, ipv6_src=src_ip, ipv6_hlim=hlim, ipv6_tc=tc).getlayer(scapy.IPv6) if encap: inner_pkt2 = self.create_ipv6_inner_pkt_only(self.DEFAULT_INNER2_V6_PKT_SRC_IP, self.DEFAULT_INNER2_V6_PKT_DST_IP, @@ -156,40 +175,71 @@ def create_ipv6_inner_pkt_only(self, src_ip, dst_ip, tc, encap=False): inner_pkt = simple_ipv6ip_packet(ipv6_src=src_ip, ipv6_dst=dst_ip, ipv6_tc=tc, - ipv6_hlim=64, + ipv6_hlim=hlim, inner_frame=inner_pkt2).getlayer(scapy.IPv6) # get only the IP layer return inner_pkt #----------------------------------------------------------------- - def create_encap_packet(self, dst_ip, outer_pkt='ipv4', triple_encap=False): + def create_encap_packet(self, dst_ip, outer_pkt='ipv4', triple_encap=False, outer_ttl=None, inner_ttl=None): """Creates an IPv4/IPv6 encapsulated packet in @outer_pkt packet @param dst_ip: Destination IP for inner packet. Depending @dst_ip IPv4 or IPv6 packet will be created @param outer_pkt: Outer packet type to encapsulate inner packet in (ipv4/ipv6) @param triple_encap: Whether to build triple encapsulated packet - @return: built packet and expected packet to match after decapsulation""" + @outer_ttl: TTL for the outer layer + @inner_ttl: TTL for the inner layer + @return: built packet and expected packet to match after decapsulation + """ src_mac = self.dataplane.get_mac(0, 0) dst_mac = '00:11:22:33:44:55' router_mac = self.test_params['router_mac'] - dscp_in = random.randint(0, 32) + + # Set DSCP value for the inner layer + dscp_in = self.DSCP_RANGE[self.dscp_in_idx] + self.dscp_in_idx = (self.dscp_in_idx + 1) % len(self.DSCP_RANGE) # Next packet will use a different DSCP + # TC for IPv6, ToS for IPv4 tc_in = tos_in = dscp_in << 2 - dscp_out = random.randint(0, 32) + + # Set DSCP value for the outer layer + dscp_out = self.DSCP_RANGE[self.dscp_out_idx] + self.dscp_out_idx = (self.dscp_out_idx + 1) % len(self.DSCP_RANGE) # Next packet will use a different DSCP + + # TC for IPv6, ToS for IPv4 tc_out = tos_out = dscp_out << 2 - if ("pipe" == self.test_params['dscp_mode']): + + if "pipe" == self.test_params['dscp_mode']: exp_tc = exp_tos = tc_in - elif("uniform" == self.test_params['dscp_mode']): + elif "uniform" == self.test_params['dscp_mode']: exp_tc = exp_tos = tc_out else: print("ERROR: no dscp is configured") exit() + # Set TTL value for the outer layer + if outer_ttl is None: + outer_ttl = self.TTL_RANGE[self.ttl_out_idx] + self.ttl_out_idx = (self.ttl_out_idx + 1) % len(self.TTL_RANGE) # Next packet will use a different TTL + + # Set TTL value for the inner layer + if inner_ttl is None: + inner_ttl = self.TTL_RANGE[self.ttl_in_idx] + self.ttl_in_idx = (self.ttl_in_idx + 1) % len(self.TTL_RANGE) # Next packet will use a different TTL + + if "pipe" == self.test_params['ttl_mode']: + exp_ttl = inner_ttl - 1 + elif "uniform" == self.test_params["ttl_mode"]: + exp_ttl = outer_ttl - 1 + else: + print("ERROR: unexpected ttl_mode is configured") + exit() + if ipaddress.ip_address(unicode(dst_ip)).version == 6: inner_src_ip = self.DEFAULT_INNER_V6_PKT_SRC_IP # build inner packet, if triple_encap is True inner_pkt would be double encapsulated - inner_pkt = self.create_ipv6_inner_pkt_only(inner_src_ip, dst_ip, tos_in, triple_encap) + inner_pkt = self.create_ipv6_inner_pkt_only(inner_src_ip, dst_ip, tos_in, triple_encap, hlim=inner_ttl) # build expected packet based on inner packet # set the correct L2 fields @@ -198,11 +248,11 @@ def create_encap_packet(self, dst_ip, outer_pkt='ipv4', triple_encap=False): # set expected TC value exp_pkt['IPv6'].tc = exp_tc # decrement TTL - exp_pkt['IPv6'].hlim -= 1 + exp_pkt['IPv6'].hlim = exp_ttl else: inner_src_ip = self.DEFAULT_INNER_V4_PKT_SRC_IP # build inner packet, if triple_encap is True inner_pkt would be double encapsulated - inner_pkt = self.create_ipv4_inner_pkt_only(inner_src_ip, dst_ip, tos_in, triple_encap) + inner_pkt = self.create_ipv4_inner_pkt_only(inner_src_ip, dst_ip, tos_in, triple_encap, ttl=inner_ttl) # build expected packet based on inner packet # set the correct L2 fields @@ -211,8 +261,7 @@ def create_encap_packet(self, dst_ip, outer_pkt='ipv4', triple_encap=False): # set expected ToS value exp_pkt['IP'].tos = exp_tos # decrement TTL - exp_pkt['IP'].ttl -= 1 - + exp_pkt['IP'].ttl = exp_ttl if outer_pkt == 'ipv4': pkt = simple_ipv4ip_packet( @@ -221,7 +270,7 @@ def create_encap_packet(self, dst_ip, outer_pkt='ipv4', triple_encap=False): ip_src='1.1.1.1', ip_dst=self.test_params['lo_ip'], ip_tos=tos_out, - ip_ttl=random.randint(2, 63), + ip_ttl=outer_ttl, inner_frame=inner_pkt) elif outer_pkt == 'ipv6': pkt = simple_ipv6ip_packet( @@ -230,27 +279,28 @@ def create_encap_packet(self, dst_ip, outer_pkt='ipv4', triple_encap=False): ipv6_src='1::1', ipv6_dst=self.test_params['lo_ipv6'], ipv6_tc=tc_out, - ipv6_hlim=random.randint(2, 63), + ipv6_hlim=outer_ttl, inner_frame=inner_pkt) else: raise Exception("ERROR: invalid outer packet type ", outer_pkt) - return pkt, exp_pkt #----------------------------------------------------------------- - def send_and_verify(self, dst_ip, expected_ports, src_port, outer_pkt='ipv4', triple_encap=False): + def send_and_verify(self, dst_ip, expected_ports, src_port, outer_pkt='ipv4', triple_encap=False, + outer_ttl=None, inner_ttl=None): ''' @summary: This function builds encap packet, send and verify their arrival. @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: True to send triple encapsulated packet + @outer_ttl: TTL for the outer layer + @inner_ttl: TTL for the inner layer ''' - pkt, exp_pkt = self.create_encap_packet(dst_ip, outer_pkt, triple_encap) - + pkt, exp_pkt = self.create_encap_packet(dst_ip, outer_pkt, triple_encap, outer_ttl, inner_ttl) 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") @@ -263,7 +313,28 @@ def send_and_verify(self, dst_ip, expected_ports, src_port, outer_pkt='ipv4', tr assert received return matched, received - #----------------------------------------------------------------- + def send_and_verify_all(self, dst_ip, expected_ports, src_port, outer_pkt_type): + """ + @summary: This method builds different encap packets, send and verify their arrival + @dest_ip: The destination ip for the inner IP header + @expected_ports: List of ports that a packet can arrive from + @src_port: The physical port that the packet will be sent from + @outer_pkt_type: Indicates whether the outer packet is ipv4 or ipv6 + """ + + self.send_and_verify(dst_ip, expected_ports, src_port, outer_pkt_type) + self.send_and_verify(dst_ip, expected_ports, src_port, outer_pkt_type, outer_ttl=64, inner_ttl=2) + if self.test_params["ttl_mode"] == "pipe": + self.send_and_verify(dst_ip, expected_ports, src_port, outer_pkt_type, outer_ttl=1, inner_ttl=64) + elif self.test_params["ttl_mode"] == "uniform": + self.send_and_verify(dst_ip, expected_ports, src_port, outer_pkt_type, outer_ttl=2, inner_ttl=64) + + self.send_and_verify(dst_ip, expected_ports, src_port, outer_pkt_type, True) + self.send_and_verify(dst_ip, expected_ports, src_port, outer_pkt_type, True, outer_ttl=64, inner_ttl=2) + if self.test_params["ttl_mode"] == "pipe": + self.send_and_verify(dst_ip, expected_ports, src_port, outer_pkt_type, True, outer_ttl=1, inner_ttl=64) + elif self.test_params["ttl_mode"] == "uniform": + self.send_and_verify(dst_ip, expected_ports, src_port, outer_pkt_type, True, outer_ttl=2, inner_ttl=64) def run_encap_combination_test(self, outer_pkt_type, inner_pkt_type): """ @@ -290,16 +361,15 @@ def run_encap_combination_test(self, outer_pkt_type, inner_pkt_type): logging.info("Check " + outer_pkt_type.replace('ip', 'IP') + " tunneled traffic on IP range:" + str(ip_range) + " on " + str(exp_port_list) + "...") # Send a packet with the first IP in the range - self.send_and_verify(ip_range.get_first_ip(), exp_port_list, src_port, outer_pkt_type) - self.send_and_verify(ip_range.get_first_ip(), exp_port_list, src_port, outer_pkt_type, True) + self.send_and_verify_all(ip_range.get_first_ip(), exp_port_list, src_port, outer_pkt_type) + # Send a packet with the last IP in the range if ip_range.length() > 1: - self.send_and_verify(ip_range.get_last_ip(), exp_port_list, src_port, outer_pkt_type) - self.send_and_verify(ip_range.get_last_ip(), exp_port_list, src_port, outer_pkt_type, True) + self.send_and_verify_all(ip_range.get_last_ip(), exp_port_list, src_port, outer_pkt_type) + # Send a packet with a random IP in the range if ip_range.length() > 2: - self.send_and_verify(ip_range.get_random_ip(), exp_port_list, src_port, outer_pkt_type) - self.send_and_verify(ip_range.get_random_ip(), exp_port_list, src_port, outer_pkt_type, True) + self.send_and_verify_all(ip_range.get_random_ip(), exp_port_list, src_port, outer_pkt_type) def runTest(self): """ @@ -356,4 +426,3 @@ def runTest(self): assert total == passed, "total tests {}, passed: {}".format(total, passed) #--------------------------------------------------------------------- - diff --git a/ansible/roles/test/tasks/decap.yml b/ansible/roles/test/tasks/decap.yml index f8f7a705c18..ac5ee5d715e 100644 --- a/ansible/roles/test/tasks/decap.yml +++ b/ansible/roles/test/tasks/decap.yml @@ -19,13 +19,22 @@ - sonic_hwsku in mellanox_hwskus - dscp_mode is not defined + - name: Set ttl_mode var + set_fact: + ttl_mode: pipe + when: + - ttl_mode is not defined + - fail: msg="information about testbed missing." when: (testbed_type is not defined) or (dscp_mode is not defined) -- fail: msg="Invalid testbed_type value '{{dscp_mode}}'" +- fail: msg="Invalid dscp_mode value '{{dscp_mode}}'" when: dscp_mode not in ['pipe','uniform'] +- fail: msg="Invalid ttl_mode value '{{ttl_mode}}'" + when: ttl_mode not in ['pipe','uniform'] + - include_vars: "vars/topo_{{testbed_type}}.yml" - name: Expand properties into props @@ -115,8 +124,9 @@ ptf_test_params: - testbed_type='{{ testbed_type }}' - fib_info='/root/fib_info.txt' - - router_mac='{{ansible_Ethernet0['macaddress']}}' + - router_mac='{{ ansible_interface_facts['Ethernet0']['macaddress'] }}' - dscp_mode='{{ dscp_mode }}' + - ttl_mode='{{ ttl_mode }}' - lo_ip='{{ lo_ip }}' - lo_ipv6='{{ lo_ipv6 }}' - outer_ipv4={{ outer_ipv4 }} diff --git a/ansible/roles/test/templates/decap_conf.j2 b/ansible/roles/test/templates/decap_conf.j2 index 6783befa9aa..74f1fdee6fd 100644 --- a/ansible/roles/test/templates/decap_conf.j2 +++ b/ansible/roles/test/templates/decap_conf.j2 @@ -6,7 +6,7 @@ "dst_ip":"{{ lo_ip }}", "dscp_mode":"{{ dscp_mode }}", "ecn_mode":"{{ ecn_mode }}", - "ttl_mode":"pipe" + "ttl_mode":"{{ ttl_mode }}" }, "OP": "{{ op }}" } @@ -20,7 +20,7 @@ "dst_ip":"{{ lo_ipv6 }}", "dscp_mode":"{{ dscp_mode }}", "ecn_mode":"{{ ecn_mode }}", - "ttl_mode":"pipe" + "ttl_mode":"{{ ttl_mode }}" }, "OP": "{{ op }}" } diff --git a/ansible/roles/test/templates/fib.j2 b/ansible/roles/test/templates/fib.j2 index ea08dc9801b..38720ce5213 100644 --- a/ansible/roles/test/templates/fib.j2 +++ b/ansible/roles/test/templates/fib.j2 @@ -11,8 +11,9 @@ {% endif %} {#routes to uplink#} -{% for podset in range(0, props.podset_number) %} -{% for tor in range(0, props.tor_number) %} +{#Limit the number of podsets and subnets to be covered to limit script execution time#} +{% for podset in range(0, [props.podset_number, 10]|min) %} +{% for tor in range(0, [props.tor_number, 10]|min) %} {% for subnet in range(0, props.tor_subnet_number) %} {% if testbed_type == 't1' %} 192.168.{{ podset }}.{{ tor * 16 + subnet }}/32 {% for ifname, v in minigraph_neighbors.iteritems() %}{% if "T2" in v.name %}{{ '[%d]' % minigraph_port_indices[ifname]}}{% if not loop.last %} {% endif %}{% endif %}{% endfor %}