Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
194 changes: 194 additions & 0 deletions ansible/roles/test/files/acstests/IP_decap_test.py
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']):
Copy link
Copy Markdown
Contributor

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()?

Copy link
Copy Markdown
Contributor Author

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

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)
#---------------------------------------------------------------------


26 changes: 25 additions & 1 deletion ansible/roles/test/files/acstests/router_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,30 @@
PORT_COUNT = 32

class RouterUtility():
route_info = {}
def load_route_info(self, route_info_path):
'''
@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.
@param route_info_path : Path to the file
'''
with open(route_info_path, 'r') as route_info_file:
for line in route_info_file:
line = line.strip()
if (0==len(line)):
continue
prefix_ports_pair = line.split(PREFIX_AND_PORT_SPLITTER)
port_list = prefix_ports_pair[1].split(PORT_LIST_SPLITTER)
self.route_info[prefix_ports_pair[0]]=port_list
return
#---------------------------------------------------------------------

def print_route_info(self):
'''
For diagnostic purposes only
'''
pprint.pprint(self.route_info)
return
#---------------------------------------------------------------------
def is_ipv4_address(self, ipaddr):
'''
@summary: Check address is valid IPv4 address.
Expand Down Expand Up @@ -178,4 +202,4 @@ def check_route(self, pkt, masked_exp_pkt, source_port_index, dest_ip_addr, dest

return received, match_index
#---------------------------------------------------------------------


107 changes: 107 additions & 0 deletions ansible/roles/test/tasks/decap.yml
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"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FIB -> IPv4 decap

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


- 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

Copy link
Copy Markdown

@andriymoroz andriymoroz Feb 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

configuration is not removed from the switch after test finishes/fails
I'm not sure how TUNNEL_DECAP_TABLE is handled but it is possible in the log there could be some "already exist" error messages when test is rerun and log analyzer will treat them as an error

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The 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
4 changes: 4 additions & 0 deletions ansible/roles/test/tasks/sonic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@
include: arpall.yml
tags: arp

- name: Decap test
include: decap.yml
tags: decap

- name: Fib test
include: fib.yml
tags: fib
Expand Down
13 changes: 13 additions & 0 deletions ansible/roles/test/templates/decap_conf.j2
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"
}
]