make test: improve test_flowperpkt

Change-Id: Id1b2bef2a93ac931a420d7a3c322d65c1fbce055
Signed-off-by: Klement Sekera <ksekera@cisco.com>
This commit is contained in:
Klement Sekera
2017-01-02 07:46:14 +01:00
committed by Dave Barach
parent 5d22448922
commit 77fabdbcee
2 changed files with 116 additions and 145 deletions

View File

@ -1,17 +1,13 @@
#!/usr/bin/env python
import unittest
import socket
import binascii
import time
from framework import VppTestCase, VppTestRunner
from scapy.packet import Raw
from scapy.layers.l2 import Ether
from scapy.layers.inet import IP, UDP
from scapy.utils import hexdump
from util import ppp
class TestFlowperpkt(VppTestCase):
""" Flow-per-packet plugin: test both L2 and IP4 reporting """
@ -22,7 +18,6 @@ class TestFlowperpkt(VppTestCase):
**Config:**
- create three PG interfaces
- create a couple of loopback interfaces
"""
super(TestFlowperpkt, self).setUp()
@ -37,11 +32,6 @@ class TestFlowperpkt(VppTestCase):
intf.config_ip4()
intf.resolve_arp()
def tearDown(self):
"""Run standard test teardown"""
super(TestFlowperpkt, self).tearDown()
def create_stream(self, src_if, dst_if, packet_sizes):
"""Create a packet stream to tickle the plugin
@ -51,8 +41,7 @@ class TestFlowperpkt(VppTestCase):
"""
pkts = []
for size in packet_sizes:
info = self.create_packet_info(src_if.sw_if_index,
dst_if.sw_if_index)
info = self.create_packet_info(src_if, dst_if)
payload = self.info_to_payload(info)
p = (Ether(src=src_if.local_mac, dst=dst_if.remote_mac) /
IP(src=src_if.remote_ip4, dst=dst_if.remote_ip4) /
@ -63,119 +52,84 @@ class TestFlowperpkt(VppTestCase):
pkts.append(p)
return pkts
@staticmethod
def compare_with_mask(payload, masked_expected_data):
if len(payload) * 2 != len(masked_expected_data):
return False
# iterate over pairs: raw byte from payload and ASCII code for that byte
# from masked payload (or XX if masked)
for i in range(len(payload)):
p = payload[i]
m = masked_expected_data[2 * i:2 * i + 2]
if m != "XX":
if "%02x" % ord(p) != m:
return False
return True
def verify_ipfix(self, collector_if):
"""Check the ipfix capture"""
found_data_packet = 0
found_template_packet = 0
found_l2_data_packet = 0
found_l2_template_packet = 0
found_data_packet = False
found_template_packet = False
found_l2_data_packet = False
found_l2_template_packet = False
# Scapy, of course, understands ipfix not at all...
# These data vetted by manual inspection in wireshark
# X'ed out fields are timestamps, which will absolutely
# fail to compare. At L2, kill the pg src MAC address, which
# is random.
data_udp_string = "1283128300370000000a002fXXXXXXXX00000000000000010100001f0000000100000002ac100102ac10020200XXXXXXXXXXXXXXXX0092"
# fail to compare.
template_udp_string = "12831283003c0000000a0034XXXXXXXX00000002000000010002002401000007000a0004000e000400080004000c000400050001009c000801380002"
data_udp_string = "1283128300370000000a002fXXXXXXXX000000000000000101"\
"00001f0000000100000002ac100102ac10020200XXXXXXXXXXXXXXXX0092"
l2_data_udp_string = "12831283003c0000000a0034XXXXXXXX0000000100000001010100240000000100000002XXXXXXXXXXXX02020000ff020008XXXXXXXXXXXXXXXX0092"
template_udp_string = "12831283003c0000000a0034XXXXXXXX00000002000000"\
"010002002401000007000a0004000e000400080004000c000400050001009c00"\
"0801380002"
l2_template_udp_string = "12831283003c0000000a0034XXXXXXXX00000002000000010002002401010007000a0004000e0004003800060050000601000002009c000801380002"
l2_data_udp_string = "12831283003c0000000a0034XXXXXXXX000000010000000"\
"1010100240000000100000002%s02020000ff020008XXXXXXXXXXX"\
"XXXXX0092" % self.pg1.local_mac.translate(None, ":")
cap_x = "X"
data_udp_len = len(data_udp_string)
template_udp_len = len(template_udp_string)
l2_data_udp_len = len(l2_data_udp_string)
l2_template_udp_len = len(l2_template_udp_string)
l2_template_udp_string = "12831283003c0000000a0034XXXXXXXX00000002000"\
"000010002002401010007000a0004000e0004003800060050000601000002009"\
"c000801380002"
self.logger.info("Look for ipfix packets on %s sw_if_index %d "
self.logger.info("Look for ipfix packets on %s sw_if_index %d "
% (collector_if.name, collector_if.sw_if_index))
capture = collector_if.get_capture()
# expecting 4 packets on collector interface based on traffic on other
# interfaces
capture = collector_if.get_capture(4)
for p in capture:
data_result = ""
template_result = ""
l2_data_result = ""
l2_template_result = ""
unmasked_result = ""
ip = p[IP]
udp = p[UDP]
self.logger.info("src %s dst %s" % (ip.src, ip.dst))
self.logger.info(" udp src_port %s dst_port %s"
self.logger.info(" udp src_port %s dst_port %s"
% (udp.sport, udp.dport))
# Hex-dump the UDP datagram 4 ways in parallel
# X'ing out incomparable fields
# Python completely bites at this sort of thing, of course
payload = str(udp)
x = str(udp)
l = len(x)
i = 0
while i < l:
# If current index within range
if i < data_udp_len/2:
# See if we're supposed to don't care the data
if ord(data_udp_string[i*2]) == ord(cap_x[0]):
data_result = data_result + "XX"
else:
data_result = data_result + ("%02x" % ord(x[i]))
else:
# index out of range, emit actual data
# The test will fail, but it may help debug, etc.
data_result = data_result + ("%02x" % ord(x[i]))
if i < template_udp_len/2:
if ord(template_udp_string[i*2]) == ord(cap_x[0]):
template_result = template_result + "XX"
else:
template_result = template_result + ("%02x" % ord(x[i]))
else:
template_result = template_result + ("%02x" % ord(x[i]))
if i < l2_data_udp_len/2:
# See if we're supposed to don't care the data
if ord(l2_data_udp_string[i*2]) == ord(cap_x[0]):
l2_data_result = l2_data_result + "XX"
else:
l2_data_result = l2_data_result + ("%02x" % ord(x[i]))
else:
# index out of range, emit actual data
# The test will fail, but it may help debug, etc.
l2_data_result = l2_data_result + ("%02x" % ord(x[i]))
if i < l2_template_udp_len/2:
if ord(l2_template_udp_string[i*2]) == ord(cap_x[0]):
l2_template_result = l2_template_result + "XX"
else:
l2_template_result = l2_template_result + ("%02x" % ord(x[i]))
else:
l2_template_result = l2_template_result + ("%02x" % ord(x[i]))
# In case we need to
unmasked_result = unmasked_result + ("%02x" % ord(x[i]))
i = i + 1
if data_result == data_udp_string:
self.logger.info ("found ip4 data packet")
found_data_packet = 1
elif template_result == template_udp_string:
self.logger.info ("found ip4 template packet")
found_template_packet = 1
elif l2_data_result == l2_data_udp_string:
self.logger.info ("found l2 data packet")
found_l2_data_packet = 1
elif l2_template_result == l2_template_udp_string:
self.logger.info ("found l2 template packet")
found_l2_template_packet = 1
if self.compare_with_mask(payload, data_udp_string):
self.logger.info("found ip4 data packet")
found_data_packet = True
elif self.compare_with_mask(payload, template_udp_string):
self.logger.info("found ip4 template packet")
found_template_packet = True
elif self.compare_with_mask(payload, l2_data_udp_string):
self.logger.info("found l2 data packet")
found_l2_data_packet = True
elif self.compare_with_mask(payload, l2_template_udp_string):
self.logger.info("found l2 template packet")
found_l2_template_packet = True
else:
self.logger.info ("unknown pkt '%s'" % unmasked_result)
self.assertTrue (found_data_packet == 1)
self.assertTrue (found_template_packet == 1)
self.assertTrue (found_l2_data_packet == 1)
self.assertTrue (found_l2_template_packet == 1)
unmasked_payload = "".join(["%02x" % ord(c) for c in payload])
self.logger.error("unknown pkt '%s'" % unmasked_payload)
self.assertTrue(found_data_packet, "Data packet not found")
self.assertTrue(found_template_packet, "Template packet not found")
self.assertTrue(found_l2_data_packet, "L2 data packet not found")
self.assertTrue(found_l2_template_packet,
"L2 template packet not found")
def test_L3_fpp(self):
""" Flow per packet L3 test """
@ -186,37 +140,34 @@ class TestFlowperpkt(VppTestCase):
# an ARP request
self.pg_enable_capture(self.pg_interfaces)
self.vapi.cli("set ip arp pg2 172.16.3.2 dead.beef.0002")
self.logger.info(self.vapi.cli("set ipfix exporter collector 172.16.3.2 src 172.16.3.1 path-mtu 1450 template-interval 1"))
self.pg2.configure_ipv4_neighbors()
self.vapi.set_ipfix_exporter(collector_address=self.pg2.remote_ip4n,
src_address=self.pg2.local_ip4n,
path_mtu=1450,
template_interval=1)
# Export flow records for all pkts transmitted on pg1
self.logger.info(self.vapi.cli("flowperpkt feature add-del pg1"))
self.logger.info(self.vapi.cli("flowperpkt feature add-del pg1 l2"))
self.vapi.cli("flowperpkt feature add-del pg1")
self.vapi.cli("flowperpkt feature add-del pg1 l2")
# Arrange to minimally trace generated ipfix packets
self.logger.info(self.vapi.cli("trace add flowperpkt-ipv4 10"))
self.logger.info(self.vapi.cli("trace add flowperpkt-l2 10"))
self.vapi.cli("trace add flowperpkt-ipv4 10")
self.vapi.cli("trace add flowperpkt-l2 10")
# Create a stream from pg0 -> pg1, which causes
# an ipfix packet to be transmitted on pg2
pkts = self.create_stream(self.pg0, self.pg1,
pkts = self.create_stream(self.pg0, self.pg1,
self.pg_if_packet_sizes)
self.pg0.add_stream(pkts)
self.pg_start()
# Flush the ipfix collector, so we don't need any
# asinine time.sleep(5) action
self.vapi.cli("ipfix flush") # FIXME this should be an API call
self.logger.info(self.vapi.cli("ipfix flush"))
# Make sure the 4 pkts we expect actually showed up
self.verify_ipfix(self.pg2)
if __name__ == '__main__':
unittest.main(testRunner=VppTestRunner)

View File

@ -901,7 +901,6 @@ class VppPapiProvider(object):
miss_next_index=0xFFFFFFFF,
current_data_flag=0,
current_data_offset=0):
"""
:param is_add:
:param mask:
@ -918,17 +917,17 @@ class VppPapiProvider(object):
return self.api(
self.papi.classify_add_del_table,
{'is_add' : is_add,
'table_index' : table_index,
'nbuckets' : nbuckets,
{'is_add': is_add,
'table_index': table_index,
'nbuckets': nbuckets,
'memory_size': memory_size,
'skip_n_vectors' : skip_n_vectors,
'match_n_vectors' : match_n_vectors,
'next_table_index' : next_table_index,
'miss_next_index' : miss_next_index,
'current_data_flag' : current_data_flag,
'current_data_offset' : current_data_offset,
'mask' : mask})
'skip_n_vectors': skip_n_vectors,
'match_n_vectors': match_n_vectors,
'next_table_index': next_table_index,
'miss_next_index': miss_next_index,
'current_data_flag': current_data_flag,
'current_data_offset': current_data_offset,
'mask': mask})
def classify_add_del_session(
self,
@ -953,14 +952,14 @@ class VppPapiProvider(object):
return self.api(
self.papi.classify_add_del_session,
{'is_add' : is_add,
'table_index' : table_index,
'hit_next_index' : hit_next_index,
'opaque_index' : opaque_index,
'advance' : advance,
'action' : action,
'metadata' : metadata,
'match' : match})
{'is_add': is_add,
'table_index': table_index,
'hit_next_index': hit_next_index,
'opaque_index': opaque_index,
'advance': advance,
'action': action,
'metadata': metadata,
'match': match})
def input_acl_set_interface(
self,
@ -979,8 +978,29 @@ class VppPapiProvider(object):
return self.api(
self.papi.input_acl_set_interface,
{'sw_if_index' : sw_if_index,
'ip4_table_index' : ip4_table_index,
'ip6_table_index' : ip6_table_index,
'l2_table_index' : l2_table_index,
'is_add' : is_add})
{'sw_if_index': sw_if_index,
'ip4_table_index': ip4_table_index,
'ip6_table_index': ip6_table_index,
'l2_table_index': l2_table_index,
'is_add': is_add})
def set_ipfix_exporter(
self,
collector_address,
src_address,
path_mtu,
template_interval,
vrf_id=0,
collector_port=4739,
udp_checksum=0):
return self.api(
self.papi.set_ipfix_exporter,
{
'collector_address': collector_address,
'collector_port': collector_port,
'src_address': src_address,
'vrf_id': vrf_id,
'path_mtu': path_mtu,
'template_interval': template_interval,
'udp_checksum': udp_checksum,
})