Skip to content

Commit 3f20a50

Browse files
engdormlguohan
authored andcommitted
IPv4 decapsulation test (#128)
* decap test v1 * decap test added * checkout on fib_test * restoring ACL test * fixes * fix #2 * removed decap configuration in the test * added tcp port fields to simple_tcp_packet * removed some vars
1 parent d671fd4 commit 3f20a50

File tree

5 files changed

+331
-1
lines changed

5 files changed

+331
-1
lines changed
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
'''
2+
Owner: Dor Marcus <[email protected]>
3+
Created on: 12/09/2017
4+
Description: This file contains the Decapasulation test for SONIC, to test Decapsulation of IPv4 with double and triple encapsulated packets
5+
6+
Design is available in https://github.com/Azure/SONiC/wiki/IPv4-Decapsulation-test
7+
8+
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
9+
topology: The test need to run on non-lag systems with at least 31 active ports
10+
11+
Usage: Examples of how to start the test
12+
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
13+
Parameters: route_info - The route_info file location
14+
lo_ip - The loop_back IP that is configured in the decap rule
15+
router_mac - The mac of the router_mac
16+
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)
17+
18+
'''
19+
20+
#---------------------------------------------------------------------
21+
# Global imports
22+
#---------------------------------------------------------------------
23+
import random
24+
import time
25+
import logging
26+
import ptf.packet as scapy
27+
import socket
28+
import ptf.dataplane as dataplane
29+
30+
from ptf.testutils import *
31+
from ptf.mask import Mask
32+
import ipaddress
33+
34+
import os
35+
import unittest
36+
37+
import ptf
38+
from ptf.base_tests import BaseTest
39+
from ptf import config
40+
import ptf.dataplane as dataplane
41+
import ptf.testutils as testutils
42+
43+
import pprint
44+
from router_utils import *
45+
46+
47+
#---------------------------------------------------------------------
48+
# Global variables
49+
#---------------------------------------------------------------------
50+
PREFIX_AND_PORT_SPLITTER=" "
51+
PORT_LIST_SPLITTER=","
52+
PORT_COUNT = 31
53+
54+
class DecapPacketTest(BaseTest, RouterUtility):
55+
def __init__(self):
56+
BaseTest.__init__(self)
57+
self.test_params = testutils.test_params_get()
58+
#-----------------------------------------------------------------
59+
def setUp(self):
60+
'''
61+
@summary: Setup for the test
62+
'''
63+
self.dataplane = ptf.dataplane_instance
64+
self.router_mac = self.test_params['router_mac']
65+
#-----------------------------------------------------------------
66+
67+
def send_and_verify(self, dst_ip, expected_ports, src_port, triple_encap = False):
68+
'''
69+
@summary: This function builds encap packet, send and verify their arrival, When a packet will not arrived as expected an exeption will be throwen
70+
@dst_ip: the destination ip for the inner IP header
71+
@expected_ports: list of ports that a packet can arrived from
72+
@src_port: the physical port that the packet will be sent from
73+
@triple_encap: Bool if to send triple encapsulated packet
74+
'''
75+
#setting parameters
76+
src_mac = self.dataplane.get_mac(0, 0)
77+
dst_mac = '00:11:22:33:44:55'
78+
inner_src_ip = '2.2.2.2'
79+
router_mac = self.test_params['router_mac']
80+
dscp_in = random.randint(0, 32)
81+
tos_in = dscp_in << 2
82+
dscp_out = random.randint(0, 32)
83+
tos_out = dscp_out << 2
84+
if ("pipe" == self.test_params['dscp_mode']):
85+
exp_tos = tos_in
86+
elif("uniform" == self.test_params['dscp_mode']):
87+
exp_tos = tos_out
88+
else:
89+
print("ERROR: no dscp is configured")
90+
exit()
91+
92+
#building the packets and the expected packets
93+
if (not triple_encap):
94+
#for the double encap packet we will use IP header with TCP header without mac
95+
inner_pkt = simple_ip_only_packet(ip_dst=dst_ip, ip_src=inner_src_ip, ip_ttl=64)
96+
#after the decap process the retuning packet will be normal tcp packet, The TTL is taked from the inner layer and redused by one
97+
exp_pkt = simple_tcp_packet(pktlen=114,
98+
eth_dst=dst_mac,
99+
eth_src=router_mac,
100+
ip_dst=dst_ip,
101+
ip_src=inner_src_ip,
102+
ip_tos=exp_tos,
103+
ip_ttl=63)
104+
else:
105+
#Building triple encap packet with SCAPY, because there is no PTF function for it, I use the defualt values for the TCP header
106+
tcp_hdr = scapy.TCP(sport=1234, dport=80, flags="S", chksum=0)
107+
inner_pkt2 = scapy.IP(src='4.4.4.4', dst='3.3.3.3', tos=0, ttl=64, id=1, ihl=None) / tcp_hdr
108+
inner_pkt = scapy.IP(src=inner_src_ip, dst=dst_ip, tos=tos_in, ttl=64, id=1, ihl=None,proto =4) / inner_pkt2
109+
inner_pkt = inner_pkt/("".join([chr(x) for x in xrange(100 - len(inner_pkt))]))
110+
#The expected packet is also built by scapy, and the TTL is taked from the inner layer and redused by one
111+
exp_pkt = scapy.Ether(dst=dst_mac, src=router_mac)/inner_pkt
112+
exp_pkt['IP'].tos = exp_tos #this parameter is taken by the decap rule configuration
113+
exp_pkt['IP'].ttl = 63
114+
115+
pkt = simple_ipv4ip_packet(
116+
eth_dst=router_mac,
117+
eth_src=src_mac,
118+
ip_src='1.1.1.1',
119+
ip_dst=self.test_params['lo_ip'],
120+
ip_tos=tos_out,
121+
ip_ttl=random.randint(2, 63),
122+
inner_frame=inner_pkt)
123+
124+
#send and verify the return packets
125+
masked_exp_pkt = Mask(exp_pkt)
126+
masked_exp_pkt.set_do_not_care_scapy(scapy.Ether, "dst")
127+
masked_exp_pkt.set_do_not_care_scapy(scapy.Ether, "src")
128+
send_packet(self, src_port, pkt)
129+
(match_index, rcv_pkt) = verify_packet_any_port(self, masked_exp_pkt, expected_ports)
130+
#-----------------------------------------------------------------
131+
132+
def runTest(self):
133+
test_result = True
134+
random.seed(1)
135+
self.load_route_info(self.test_params["route_info"])
136+
default_route_ports =[]
137+
unicast_ip = 'none'
138+
unicast_dst_port = []
139+
print self.route_info.iteritems()
140+
#running on the routes_info file and extractin ECMP route and unicast route
141+
for prefix, port_index_list in self.route_info.iteritems():
142+
dest_ip_addr = prefix.split("/")[0]
143+
144+
if (self.is_ipv6_address(dest_ip_addr)):
145+
continue
146+
147+
if (len(port_index_list) > 1):
148+
for port_index in port_index_list:
149+
if (len(port_index)> 0):
150+
default_route_ports.append(int(port_index))
151+
default_route_dst_ip = dest_ip_addr
152+
elif (len(port_index_list) == 1):
153+
unicast_ip = dest_ip_addr
154+
unicast_dst_port = [int(port_index_list[0])]
155+
#when found unicast and ECMP routes stop
156+
if ((unicast_ip != 'none') and (len(default_route_ports) != 0)):
157+
break
158+
159+
#Sending double and triple encapsulated packets from all ports with unicast and ECMP IP routes
160+
for src_port in range(PORT_COUNT):
161+
162+
try:
163+
self.send_and_verify(default_route_dst_ip, default_route_ports, src_port)
164+
except:
165+
print("ERROR: failed to send encap packet with default route from port: " + str(src_port))
166+
test_result = False
167+
168+
try:
169+
self.send_and_verify(default_route_dst_ip, default_route_ports, src_port, True)
170+
except:
171+
print("ERROR: failed to send triple encap packet with default route from port: " + str(src_port))
172+
test_result = False
173+
174+
try:
175+
self.send_and_verify(unicast_ip, unicast_dst_port, src_port)
176+
except:
177+
print("ERROR: failed to send encap packet with unicast route from port: " + str(src_port))
178+
test_result = False
179+
180+
try:
181+
self.send_and_verify(unicast_ip, unicast_dst_port, src_port, True)
182+
except:
183+
print("ERROR: faield to send triple encap packet with unicast route from port: " + str(src_port))
184+
test_result = False
185+
186+
assert(test_result)
187+
#---------------------------------------------------------------------
188+
189+

ansible/roles/test/files/acstests/router_utils.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,30 @@
1919
PORT_COUNT = 32
2020

2121
class RouterUtility():
22+
route_info = {}
23+
def load_route_info(self, route_info_path):
24+
'''
25+
@summary: Load route_info file into self.route_info. For details see section 'Format of the route_info file' in the summary of the class.
26+
@param route_info_path : Path to the file
27+
'''
28+
with open(route_info_path, 'r') as route_info_file:
29+
for line in route_info_file:
30+
line = line.strip()
31+
if (0==len(line)):
32+
continue
33+
prefix_ports_pair = line.split(PREFIX_AND_PORT_SPLITTER)
34+
port_list = prefix_ports_pair[1].split(PORT_LIST_SPLITTER)
35+
self.route_info[prefix_ports_pair[0]]=port_list
36+
return
37+
#---------------------------------------------------------------------
38+
39+
def print_route_info(self):
40+
'''
41+
For diagnostic purposes only
42+
'''
43+
pprint.pprint(self.route_info)
44+
return
45+
#---------------------------------------------------------------------
2246
def is_ipv4_address(self, ipaddr):
2347
'''
2448
@summary: Check address is valid IPv4 address.
@@ -178,4 +202,4 @@ def check_route(self, pkt, masked_exp_pkt, source_port_index, dest_ip_addr, dest
178202

179203
return received, match_index
180204
#---------------------------------------------------------------------
181-
205+

ansible/roles/test/tasks/decap.yml

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#-----------------------------------------
2+
# Run Decap test and Perform log analysis.
3+
#-----------------------------------------
4+
5+
- fail: msg="information about testbed missing."
6+
when: (testbed_type is not defined) or
7+
(lo_ip is not defined) or
8+
(dscp_mode is not defined)
9+
10+
- fail: msg="Invalid testbed_type value '{{testbed_type}}'"
11+
when: testbed_type not in [ 'fib']
12+
13+
- fail: msg="Invalid testbed_type value '{{dscp_mode}}'"
14+
when: dscp_mode not in [ 'pipe','uniform']
15+
16+
# Gather minigraph facts
17+
- name: Gathering minigraph facts about the device
18+
minigraph_facts: host={{ inventory_hostname }}
19+
become: no
20+
connection: local
21+
22+
- name: Print neighbors in minigraph
23+
debug: msg="{{ minigraph_neighbors }}"
24+
25+
- name: Read port reverse alias mapping
26+
set_fact:
27+
alias_reverse_map: "{{ lookup('file', 'roles/sonicv2/files/ssw/{{ sonic_hwsku }}/alias_reverse_map.json') | from_json }}"
28+
29+
- name: Print alias reverse mapping
30+
debug: msg="{{ alias_reverse_map }}"
31+
32+
# Generate file with BGP routes information
33+
- template: src=fib.j2 dest=/tmp/fib.txt
34+
connection: local
35+
36+
37+
38+
- name: Copy route into file to the PTF host
39+
copy: src="/tmp/fib.txt" dest="/tmp/route_info.txt"
40+
delegate_to: "{{ ptf_host }}"
41+
42+
- set_fact:
43+
testname: decap
44+
run_dir: /tmp
45+
out_dir: /tmp/ansible-loganalyzer-results
46+
test_match_file: decap_match_messages.txt
47+
test_ignore_file: decap_ignore_messages.txt
48+
test_expect_file: decap_expect_messages.txt
49+
match_file: loganalyzer_common_match.txt
50+
ignore_file: loganalyzer_common_ignore.txt
51+
tests_location: "{{ 'roles/test/tasks' }}"
52+
53+
# Separate set_fact is required to be able to use 'testname' fact.
54+
- set_fact:
55+
testname_unique: "{{ testname }}.{{ ansible_date_time.date}}.{{ ansible_date_time.hour}}-{{ ansible_date_time.minute}}-{{ ansible_date_time.second}}"
56+
57+
# Separate set_fact is required to be able to use 'testname_unique' fact.
58+
- set_fact:
59+
test_out_dir: "{{ out_dir }}/{{testname_unique}}"
60+
match_file_list: "{{ run_dir }}/{{test_match_file}},{{ run_dir }}/{{match_file}}"
61+
ignore_file_list: "{{ run_dir }}/{{test_ignore_file}},{{ run_dir }}/{{ignore_file}}"
62+
63+
- debug: msg="output directory for current test run {{ test_out_dir }}"
64+
- debug: msg="generated run id:{{testname_unique}}"
65+
66+
- include: roles/test/files/tools/loganalyzer/loganalyzer_init.yml
67+
68+
- debug : msg="INVOKE DECAP TEST"
69+
70+
- block:
71+
72+
73+
74+
- name: copy the test to ptf container
75+
copy: src=roles/test/files/acstests dest=/root
76+
delegate_to: "{{ ptf_host }}"
77+
78+
- name: "Running test {{ testname }}"
79+
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
80+
args:
81+
chdir: /root
82+
delegate_to: "{{ ptf_host }}"
83+
register: out
84+
85+
- debug: var=out.stdout_lines
86+
when: out.rc != 0
87+
88+
always:
89+
90+
91+
92+
- include: roles/test/files/tools/loganalyzer/loganalyzer_analyze.yml
93+
94+
# Output content of result files to ansible console
95+
- shell: cat {{ test_out_dir }}/*
96+
register: out
97+
- debug: var=out.stdout_lines
98+
99+
- include: roles/test/files/tools/loganalyzer/loganalyzer_end.yml

ansible/roles/test/tasks/sonic.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@
6969
include: arpall.yml
7070
tags: arp
7171

72+
73+
- name: Decap test
74+
include: decap.yml
75+
tags: decap
76+
7277
### When calling this FIB test, please add command line of what testbed_type and which PTF docker to test against
7378
### -e "testbed_type=t1-lag ptf_host=10.0.0.200"
7479
- name: Fib test
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[
2+
{
3+
"TUNNEL_DECAP_TABLE:NETBOUNCER" : {
4+
"tunnel_type":"IPINIP",
5+
"dst_ip":"{{lo_ip}}",
6+
"src_ip":"1.1.1.1",
7+
"dscp_mode":"{{dscp_mode}}",
8+
"ecn_mode":"standard",
9+
"ttl_mode":"pipe"
10+
},
11+
"OP": "SET"
12+
}
13+
]

0 commit comments

Comments
 (0)