diff options
-rw-r--r-- | test/test_flowperpkt.py | 195 | ||||
-rw-r--r-- | test/vpp_papi_provider.py | 68 |
2 files changed, 117 insertions, 146 deletions
diff --git a/test/test_flowperpkt.py b/test/test_flowperpkt.py index af68a69e..16fe2a3e 100644 --- a/test/test_flowperpkt.py +++ b/test/test_flowperpkt.py @@ -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 - - 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 + payload = str(udp) + + 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) - - - - diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 3279a274..bdbcc3d2 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -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, + }) |