MAP: Convert from DPO to input feature.
[vpp.git] / test / test_classify_l2_acl.py
1 #!/usr/bin/env python
2 """ Classifier-based L2 ACL Test Case HLD:
3 """
4
5 import unittest
6 import random
7 import binascii
8 import socket
9
10
11 from scapy.packet import Raw
12 from scapy.data import ETH_P_IP
13 from scapy.layers.l2 import Ether
14 from scapy.layers.inet import IP, TCP, UDP, ICMP
15 from scapy.layers.inet6 import IPv6, ICMPv6EchoRequest
16 from scapy.layers.inet6 import IPv6ExtHdrFragment
17 from framework import VppTestCase, VppTestRunner
18 from util import Host, ppp
19
20
21 class TestClassifyAcl(VppTestCase):
22     """ Classifier-based L2 input and output ACL Test Case """
23
24     # traffic types
25     IP = 0
26     ICMP = 1
27
28     # IP version
29     IPRANDOM = -1
30     IPV4 = 0
31     IPV6 = 1
32
33     # rule types
34     DENY = 0
35     PERMIT = 1
36
37     # supported protocols
38     proto = [[6, 17], [1, 58]]
39     proto_map = {1: 'ICMP', 58: 'ICMPv6EchoRequest', 6: 'TCP', 17: 'UDP'}
40     ICMPv4 = 0
41     ICMPv6 = 1
42     TCP = 0
43     UDP = 1
44     PROTO_ALL = 0
45
46     # port ranges
47     PORTS_ALL = -1
48     PORTS_RANGE = 0
49     PORTS_RANGE_2 = 1
50     udp_sport_from = 10
51     udp_sport_to = udp_sport_from + 5
52     udp_dport_from = 20000
53     udp_dport_to = udp_dport_from + 5000
54     tcp_sport_from = 30
55     tcp_sport_to = tcp_sport_from + 5
56     tcp_dport_from = 40000
57     tcp_dport_to = tcp_dport_from + 5000
58
59     udp_sport_from_2 = 90
60     udp_sport_to_2 = udp_sport_from_2 + 5
61     udp_dport_from_2 = 30000
62     udp_dport_to_2 = udp_dport_from_2 + 5000
63     tcp_sport_from_2 = 130
64     tcp_sport_to_2 = tcp_sport_from_2 + 5
65     tcp_dport_from_2 = 20000
66     tcp_dport_to_2 = tcp_dport_from_2 + 5000
67
68     icmp4_type = 8  # echo request
69     icmp4_code = 3
70     icmp6_type = 128  # echo request
71     icmp6_code = 3
72
73     icmp4_type_2 = 8
74     icmp4_code_from_2 = 5
75     icmp4_code_to_2 = 20
76     icmp6_type_2 = 128
77     icmp6_code_from_2 = 8
78     icmp6_code_to_2 = 42
79
80     # Test variables
81     bd_id = 1
82
83     @classmethod
84     def setUpClass(cls):
85         """
86         Perform standard class setup (defined by class method setUpClass in
87         class VppTestCase) before running the test case, set test case related
88         variables and configure VPP.
89         """
90         super(TestClassifyAcl, cls).setUpClass()
91
92         try:
93             # Create 2 pg interfaces
94             cls.create_pg_interfaces(range(2))
95
96             # Packet flows mapping pg0 -> pg1, pg2 etc.
97             cls.flows = dict()
98             cls.flows[cls.pg0] = [cls.pg1]
99
100             # Packet sizes
101             cls.pg_if_packet_sizes = [64, 512, 1518, 9018]
102
103             # Create BD with MAC learning and unknown unicast flooding disabled
104             # and put interfaces to this BD
105             cls.vapi.bridge_domain_add_del(bd_id=cls.bd_id, uu_flood=1,
106                                            learn=1)
107             for pg_if in cls.pg_interfaces:
108                 cls.vapi.sw_interface_set_l2_bridge(pg_if.sw_if_index,
109                                                     bd_id=cls.bd_id)
110
111             # Set up all interfaces
112             for i in cls.pg_interfaces:
113                 i.admin_up()
114
115             # Mapping between packet-generator index and lists of test hosts
116             cls.hosts_by_pg_idx = dict()
117             for pg_if in cls.pg_interfaces:
118                 cls.hosts_by_pg_idx[pg_if.sw_if_index] = []
119
120             # Create list of deleted hosts
121             cls.deleted_hosts_by_pg_idx = dict()
122             for pg_if in cls.pg_interfaces:
123                 cls.deleted_hosts_by_pg_idx[pg_if.sw_if_index] = []
124
125             # warm-up the mac address tables
126             # self.warmup_test()
127
128             # Holder of the active classify table key
129             cls.acl_active_table = ''
130
131         except Exception:
132             super(TestClassifyAcl, cls).tearDownClass()
133             raise
134
135     def setUp(self):
136         super(TestClassifyAcl, self).setUp()
137
138         self.acl_tbl_idx = {}
139         self.reset_packet_infos()
140
141     def tearDown(self):
142         """
143         Show various debug prints after each test.
144         """
145         if not self.vpp_dead:
146             self.logger.info(self.vapi.ppcli("show inacl type l2"))
147             self.logger.info(self.vapi.ppcli("show outacl type l2"))
148             self.logger.info(self.vapi.ppcli("show classify tables verbose"))
149             self.logger.info(self.vapi.ppcli("show bridge-domain %s detail"
150                                              % self.bd_id))
151             if self.acl_active_table == 'mac_inout':
152                 self.output_acl_set_interface(
153                     self.pg1, self.acl_tbl_idx.get(self.acl_active_table), 0)
154                 self.input_acl_set_interface(
155                     self.pg0, self.acl_tbl_idx.get(self.acl_active_table), 0)
156                 self.acl_active_table = ''
157             elif self.acl_active_table == 'mac_out':
158                 self.output_acl_set_interface(
159                     self.pg1, self.acl_tbl_idx.get(self.acl_active_table), 0)
160                 self.acl_active_table = ''
161             elif self.acl_active_table == 'mac_in':
162                 self.input_acl_set_interface(
163                     self.pg0, self.acl_tbl_idx.get(self.acl_active_table), 0)
164                 self.acl_active_table = ''
165
166         super(TestClassifyAcl, self).tearDown()
167
168     @staticmethod
169     def build_mac_mask(dst_mac='', src_mac='', ether_type=''):
170         """Build MAC ACL mask data with hexstring format
171
172         :param str dst_mac: source MAC address <0-ffffffffffff>
173         :param str src_mac: destination MAC address <0-ffffffffffff>
174         :param str ether_type: ethernet type <0-ffff>
175         """
176
177         return ('{:0>12}{:0>12}{:0>4}'.format(dst_mac, src_mac,
178                                               ether_type)).rstrip('0')
179
180     @staticmethod
181     def build_mac_match(dst_mac='', src_mac='', ether_type=''):
182         """Build MAC ACL match data with hexstring format
183
184         :param str dst_mac: source MAC address <x:x:x:x:x:x>
185         :param str src_mac: destination MAC address <x:x:x:x:x:x>
186         :param str ether_type: ethernet type <0-ffff>
187         """
188         if dst_mac:
189             dst_mac = dst_mac.replace(':', '')
190         if src_mac:
191             src_mac = src_mac.replace(':', '')
192
193         return ('{:0>12}{:0>12}{:0>4}'.format(dst_mac, src_mac,
194                                               ether_type)).rstrip('0')
195
196     def create_classify_table(self, key, mask, data_offset=0, is_add=1):
197         """Create Classify Table
198
199         :param str key: key for classify table (ex, ACL name).
200         :param str mask: mask value for interested traffic.
201         :param int match_n_vectors:
202         :param int is_add: option to configure classify table.
203             - create(1) or delete(0)
204         """
205         r = self.vapi.classify_add_del_table(
206             is_add,
207             binascii.unhexlify(mask),
208             match_n_vectors=(len(mask) - 1) // 32 + 1,
209             miss_next_index=0,
210             current_data_flag=1,
211             current_data_offset=data_offset)
212         self.assertIsNotNone(r, msg='No response msg for add_del_table')
213         self.acl_tbl_idx[key] = r.new_table_index
214
215     def create_classify_session(self, intf, table_index, match,
216                                 hit_next_index=0xffffffff, is_add=1):
217         """Create Classify Session
218
219         :param VppInterface intf: Interface to apply classify session.
220         :param int table_index: table index to identify classify table.
221         :param str match: matched value for interested traffic.
222         :param int pbr_action: enable/disable PBR feature.
223         :param int vrfid: VRF id.
224         :param int is_add: option to configure classify session.
225             - create(1) or delete(0)
226         """
227         r = self.vapi.classify_add_del_session(
228             is_add,
229             table_index,
230             binascii.unhexlify(match),
231             hit_next_index=hit_next_index)
232         self.assertIsNotNone(r, msg='No response msg for add_del_session')
233
234     def input_acl_set_interface(self, intf, table_index, is_add=1):
235         """Configure Input ACL interface
236
237         :param VppInterface intf: Interface to apply Input ACL feature.
238         :param int table_index: table index to identify classify table.
239         :param int is_add: option to configure classify session.
240             - enable(1) or disable(0)
241         """
242         r = self.vapi.input_acl_set_interface(
243             is_add,
244             intf.sw_if_index,
245             l2_table_index=table_index)
246         self.assertIsNotNone(r, msg='No response msg for acl_set_interface')
247
248     def output_acl_set_interface(self, intf, table_index, is_add=1):
249         """Configure Output ACL interface
250
251         :param VppInterface intf: Interface to apply Output ACL feature.
252         :param int table_index: table index to identify classify table.
253         :param int is_add: option to configure classify session.
254             - enable(1) or disable(0)
255         """
256         r = self.vapi.output_acl_set_interface(
257             is_add,
258             intf.sw_if_index,
259             l2_table_index=table_index)
260         self.assertIsNotNone(r, msg='No response msg for acl_set_interface')
261
262     def create_hosts(self, count, start=0):
263         """
264         Create required number of host MAC addresses and distribute them among
265         interfaces. Create host IPv4 address for every host MAC address.
266
267         :param int count: Number of hosts to create MAC/IPv4 addresses for.
268         :param int start: Number to start numbering from.
269         """
270         n_int = len(self.pg_interfaces)
271         macs_per_if = count / n_int
272         i = -1
273         for pg_if in self.pg_interfaces:
274             i += 1
275             start_nr = macs_per_if * i + start
276             end_nr = count + start if i == (n_int - 1) \
277                 else macs_per_if * (i + 1) + start
278             hosts = self.hosts_by_pg_idx[pg_if.sw_if_index]
279             for j in range(start_nr, end_nr):
280                 host = Host(
281                     "00:00:00:ff:%02x:%02x" % (pg_if.sw_if_index, j),
282                     "172.17.1%02x.%u" % (pg_if.sw_if_index, j),
283                     "2017:dead:%02x::%u" % (pg_if.sw_if_index, j))
284                 hosts.append(host)
285
286     def create_upper_layer(self, packet_index, proto, ports=0):
287         p = self.proto_map[proto]
288         if p == 'UDP':
289             if ports == 0:
290                 return UDP(sport=random.randint(self.udp_sport_from,
291                                                 self.udp_sport_to),
292                            dport=random.randint(self.udp_dport_from,
293                                                 self.udp_dport_to))
294             else:
295                 return UDP(sport=ports, dport=ports)
296         elif p == 'TCP':
297             if ports == 0:
298                 return TCP(sport=random.randint(self.tcp_sport_from,
299                                                 self.tcp_sport_to),
300                            dport=random.randint(self.tcp_dport_from,
301                                                 self.tcp_dport_to))
302             else:
303                 return TCP(sport=ports, dport=ports)
304         return ''
305
306     def create_stream(self, src_if, packet_sizes, traffic_type=0, ipv6=0,
307                       proto=-1, ports=0, fragments=False,
308                       pkt_raw=True, etype=-1):
309         """
310         Create input packet stream for defined interface using hosts or
311         deleted_hosts list.
312
313         :param object src_if: Interface to create packet stream for.
314         :param list packet_sizes: List of required packet sizes.
315         :param traffic_type: 1: ICMP packet, 2: IPv6 with EH, 0: otherwise.
316         :return: Stream of packets.
317         """
318         pkts = []
319         if self.flows.__contains__(src_if):
320             src_hosts = self.hosts_by_pg_idx[src_if.sw_if_index]
321             for dst_if in self.flows[src_if]:
322                 dst_hosts = self.hosts_by_pg_idx[dst_if.sw_if_index]
323                 n_int = len(dst_hosts) * len(src_hosts)
324                 for i in range(0, n_int):
325                     dst_host = dst_hosts[i / len(src_hosts)]
326                     src_host = src_hosts[i % len(src_hosts)]
327                     pkt_info = self.create_packet_info(src_if, dst_if)
328                     if ipv6 == 1:
329                         pkt_info.ip = 1
330                     elif ipv6 == 0:
331                         pkt_info.ip = 0
332                     else:
333                         pkt_info.ip = random.choice([0, 1])
334                     if proto == -1:
335                         pkt_info.proto = random.choice(self.proto[self.IP])
336                     else:
337                         pkt_info.proto = proto
338                     payload = self.info_to_payload(pkt_info)
339                     p = Ether(dst=dst_host.mac, src=src_host.mac)
340                     if etype > 0:
341                         p = Ether(dst=dst_host.mac,
342                                   src=src_host.mac,
343                                   type=etype)
344                     if pkt_info.ip:
345                         p /= IPv6(dst=dst_host.ip6, src=src_host.ip6)
346                         if fragments:
347                             p /= IPv6ExtHdrFragment(offset=64, m=1)
348                     else:
349                         if fragments:
350                             p /= IP(src=src_host.ip4, dst=dst_host.ip4,
351                                     flags=1, frag=64)
352                         else:
353                             p /= IP(src=src_host.ip4, dst=dst_host.ip4)
354                     if traffic_type == self.ICMP:
355                         if pkt_info.ip:
356                             p /= ICMPv6EchoRequest(type=self.icmp6_type,
357                                                    code=self.icmp6_code)
358                         else:
359                             p /= ICMP(type=self.icmp4_type,
360                                       code=self.icmp4_code)
361                     else:
362                         p /= self.create_upper_layer(i, pkt_info.proto, ports)
363                     if pkt_raw:
364                         p /= Raw(payload)
365                         pkt_info.data = p.copy()
366                     if pkt_raw:
367                         size = random.choice(packet_sizes)
368                         self.extend_packet(p, size)
369                     pkts.append(p)
370         return pkts
371
372     def verify_capture(self, pg_if, capture,
373                        traffic_type=0, ip_type=0, etype=-1):
374         """
375         Verify captured input packet stream for defined interface.
376
377         :param object pg_if: Interface to verify captured packet stream for.
378         :param list capture: Captured packet stream.
379         :param traffic_type: 1: ICMP packet, 2: IPv6 with EH, 0: otherwise.
380         """
381         last_info = dict()
382         for i in self.pg_interfaces:
383             last_info[i.sw_if_index] = None
384         dst_sw_if_index = pg_if.sw_if_index
385         for packet in capture:
386             if etype > 0:
387                 if packet[Ether].type != etype:
388                     self.logger.error(ppp("Unexpected ethertype in packet:",
389                                           packet))
390                 else:
391                     continue
392             try:
393                 # Raw data for ICMPv6 are stored in ICMPv6EchoRequest.data
394                 if traffic_type == self.ICMP and ip_type == self.IPV6:
395                     payload_info = self.payload_to_info(
396                         packet[ICMPv6EchoRequest].data)
397                     payload = packet[ICMPv6EchoRequest]
398                 else:
399                     payload_info = self.payload_to_info(str(packet[Raw]))
400                     payload = packet[self.proto_map[payload_info.proto]]
401             except:
402                 self.logger.error(ppp("Unexpected or invalid packet "
403                                       "(outside network):", packet))
404                 raise
405
406             if ip_type != 0:
407                 self.assertEqual(payload_info.ip, ip_type)
408             if traffic_type == self.ICMP:
409                 try:
410                     if payload_info.ip == 0:
411                         self.assertEqual(payload.type, self.icmp4_type)
412                         self.assertEqual(payload.code, self.icmp4_code)
413                     else:
414                         self.assertEqual(payload.type, self.icmp6_type)
415                         self.assertEqual(payload.code, self.icmp6_code)
416                 except:
417                     self.logger.error(ppp("Unexpected or invalid packet "
418                                           "(outside network):", packet))
419                     raise
420             else:
421                 try:
422                     ip_version = IPv6 if payload_info.ip == 1 else IP
423
424                     ip = packet[ip_version]
425                     packet_index = payload_info.index
426
427                     self.assertEqual(payload_info.dst, dst_sw_if_index)
428                     self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
429                                       (pg_if.name, payload_info.src,
430                                        packet_index))
431                     next_info = self.get_next_packet_info_for_interface2(
432                         payload_info.src, dst_sw_if_index,
433                         last_info[payload_info.src])
434                     last_info[payload_info.src] = next_info
435                     self.assertTrue(next_info is not None)
436                     self.assertEqual(packet_index, next_info.index)
437                     saved_packet = next_info.data
438                     # Check standard fields
439                     self.assertEqual(ip.src, saved_packet[ip_version].src)
440                     self.assertEqual(ip.dst, saved_packet[ip_version].dst)
441                     p = self.proto_map[payload_info.proto]
442                     if p == 'TCP':
443                         tcp = packet[TCP]
444                         self.assertEqual(tcp.sport, saved_packet[
445                             TCP].sport)
446                         self.assertEqual(tcp.dport, saved_packet[
447                             TCP].dport)
448                     elif p == 'UDP':
449                         udp = packet[UDP]
450                         self.assertEqual(udp.sport, saved_packet[
451                             UDP].sport)
452                         self.assertEqual(udp.dport, saved_packet[
453                             UDP].dport)
454                 except:
455                     self.logger.error(ppp("Unexpected or invalid packet:",
456                                           packet))
457                     raise
458         for i in self.pg_interfaces:
459             remaining_packet = self.get_next_packet_info_for_interface2(
460                 i, dst_sw_if_index, last_info[i.sw_if_index])
461             self.assertTrue(
462                 remaining_packet is None,
463                 "Port %u: Packet expected from source %u didn't arrive" %
464                 (dst_sw_if_index, i.sw_if_index))
465
466     def run_traffic_no_check(self):
467         # Test
468         # Create incoming packet streams for packet-generator interfaces
469         for i in self.pg_interfaces:
470             if self.flows.__contains__(i):
471                 pkts = self.create_stream(i, self.pg_if_packet_sizes)
472                 if len(pkts) > 0:
473                     i.add_stream(pkts)
474
475         # Enable packet capture and start packet sending
476         self.pg_enable_capture(self.pg_interfaces)
477         self.pg_start()
478
479     def run_verify_test(self, traffic_type=0, ip_type=0, proto=-1, ports=0,
480                         frags=False, pkt_raw=True, etype=-1):
481         # Test
482         # Create incoming packet streams for packet-generator interfaces
483         pkts_cnt = 0
484         for i in self.pg_interfaces:
485             if self.flows.__contains__(i):
486                 pkts = self.create_stream(i, self.pg_if_packet_sizes,
487                                           traffic_type, ip_type, proto, ports,
488                                           frags, pkt_raw, etype)
489                 if len(pkts) > 0:
490                     i.add_stream(pkts)
491                     pkts_cnt += len(pkts)
492
493         # Enable packet capture and start packet sendingself.IPV
494         self.pg_enable_capture(self.pg_interfaces)
495         self.pg_start()
496
497         # Verify
498         # Verify outgoing packet streams per packet-generator interface
499         for src_if in self.pg_interfaces:
500             if self.flows.__contains__(src_if):
501                 for dst_if in self.flows[src_if]:
502                     capture = dst_if.get_capture(pkts_cnt)
503                     self.logger.info("Verifying capture on interface %s" %
504                                      dst_if.name)
505                     self.verify_capture(dst_if, capture,
506                                         traffic_type, ip_type, etype)
507
508     def run_verify_negat_test(self, traffic_type=0, ip_type=0, proto=-1,
509                               ports=0, frags=False, etype=-1):
510         # Test
511         self.reset_packet_infos()
512         for i in self.pg_interfaces:
513             if self.flows.__contains__(i):
514                 pkts = self.create_stream(i, self.pg_if_packet_sizes,
515                                           traffic_type, ip_type, proto, ports,
516                                           frags, True, etype)
517                 if len(pkts) > 0:
518                     i.add_stream(pkts)
519
520         # Enable packet capture and start packet sending
521         self.pg_enable_capture(self.pg_interfaces)
522         self.pg_start()
523
524         # Verify
525         # Verify outgoing packet streams per packet-generator interface
526         for src_if in self.pg_interfaces:
527             if self.flows.__contains__(src_if):
528                 for dst_if in self.flows[src_if]:
529                     self.logger.info("Verifying capture on interface %s" %
530                                      dst_if.name)
531                     capture = dst_if.get_capture(0)
532                     self.assertEqual(len(capture), 0)
533
534     def build_classify_table(self, src_mac='', dst_mac='', ether_type='',
535                              etype='', key='mac', hit_next_index=0xffffffff):
536         # Basic ACL testing
537         a_mask = self.build_mac_mask(src_mac=src_mac, dst_mac=dst_mac,
538                                      ether_type=ether_type)
539         self.create_classify_table(key, a_mask)
540         for host in self.hosts_by_pg_idx[self.pg0.sw_if_index]:
541             s_mac = host.mac if src_mac else ''
542             if dst_mac:
543                 for dst_if in self.flows[self.pg0]:
544                     for dst_host in self.hosts_by_pg_idx[dst_if.sw_if_index]:
545                         self.create_classify_session(
546                             self.pg0, self.acl_tbl_idx.get(key),
547                             self.build_mac_match(src_mac=s_mac,
548                                                  dst_mac=dst_host.mac,
549                                                  ether_type=etype),
550                             hit_next_index=hit_next_index)
551             else:
552                 self.create_classify_session(
553                     self.pg0, self.acl_tbl_idx.get(key),
554                     self.build_mac_match(src_mac=s_mac, dst_mac='',
555                                          ether_type=etype),
556                     hit_next_index=hit_next_index)
557
558     def test_0000_warmup_test(self):
559         """ Learn the MAC addresses
560         """
561         self.create_hosts(2)
562         self.run_traffic_no_check()
563
564     def test_0010_inacl_permit_src_mac(self):
565         """ Input  L2 ACL test - permit source MAC
566
567         Test scenario for basic IP ACL with source IP
568             - Create IPv4 stream for pg0 -> pg1 interface.
569             - Create ACL with source MAC address.
570             - Send and verify received packets on pg1 interface.
571         """
572         key = 'mac_in'
573         self.build_classify_table(src_mac='ffffffffffff', key=key)
574         self.input_acl_set_interface(self.pg0, self.acl_tbl_idx.get(key))
575         self.acl_active_table = key
576         self.run_verify_test(self.IP, self.IPV4, -1)
577
578     def test_0011_inacl_permit_dst_mac(self):
579         """ Input  L2 ACL test - permit destination MAC
580
581         Test scenario for basic IP ACL with source IP
582             - Create IPv4 stream for pg0 -> pg1 interface.
583             - Create ACL with destination MAC address.
584             - Send and verify received packets on pg1 interface.
585         """
586         key = 'mac_in'
587         self.build_classify_table(dst_mac='ffffffffffff', key=key)
588         self.input_acl_set_interface(self.pg0, self.acl_tbl_idx.get(key))
589         self.acl_active_table = key
590         self.run_verify_test(self.IP, self.IPV4, -1)
591
592     def test_0012_inacl_permit_src_dst_mac(self):
593         """ Input  L2 ACL test - permit source and destination MAC
594
595         Test scenario for basic IP ACL with source IP
596             - Create IPv4 stream for pg0 -> pg1 interface.
597             - Create ACL with source and destination MAC addresses.
598             - Send and verify received packets on pg1 interface.
599         """
600         key = 'mac_in'
601         self.build_classify_table(
602             src_mac='ffffffffffff', dst_mac='ffffffffffff', key=key)
603         self.input_acl_set_interface(self.pg0, self.acl_tbl_idx.get(key))
604         self.acl_active_table = key
605         self.run_verify_test(self.IP, self.IPV4, -1)
606
607     def test_0013_inacl_permit_ether_type(self):
608         """ Input  L2 ACL test - permit ether_type
609
610         Test scenario for basic IP ACL with source IP
611             - Create IPv4 stream for pg0 -> pg1 interface.
612             - Create ACL with destination MAC address.
613             - Send and verify received packets on pg1 interface.
614         """
615         key = 'mac_in'
616         self.build_classify_table(
617             ether_type='ffff', etype=hex(ETH_P_IP)[2:], key=key)
618         self.input_acl_set_interface(self.pg0, self.acl_tbl_idx.get(key))
619         self.acl_active_table = key
620         self.run_verify_test(self.IP, self.IPV4, -1)
621
622     def test_0015_inacl_deny(self):
623         """ Input  L2 ACL test - deny
624
625         Test scenario for basic IP ACL with source IP
626             - Create IPv4 stream for pg0 -> pg1 interface.
627
628             - Create ACL with source MAC address.
629             - Send and verify no received packets on pg1 interface.
630         """
631         key = 'mac_in'
632         self.build_classify_table(
633             src_mac='ffffffffffff', hit_next_index=0, key=key)
634         self.input_acl_set_interface(self.pg0, self.acl_tbl_idx.get(key))
635         self.acl_active_table = key
636         self.run_verify_negat_test(self.IP, self.IPV4, -1)
637
638     def test_0020_outacl_permit(self):
639         """ Output L2 ACL test - permit
640
641         Test scenario for basic IP ACL with source IP
642             - Create IPv4 stream for pg0 -> pg1 interface.
643             - Create ACL with source MAC address.
644             - Send and verify received packets on pg1 interface.
645         """
646         key = 'mac_out'
647         self.build_classify_table(src_mac='ffffffffffff', key=key)
648         self.output_acl_set_interface(self.pg1, self.acl_tbl_idx.get(key))
649         self.acl_active_table = key
650         self.run_verify_test(self.IP, self.IPV4, -1)
651
652     def test_0025_outacl_deny(self):
653         """ Output L2 ACL test - deny
654
655         Test scenario for basic IP ACL with source IP
656             - Create IPv4 stream for pg0 -> pg1 interface.
657             - Create ACL with source MAC address.
658             - Send and verify no received packets on pg1 interface.
659         """
660         key = 'mac_out'
661         self.build_classify_table(
662             src_mac='ffffffffffff', hit_next_index=0, key=key)
663         self.output_acl_set_interface(self.pg1, self.acl_tbl_idx.get(key))
664         self.acl_active_table = key
665         self.run_verify_negat_test(self.IP, self.IPV4, -1)
666
667     def test_0030_inoutacl_permit(self):
668         """ Input+Output L2 ACL test - permit
669
670         Test scenario for basic IP ACL with source IP
671             - Create IPv4 stream for pg0 -> pg1 interface.
672             - Create ACLs with source MAC address.
673             - Send and verify received packets on pg1 interface.
674         """
675         key = 'mac_inout'
676         self.build_classify_table(src_mac='ffffffffffff', key=key)
677         self.output_acl_set_interface(self.pg1, self.acl_tbl_idx.get(key))
678         self.input_acl_set_interface(self.pg0, self.acl_tbl_idx.get(key))
679         self.acl_active_table = key
680         self.run_verify_test(self.IP, self.IPV4, -1)
681
682 if __name__ == '__main__':
683     unittest.main(testRunner=VppTestRunner)