From 932f74196d9571fd007cef32c234bd00ab75975e Mon Sep 17 00:00:00 2001 From: Pavel Kotucek Date: Thu, 7 Sep 2017 14:44:52 +0200 Subject: ACL-plugin MACIP ACLs tests Change-Id: Ie40c837358454cfe9475cb2c14fdf20b24fa6602 Signed-off-by: Pavel Kotucek --- src/plugins/acl/acl.c | 18 +- test/test_acl_plugin_macip.py | 760 ++++++++++++++++++++++++++++++++++++++++++ test/vpp_lo_interface.py | 2 +- test/vpp_papi_provider.py | 55 +++ 4 files changed, 832 insertions(+), 3 deletions(-) create mode 100644 test/test_acl_plugin_macip.py diff --git a/src/plugins/acl/acl.c b/src/plugins/acl/acl.c index 1ded1afa..bf484f6c 100644 --- a/src/plugins/acl/acl.c +++ b/src/plugins/acl/acl.c @@ -949,8 +949,22 @@ macip_find_match_type (macip_match_type_t * mv, u8 * mac_mask, u8 prefix_len, static int match_type_metric (macip_match_type_t * m) { - /* FIXME: count the ones in the MAC mask as well, check how well this heuristic works in real life */ - return m->prefix_len + m->is_ipv6 + 10 * m->count; + unsigned int mac_bits_set = 0; + unsigned int mac_byte; + int i; + for (i=0; i<6; i++) + { + mac_byte = m->mac_mask[i]; + for (; mac_byte; mac_byte >>= 1) + mac_bits_set += mac_byte & 1; + } + /* + * Attempt to place the more specific and the more used rules on top. + * There are obvious caveat corner cases to this, but they do not + * seem to be sensible in real world (e.g. specific IPv4 with wildcard MAC + * going with a wildcard IPv4 with a specific MAC). + */ + return m->prefix_len + mac_bits_set + m->is_ipv6 + 10 * m->count; } static int diff --git a/test/test_acl_plugin_macip.py b/test/test_acl_plugin_macip.py new file mode 100644 index 00000000..de57f6bb --- /dev/null +++ b/test/test_acl_plugin_macip.py @@ -0,0 +1,760 @@ +#!/usr/bin/env python +"""ACL plugin - MACIP tests +""" +import random +import re +import unittest + +from socket import inet_ntop, inet_pton, AF_INET, AF_INET6 +from struct import * +from scapy.packet import Raw +from scapy.layers.l2 import Ether +from scapy.layers.inet import IP, UDP, TCP +from scapy.layers.inet6 import IPv6 + +from framework import VppTestCase, VppTestRunner, running_extended_tests +from vpp_lo_interface import VppLoInterface + + +class TestMACIP(VppTestCase): + """MACIP Test Case""" + + DEBUG = False + + BRIDGED = True + ROUTED = False + + IS_IP4 = False + IS_IP6 = True + + # rule types + DENY = 0 + PERMIT = 1 + + # ACL types + EXACT_IP = 1 + SUBNET_IP = 2 + WILD_IP = 3 + + EXACT_MAC = 1 + WILD_MAC = 2 + OUI_MAC = 3 + + ACLS = [] + + @classmethod + def setUpClass(cls): + """ + Perform standard class setup (defined by class method setUpClass in + class VppTestCase) before running the test case, set test case related + variables and configure VPP. + """ + super(TestMACIP, cls).setUpClass() + + cls.pg_if_packet_sizes = [64, 512, 1518, 9018] # packet sizes + cls.bd_id = 10 + cls.remote_hosts_count = 250 + + try: + # create 3 pg interfaces, 1 loopback interface + cls.create_pg_interfaces(range(3)) + cls.create_loopback_interfaces(range(1)) + + cls.interfaces = list(cls.pg_interfaces) + cls.interfaces.extend(cls.lo_interfaces) + + for i in cls.interfaces: + i.admin_up() + + # Create BD with MAC learning enabled and put interfaces to this BD + cls.vapi.sw_interface_set_l2_bridge( + cls.loop0.sw_if_index, bd_id=cls.bd_id, bvi=1) + cls.vapi.sw_interface_set_l2_bridge( + cls.pg0.sw_if_index, bd_id=cls.bd_id) + cls.vapi.sw_interface_set_l2_bridge( + cls.pg1.sw_if_index, bd_id=cls.bd_id) + + # Configure IPv4 addresses on loop interface and routed interface + cls.loop0.config_ip4() + cls.loop0.config_ip6() + cls.pg2.config_ip4() + cls.pg2.config_ip6() + + # Configure MAC address binding to IPv4 neighbors on loop0 + cls.loop0.generate_remote_hosts(cls.remote_hosts_count) + # Modify host mac addresses to have different OUI parts + for i in range(2, cls.remote_hosts_count + 2): + mac = cls.loop0.remote_hosts[i-2]._mac.split(':') + mac[2] = format(int(mac[2], 16) + i, "02x") + cls.loop0.remote_hosts[i - 2]._mac = ":".join(mac) + + cls.loop0.configure_ipv4_neighbors() + cls.loop0.configure_ipv6_neighbors() + # configure MAC address on pg2 + cls.pg2.resolve_arp() + cls.pg2.resolve_ndp() + + # Loopback BVI interface has remote hosts + # one half of hosts are behind pg0 second behind pg1 + half = cls.remote_hosts_count // 2 + cls.pg0.remote_hosts = cls.loop0.remote_hosts[:half] + cls.pg1.remote_hosts = cls.loop0.remote_hosts[half:] + + except Exception: + super(TestMACIP, cls).tearDownClass() + raise + + def setUp(self): + super(TestMACIP, self).setUp() + self.reset_packet_infos() + del self.ACLS[:] + + def tearDown(self): + """ + Show various debug prints after each test. + """ + super(TestMACIP, self).tearDown() + if not self.vpp_dead: + self.logger.info(self.vapi.ppcli("show interface address")) + self.logger.info(self.vapi.ppcli("show hardware")) + self.logger.info(self.vapi.ppcli("sh acl-plugin macip acl")) + self.logger.info(self.vapi.ppcli("sh acl-plugin macip interface")) + self.logger.info(self.vapi.ppcli("sh classify tables verbose")) + # print self.vapi.ppcli("show interface address") + # print self.vapi.ppcli("show hardware") + # print self.vapi.ppcli("sh acl-plugin macip interface") + # print self.vapi.ppcli("sh acl-plugin macip acl") + self.delete_acls() + + def macip_acl_dump_debug(self): + acls = self.vapi.macip_acl_dump() + if self.DEBUG: + for acl in acls: + for r in acl.r: + rule = "ACTION" + if r.is_permit == 1: + rule = "PERMIT" + elif r.is_permit == 0: + rule = "DENY " + print "IP6" if r.is_ipv6 else "IP4", \ + rule, \ + r.src_mac.encode('hex'), \ + r.src_mac_mask.encode('hex'),\ + unpack('<16B', r.src_ip_addr), \ + r.src_ip_prefix_len + return acls + + def create_acls(self, mac_type, ip_type, acl_count, rules_count): + rules = [] + src_mac = int("220000dead00", 16) + for acl in range(2, (acl_count+1) * 2): + host = random.choice(self.loop0.remote_hosts) + is_ip6 = acl % 2 + ip4 = host.ip4.split('.') + ip6 = list(unpack('<16B', inet_pton(AF_INET6, host.ip6))) + + if ip_type == self.EXACT_IP: + prefix_len4 = 32 + prefix_len6 = 128 + elif ip_type == self.WILD_IP: + ip4 = [0, 0, 0, 0] + ip6 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + prefix_len4 = 0 + prefix_len6 = 0 + rules_count[(acl / 2) - 1] = 1 + else: + prefix_len4 = 24 + prefix_len6 = 64 + + if mac_type == self.EXACT_MAC: + mask = "ff:ff:ff:ff:ff:ff" + elif mac_type == self.WILD_MAC: + mask = "00:00:00:00:00:00" + elif mac_type == self.OUI_MAC: + mask = "ff:ff:ff:00:00:00" + else: + mask = "ff:ff:ff:ff:ff:00" + + ip = ip6 if is_ip6 else ip4 + ip_len = prefix_len6 if is_ip6 else prefix_len4 + + for i in range(0, rules_count[(acl / 2) - 1]): + src_mac += 16777217 + if mac_type == self.WILD_MAC: + mac = "00:00:00:00:00:00" + elif mac_type == self.OUI_MAC: + mac = ':'.join(re.findall('..', '{:02x}'.format( + src_mac))[:3])+":00:00:00" + else: + mac = ':'.join(re.findall('..', '{:02x}'.format(src_mac))) + + if ip_type == self.EXACT_IP: + ip4[3] = random.randint(100, 200) + ip6[15] = random.randint(100, 200) + elif ip_type == self.SUBNET_IP: + ip4[2] = random.randint(100, 200) + ip4[3] = 0 + ip6[8] = random.randint(100, 200) + ip6[15] = 0 + ip_pack = '' + for j in range(0, len(ip)): + ip_pack += pack(' 0: + continue + + if is_permit: + rule = ({'is_permit': is_permit, + 'is_ipv6': is_ip6, + 'src_ip_addr': ip_rule, + 'src_ip_prefix_len': prefix_len, + 'src_mac': mac_rule.replace(':', '').decode('hex'), + 'src_mac_mask': mac_mask.replace(':', '').decode( + 'hex')}) + rules.append(rule) + + # deny all other packets + if not (mac_type == self.WILD_MAC and ip_type == self.WILD_IP): + rule = ({'is_permit': 0, + 'is_ipv6': is_ip6, + 'src_ip_addr': "", + 'src_ip_prefix_len': 0, + 'src_mac': "", + 'src_mac_mask': ""}) + rules.append(rule) + + return {'stream': packets, 'rules': rules} + + def verify_capture(self, stream, capture, is_ip6): + p_l3 = IPv6 if is_ip6 else IP + if self.DEBUG: + for p in stream: + print p[Ether].src, p[Ether].dst, p[p_l3].src, p[p_l3].dst + + acls = self.macip_acl_dump_debug() + + # TODO : verify + # for acl in acls: + # for r in acl.r: + # print r.src_mac.encode('hex'), \ + # r.src_mac_mask.encode('hex'),\ + # unpack('<16B', r.src_ip_addr), \ + # r.src_ip_prefix_len + # + # for p in capture: + # print p[Ether].src, p[Ether].dst, p[p_l3].src, p[p_l3].dst + # data = p[Raw].load.split(':',1)[1] + # print p[p_l3].src, data + + def run_traffic(self, mac_type, ip_type, bridged_routed, is_ip6, packets): + self.reset_packet_infos() + + tx_if = self.pg0 if bridged_routed else self.pg2 + rx_if = self.pg2 if bridged_routed else self.pg0 + + test_dict = self.create_stream(mac_type, ip_type, packets, + self.pg2, self.loop0, + bridged_routed, is_ip6) + + reply = self.vapi.macip_acl_add_replace(test_dict['rules']) + self.assertEqual(reply.retval, 0) + acl_index = reply.acl_index + + self.vapi.macip_acl_interface_add_del(sw_if_index=tx_if.sw_if_index, + acl_index=acl_index) + reply = self.vapi.macip_acl_interface_get() + self.assertEqual(reply.acls[tx_if.sw_if_index], acl_index) + self.ACLS.append(reply.acls[tx_if.sw_if_index]) + + tx_if.add_stream(test_dict['stream']) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + packet_count = self.get_packet_count_for_if_idx(self.loop0.sw_if_index) + if mac_type == self.WILD_MAC and ip_type == self.WILD_IP: + packet_count = packets + capture = rx_if.get_capture(packet_count) + self.verify_capture(test_dict['stream'], capture, is_ip6) + + def run_test_acls(self, mac_type, ip_type, acl_count, + rules_count, traffic=None, ip=None): + self.create_acls(mac_type, ip_type, acl_count, rules_count) + self.verify_acls(acl_count, rules_count) + + if traffic is not None: + self.run_traffic(self.EXACT_MAC, self.EXACT_IP, traffic, ip, 9) + + def test_acl_ip4_exactMAC_exactIP(self): + """ IP4 MACIP exactMAC|exactIP ACL + """ + self.run_traffic(self.EXACT_MAC, self.EXACT_IP, + self.BRIDGED, self.IS_IP4, 9) + + def test_acl_ip6_exactMAC_exactIP(self): + """ IP6 MACIP exactMAC|exactIP ACL + """ + + self.run_traffic(self.EXACT_MAC, self.EXACT_IP, + self.BRIDGED, self.IS_IP6, 9) + + def test_acl_ip4_exactMAC_subnetIP(self): + """ IP4 MACIP exactMAC|subnetIP ACL + """ + + self.run_traffic(self.EXACT_MAC, self.SUBNET_IP, + self.BRIDGED, self.IS_IP4, 9) + + def test_acl_ip6_exactMAC_subnetIP(self): + """ IP6 MACIP exactMAC|subnetIP ACL + """ + + self.run_traffic(self.EXACT_MAC, self.SUBNET_IP, + self.BRIDGED, self.IS_IP6, 9) + + def test_acl_ip4_exactMAC_wildIP(self): + """ IP4 MACIP exactMAC|wildIP ACL + """ + + self.run_traffic(self.EXACT_MAC, self.WILD_IP, + self.BRIDGED, self.IS_IP4, 9) + + def test_acl_ip6_exactMAC_wildIP(self): + """ IP6 MACIP exactMAC|wildIP ACL + """ + + self.run_traffic(self.EXACT_MAC, self.WILD_IP, + self.BRIDGED, self.IS_IP6, 9) + + def test_acl_ip4_ouiMAC_exactIP(self): + """ IP4 MACIP ouiMAC|exactIP ACL + """ + + self.run_traffic(self.OUI_MAC, self.EXACT_IP, + self.BRIDGED, self.IS_IP4, 3) + + def test_acl_ip6_ouiMAC_exactIP(self): + """ IP6 MACIP oui_MAC|exactIP ACL + """ + + self.run_traffic(self.OUI_MAC, self.EXACT_IP, + self.BRIDGED, self.IS_IP6, 9) + + def test_acl_ip4_ouiMAC_subnetIP(self): + """ IP4 MACIP ouiMAC|subnetIP ACL + """ + + self.run_traffic(self.OUI_MAC, self.SUBNET_IP, + self.BRIDGED, self.IS_IP4, 9) + + def test_acl_ip6_ouiMAC_subnetIP(self): + """ IP6 MACIP ouiMAC|subnetIP ACL + """ + + self.run_traffic(self.OUI_MAC, self.SUBNET_IP, + self.BRIDGED, self.IS_IP6, 9) + + def test_acl_ip4_ouiMAC_wildIP(self): + """ IP4 MACIP ouiMAC|wildIP ACL + """ + + self.run_traffic(self.OUI_MAC, self.WILD_IP, + self.BRIDGED, self.IS_IP4, 9) + + def test_acl_ip6_ouiMAC_wildIP(self): + """ IP6 MACIP ouiMAC|wildIP ACL + """ + + self.run_traffic(self.OUI_MAC, self.WILD_IP, + self.BRIDGED, self.IS_IP6, 9) + + def test_acl_ip4_wildMAC_exactIP(self): + """ IP4 MACIP wildcardMAC|exactIP ACL + """ + + self.run_traffic(self.WILD_MAC, self.EXACT_IP, + self.BRIDGED, self.IS_IP4, 9) + + def test_acl_ip6_wildMAC_exactIP(self): + """ IP6 MACIP wildcardMAC|exactIP ACL + """ + + self.run_traffic(self.WILD_MAC, self.EXACT_IP, + self.BRIDGED, self.IS_IP6, 9) + + def test_acl_ip4_wildMAC_subnetIP(self): + """ IP4 MACIP wildcardMAC|subnetIP ACL + """ + + self.run_traffic(self.WILD_MAC, self.SUBNET_IP, + self.BRIDGED, self.IS_IP4, 9) + + def test_acl_ip6_wildMAC_subnetIP(self): + """ IP6 MACIP wildcardMAC|subnetIP ACL + """ + + self.run_traffic(self.WILD_MAC, self.SUBNET_IP, + self.BRIDGED, self.IS_IP6, 9) + + def test_acl_ip4_wildMAC_wildIP(self): + """ IP4 MACIP wildcardMAC|wildIP ACL + """ + + self.run_traffic(self.WILD_MAC, self.WILD_IP, + self.BRIDGED, self.IS_IP4, 9) + + def test_acl_ip6_wildMAC_wildIP(self): + """ IP6 MACIP wildcardMAC|wildIP ACL + """ + + self.run_traffic(self.WILD_MAC, self.WILD_IP, + self.BRIDGED, self.IS_IP6, 9) + + def test_acl_1_2(self): + """ MACIP ACL with 2 entries + """ + + self.run_test_acls(self.EXACT_MAC, self.WILD_IP, 1, [2]) + + def test_acl_1_5(self): + """ MACIP ACL with 5 entries + """ + + self.run_test_acls(self.EXACT_MAC, self.SUBNET_IP, 1, [5]) + + def test_acl_1_10(self): + """ MACIP ACL with 10 entries + """ + + self.run_test_acls(self.EXACT_MAC, self.EXACT_IP, 1, [10]) + + def test_acl_1_20(self): + """ MACIP ACL with 20 entries + """ + + self.run_test_acls(self.OUI_MAC, self.WILD_IP, 1, [20]) + + def test_acl_1_50(self): + """ MACIP ACL with 50 entries + """ + + self.run_test_acls(self.OUI_MAC, self.SUBNET_IP, 1, [50]) + + def test_acl_1_100(self): + """ MACIP ACL with 100 entries + """ + + self.run_test_acls(self.OUI_MAC, self.EXACT_IP, 1, [100]) + + def test_acl_2_X(self): + """ MACIP 2 ACLs each with 100+ entries + """ + + self.run_test_acls(self.OUI_MAC, self.SUBNET_IP, 2, [100, 200]) + + def test_acl_10_X(self): + """ MACIP 10 ACLs each with 100+ entries + """ + + self.run_test_acls(self.EXACT_MAC, self.EXACT_IP, 10, + [100, 120, 140, 160, 180, 200, 210, 220, 230, 240]) + + def test_acl_10_X_traffic_ip4(self): + """ MACIP 10 ACLs each with 100+ entries with IP4 traffic + """ + + self.run_test_acls(self.EXACT_MAC, self.EXACT_IP, 10, + [100, 120, 140, 160, 180, 200, 210, 220, 230, 240], + self.BRIDGED, self.IS_IP4) + + def test_acl_10_X_traffic_ip6(self): + """ MACIP 10 ACLs each with 100+ entries with IP6 traffic + """ + + self.run_test_acls(self.EXACT_MAC, self.EXACT_IP, 10, + [100, 120, 140, 160, 180, 200, 210, 220, 230, 240], + self.BRIDGED, self.IS_IP6) + + def test_delete_intf(self): + """ MACIP ACL delete intf with acl + """ + + intf_count = len(self.interfaces)+1 + rules_count = [3, 5, 4] + intf = [] + self.create_acls(self.EXACT_IP, self.EXACT_MAC, 3, rules_count) + + intf.append(VppLoInterface(self, 0)) + intf.append(VppLoInterface(self, 1)) + + sw_if_index0 = intf[0].sw_if_index + self.vapi.macip_acl_interface_add_del(sw_if_index0, 1) + + reply = self.vapi.macip_acl_interface_get() + self.assertEqual(reply.count, intf_count+1) + self.assertEqual(reply.acls[sw_if_index0], 1) + + sw_if_index1 = intf[1].sw_if_index + self.vapi.macip_acl_interface_add_del(sw_if_index1, 0) + + reply = self.vapi.macip_acl_interface_get() + self.assertEqual(reply.count, intf_count+2) + self.assertEqual(reply.acls[sw_if_index1], 0) + + intf[0].remove_vpp_config() + reply = self.vapi.macip_acl_interface_get() + self.assertEqual(reply.count, intf_count+2) + self.assertEqual(reply.acls[sw_if_index0], 4294967295) + self.assertEqual(reply.acls[sw_if_index1], 0) + + intf.append(VppLoInterface(self, 2)) + intf.append(VppLoInterface(self, 3)) + sw_if_index2 = intf[2].sw_if_index + sw_if_index3 = intf[3].sw_if_index + self.vapi.macip_acl_interface_add_del(sw_if_index2, 1) + self.vapi.macip_acl_interface_add_del(sw_if_index3, 1) + + reply = self.vapi.macip_acl_interface_get() + self.assertEqual(reply.count, intf_count+3) + self.assertEqual(reply.acls[sw_if_index1], 0) + self.assertEqual(reply.acls[sw_if_index2], 1) + self.assertEqual(reply.acls[sw_if_index3], 1) + + intf[2].remove_vpp_config() + intf[1].remove_vpp_config() + + reply = self.vapi.macip_acl_interface_get() + self.assertEqual(reply.count, intf_count+3) + self.assertEqual(reply.acls[sw_if_index0], 4294967295) + self.assertEqual(reply.acls[sw_if_index1], 4294967295) + self.assertEqual(reply.acls[sw_if_index2], 4294967295) + self.assertEqual(reply.acls[sw_if_index3], 1) + + intf[3].remove_vpp_config() + reply = self.vapi.macip_acl_interface_get() + + self.assertEqual(len([x for x in reply.acls if x != 4294967295]), 0) + + @unittest.skipUnless(running_extended_tests(), "part of extended tests") + def test_check(self): + """ MACIP with routed traffic + """ + # TODO: routed do not work yet !!! + # self.run_traffic(self.EXACT_IP, self.EXACT_MAC, False, False, 9) + # self.run_traffic(self.EXACT_IP, self.EXACT_MAC, False, True, 9) + + +if __name__ == '__main__': + unittest.main(testRunner=VppTestRunner) diff --git a/test/vpp_lo_interface.py b/test/vpp_lo_interface.py index 9493a700..963123f6 100644 --- a/test/vpp_lo_interface.py +++ b/test/vpp_lo_interface.py @@ -20,7 +20,7 @@ class VppLoInterface(VppInterface, VppObject): self.test.vapi.delete_loopback(self.sw_if_index) def query_vpp_config(self): - dump = self.vapi.sw_interface_dump() + dump = self.test.vapi.sw_interface_dump() return self.is_interface_config_in_dump(dump) def is_interface_config_in_dump(self, dump): diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 03238b9d..b70da026 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -2243,3 +2243,58 @@ class VppPapiProvider(object): 'sw_if_index': sw_if_index, 'traffic_type': traffic_type }) + + def macip_acl_add_replace(self, rules, acl_index=0xFFFFFFFF, tag=""): + """ Add MACIP acl + + :param rules: list of rules for given acl + :param tag: acl tag + """ + + # return self.api(self.papi.macip_acl_add_replace, + # {'acl_index': acl_index, + # 'r': rules, + # 'count': len(rules), + # 'tag': tag}) + return self.api(self.papi.macip_acl_add, + {'r': rules, + 'count': len(rules), + 'tag': tag}) + + def macip_acl_del(self, acl_index): + """ + + :param acl_index: + :return: + """ + return self.api(self.papi.macip_acl_del, + {'acl_index': acl_index}) + + def macip_acl_interface_add_del(self, + sw_if_index, + acl_index, + is_add=1): + """ Add MACIP acl to interface + + :param sw_if_index: + :param acl_index: + :param is_add: (Default value = 1) + """ + + return self.api(self.papi.macip_acl_interface_add_del, + {'is_add': is_add, + 'sw_if_index': sw_if_index, + 'acl_index': acl_index}) + + def macip_acl_interface_get(self): + """ Return interface acls dump + """ + return self.api( + self.papi.macip_acl_interface_get, {}) + + def macip_acl_dump(self, acl_index=4294967295): + """ Return MACIP acl dump + """ + + return self.api( + self.papi.macip_acl_dump, {'acl_index': acl_index}) -- cgit 1.2.3-korg