ip: The check for 'same packet' must include the FIB index
[vpp.git] / test / test_ip4.py
1 #!/usr/bin/env python3
2 import binascii
3 import random
4 import socket
5 import unittest
6
7 import scapy.compat
8 from scapy.contrib.mpls import MPLS
9 from scapy.layers.inet import IP, UDP, TCP, ICMP, icmptypes, icmpcodes
10 from scapy.layers.inet6 import IPv6
11 from scapy.layers.l2 import Ether, Dot1Q, ARP
12 from scapy.packet import Raw
13 from six import moves
14
15 from framework import tag_fixme_vpp_workers
16 from framework import VppTestCase, VppTestRunner
17 from util import ppp
18 from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpMRoute, \
19     VppMRoutePath, VppMplsIpBind, \
20     VppMplsTable, VppIpTable, FibPathType, find_route, \
21     VppIpInterfaceAddress, find_route_in_dump, find_mroute_in_dump
22 from vpp_ip import VppIpPuntPolicer, VppIpPuntRedirect, VppIpPathMtu
23 from vpp_sub_interface import VppSubInterface, VppDot1QSubint, VppDot1ADSubint
24 from vpp_papi import vpp_papi, VppEnum
25 from vpp_neighbor import VppNeighbor
26 from vpp_lo_interface import VppLoInterface
27 from vpp_policer import VppPolicer, PolicerAction
28
29 NUM_PKTS = 67
30
31
32 class TestIPv4(VppTestCase):
33     """ IPv4 Test Case """
34
35     @classmethod
36     def setUpClass(cls):
37         super(TestIPv4, cls).setUpClass()
38
39     @classmethod
40     def tearDownClass(cls):
41         super(TestIPv4, cls).tearDownClass()
42
43     def setUp(self):
44         """
45         Perform test setup before test case.
46
47         **Config:**
48             - create 3 pg interfaces
49                 - untagged pg0 interface
50                 - Dot1Q subinterface on pg1
51                 - Dot1AD subinterface on pg2
52             - setup interfaces:
53                 - put it into UP state
54                 - set IPv4 addresses
55                 - resolve neighbor address using ARP
56             - configure 200 fib entries
57
58         :ivar list interfaces: pg interfaces and subinterfaces.
59         :ivar dict flows: IPv4 packet flows in test.
60         """
61         super(TestIPv4, self).setUp()
62
63         # create 3 pg interfaces
64         self.create_pg_interfaces(range(3))
65
66         # create 2 subinterfaces for pg1 and pg2
67         self.sub_interfaces = [
68             VppDot1QSubint(self, self.pg1, 100),
69             VppDot1ADSubint(self, self.pg2, 200, 300, 400)]
70
71         # packet flows mapping pg0 -> pg1.sub, pg2.sub, etc.
72         self.flows = dict()
73         self.flows[self.pg0] = [self.pg1.sub_if, self.pg2.sub_if]
74         self.flows[self.pg1.sub_if] = [self.pg0, self.pg2.sub_if]
75         self.flows[self.pg2.sub_if] = [self.pg0, self.pg1.sub_if]
76
77         # packet sizes
78         self.pg_if_packet_sizes = [64, 1500, 9020]
79
80         self.interfaces = list(self.pg_interfaces)
81         self.interfaces.extend(self.sub_interfaces)
82
83         # setup all interfaces
84         for i in self.interfaces:
85             i.admin_up()
86             i.config_ip4()
87             i.resolve_arp()
88
89         # config 2M FIB entries
90
91     def tearDown(self):
92         """Run standard test teardown and log ``show ip arp``."""
93         super(TestIPv4, self).tearDown()
94
95     def show_commands_at_teardown(self):
96         self.logger.info(self.vapi.cli("show ip4 neighbors"))
97         # info(self.vapi.cli("show ip fib"))  # many entries
98
99     def modify_packet(self, src_if, packet_size, pkt):
100         """Add load, set destination IP and extend packet to required packet
101         size for defined interface.
102
103         :param VppInterface src_if: Interface to create packet for.
104         :param int packet_size: Required packet size.
105         :param Scapy pkt: Packet to be modified.
106         """
107         dst_if_idx = int(packet_size / 10 % 2)
108         dst_if = self.flows[src_if][dst_if_idx]
109         info = self.create_packet_info(src_if, dst_if)
110         payload = self.info_to_payload(info)
111         p = pkt/Raw(payload)
112         p[IP].dst = dst_if.remote_ip4
113         info.data = p.copy()
114         if isinstance(src_if, VppSubInterface):
115             p = src_if.add_dot1_layer(p)
116         self.extend_packet(p, packet_size)
117
118         return p
119
120     def create_stream(self, src_if):
121         """Create input packet stream for defined interface.
122
123         :param VppInterface src_if: Interface to create packet stream for.
124         """
125         hdr_ext = 4 if isinstance(src_if, VppSubInterface) else 0
126         pkt_tmpl = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
127                     IP(src=src_if.remote_ip4) /
128                     UDP(sport=1234, dport=1234))
129
130         pkts = [self.modify_packet(src_if, i, pkt_tmpl)
131                 for i in moves.range(self.pg_if_packet_sizes[0],
132                                      self.pg_if_packet_sizes[1], 10)]
133         pkts_b = [self.modify_packet(src_if, i, pkt_tmpl)
134                   for i in moves.range(self.pg_if_packet_sizes[1] + hdr_ext,
135                                        self.pg_if_packet_sizes[2] + hdr_ext,
136                                        50)]
137         pkts.extend(pkts_b)
138
139         return pkts
140
141     def verify_capture(self, dst_if, capture):
142         """Verify captured input packet stream for defined interface.
143
144         :param VppInterface dst_if: Interface to verify captured packet stream
145             for.
146         :param list capture: Captured packet stream.
147         """
148         self.logger.info("Verifying capture on interface %s" % dst_if.name)
149         last_info = dict()
150         for i in self.interfaces:
151             last_info[i.sw_if_index] = None
152         is_sub_if = False
153         dst_sw_if_index = dst_if.sw_if_index
154         if hasattr(dst_if, 'parent'):
155             is_sub_if = True
156         for packet in capture:
157             if is_sub_if:
158                 # Check VLAN tags and Ethernet header
159                 packet = dst_if.remove_dot1_layer(packet)
160             self.assertTrue(Dot1Q not in packet)
161             try:
162                 ip = packet[IP]
163                 udp = packet[UDP]
164                 payload_info = self.payload_to_info(packet[Raw])
165                 packet_index = payload_info.index
166                 self.assertEqual(payload_info.dst, dst_sw_if_index)
167                 self.logger.debug(
168                     "Got packet on port %s: src=%u (id=%u)" %
169                     (dst_if.name, payload_info.src, packet_index))
170                 next_info = self.get_next_packet_info_for_interface2(
171                     payload_info.src, dst_sw_if_index,
172                     last_info[payload_info.src])
173                 last_info[payload_info.src] = next_info
174                 self.assertTrue(next_info is not None)
175                 self.assertEqual(packet_index, next_info.index)
176                 saved_packet = next_info.data
177                 # Check standard fields
178                 self.assertEqual(ip.src, saved_packet[IP].src)
179                 self.assertEqual(ip.dst, saved_packet[IP].dst)
180                 self.assertEqual(udp.sport, saved_packet[UDP].sport)
181                 self.assertEqual(udp.dport, saved_packet[UDP].dport)
182             except:
183                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
184                 raise
185         for i in self.interfaces:
186             remaining_packet = self.get_next_packet_info_for_interface2(
187                 i.sw_if_index, dst_sw_if_index, last_info[i.sw_if_index])
188             self.assertTrue(remaining_packet is None,
189                             "Interface %s: Packet expected from interface %s "
190                             "didn't arrive" % (dst_if.name, i.name))
191
192     def test_fib(self):
193         """ IPv4 FIB test
194
195         Test scenario:
196
197             - Create IPv4 stream for pg0 interface
198             - Create IPv4 tagged streams for pg1's and pg2's sub-interface.
199             - Send and verify received packets on each interface.
200         """
201
202         pkts = self.create_stream(self.pg0)
203         self.pg0.add_stream(pkts)
204
205         for i in self.sub_interfaces:
206             pkts = self.create_stream(i)
207             i.parent.add_stream(pkts)
208
209         self.pg_enable_capture(self.pg_interfaces)
210         self.pg_start()
211
212         pkts = self.pg0.get_capture()
213         self.verify_capture(self.pg0, pkts)
214
215         for i in self.sub_interfaces:
216             pkts = i.parent.get_capture()
217             self.verify_capture(i, pkts)
218
219
220 class TestIPv4RouteLookup(VppTestCase):
221     """ IPv4 Route Lookup Test Case """
222     routes = []
223
224     def route_lookup(self, prefix, exact):
225         return self.vapi.api(self.vapi.papi.ip_route_lookup,
226                              {
227                                  'table_id': 0,
228                                  'exact': exact,
229                                  'prefix': prefix,
230                              })
231
232     @classmethod
233     def setUpClass(cls):
234         super(TestIPv4RouteLookup, cls).setUpClass()
235
236     @classmethod
237     def tearDownClass(cls):
238         super(TestIPv4RouteLookup, cls).tearDownClass()
239
240     def setUp(self):
241         super(TestIPv4RouteLookup, self).setUp()
242
243         drop_nh = VppRoutePath("127.0.0.1", 0xffffffff,
244                                type=FibPathType.FIB_PATH_TYPE_DROP)
245
246         # Add 3 routes
247         r = VppIpRoute(self, "1.1.0.0", 16, [drop_nh])
248         r.add_vpp_config()
249         self.routes.append(r)
250
251         r = VppIpRoute(self, "1.1.1.0", 24, [drop_nh])
252         r.add_vpp_config()
253         self.routes.append(r)
254
255         r = VppIpRoute(self, "1.1.1.1", 32, [drop_nh])
256         r.add_vpp_config()
257         self.routes.append(r)
258
259     def tearDown(self):
260         # Remove the routes we added
261         for r in self.routes:
262             r.remove_vpp_config()
263
264         super(TestIPv4RouteLookup, self).tearDown()
265
266     def test_exact_match(self):
267         # Verify we find the host route
268         prefix = "1.1.1.1/32"
269         result = self.route_lookup(prefix, True)
270         assert (prefix == str(result.route.prefix))
271
272         # Verify we find a middle prefix route
273         prefix = "1.1.1.0/24"
274         result = self.route_lookup(prefix, True)
275         assert (prefix == str(result.route.prefix))
276
277         # Verify we do not find an available LPM.
278         with self.vapi.assert_negative_api_retval():
279             self.route_lookup("1.1.1.2/32", True)
280
281     def test_longest_prefix_match(self):
282         # verify we find lpm
283         lpm_prefix = "1.1.1.0/24"
284         result = self.route_lookup("1.1.1.2/32", False)
285         assert (lpm_prefix == str(result.route.prefix))
286
287         # Verify we find the exact when not requested
288         result = self.route_lookup(lpm_prefix, False)
289         assert (lpm_prefix == str(result.route.prefix))
290
291         # Can't seem to delete the default route so no negative LPM test.
292
293
294 class TestIPv4IfAddrRoute(VppTestCase):
295     """ IPv4 Interface Addr Route Test Case """
296
297     @classmethod
298     def setUpClass(cls):
299         super(TestIPv4IfAddrRoute, cls).setUpClass()
300
301     @classmethod
302     def tearDownClass(cls):
303         super(TestIPv4IfAddrRoute, cls).tearDownClass()
304
305     def setUp(self):
306         super(TestIPv4IfAddrRoute, self).setUp()
307
308         # create 1 pg interface
309         self.create_pg_interfaces(range(1))
310
311         for i in self.pg_interfaces:
312             i.admin_up()
313             i.config_ip4()
314             i.resolve_arp()
315
316     def tearDown(self):
317         super(TestIPv4IfAddrRoute, self).tearDown()
318         for i in self.pg_interfaces:
319             i.unconfig_ip4()
320             i.admin_down()
321
322     def test_ipv4_ifaddrs_same_prefix(self):
323         """ IPv4 Interface Addresses Same Prefix test
324
325         Test scenario:
326
327             - Verify no route in FIB for prefix 10.10.10.0/24
328             - Configure IPv4 address 10.10.10.10/24 on an interface
329             - Verify route in FIB for prefix 10.10.10.0/24
330             - Configure IPv4 address 10.10.10.20/24 on an interface
331             - Delete 10.10.10.10/24 from interface
332             - Verify route in FIB for prefix 10.10.10.0/24
333             - Delete 10.10.10.20/24 from interface
334             - Verify no route in FIB for prefix 10.10.10.0/24
335         """
336
337         # create two addresses, verify route not present
338         if_addr1 = VppIpInterfaceAddress(self, self.pg0, "10.10.10.10", 24)
339         if_addr2 = VppIpInterfaceAddress(self, self.pg0, "10.10.10.20", 24)
340         self.assertFalse(if_addr1.query_vpp_config())  # 10.10.10.10/24
341         self.assertFalse(find_route(self, "10.10.10.10", 32))
342         self.assertFalse(find_route(self, "10.10.10.20", 32))
343         self.assertFalse(find_route(self, "10.10.10.255", 32))
344         self.assertFalse(find_route(self, "10.10.10.0", 32))
345
346         # configure first address, verify route present
347         if_addr1.add_vpp_config()
348         self.assertTrue(if_addr1.query_vpp_config())  # 10.10.10.10/24
349         self.assertTrue(find_route(self, "10.10.10.10", 32))
350         self.assertFalse(find_route(self, "10.10.10.20", 32))
351         self.assertTrue(find_route(self, "10.10.10.255", 32))
352         self.assertTrue(find_route(self, "10.10.10.0", 32))
353
354         # configure second address, delete first, verify route not removed
355         if_addr2.add_vpp_config()
356         if_addr1.remove_vpp_config()
357         self.assertFalse(if_addr1.query_vpp_config())  # 10.10.10.10/24
358         self.assertTrue(if_addr2.query_vpp_config())  # 10.10.10.20/24
359         self.assertFalse(find_route(self, "10.10.10.10", 32))
360         self.assertTrue(find_route(self, "10.10.10.20", 32))
361         self.assertTrue(find_route(self, "10.10.10.255", 32))
362         self.assertTrue(find_route(self, "10.10.10.0", 32))
363
364         # delete second address, verify route removed
365         if_addr2.remove_vpp_config()
366         self.assertFalse(if_addr2.query_vpp_config())  # 10.10.10.20/24
367         self.assertFalse(find_route(self, "10.10.10.10", 32))
368         self.assertFalse(find_route(self, "10.10.10.20", 32))
369         self.assertFalse(find_route(self, "10.10.10.255", 32))
370         self.assertFalse(find_route(self, "10.10.10.0", 32))
371
372     def test_ipv4_ifaddr_route(self):
373         """ IPv4 Interface Address Route test
374
375         Test scenario:
376
377             - Create loopback
378             - Configure IPv4 address on loopback
379             - Verify that address is not in the FIB
380             - Bring loopback up
381             - Verify that address is in the FIB now
382             - Bring loopback down
383             - Verify that address is not in the FIB anymore
384             - Bring loopback up
385             - Configure IPv4 address on loopback
386             - Verify that address is in the FIB now
387         """
388
389         # create a loopback and configure IPv4
390         loopbacks = self.create_loopback_interfaces(1)
391         lo_if = self.lo_interfaces[0]
392
393         lo_if.local_ip4_prefix_len = 32
394         lo_if.config_ip4()
395
396         # The intf was down when addr was added -> entry not in FIB
397         fib4_dump = self.vapi.ip_route_dump(0)
398         self.assertFalse(lo_if.is_ip4_entry_in_fib_dump(fib4_dump))
399
400         # When intf is brought up, entry is added
401         lo_if.admin_up()
402         fib4_dump = self.vapi.ip_route_dump(0)
403         self.assertTrue(lo_if.is_ip4_entry_in_fib_dump(fib4_dump))
404
405         # When intf is brought down, entry is removed
406         lo_if.admin_down()
407         fib4_dump = self.vapi.ip_route_dump(0)
408         self.assertFalse(lo_if.is_ip4_entry_in_fib_dump(fib4_dump))
409
410         # Remove addr, bring up interface, re-add -> entry in FIB
411         lo_if.unconfig_ip4()
412         lo_if.admin_up()
413         lo_if.config_ip4()
414         fib4_dump = self.vapi.ip_route_dump(0)
415         self.assertTrue(lo_if.is_ip4_entry_in_fib_dump(fib4_dump))
416
417     def test_ipv4_ifaddr_del(self):
418         """ Delete an interface address that does not exist """
419
420         loopbacks = self.create_loopback_interfaces(1)
421         lo = self.lo_interfaces[0]
422
423         lo.config_ip4()
424         lo.admin_up()
425
426         #
427         # try and remove pg0's subnet from lo
428         #
429         with self.vapi.assert_negative_api_retval():
430             self.vapi.sw_interface_add_del_address(
431                 sw_if_index=lo.sw_if_index,
432                 prefix=self.pg0.local_ip4_prefix,
433                 is_add=0)
434
435
436 class TestICMPEcho(VppTestCase):
437     """ ICMP Echo Test Case """
438
439     @classmethod
440     def setUpClass(cls):
441         super(TestICMPEcho, cls).setUpClass()
442
443     @classmethod
444     def tearDownClass(cls):
445         super(TestICMPEcho, cls).tearDownClass()
446
447     def setUp(self):
448         super(TestICMPEcho, self).setUp()
449
450         # create 1 pg interface
451         self.create_pg_interfaces(range(1))
452
453         for i in self.pg_interfaces:
454             i.admin_up()
455             i.config_ip4()
456             i.resolve_arp()
457
458     def tearDown(self):
459         super(TestICMPEcho, self).tearDown()
460         for i in self.pg_interfaces:
461             i.unconfig_ip4()
462             i.admin_down()
463
464     def test_icmp_echo(self):
465         """ VPP replies to ICMP Echo Request
466
467         Test scenario:
468
469             - Receive ICMP Echo Request message on pg0 interface.
470             - Check outgoing ICMP Echo Reply message on pg0 interface.
471         """
472
473         icmp_id = 0xb
474         icmp_seq = 5
475         icmp_load = b'\x0a' * 18
476         p_echo_request = (Ether(src=self.pg0.remote_mac,
477                                 dst=self.pg0.local_mac) /
478                           IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
479                           ICMP(id=icmp_id, seq=icmp_seq) /
480                           Raw(load=icmp_load))
481
482         self.pg0.add_stream(p_echo_request)
483         self.pg_enable_capture(self.pg_interfaces)
484         self.pg_start()
485
486         rx = self.pg0.get_capture(1)
487         rx = rx[0]
488         ether = rx[Ether]
489         ipv4 = rx[IP]
490         icmp = rx[ICMP]
491
492         self.assertEqual(ether.src, self.pg0.local_mac)
493         self.assertEqual(ether.dst, self.pg0.remote_mac)
494
495         self.assertEqual(ipv4.src, self.pg0.local_ip4)
496         self.assertEqual(ipv4.dst, self.pg0.remote_ip4)
497
498         self.assertEqual(icmptypes[icmp.type], "echo-reply")
499         self.assertEqual(icmp.id, icmp_id)
500         self.assertEqual(icmp.seq, icmp_seq)
501         self.assertEqual(icmp[Raw].load, icmp_load)
502
503
504 class TestIPv4FibCrud(VppTestCase):
505     """ FIB - add/update/delete - ip4 routes
506
507     Test scenario:
508         - add 1k,
509         - del 100,
510         - add new 1k,
511         - del 1.5k
512
513     ..note:: Python API is too slow to add many routes, needs replacement.
514     """
515
516     def config_fib_many_to_one(self, start_dest_addr, next_hop_addr,
517                                count, start=0):
518         """
519
520         :param start_dest_addr:
521         :param next_hop_addr:
522         :param count:
523         :return list: added ips with 32 prefix
524         """
525         routes = []
526         for i in range(count):
527             r = VppIpRoute(self, start_dest_addr % (i + start), 32,
528                            [VppRoutePath(next_hop_addr, 0xffffffff)])
529             r.add_vpp_config()
530             routes.append(r)
531         return routes
532
533     def unconfig_fib_many_to_one(self, start_dest_addr, next_hop_addr,
534                                  count, start=0):
535
536         routes = []
537         for i in range(count):
538             r = VppIpRoute(self, start_dest_addr % (i + start), 32,
539                            [VppRoutePath(next_hop_addr, 0xffffffff)])
540             r.remove_vpp_config()
541             routes.append(r)
542         return routes
543
544     def create_stream(self, src_if, dst_if, routes, count):
545         pkts = []
546
547         for _ in range(count):
548             dst_addr = random.choice(routes).prefix.network_address
549             info = self.create_packet_info(src_if, dst_if)
550             payload = self.info_to_payload(info)
551             p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
552                  IP(src=src_if.remote_ip4, dst=str(dst_addr)) /
553                  UDP(sport=1234, dport=1234) /
554                  Raw(payload))
555             info.data = p.copy()
556             self.extend_packet(p, random.choice(self.pg_if_packet_sizes))
557             pkts.append(p)
558
559         return pkts
560
561     def _find_ip_match(self, find_in, pkt):
562         for p in find_in:
563             if self.payload_to_info(p[Raw]) == \
564                     self.payload_to_info(pkt[Raw]):
565                 if p[IP].src != pkt[IP].src:
566                     break
567                 if p[IP].dst != pkt[IP].dst:
568                     break
569                 if p[UDP].sport != pkt[UDP].sport:
570                     break
571                 if p[UDP].dport != pkt[UDP].dport:
572                     break
573                 return p
574         return None
575
576     def verify_capture(self, dst_interface, received_pkts, expected_pkts):
577         self.assertEqual(len(received_pkts), len(expected_pkts))
578         to_verify = list(expected_pkts)
579         for p in received_pkts:
580             self.assertEqual(p.src, dst_interface.local_mac)
581             self.assertEqual(p.dst, dst_interface.remote_mac)
582             x = self._find_ip_match(to_verify, p)
583             to_verify.remove(x)
584         self.assertListEqual(to_verify, [])
585
586     def verify_route_dump(self, routes):
587         for r in routes:
588             self.assertTrue(find_route(self,
589                                        r.prefix.network_address,
590                                        r.prefix.prefixlen))
591
592     def verify_not_in_route_dump(self, routes):
593         for r in routes:
594             self.assertFalse(find_route(self,
595                                         r.prefix.network_address,
596                                         r.prefix.prefixlen))
597
598     @classmethod
599     def setUpClass(cls):
600         """
601         #. Create and initialize 3 pg interfaces.
602         #. initialize class attributes configured_routes and deleted_routes
603            to store information between tests.
604         """
605         super(TestIPv4FibCrud, cls).setUpClass()
606
607         try:
608             # create 3 pg interfaces
609             cls.create_pg_interfaces(range(3))
610
611             cls.interfaces = list(cls.pg_interfaces)
612
613             # setup all interfaces
614             for i in cls.interfaces:
615                 i.admin_up()
616                 i.config_ip4()
617                 i.resolve_arp()
618
619             cls.configured_routes = []
620             cls.deleted_routes = []
621             cls.pg_if_packet_sizes = [64, 512, 1518, 9018]
622
623         except Exception:
624             super(TestIPv4FibCrud, cls).tearDownClass()
625             raise
626
627     @classmethod
628     def tearDownClass(cls):
629         super(TestIPv4FibCrud, cls).tearDownClass()
630
631     def setUp(self):
632         super(TestIPv4FibCrud, self).setUp()
633         self.reset_packet_infos()
634
635         self.configured_routes = []
636         self.deleted_routes = []
637
638     def test_1_add_routes(self):
639         """ Add 1k routes """
640
641         # add 100 routes check with traffic script.
642         self.configured_routes.extend(self.config_fib_many_to_one(
643             "10.0.0.%d", self.pg0.remote_ip4, 100))
644
645         self.verify_route_dump(self.configured_routes)
646
647         self.stream_1 = self.create_stream(
648             self.pg1, self.pg0, self.configured_routes, 100)
649         self.stream_2 = self.create_stream(
650             self.pg2, self.pg0, self.configured_routes, 100)
651         self.pg1.add_stream(self.stream_1)
652         self.pg2.add_stream(self.stream_2)
653
654         self.pg_enable_capture(self.pg_interfaces)
655         self.pg_start()
656
657         pkts = self.pg0.get_capture(len(self.stream_1) + len(self.stream_2))
658         self.verify_capture(self.pg0, pkts, self.stream_1 + self.stream_2)
659
660     def test_2_del_routes(self):
661         """ Delete 100 routes
662
663         - delete 10 routes check with traffic script.
664         """
665         # config 1M FIB entries
666         self.configured_routes.extend(self.config_fib_many_to_one(
667             "10.0.0.%d", self.pg0.remote_ip4, 100))
668         self.deleted_routes.extend(self.unconfig_fib_many_to_one(
669             "10.0.0.%d", self.pg0.remote_ip4, 10, start=10))
670         for x in self.deleted_routes:
671             self.configured_routes.remove(x)
672
673         self.verify_route_dump(self.configured_routes)
674
675         self.stream_1 = self.create_stream(
676             self.pg1, self.pg0, self.configured_routes, 100)
677         self.stream_2 = self.create_stream(
678             self.pg2, self.pg0, self.configured_routes, 100)
679         self.stream_3 = self.create_stream(
680             self.pg1, self.pg0, self.deleted_routes, 100)
681         self.stream_4 = self.create_stream(
682             self.pg2, self.pg0, self.deleted_routes, 100)
683         self.pg1.add_stream(self.stream_1 + self.stream_3)
684         self.pg2.add_stream(self.stream_2 + self.stream_4)
685         self.pg_enable_capture(self.pg_interfaces)
686         self.pg_start()
687
688         pkts = self.pg0.get_capture(len(self.stream_1) + len(self.stream_2))
689         self.verify_capture(self.pg0, pkts, self.stream_1 + self.stream_2)
690
691     def test_3_add_new_routes(self):
692         """ Add 1k routes
693
694         - re-add 5 routes check with traffic script.
695         - add 100 routes check with traffic script.
696         """
697         # config 1M FIB entries
698         self.configured_routes.extend(self.config_fib_many_to_one(
699             "10.0.0.%d", self.pg0.remote_ip4, 100))
700         self.deleted_routes.extend(self.unconfig_fib_many_to_one(
701             "10.0.0.%d", self.pg0.remote_ip4, 10, start=10))
702         for x in self.deleted_routes:
703             self.configured_routes.remove(x)
704
705         tmp = self.config_fib_many_to_one(
706             "10.0.0.%d", self.pg0.remote_ip4, 5, start=10)
707         self.configured_routes.extend(tmp)
708         for x in tmp:
709             self.deleted_routes.remove(x)
710
711         self.configured_routes.extend(self.config_fib_many_to_one(
712             "10.0.1.%d", self.pg0.remote_ip4, 100))
713
714         self.verify_route_dump(self.configured_routes)
715
716         self.stream_1 = self.create_stream(
717             self.pg1, self.pg0, self.configured_routes, 300)
718         self.stream_2 = self.create_stream(
719             self.pg2, self.pg0, self.configured_routes, 300)
720         self.stream_3 = self.create_stream(
721             self.pg1, self.pg0, self.deleted_routes, 100)
722         self.stream_4 = self.create_stream(
723             self.pg2, self.pg0, self.deleted_routes, 100)
724
725         self.pg1.add_stream(self.stream_1 + self.stream_3)
726         self.pg2.add_stream(self.stream_2 + self.stream_4)
727         self.pg_enable_capture(self.pg_interfaces)
728         self.pg_start()
729
730         pkts = self.pg0.get_capture(len(self.stream_1) + len(self.stream_2))
731         self.verify_capture(self.pg0, pkts, self.stream_1 + self.stream_2)
732
733         # delete 5 routes check with traffic script.
734         # add 100 routes check with traffic script.
735         self.deleted_routes.extend(self.unconfig_fib_many_to_one(
736             "10.0.0.%d", self.pg0.remote_ip4, 15))
737         self.deleted_routes.extend(self.unconfig_fib_many_to_one(
738             "10.0.0.%d", self.pg0.remote_ip4, 85))
739         self.deleted_routes.extend(self.unconfig_fib_many_to_one(
740             "10.0.1.%d", self.pg0.remote_ip4, 100))
741         self.verify_not_in_route_dump(self.deleted_routes)
742
743
744 class TestIPNull(VppTestCase):
745     """ IPv4 routes via NULL """
746
747     @classmethod
748     def setUpClass(cls):
749         super(TestIPNull, cls).setUpClass()
750
751     @classmethod
752     def tearDownClass(cls):
753         super(TestIPNull, cls).tearDownClass()
754
755     def setUp(self):
756         super(TestIPNull, self).setUp()
757
758         # create 2 pg interfaces
759         self.create_pg_interfaces(range(2))
760
761         for i in self.pg_interfaces:
762             i.admin_up()
763             i.config_ip4()
764             i.resolve_arp()
765
766     def tearDown(self):
767         super(TestIPNull, self).tearDown()
768         for i in self.pg_interfaces:
769             i.unconfig_ip4()
770             i.admin_down()
771
772     def test_ip_null(self):
773         """ IP NULL route """
774
775         #
776         # A route via IP NULL that will reply with ICMP unreachables
777         #
778         ip_unreach = VppIpRoute(
779             self, "10.0.0.1", 32,
780             [VppRoutePath("0.0.0.0",
781                           0xffffffff,
782                           type=FibPathType.FIB_PATH_TYPE_ICMP_UNREACH)])
783         ip_unreach.add_vpp_config()
784
785         p_unreach = (Ether(src=self.pg0.remote_mac,
786                            dst=self.pg0.local_mac) /
787                      IP(src=self.pg0.remote_ip4, dst="10.0.0.1") /
788                      UDP(sport=1234, dport=1234) /
789                      Raw(b'\xa5' * 100))
790         self.pg0.add_stream(p_unreach)
791         self.pg_enable_capture(self.pg_interfaces)
792         self.pg_start()
793
794         rx = self.pg0.get_capture(1)
795         rx = rx[0]
796         icmp = rx[ICMP]
797
798         self.assertEqual(icmptypes[icmp.type], "dest-unreach")
799         self.assertEqual(icmpcodes[icmp.type][icmp.code], "host-unreachable")
800         self.assertEqual(icmp.src, self.pg0.remote_ip4)
801         self.assertEqual(icmp.dst, "10.0.0.1")
802
803         #
804         # ICMP replies are rate limited. so sit and spin.
805         #
806         self.sleep(1)
807
808         #
809         # A route via IP NULL that will reply with ICMP prohibited
810         #
811         ip_prohibit = VppIpRoute(
812             self, "10.0.0.2", 32,
813             [VppRoutePath("0.0.0.0",
814                           0xffffffff,
815                           type=FibPathType.FIB_PATH_TYPE_ICMP_PROHIBIT)])
816         ip_prohibit.add_vpp_config()
817
818         p_prohibit = (Ether(src=self.pg0.remote_mac,
819                             dst=self.pg0.local_mac) /
820                       IP(src=self.pg0.remote_ip4, dst="10.0.0.2") /
821                       UDP(sport=1234, dport=1234) /
822                       Raw(b'\xa5' * 100))
823
824         self.pg0.add_stream(p_prohibit)
825         self.pg_enable_capture(self.pg_interfaces)
826         self.pg_start()
827
828         rx = self.pg0.get_capture(1)
829
830         rx = rx[0]
831         icmp = rx[ICMP]
832
833         self.assertEqual(icmptypes[icmp.type], "dest-unreach")
834         self.assertEqual(icmpcodes[icmp.type][icmp.code], "host-prohibited")
835         self.assertEqual(icmp.src, self.pg0.remote_ip4)
836         self.assertEqual(icmp.dst, "10.0.0.2")
837
838     def test_ip_drop(self):
839         """ IP Drop Routes """
840
841         p = (Ether(src=self.pg0.remote_mac,
842                    dst=self.pg0.local_mac) /
843              IP(src=self.pg0.remote_ip4, dst="1.1.1.1") /
844              UDP(sport=1234, dport=1234) /
845              Raw(b'\xa5' * 100))
846
847         r1 = VppIpRoute(self, "1.1.1.0", 24,
848                         [VppRoutePath(self.pg1.remote_ip4,
849                                       self.pg1.sw_if_index)])
850         r1.add_vpp_config()
851
852         rx = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg1)
853
854         #
855         # insert a more specific as a drop
856         #
857         r2 = VppIpRoute(self, "1.1.1.1", 32,
858                         [VppRoutePath("0.0.0.0",
859                                       0xffffffff,
860                                       type=FibPathType.FIB_PATH_TYPE_DROP)])
861         r2.add_vpp_config()
862
863         self.send_and_assert_no_replies(self.pg0, p * NUM_PKTS, "Drop Route")
864         r2.remove_vpp_config()
865         rx = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg1)
866
867
868 class TestIPDisabled(VppTestCase):
869     """ IPv4 disabled """
870
871     @classmethod
872     def setUpClass(cls):
873         super(TestIPDisabled, cls).setUpClass()
874
875     @classmethod
876     def tearDownClass(cls):
877         super(TestIPDisabled, cls).tearDownClass()
878
879     def setUp(self):
880         super(TestIPDisabled, self).setUp()
881
882         # create 2 pg interfaces
883         self.create_pg_interfaces(range(2))
884
885         # PG0 is IP enalbed
886         self.pg0.admin_up()
887         self.pg0.config_ip4()
888         self.pg0.resolve_arp()
889
890         # PG 1 is not IP enabled
891         self.pg1.admin_up()
892
893     def tearDown(self):
894         super(TestIPDisabled, self).tearDown()
895         for i in self.pg_interfaces:
896             i.unconfig_ip4()
897             i.admin_down()
898
899     def test_ip_disabled(self):
900         """ IP Disabled """
901
902         MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
903         MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
904
905         #
906         # An (S,G).
907         # one accepting interface, pg0, 2 forwarding interfaces
908         #
909         route_232_1_1_1 = VppIpMRoute(
910             self,
911             "0.0.0.0",
912             "232.1.1.1", 32,
913             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
914             [VppMRoutePath(self.pg1.sw_if_index,
915                            MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT),
916              VppMRoutePath(self.pg0.sw_if_index,
917                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD)])
918         route_232_1_1_1.add_vpp_config()
919
920         pu = (Ether(src=self.pg1.remote_mac,
921                     dst=self.pg1.local_mac) /
922               IP(src="10.10.10.10", dst=self.pg0.remote_ip4) /
923               UDP(sport=1234, dport=1234) /
924               Raw(b'\xa5' * 100))
925         pm = (Ether(src=self.pg1.remote_mac,
926                     dst=self.pg1.local_mac) /
927               IP(src="10.10.10.10", dst="232.1.1.1") /
928               UDP(sport=1234, dport=1234) /
929               Raw(b'\xa5' * 100))
930
931         #
932         # PG1 does not forward IP traffic
933         #
934         self.send_and_assert_no_replies(self.pg1, pu, "IP disabled")
935         self.send_and_assert_no_replies(self.pg1, pm, "IP disabled")
936
937         #
938         # IP enable PG1
939         #
940         self.pg1.config_ip4()
941
942         #
943         # Now we get packets through
944         #
945         self.pg1.add_stream(pu)
946         self.pg_enable_capture(self.pg_interfaces)
947         self.pg_start()
948         rx = self.pg0.get_capture(1)
949
950         self.pg1.add_stream(pm)
951         self.pg_enable_capture(self.pg_interfaces)
952         self.pg_start()
953         rx = self.pg0.get_capture(1)
954
955         #
956         # Disable PG1
957         #
958         self.pg1.unconfig_ip4()
959
960         #
961         # PG1 does not forward IP traffic
962         #
963         self.send_and_assert_no_replies(self.pg1, pu, "IP disabled")
964         self.send_and_assert_no_replies(self.pg1, pm, "IP disabled")
965
966
967 class TestIPSubNets(VppTestCase):
968     """ IPv4 Subnets """
969
970     @classmethod
971     def setUpClass(cls):
972         super(TestIPSubNets, cls).setUpClass()
973
974     @classmethod
975     def tearDownClass(cls):
976         super(TestIPSubNets, cls).tearDownClass()
977
978     def setUp(self):
979         super(TestIPSubNets, self).setUp()
980
981         # create a 2 pg interfaces
982         self.create_pg_interfaces(range(2))
983
984         # pg0 we will use to experiment
985         self.pg0.admin_up()
986
987         # pg1 is setup normally
988         self.pg1.admin_up()
989         self.pg1.config_ip4()
990         self.pg1.resolve_arp()
991
992     def tearDown(self):
993         super(TestIPSubNets, self).tearDown()
994         for i in self.pg_interfaces:
995             i.admin_down()
996
997     def test_ip_sub_nets(self):
998         """ IP Sub Nets """
999
1000         #
1001         # Configure a covering route to forward so we know
1002         # when we are dropping
1003         #
1004         cover_route = VppIpRoute(self, "10.0.0.0", 8,
1005                                  [VppRoutePath(self.pg1.remote_ip4,
1006                                                self.pg1.sw_if_index)])
1007         cover_route.add_vpp_config()
1008
1009         p = (Ether(src=self.pg1.remote_mac,
1010                    dst=self.pg1.local_mac) /
1011              IP(dst="10.10.10.10", src=self.pg0.local_ip4) /
1012              UDP(sport=1234, dport=1234) /
1013              Raw(b'\xa5' * 100))
1014
1015         self.pg1.add_stream(p)
1016         self.pg_enable_capture(self.pg_interfaces)
1017         self.pg_start()
1018         rx = self.pg1.get_capture(1)
1019
1020         #
1021         # Configure some non-/24 subnets on an IP interface
1022         #
1023         ip_addr_n = socket.inet_pton(socket.AF_INET, "10.10.10.10")
1024
1025         self.vapi.sw_interface_add_del_address(
1026             sw_if_index=self.pg0.sw_if_index,
1027             prefix="10.10.10.10/16")
1028
1029         pn = (Ether(src=self.pg1.remote_mac,
1030                     dst=self.pg1.local_mac) /
1031               IP(dst="10.10.0.0", src=self.pg0.local_ip4) /
1032               UDP(sport=1234, dport=1234) /
1033               Raw(b'\xa5' * 100))
1034         pb = (Ether(src=self.pg1.remote_mac,
1035                     dst=self.pg1.local_mac) /
1036               IP(dst="10.10.255.255", src=self.pg0.local_ip4) /
1037               UDP(sport=1234, dport=1234) /
1038               Raw(b'\xa5' * 100))
1039
1040         self.send_and_assert_no_replies(self.pg1, pn, "IP Network address")
1041         self.send_and_assert_no_replies(self.pg1, pb, "IP Broadcast address")
1042
1043         # remove the sub-net and we are forwarding via the cover again
1044         self.vapi.sw_interface_add_del_address(
1045             sw_if_index=self.pg0.sw_if_index,
1046             prefix="10.10.10.10/16",
1047             is_add=0)
1048
1049         self.pg1.add_stream(pn)
1050         self.pg_enable_capture(self.pg_interfaces)
1051         self.pg_start()
1052         rx = self.pg1.get_capture(1)
1053         self.pg1.add_stream(pb)
1054         self.pg_enable_capture(self.pg_interfaces)
1055         self.pg_start()
1056         rx = self.pg1.get_capture(1)
1057
1058         #
1059         # A /31 is a special case where the 'other-side' is an attached host
1060         # packets to that peer generate ARP requests
1061         #
1062         ip_addr_n = socket.inet_pton(socket.AF_INET, "10.10.10.10")
1063
1064         self.vapi.sw_interface_add_del_address(
1065             sw_if_index=self.pg0.sw_if_index,
1066             prefix="10.10.10.10/31")
1067
1068         pn = (Ether(src=self.pg1.remote_mac,
1069                     dst=self.pg1.local_mac) /
1070               IP(dst="10.10.10.11", src=self.pg0.local_ip4) /
1071               UDP(sport=1234, dport=1234) /
1072               Raw(b'\xa5' * 100))
1073
1074         self.pg1.add_stream(pn)
1075         self.pg_enable_capture(self.pg_interfaces)
1076         self.pg_start()
1077         rx = self.pg0.get_capture(1)
1078         rx[ARP]
1079
1080         # remove the sub-net and we are forwarding via the cover again
1081         self.vapi.sw_interface_add_del_address(
1082             sw_if_index=self.pg0.sw_if_index,
1083             prefix="10.10.10.10/31", is_add=0)
1084
1085         self.pg1.add_stream(pn)
1086         self.pg_enable_capture(self.pg_interfaces)
1087         self.pg_start()
1088         rx = self.pg1.get_capture(1)
1089
1090
1091 class TestIPLoadBalance(VppTestCase):
1092     """ IPv4 Load-Balancing """
1093
1094     @classmethod
1095     def setUpClass(cls):
1096         super(TestIPLoadBalance, cls).setUpClass()
1097
1098     @classmethod
1099     def tearDownClass(cls):
1100         super(TestIPLoadBalance, cls).tearDownClass()
1101
1102     def setUp(self):
1103         super(TestIPLoadBalance, self).setUp()
1104
1105         self.create_pg_interfaces(range(5))
1106         mpls_tbl = VppMplsTable(self, 0)
1107         mpls_tbl.add_vpp_config()
1108
1109         for i in self.pg_interfaces:
1110             i.admin_up()
1111             i.config_ip4()
1112             i.resolve_arp()
1113             i.enable_mpls()
1114
1115     def tearDown(self):
1116         for i in self.pg_interfaces:
1117             i.disable_mpls()
1118             i.unconfig_ip4()
1119             i.admin_down()
1120         super(TestIPLoadBalance, self).tearDown()
1121
1122     def total_len(self, rxs):
1123         n = 0
1124         for rx in rxs:
1125             n += len(rx)
1126         return n
1127
1128     def test_ip_load_balance(self):
1129         """ IP Load-Balancing """
1130
1131         fhc = VppEnum.vl_api_ip_flow_hash_config_t
1132         af = VppEnum.vl_api_address_family_t
1133
1134         #
1135         # An array of packets that differ only in the destination port
1136         #
1137         port_ip_pkts = []
1138         port_mpls_pkts = []
1139
1140         #
1141         # An array of packets that differ only in the source address
1142         #
1143         src_ip_pkts = []
1144         src_mpls_pkts = []
1145
1146         for ii in range(NUM_PKTS):
1147             port_ip_hdr = (IP(dst="10.0.0.1", src="20.0.0.1") /
1148                            UDP(sport=1234, dport=1234 + ii) /
1149                            Raw(b'\xa5' * 100))
1150             port_ip_pkts.append((Ether(src=self.pg0.remote_mac,
1151                                        dst=self.pg0.local_mac) /
1152                                  port_ip_hdr))
1153             port_mpls_pkts.append((Ether(src=self.pg0.remote_mac,
1154                                          dst=self.pg0.local_mac) /
1155                                    MPLS(label=66, ttl=2) /
1156                                    port_ip_hdr))
1157
1158             src_ip_hdr = (IP(dst="10.0.0.1", src="20.0.0.%d" % ii) /
1159                           UDP(sport=1234, dport=1234) /
1160                           Raw(b'\xa5' * 100))
1161             src_ip_pkts.append((Ether(src=self.pg0.remote_mac,
1162                                       dst=self.pg0.local_mac) /
1163                                 src_ip_hdr))
1164             src_mpls_pkts.append((Ether(src=self.pg0.remote_mac,
1165                                         dst=self.pg0.local_mac) /
1166                                   MPLS(label=66, ttl=2) /
1167                                   src_ip_hdr))
1168
1169         route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
1170                                     [VppRoutePath(self.pg1.remote_ip4,
1171                                                   self.pg1.sw_if_index),
1172                                      VppRoutePath(self.pg2.remote_ip4,
1173                                                   self.pg2.sw_if_index)])
1174         route_10_0_0_1.add_vpp_config()
1175
1176         binding = VppMplsIpBind(self, 66, "10.0.0.1", 32)
1177         binding.add_vpp_config()
1178
1179         #
1180         # inject the packet on pg0 - expect load-balancing across the 2 paths
1181         #  - since the default hash config is to use IP src,dst and port
1182         #    src,dst
1183         # We are not going to ensure equal amounts of packets across each link,
1184         # since the hash algorithm is statistical and therefore this can never
1185         # be guaranteed. But with 64 different packets we do expect some
1186         # balancing. So instead just ensure there is traffic on each link.
1187         #
1188         rx = self.send_and_expect_load_balancing(self.pg0, port_ip_pkts,
1189                                                  [self.pg1, self.pg2])
1190         n_ip_pg0 = len(rx[0])
1191         self.send_and_expect_load_balancing(self.pg0, src_ip_pkts,
1192                                             [self.pg1, self.pg2])
1193         self.send_and_expect_load_balancing(self.pg0, port_mpls_pkts,
1194                                             [self.pg1, self.pg2])
1195         rx = self.send_and_expect_load_balancing(self.pg0, src_mpls_pkts,
1196                                                  [self.pg1, self.pg2])
1197         n_mpls_pg0 = len(rx[0])
1198
1199         #
1200         # change the router ID and expect the distribution changes
1201         #
1202         self.vapi.set_ip_flow_hash_router_id(router_id=0x11111111)
1203
1204         rx = self.send_and_expect_load_balancing(self.pg0, port_ip_pkts,
1205                                                  [self.pg1, self.pg2])
1206         self.assertNotEqual(n_ip_pg0, len(rx[0]))
1207
1208         rx = self.send_and_expect_load_balancing(self.pg0, src_mpls_pkts,
1209                                                  [self.pg1, self.pg2])
1210         self.assertNotEqual(n_mpls_pg0, len(rx[0]))
1211
1212         #
1213         # change the flow hash config so it's only IP src,dst
1214         #  - now only the stream with differing source address will
1215         #    load-balance
1216         #
1217         self.vapi.set_ip_flow_hash_v2(
1218             af=af.ADDRESS_IP4,
1219             table_id=0,
1220             flow_hash_config=(fhc.IP_API_FLOW_HASH_SRC_IP |
1221                               fhc.IP_API_FLOW_HASH_DST_IP |
1222                               fhc.IP_API_FLOW_HASH_PROTO))
1223
1224         self.send_and_expect_load_balancing(self.pg0, src_ip_pkts,
1225                                             [self.pg1, self.pg2])
1226         self.send_and_expect_load_balancing(self.pg0, src_mpls_pkts,
1227                                             [self.pg1, self.pg2])
1228
1229         self.send_and_expect_only(self.pg0, port_ip_pkts, self.pg2)
1230
1231         #
1232         # change the flow hash config back to defaults
1233         #
1234         self.vapi.set_ip_flow_hash(vrf_id=0, src=1, dst=1,
1235                                    proto=1, sport=1, dport=1)
1236
1237         #
1238         # Recursive prefixes
1239         #  - testing that 2 stages of load-balancing occurs and there is no
1240         #    polarisation (i.e. only 2 of 4 paths are used)
1241         #
1242         port_pkts = []
1243         src_pkts = []
1244
1245         for ii in range(257):
1246             port_pkts.append((Ether(src=self.pg0.remote_mac,
1247                                     dst=self.pg0.local_mac) /
1248                               IP(dst="1.1.1.1", src="20.0.0.1") /
1249                               UDP(sport=1234, dport=1234 + ii) /
1250                               Raw(b'\xa5' * 100)))
1251             src_pkts.append((Ether(src=self.pg0.remote_mac,
1252                                    dst=self.pg0.local_mac) /
1253                              IP(dst="1.1.1.1", src="20.0.0.%d" % ii) /
1254                              UDP(sport=1234, dport=1234) /
1255                              Raw(b'\xa5' * 100)))
1256
1257         route_10_0_0_2 = VppIpRoute(self, "10.0.0.2", 32,
1258                                     [VppRoutePath(self.pg3.remote_ip4,
1259                                                   self.pg3.sw_if_index),
1260                                      VppRoutePath(self.pg4.remote_ip4,
1261                                                   self.pg4.sw_if_index)])
1262         route_10_0_0_2.add_vpp_config()
1263
1264         route_1_1_1_1 = VppIpRoute(self, "1.1.1.1", 32,
1265                                    [VppRoutePath("10.0.0.2", 0xffffffff),
1266                                     VppRoutePath("10.0.0.1", 0xffffffff)])
1267         route_1_1_1_1.add_vpp_config()
1268
1269         #
1270         # inject the packet on pg0 - expect load-balancing across all 4 paths
1271         #
1272         self.vapi.cli("clear trace")
1273         self.send_and_expect_load_balancing(self.pg0, port_pkts,
1274                                             [self.pg1, self.pg2,
1275                                              self.pg3, self.pg4])
1276         self.send_and_expect_load_balancing(self.pg0, src_pkts,
1277                                             [self.pg1, self.pg2,
1278                                              self.pg3, self.pg4])
1279
1280         #
1281         # bring down pg1 expect LB to adjust to use only those that are up
1282         #
1283         self.pg1.link_down()
1284
1285         rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
1286                                                  [self.pg2, self.pg3,
1287                                                   self.pg4])
1288         self.assertEqual(len(src_pkts), self.total_len(rx))
1289
1290         #
1291         # bring down pg2 expect LB to adjust to use only those that are up
1292         #
1293         self.pg2.link_down()
1294
1295         rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
1296                                                  [self.pg3, self.pg4])
1297         self.assertEqual(len(src_pkts), self.total_len(rx))
1298
1299         #
1300         # bring the links back up - expect LB over all again
1301         #
1302         self.pg1.link_up()
1303         self.pg2.link_up()
1304
1305         rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
1306                                                  [self.pg1, self.pg2,
1307                                                   self.pg3, self.pg4])
1308         self.assertEqual(len(src_pkts), self.total_len(rx))
1309
1310         #
1311         # The same link-up/down but this time admin state
1312         #
1313         self.pg1.admin_down()
1314         self.pg2.admin_down()
1315         rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
1316                                                  [self.pg3, self.pg4])
1317         self.assertEqual(len(src_pkts), self.total_len(rx))
1318         self.pg1.admin_up()
1319         self.pg2.admin_up()
1320         self.pg1.resolve_arp()
1321         self.pg2.resolve_arp()
1322         rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
1323                                                  [self.pg1, self.pg2,
1324                                                   self.pg3, self.pg4])
1325         self.assertEqual(len(src_pkts), self.total_len(rx))
1326
1327         #
1328         # Recursive prefixes
1329         #  - testing that 2 stages of load-balancing, no choices
1330         #
1331         port_pkts = []
1332
1333         for ii in range(257):
1334             port_pkts.append((Ether(src=self.pg0.remote_mac,
1335                                     dst=self.pg0.local_mac) /
1336                               IP(dst="1.1.1.2", src="20.0.0.2") /
1337                               UDP(sport=1234, dport=1234 + ii) /
1338                               Raw(b'\xa5' * 100)))
1339
1340         route_10_0_0_3 = VppIpRoute(self, "10.0.0.3", 32,
1341                                     [VppRoutePath(self.pg3.remote_ip4,
1342                                                   self.pg3.sw_if_index)])
1343         route_10_0_0_3.add_vpp_config()
1344
1345         route_1_1_1_2 = VppIpRoute(self, "1.1.1.2", 32,
1346                                    [VppRoutePath("10.0.0.3", 0xffffffff)])
1347         route_1_1_1_2.add_vpp_config()
1348
1349         #
1350         # inject the packet on pg0 - rx only on via routes output interface
1351         #
1352         self.vapi.cli("clear trace")
1353         self.send_and_expect_only(self.pg0, port_pkts, self.pg3)
1354
1355         #
1356         # Add a LB route in the presence of a down link - expect no
1357         # packets over the down link
1358         #
1359         self.pg3.link_down()
1360
1361         route_10_0_0_3 = VppIpRoute(self, "10.0.0.3", 32,
1362                                     [VppRoutePath(self.pg3.remote_ip4,
1363                                                   self.pg3.sw_if_index),
1364                                      VppRoutePath(self.pg4.remote_ip4,
1365                                                   self.pg4.sw_if_index)])
1366         route_10_0_0_3.add_vpp_config()
1367
1368         port_pkts = []
1369         for ii in range(257):
1370             port_pkts.append(Ether(src=self.pg0.remote_mac,
1371                                    dst=self.pg0.local_mac) /
1372                              IP(dst="10.0.0.3", src="20.0.0.2") /
1373                              UDP(sport=1234, dport=1234 + ii) /
1374                              Raw(b'\xa5' * 100))
1375
1376         self.send_and_expect_only(self.pg0, port_pkts, self.pg4)
1377
1378         # bring the link back up
1379         self.pg3.link_up()
1380
1381         rx = self.send_and_expect_load_balancing(self.pg0, port_pkts,
1382                                                  [self.pg3, self.pg4])
1383         self.assertEqual(len(src_pkts), self.total_len(rx))
1384
1385
1386 class TestIPVlan0(VppTestCase):
1387     """ IPv4 VLAN-0 """
1388
1389     @classmethod
1390     def setUpClass(cls):
1391         super(TestIPVlan0, cls).setUpClass()
1392
1393     @classmethod
1394     def tearDownClass(cls):
1395         super(TestIPVlan0, cls).tearDownClass()
1396
1397     def setUp(self):
1398         super(TestIPVlan0, self).setUp()
1399
1400         self.create_pg_interfaces(range(2))
1401         mpls_tbl = VppMplsTable(self, 0)
1402         mpls_tbl.add_vpp_config()
1403
1404         for i in self.pg_interfaces:
1405             i.admin_up()
1406             i.config_ip4()
1407             i.resolve_arp()
1408             i.enable_mpls()
1409
1410     def tearDown(self):
1411         for i in self.pg_interfaces:
1412             i.disable_mpls()
1413             i.unconfig_ip4()
1414             i.admin_down()
1415         super(TestIPVlan0, self).tearDown()
1416
1417     def test_ip_vlan_0(self):
1418         """ IP VLAN-0 """
1419
1420         pkts = (Ether(src=self.pg0.remote_mac,
1421                       dst=self.pg0.local_mac) /
1422                 Dot1Q(vlan=0) /
1423                 IP(dst=self.pg1.remote_ip4,
1424                    src=self.pg0.remote_ip4) /
1425                 UDP(sport=1234, dport=1234) /
1426                 Raw(b'\xa5' * 100)) * NUM_PKTS
1427
1428         #
1429         # Expect that packets sent on VLAN-0 are forwarded on the
1430         # main interface.
1431         #
1432         self.send_and_expect(self.pg0, pkts, self.pg1)
1433
1434
1435 class IPPuntSetup(object):
1436     """ Setup for IPv4 Punt Police/Redirect """
1437
1438     def punt_setup(self):
1439         self.create_pg_interfaces(range(4))
1440
1441         for i in self.pg_interfaces:
1442             i.admin_up()
1443             i.config_ip4()
1444             i.resolve_arp()
1445
1446         # use UDP packet that have a port we need to explicitly
1447         # register to get punted.
1448         pt_l4 = VppEnum.vl_api_punt_type_t.PUNT_API_TYPE_L4
1449         af_ip4 = VppEnum.vl_api_address_family_t.ADDRESS_IP4
1450         udp_proto = VppEnum.vl_api_ip_proto_t.IP_API_PROTO_UDP
1451         punt_udp = {
1452             'type': pt_l4,
1453             'punt': {
1454                 'l4': {
1455                     'af': af_ip4,
1456                     'protocol': udp_proto,
1457                     'port': 1234,
1458                 }
1459             }
1460         }
1461
1462         self.vapi.set_punt(is_add=1, punt=punt_udp)
1463
1464         af_ip6 = VppEnum.vl_api_address_family_t.ADDRESS_IP6
1465         punt_udp = {
1466             'type': pt_l4,
1467             'punt': {
1468                 'l4': {
1469                     'af': af_ip6,
1470                     'protocol': udp_proto,
1471                     'port': 1236,
1472                 }
1473             }
1474         }
1475
1476         self.vapi.set_punt(is_add=1, punt=punt_udp)
1477
1478         self.pkt = (Ether(src=self.pg0.remote_mac,
1479                           dst=self.pg0.local_mac) /
1480                     IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
1481                     UDP(sport=1234, dport=1234) /
1482                     Raw(b'\xa5' * 100))
1483
1484     def punt_teardown(self):
1485         for i in self.pg_interfaces:
1486             i.unconfig_ip4()
1487             i.admin_down()
1488
1489
1490 class TestIPPunt(IPPuntSetup, VppTestCase):
1491     """ IPv4 Punt Police/Redirect """
1492
1493     def setUp(self):
1494         super().setUp()
1495         super().punt_setup()
1496
1497     def tearDown(self):
1498         super().punt_teardown()
1499         super().tearDown()
1500
1501     def test_ip_punt_api_validation(self):
1502         """ IP punt API parameter validation """
1503
1504         nh_addr = self.pg1.remote_ip4
1505         punt = {"rx_sw_if_index": self.pg0.sw_if_index,
1506                 "af": VppEnum.vl_api_address_family_t.ADDRESS_IP4,
1507                 "n_paths": 1000000,
1508                 "paths": []}
1509
1510         with self.assertRaises(vpp_papi.VPPIOError):
1511             self.vapi.add_del_ip_punt_redirect_v2(punt=punt, is_add=True)
1512
1513         punt = {"rx_sw_if_index": self.pg0.sw_if_index,
1514                 "af": VppEnum.vl_api_address_family_t.ADDRESS_IP4,
1515                 "n_paths": 0,
1516                 "paths": []}
1517
1518         self.vapi.add_del_ip_punt_redirect_v2(punt=punt, is_add=True)
1519
1520     def test_ip_punt(self):
1521         """ IP punt police and redirect """
1522
1523         pkts = self.pkt * 1025
1524
1525         #
1526         # Configure a punt redirect via pg1.
1527         #
1528         nh_addr = self.pg1.remote_ip4
1529         ip_punt_redirect = VppIpPuntRedirect(self, self.pg0.sw_if_index,
1530                                              self.pg1.sw_if_index, nh_addr)
1531         ip_punt_redirect.add_vpp_config()
1532
1533         self.send_and_expect(self.pg0, pkts, self.pg1)
1534
1535         #
1536         # add a policer
1537         #
1538         policer = VppPolicer(self, "ip4-punt", 400, 0, 10, 0, rate_type=1)
1539         policer.add_vpp_config()
1540         ip_punt_policer = VppIpPuntPolicer(self, policer.policer_index)
1541         ip_punt_policer.add_vpp_config()
1542
1543         self.vapi.cli("clear trace")
1544         self.pg0.add_stream(pkts)
1545         self.pg_enable_capture(self.pg_interfaces)
1546         self.pg_start()
1547
1548         #
1549         # the number of packet received should be greater than 0,
1550         # but not equal to the number sent, since some were policed
1551         #
1552         rx = self.pg1._get_capture(1)
1553
1554         stats = policer.get_stats()
1555
1556         # Single rate policer - expect conform, violate but no exceed
1557         self.assertGreater(stats['conform_packets'], 0)
1558         self.assertEqual(stats['exceed_packets'], 0)
1559         self.assertGreater(stats['violate_packets'], 0)
1560
1561         self.assertGreater(len(rx), 0)
1562         self.assertLess(len(rx), len(pkts))
1563
1564         #
1565         # remove the policer. back to full rx
1566         #
1567         ip_punt_policer.remove_vpp_config()
1568         policer.remove_vpp_config()
1569         self.send_and_expect(self.pg0, pkts, self.pg1)
1570
1571         #
1572         # remove the redirect. expect full drop.
1573         #
1574         ip_punt_redirect.remove_vpp_config()
1575         self.send_and_assert_no_replies(self.pg0, pkts,
1576                                         "IP no punt config")
1577
1578         #
1579         # Add a redirect that is not input port selective
1580         #
1581         ip_punt_redirect = VppIpPuntRedirect(self, 0xffffffff,
1582                                              self.pg1.sw_if_index, nh_addr)
1583         ip_punt_redirect.add_vpp_config()
1584         self.send_and_expect(self.pg0, pkts, self.pg1)
1585         ip_punt_redirect.remove_vpp_config()
1586
1587     def test_ip_punt_vrf(self):
1588         """ IP punt/local with VRFs """
1589
1590         # use a punt redirect to test if for-us  packets are accepted
1591         pkts = self.pkt * 1025
1592
1593         vlans_pg0 = [VppDot1QSubint(self, self.pg0, v)
1594                      for v in range(100, 104)]
1595         vlans_pg1 = [VppDot1QSubint(self, self.pg1, v)
1596                      for v in range(100, 104)]
1597         tbl4 = [VppIpTable(self, v).add_vpp_config()
1598                 for v in range(100, 104)]
1599         tbl6 = [VppIpTable(self, v, True).add_vpp_config()
1600                 for v in range(100, 104)]
1601
1602         for v in vlans_pg0 + vlans_pg1:
1603             v.admin_up()
1604             v.set_table_ip4(v.vlan)
1605             v.set_table_ip6(v.vlan)
1606             v.config_ip4()
1607             v.config_ip6()
1608             v.resolve_arp()
1609             v.resolve_ndp()
1610
1611         [VppIpPuntRedirect
1612          (self,
1613           vlans_pg0[i].sw_if_index,
1614           vlans_pg1[i].sw_if_index,
1615           vlans_pg1[i].remote_ip4).add_vpp_config()
1616          for i in range(4)]
1617         [VppIpPuntRedirect
1618          (self,
1619           vlans_pg0[i].sw_if_index,
1620           vlans_pg1[i].sw_if_index,
1621           vlans_pg1[i].remote_ip6).add_vpp_config()
1622          for i in range(4)]
1623
1624         pkts = [(Ether(src=self.pg0.remote_mac,
1625                        dst=self.pg0.local_mac) /
1626                  Dot1Q(vlan=i.vlan) /
1627                  IP(src=i.remote_ip4,
1628                     dst=i.local_ip4) /
1629                  UDP(sport=1234, dport=1234) /
1630                  Raw(b'\xa5' * 100))
1631                 for i in vlans_pg0]
1632
1633         self.send_and_expect(self.pg0, pkts, self.pg1)
1634
1635         #
1636         # IPv4
1637         #
1638
1639         # we reject packets for source addresses in the wrong vlan/VRF
1640         pkts = [(Ether(src=self.pg0.remote_mac,
1641                        dst=self.pg0.local_mac) /
1642                  Dot1Q(vlan=i.vlan) /
1643                  IP(src="1.1.1.1",
1644                     dst=i.local_ip4) /
1645                  UDP(sport=1234, dport=1234) /
1646                  Raw(b'\xa5' * 100))
1647                 for i in vlans_pg0]
1648         # single and dual loop
1649         self.send_and_assert_no_replies(self.pg0, [pkts[0]])
1650         self.send_and_assert_no_replies(self.pg0, pkts)
1651
1652         self.assert_error_counter_equal(
1653             "/err/ip4-local/ip4 source lookup miss",
1654             len(pkts) + 1)
1655
1656         # using the same source in different tables, should reject
1657         # for the table that the source is not present in
1658         # the first packet in the stream is drop
1659         pkts = [(Ether(src=self.pg0.remote_mac,
1660                        dst=self.pg0.local_mac) /
1661                  Dot1Q(vlan=i.vlan) /
1662                  IP(src=vlans_pg0[0].remote_ip4,
1663                     dst=i.local_ip4) /
1664                  UDP(sport=1234, dport=1234) /
1665                  Raw(b'\xa5' * 100))
1666                 for i in vlans_pg0]
1667         # single loop accept and drop
1668         # followed by both in the same frame/loop
1669         self.send_and_expect(self.pg0, [pkts[0]], self.pg1)
1670         self.send_and_assert_no_replies(self.pg0, [pkts[1]])
1671         self.send_and_expect(self.pg0, pkts * 4, self.pg1, n_rx=4)
1672
1673         # using the same source in different tables, should reject
1674         # for the table that the source is not present in
1675         # the first packet in the stream is accept
1676         pkts = [(Ether(src=self.pg0.remote_mac,
1677                        dst=self.pg0.local_mac) /
1678                  Dot1Q(vlan=i.vlan) /
1679                  IP(src=vlans_pg0[3].remote_ip4,
1680                     dst=i.local_ip4) /
1681                  UDP(sport=1234, dport=1234) /
1682                  Raw(b'\xa5' * 100))
1683                 for i in vlans_pg0]
1684
1685         # single loop accept and drop
1686         # followed by both in the same frame/loop
1687         self.send_and_expect(self.pg0, [pkts[3]], self.pg1)
1688         self.send_and_assert_no_replies(self.pg0, [pkts[1]])
1689         self.send_and_expect(self.pg0, pkts * 4, self.pg1, n_rx=4)
1690
1691         #
1692         # IPv6
1693         #
1694
1695         # we reject packets for source addresses in the wrong vlan/VRF
1696         pkts = [(Ether(src=self.pg0.remote_mac,
1697                        dst=self.pg0.local_mac) /
1698                  Dot1Q(vlan=i.vlan) /
1699                  IPv6(src="1::1",
1700                       dst=i.local_ip6) /
1701                  UDP(sport=1236, dport=1236) /
1702                  Raw(b'\xa5' * 100))
1703                 for i in vlans_pg0]
1704         # single and dual loop
1705         self.send_and_assert_no_replies(self.pg0, [pkts[0]])
1706         self.send_and_assert_no_replies(self.pg0, pkts)
1707
1708         self.assert_error_counter_equal(
1709             "/err/ip6-input/ip6 source lookup miss",
1710             len(pkts) + 1)
1711
1712         # using the same source in different tables, should reject
1713         # for the table that the source is not present in
1714         # the first packet in the stream is drop
1715         pkts = [(Ether(src=self.pg0.remote_mac,
1716                        dst=self.pg0.local_mac) /
1717                  Dot1Q(vlan=i.vlan) /
1718                  IPv6(src=vlans_pg0[0].remote_ip6,
1719                       dst=i.local_ip6) /
1720                  UDP(sport=1236, dport=1236) /
1721                  Raw(b'\xa5' * 100))
1722                 for i in vlans_pg0]
1723         # single loop accept and drop
1724         # followed by both in the same frame/loop
1725         self.send_and_expect(self.pg0, [pkts[0]], self.pg1)
1726         self.send_and_assert_no_replies(self.pg0, [pkts[1]])
1727         self.send_and_expect(self.pg0, pkts * 4, self.pg1, n_rx=4)
1728
1729         # using the same source in different tables, should reject
1730         # for the table that the source is not present in
1731         # the first packet in the stream is accept
1732         pkts = [(Ether(src=self.pg0.remote_mac,
1733                        dst=self.pg0.local_mac) /
1734                  Dot1Q(vlan=i.vlan) /
1735                  IPv6(src=vlans_pg0[3].remote_ip6,
1736                       dst=i.local_ip6) /
1737                  UDP(sport=1236, dport=1236) /
1738                  Raw(b'\xa5' * 100))
1739                 for i in vlans_pg0]
1740
1741         # single loop accept and drop
1742         # followed by both in the same frame/loop
1743         self.send_and_expect(self.pg0, [pkts[3]], self.pg1)
1744         self.send_and_assert_no_replies(self.pg0, [pkts[1]])
1745         self.send_and_expect(self.pg0, pkts * 4, self.pg1, n_rx=4)
1746
1747         for v in vlans_pg0 + vlans_pg1:
1748             v.unconfig_ip4()
1749             v.unconfig_ip6()
1750             v.set_table_ip4(0)
1751             v.set_table_ip6(0)
1752
1753     def test_ip_punt_dump(self):
1754         """ IP4 punt redirect dump"""
1755
1756         #
1757         # Configure a punt redirects
1758         #
1759         nh_address = self.pg3.remote_ip4
1760         ipr_03 = VppIpPuntRedirect(self, self.pg0.sw_if_index,
1761                                    self.pg3.sw_if_index, nh_address)
1762         ipr_13 = VppIpPuntRedirect(self, self.pg1.sw_if_index,
1763                                    self.pg3.sw_if_index, nh_address)
1764         ipr_23 = VppIpPuntRedirect(self, self.pg2.sw_if_index,
1765                                    self.pg3.sw_if_index, "0.0.0.0")
1766         ipr_03.add_vpp_config()
1767         ipr_13.add_vpp_config()
1768         ipr_23.add_vpp_config()
1769
1770         #
1771         # Dump pg0 punt redirects
1772         #
1773         self.assertTrue(ipr_03.query_vpp_config())
1774         self.assertTrue(ipr_13.query_vpp_config())
1775         self.assertTrue(ipr_23.query_vpp_config())
1776
1777         #
1778         # Dump punt redirects for all interfaces
1779         #
1780         punts = self.vapi.ip_punt_redirect_dump(0xffffffff)
1781         self.assertEqual(len(punts), 3)
1782         for p in punts:
1783             self.assertEqual(p.punt.tx_sw_if_index, self.pg3.sw_if_index)
1784         self.assertNotEqual(punts[1].punt.nh, self.pg3.remote_ip4)
1785         self.assertEqual(str(punts[2].punt.nh), '0.0.0.0')
1786
1787
1788 class TestIPPuntHandoff(IPPuntSetup, VppTestCase):
1789     """ IPv4 Punt Policer thread handoff """
1790     vpp_worker_count = 2
1791
1792     def setUp(self):
1793         super(TestIPPuntHandoff, self).setUp()
1794         super(TestIPPuntHandoff, self).punt_setup()
1795
1796     def tearDown(self):
1797         super(TestIPPuntHandoff, self).punt_teardown()
1798         super(TestIPPuntHandoff, self).tearDown()
1799
1800     def test_ip_punt_policer_handoff(self):
1801         """ IP4 punt policer thread handoff """
1802         pkts = self.pkt * NUM_PKTS
1803
1804         #
1805         # Configure a punt redirect via pg1.
1806         #
1807         nh_addr = self.pg1.remote_ip4
1808         ip_punt_redirect = VppIpPuntRedirect(self, self.pg0.sw_if_index,
1809                                              self.pg1.sw_if_index, nh_addr)
1810         ip_punt_redirect.add_vpp_config()
1811
1812         action_tx = PolicerAction(
1813             VppEnum.vl_api_sse2_qos_action_type_t.SSE2_QOS_ACTION_API_TRANSMIT,
1814             0)
1815         #
1816         # This policer drops no packets, we are just
1817         # testing that they get to the right thread.
1818         #
1819         policer = VppPolicer(self, "ip4-punt", 400, 0, 10, 0, 1,
1820                              0, 0, False, action_tx, action_tx, action_tx)
1821         policer.add_vpp_config()
1822         ip_punt_policer = VppIpPuntPolicer(self, policer.policer_index)
1823         ip_punt_policer.add_vpp_config()
1824
1825         for worker in [0, 1]:
1826             self.send_and_expect(self.pg0, pkts, self.pg1, worker=worker)
1827             self.logger.debug(self.vapi.cli("show trace max 100"))
1828
1829         # Combined stats, all threads
1830         stats = policer.get_stats()
1831
1832         # Single rate policer - expect conform, violate but no exceed
1833         self.assertGreater(stats['conform_packets'], 0)
1834         self.assertEqual(stats['exceed_packets'], 0)
1835         self.assertGreater(stats['violate_packets'], 0)
1836
1837         # Worker 0, should have done all the policing
1838         stats0 = policer.get_stats(worker=0)
1839         self.assertEqual(stats, stats0)
1840
1841         # Worker 1, should have handed everything off
1842         stats1 = policer.get_stats(worker=1)
1843         self.assertEqual(stats1['conform_packets'], 0)
1844         self.assertEqual(stats1['exceed_packets'], 0)
1845         self.assertEqual(stats1['violate_packets'], 0)
1846
1847         # Bind the policer to worker 1 and repeat
1848         policer.bind_vpp_config(1, True)
1849         for worker in [0, 1]:
1850             self.send_and_expect(self.pg0, pkts, self.pg1, worker=worker)
1851             self.logger.debug(self.vapi.cli("show trace max 100"))
1852
1853         # The 2 workers should now have policed the same amount
1854         stats = policer.get_stats()
1855         stats0 = policer.get_stats(worker=0)
1856         stats1 = policer.get_stats(worker=1)
1857
1858         self.assertGreater(stats0['conform_packets'], 0)
1859         self.assertEqual(stats0['exceed_packets'], 0)
1860         self.assertGreater(stats0['violate_packets'], 0)
1861
1862         self.assertGreater(stats1['conform_packets'], 0)
1863         self.assertEqual(stats1['exceed_packets'], 0)
1864         self.assertGreater(stats1['violate_packets'], 0)
1865
1866         self.assertEqual(stats0['conform_packets'] + stats1['conform_packets'],
1867                          stats['conform_packets'])
1868
1869         self.assertEqual(stats0['violate_packets'] + stats1['violate_packets'],
1870                          stats['violate_packets'])
1871
1872         # Unbind the policer and repeat
1873         policer.bind_vpp_config(1, False)
1874         for worker in [0, 1]:
1875             self.send_and_expect(self.pg0, pkts, self.pg1, worker=worker)
1876             self.logger.debug(self.vapi.cli("show trace max 100"))
1877
1878         # The policer should auto-bind to worker 0 when packets arrive
1879         stats = policer.get_stats()
1880         stats0new = policer.get_stats(worker=0)
1881         stats1new = policer.get_stats(worker=1)
1882
1883         self.assertGreater(stats0new['conform_packets'],
1884                            stats0['conform_packets'])
1885         self.assertEqual(stats0new['exceed_packets'], 0)
1886         self.assertGreater(stats0new['violate_packets'],
1887                            stats0['violate_packets'])
1888
1889         self.assertEqual(stats1, stats1new)
1890
1891         #
1892         # Clean up
1893         #
1894         ip_punt_policer.remove_vpp_config()
1895         policer.remove_vpp_config()
1896         ip_punt_redirect.remove_vpp_config()
1897
1898
1899 class TestIPDeag(VppTestCase):
1900     """ IPv4 Deaggregate Routes """
1901
1902     @classmethod
1903     def setUpClass(cls):
1904         super(TestIPDeag, cls).setUpClass()
1905
1906     @classmethod
1907     def tearDownClass(cls):
1908         super(TestIPDeag, cls).tearDownClass()
1909
1910     def setUp(self):
1911         super(TestIPDeag, self).setUp()
1912
1913         self.create_pg_interfaces(range(3))
1914
1915         for i in self.pg_interfaces:
1916             i.admin_up()
1917             i.config_ip4()
1918             i.resolve_arp()
1919
1920     def tearDown(self):
1921         super(TestIPDeag, self).tearDown()
1922         for i in self.pg_interfaces:
1923             i.unconfig_ip4()
1924             i.admin_down()
1925
1926     def test_ip_deag(self):
1927         """ IP Deag Routes """
1928
1929         #
1930         # Create a table to be used for:
1931         #  1 - another destination address lookup
1932         #  2 - a source address lookup
1933         #
1934         table_dst = VppIpTable(self, 1)
1935         table_src = VppIpTable(self, 2)
1936         table_dst.add_vpp_config()
1937         table_src.add_vpp_config()
1938
1939         #
1940         # Add a route in the default table to point to a deag/
1941         # second lookup in each of these tables
1942         #
1943         route_to_dst = VppIpRoute(self, "1.1.1.1", 32,
1944                                   [VppRoutePath("0.0.0.0",
1945                                                 0xffffffff,
1946                                                 nh_table_id=1)])
1947         route_to_src = VppIpRoute(
1948             self, "1.1.1.2", 32,
1949             [VppRoutePath("0.0.0.0",
1950                           0xffffffff,
1951                           nh_table_id=2,
1952                           type=FibPathType.FIB_PATH_TYPE_SOURCE_LOOKUP)])
1953         route_to_dst.add_vpp_config()
1954         route_to_src.add_vpp_config()
1955
1956         #
1957         # packets to these destination are dropped, since they'll
1958         # hit the respective default routes in the second table
1959         #
1960         p_dst = (Ether(src=self.pg0.remote_mac,
1961                        dst=self.pg0.local_mac) /
1962                  IP(src="5.5.5.5", dst="1.1.1.1") /
1963                  TCP(sport=1234, dport=1234) /
1964                  Raw(b'\xa5' * 100))
1965         p_src = (Ether(src=self.pg0.remote_mac,
1966                        dst=self.pg0.local_mac) /
1967                  IP(src="2.2.2.2", dst="1.1.1.2") /
1968                  TCP(sport=1234, dport=1234) /
1969                  Raw(b'\xa5' * 100))
1970         pkts_dst = p_dst * 257
1971         pkts_src = p_src * 257
1972
1973         self.send_and_assert_no_replies(self.pg0, pkts_dst,
1974                                         "IP in dst table")
1975         self.send_and_assert_no_replies(self.pg0, pkts_src,
1976                                         "IP in src table")
1977
1978         #
1979         # add a route in the dst table to forward via pg1
1980         #
1981         route_in_dst = VppIpRoute(self, "1.1.1.1", 32,
1982                                   [VppRoutePath(self.pg1.remote_ip4,
1983                                                 self.pg1.sw_if_index)],
1984                                   table_id=1)
1985         route_in_dst.add_vpp_config()
1986
1987         self.send_and_expect(self.pg0, pkts_dst, self.pg1)
1988
1989         #
1990         # add a route in the src table to forward via pg2
1991         #
1992         route_in_src = VppIpRoute(self, "2.2.2.2", 32,
1993                                   [VppRoutePath(self.pg2.remote_ip4,
1994                                                 self.pg2.sw_if_index)],
1995                                   table_id=2)
1996         route_in_src.add_vpp_config()
1997         self.send_and_expect(self.pg0, pkts_src, self.pg2)
1998
1999         #
2000         # loop in the lookup DP
2001         #
2002         route_loop = VppIpRoute(self, "2.2.2.3", 32,
2003                                 [VppRoutePath("0.0.0.0",
2004                                               0xffffffff,
2005                                               nh_table_id=0)])
2006         route_loop.add_vpp_config()
2007
2008         p_l = (Ether(src=self.pg0.remote_mac,
2009                      dst=self.pg0.local_mac) /
2010                IP(src="2.2.2.4", dst="2.2.2.3") /
2011                TCP(sport=1234, dport=1234) /
2012                Raw(b'\xa5' * 100))
2013
2014         self.send_and_assert_no_replies(self.pg0, p_l * 257,
2015                                         "IP lookup loop")
2016
2017
2018 class TestIPInput(VppTestCase):
2019     """ IPv4 Input Exceptions """
2020
2021     @classmethod
2022     def setUpClass(cls):
2023         super(TestIPInput, cls).setUpClass()
2024
2025     @classmethod
2026     def tearDownClass(cls):
2027         super(TestIPInput, cls).tearDownClass()
2028
2029     def setUp(self):
2030         super(TestIPInput, self).setUp()
2031
2032         self.create_pg_interfaces(range(2))
2033
2034         for i in self.pg_interfaces:
2035             i.admin_up()
2036             i.config_ip4()
2037             i.resolve_arp()
2038
2039     def tearDown(self):
2040         super(TestIPInput, self).tearDown()
2041         for i in self.pg_interfaces:
2042             i.unconfig_ip4()
2043             i.admin_down()
2044
2045     def test_ip_input(self):
2046         """ IP Input Exceptions """
2047
2048         # i can't find a way in scapy to construct an IP packet
2049         # with a length less than the IP header length
2050
2051         #
2052         # Packet too short - this is forwarded
2053         #
2054         p_short = (Ether(src=self.pg0.remote_mac,
2055                          dst=self.pg0.local_mac) /
2056                    IP(src=self.pg0.remote_ip4,
2057                       dst=self.pg1.remote_ip4,
2058                       len=40) /
2059                    UDP(sport=1234, dport=1234) /
2060                    Raw(b'\xa5' * 100))
2061
2062         rx = self.send_and_expect(self.pg0, p_short * NUM_PKTS, self.pg1)
2063
2064         #
2065         # Packet too long - this is dropped
2066         #
2067         p_long = (Ether(src=self.pg0.remote_mac,
2068                         dst=self.pg0.local_mac) /
2069                   IP(src=self.pg0.remote_ip4,
2070                      dst=self.pg1.remote_ip4,
2071                      len=400) /
2072                   UDP(sport=1234, dport=1234) /
2073                   Raw(b'\xa5' * 100))
2074
2075         rx = self.send_and_assert_no_replies(self.pg0, p_long * NUM_PKTS,
2076                                              "too long")
2077
2078         #
2079         # bad chksum - this is dropped
2080         #
2081         p_chksum = (Ether(src=self.pg0.remote_mac,
2082                           dst=self.pg0.local_mac) /
2083                     IP(src=self.pg0.remote_ip4,
2084                        dst=self.pg1.remote_ip4,
2085                        chksum=400) /
2086                     UDP(sport=1234, dport=1234) /
2087                     Raw(b'\xa5' * 100))
2088
2089         rx = self.send_and_assert_no_replies(self.pg0, p_chksum * NUM_PKTS,
2090                                              "bad checksum")
2091
2092         #
2093         # bad version - this is dropped
2094         #
2095         p_ver = (Ether(src=self.pg0.remote_mac,
2096                        dst=self.pg0.local_mac) /
2097                  IP(src=self.pg0.remote_ip4,
2098                     dst=self.pg1.remote_ip4,
2099                     version=3) /
2100                  UDP(sport=1234, dport=1234) /
2101                  Raw(b'\xa5' * 100))
2102
2103         rx = self.send_and_assert_no_replies(self.pg0, p_ver * NUM_PKTS,
2104                                              "funky version")
2105
2106         #
2107         # fragment offset 1 - this is dropped
2108         #
2109         p_frag = (Ether(src=self.pg0.remote_mac,
2110                         dst=self.pg0.local_mac) /
2111                   IP(src=self.pg0.remote_ip4,
2112                      dst=self.pg1.remote_ip4,
2113                      frag=1) /
2114                   UDP(sport=1234, dport=1234) /
2115                   Raw(b'\xa5' * 100))
2116
2117         rx = self.send_and_assert_no_replies(self.pg0, p_frag * NUM_PKTS,
2118                                              "frag offset")
2119
2120         #
2121         # TTL expired packet
2122         #
2123         p_ttl = (Ether(src=self.pg0.remote_mac,
2124                        dst=self.pg0.local_mac) /
2125                  IP(src=self.pg0.remote_ip4,
2126                     dst=self.pg1.remote_ip4,
2127                     ttl=1) /
2128                  UDP(sport=1234, dport=1234) /
2129                  Raw(b'\xa5' * 100))
2130
2131         rxs = self.send_and_expect_some(self.pg0, p_ttl * NUM_PKTS, self.pg0)
2132
2133         for rx in rxs:
2134             icmp = rx[ICMP]
2135             self.assertEqual(icmptypes[icmp.type], "time-exceeded")
2136             self.assertEqual(icmpcodes[icmp.type][icmp.code],
2137                              "ttl-zero-during-transit")
2138             self.assertEqual(icmp.src, self.pg0.remote_ip4)
2139             self.assertEqual(icmp.dst, self.pg1.remote_ip4)
2140
2141         #
2142         # MTU exceeded
2143         #
2144         p_mtu = (Ether(src=self.pg0.remote_mac,
2145                        dst=self.pg0.local_mac) /
2146                  IP(src=self.pg0.remote_ip4,
2147                     dst=self.pg1.remote_ip4,
2148                     ttl=10, flags='DF') /
2149                  UDP(sport=1234, dport=1234) /
2150                  Raw(b'\xa5' * 2000))
2151
2152         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [1500, 0, 0, 0])
2153
2154         rxs = self.send_and_expect_some(self.pg0, p_mtu * NUM_PKTS, self.pg0)
2155
2156         for rx in rxs:
2157             icmp = rx[ICMP]
2158             self.assertEqual(icmptypes[icmp.type], "dest-unreach")
2159             self.assertEqual(icmpcodes[icmp.type][icmp.code],
2160                              "fragmentation-needed")
2161             self.assertEqual(icmp.nexthopmtu, 1500)
2162             self.assertEqual(icmp.src, self.pg0.remote_ip4)
2163             self.assertEqual(icmp.dst, self.pg1.remote_ip4)
2164
2165         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [2500, 0, 0, 0])
2166         rx = self.send_and_expect(self.pg0, p_mtu * NUM_PKTS, self.pg1)
2167
2168         # Reset MTU for subsequent tests
2169         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [9000, 0, 0, 0])
2170
2171         #
2172         # source address 0.0.0.0 and 25.255.255.255 and for-us
2173         #
2174         p_s0 = (Ether(src=self.pg0.remote_mac,
2175                       dst=self.pg0.local_mac) /
2176                 IP(src="0.0.0.0",
2177                    dst=self.pg0.local_ip4) /
2178                 ICMP(id=4, seq=4) /
2179                 Raw(load=b'\x0a' * 18))
2180         rx = self.send_and_assert_no_replies(self.pg0, p_s0 * 17)
2181
2182         p_s0 = (Ether(src=self.pg0.remote_mac,
2183                       dst=self.pg0.local_mac) /
2184                 IP(src="255.255.255.255",
2185                    dst=self.pg0.local_ip4) /
2186                 ICMP(id=4, seq=4) /
2187                 Raw(load=b'\x0a' * 18))
2188         rx = self.send_and_assert_no_replies(self.pg0, p_s0 * 17)
2189
2190
2191 class TestIPDirectedBroadcast(VppTestCase):
2192     """ IPv4 Directed Broadcast """
2193
2194     @classmethod
2195     def setUpClass(cls):
2196         super(TestIPDirectedBroadcast, cls).setUpClass()
2197
2198     @classmethod
2199     def tearDownClass(cls):
2200         super(TestIPDirectedBroadcast, cls).tearDownClass()
2201
2202     def setUp(self):
2203         super(TestIPDirectedBroadcast, self).setUp()
2204
2205         self.create_pg_interfaces(range(2))
2206
2207         for i in self.pg_interfaces:
2208             i.admin_up()
2209
2210     def tearDown(self):
2211         super(TestIPDirectedBroadcast, self).tearDown()
2212         for i in self.pg_interfaces:
2213             i.admin_down()
2214
2215     def test_ip_input(self):
2216         """ IP Directed Broadcast """
2217
2218         #
2219         # set the directed broadcast on pg0 first, then config IP4 addresses
2220         # for pg1 directed broadcast is always disabled
2221         self.vapi.sw_interface_set_ip_directed_broadcast(
2222             self.pg0.sw_if_index, 1)
2223
2224         p0 = (Ether(src=self.pg1.remote_mac,
2225                     dst=self.pg1.local_mac) /
2226               IP(src="1.1.1.1",
2227                  dst=self.pg0._local_ip4_bcast) /
2228               UDP(sport=1234, dport=1234) /
2229               Raw(b'\xa5' * 2000))
2230         p1 = (Ether(src=self.pg0.remote_mac,
2231                     dst=self.pg0.local_mac) /
2232               IP(src="1.1.1.1",
2233                  dst=self.pg1._local_ip4_bcast) /
2234               UDP(sport=1234, dport=1234) /
2235               Raw(b'\xa5' * 2000))
2236
2237         self.pg0.config_ip4()
2238         self.pg0.resolve_arp()
2239         self.pg1.config_ip4()
2240         self.pg1.resolve_arp()
2241
2242         #
2243         # test packet is L2 broadcast
2244         #
2245         rx = self.send_and_expect(self.pg1, p0 * NUM_PKTS, self.pg0)
2246         self.assertTrue(rx[0][Ether].dst, "ff:ff:ff:ff:ff:ff")
2247
2248         self.send_and_assert_no_replies(self.pg0, p1 * NUM_PKTS,
2249                                         "directed broadcast disabled")
2250
2251         #
2252         # toggle directed broadcast on pg0
2253         #
2254         self.vapi.sw_interface_set_ip_directed_broadcast(
2255             self.pg0.sw_if_index, 0)
2256         self.send_and_assert_no_replies(self.pg1, p0 * NUM_PKTS,
2257                                         "directed broadcast disabled")
2258
2259         self.vapi.sw_interface_set_ip_directed_broadcast(
2260             self.pg0.sw_if_index, 1)
2261         rx = self.send_and_expect(self.pg1, p0 * NUM_PKTS, self.pg0)
2262
2263         self.pg0.unconfig_ip4()
2264         self.pg1.unconfig_ip4()
2265
2266
2267 class TestIPLPM(VppTestCase):
2268     """ IPv4 longest Prefix Match """
2269
2270     @classmethod
2271     def setUpClass(cls):
2272         super(TestIPLPM, cls).setUpClass()
2273
2274     @classmethod
2275     def tearDownClass(cls):
2276         super(TestIPLPM, cls).tearDownClass()
2277
2278     def setUp(self):
2279         super(TestIPLPM, self).setUp()
2280
2281         self.create_pg_interfaces(range(4))
2282
2283         for i in self.pg_interfaces:
2284             i.admin_up()
2285             i.config_ip4()
2286             i.resolve_arp()
2287
2288     def tearDown(self):
2289         super(TestIPLPM, self).tearDown()
2290         for i in self.pg_interfaces:
2291             i.admin_down()
2292             i.unconfig_ip4()
2293
2294     def test_ip_lpm(self):
2295         """ IP longest Prefix Match """
2296
2297         s_24 = VppIpRoute(self, "10.1.2.0", 24,
2298                           [VppRoutePath(self.pg1.remote_ip4,
2299                                         self.pg1.sw_if_index)])
2300         s_24.add_vpp_config()
2301         s_8 = VppIpRoute(self, "10.0.0.0", 8,
2302                          [VppRoutePath(self.pg2.remote_ip4,
2303                                        self.pg2.sw_if_index)])
2304         s_8.add_vpp_config()
2305
2306         p_8 = (Ether(src=self.pg0.remote_mac,
2307                      dst=self.pg0.local_mac) /
2308                IP(src="1.1.1.1",
2309                   dst="10.1.1.1") /
2310                UDP(sport=1234, dport=1234) /
2311                Raw(b'\xa5' * 2000))
2312         p_24 = (Ether(src=self.pg0.remote_mac,
2313                       dst=self.pg0.local_mac) /
2314                 IP(src="1.1.1.1",
2315                    dst="10.1.2.1") /
2316                 UDP(sport=1234, dport=1234) /
2317                 Raw(b'\xa5' * 2000))
2318
2319         self.logger.info(self.vapi.cli("sh ip fib mtrie"))
2320         rx = self.send_and_expect(self.pg0, p_8 * NUM_PKTS, self.pg2)
2321         rx = self.send_and_expect(self.pg0, p_24 * NUM_PKTS, self.pg1)
2322
2323
2324 @tag_fixme_vpp_workers
2325 class TestIPv4Frag(VppTestCase):
2326     """ IPv4 fragmentation """
2327
2328     @classmethod
2329     def setUpClass(cls):
2330         super(TestIPv4Frag, cls).setUpClass()
2331
2332         cls.create_pg_interfaces([0, 1])
2333         cls.src_if = cls.pg0
2334         cls.dst_if = cls.pg1
2335
2336         # setup all interfaces
2337         for i in cls.pg_interfaces:
2338             i.admin_up()
2339             i.config_ip4()
2340             i.resolve_arp()
2341
2342     @classmethod
2343     def tearDownClass(cls):
2344         super(TestIPv4Frag, cls).tearDownClass()
2345
2346     def test_frag_large_packets(self):
2347         """ Fragmentation of large packets """
2348
2349         self.vapi.cli("adjacency counters enable")
2350
2351         p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
2352              IP(src=self.src_if.remote_ip4, dst=self.dst_if.remote_ip4) /
2353              UDP(sport=1234, dport=5678) / Raw())
2354         self.extend_packet(p, 6000, "abcde")
2355         saved_payload = p[Raw].load
2356
2357         nbr = VppNeighbor(self,
2358                           self.dst_if.sw_if_index,
2359                           self.dst_if.remote_mac,
2360                           self.dst_if.remote_ip4).add_vpp_config()
2361
2362         # Force fragmentation by setting MTU of output interface
2363         # lower than packet size
2364         self.vapi.sw_interface_set_mtu(self.dst_if.sw_if_index,
2365                                        [5000, 0, 0, 0])
2366
2367         self.pg_enable_capture()
2368         self.src_if.add_stream(p)
2369         self.pg_start()
2370
2371         # Expecting 3 fragments because size of created fragments currently
2372         # cannot be larger then VPP buffer size (which is 2048)
2373         packets = self.dst_if.get_capture(3)
2374
2375         # we should show 3 packets thru the neighbor
2376         self.assertEqual(3, nbr.get_stats()['packets'])
2377
2378         # Assume VPP sends the fragments in order
2379         payload = b''
2380         for p in packets:
2381             payload_offset = p.frag * 8
2382             if payload_offset > 0:
2383                 payload_offset -= 8  # UDP header is not in payload
2384             self.assert_equal(payload_offset, len(payload))
2385             payload += p[Raw].load
2386         self.assert_equal(payload, saved_payload, "payload")
2387
2388
2389 class TestIPReplace(VppTestCase):
2390     """ IPv4 Table Replace """
2391
2392     @classmethod
2393     def setUpClass(cls):
2394         super(TestIPReplace, cls).setUpClass()
2395
2396     @classmethod
2397     def tearDownClass(cls):
2398         super(TestIPReplace, cls).tearDownClass()
2399
2400     def setUp(self):
2401         super(TestIPReplace, self).setUp()
2402
2403         self.create_pg_interfaces(range(4))
2404
2405         table_id = 1
2406         self.tables = []
2407
2408         for i in self.pg_interfaces:
2409             i.admin_up()
2410             i.config_ip4()
2411             i.resolve_arp()
2412             i.generate_remote_hosts(2)
2413             self.tables.append(VppIpTable(self, table_id).add_vpp_config())
2414             table_id += 1
2415
2416     def tearDown(self):
2417         super(TestIPReplace, self).tearDown()
2418         for i in self.pg_interfaces:
2419             i.admin_down()
2420             i.unconfig_ip4()
2421
2422     def test_replace(self):
2423         """ IP Table Replace """
2424
2425         MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
2426         MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
2427         N_ROUTES = 20
2428         links = [self.pg0, self.pg1, self.pg2, self.pg3]
2429         routes = [[], [], [], []]
2430
2431         # load up the tables with some routes
2432         for ii, t in enumerate(self.tables):
2433             for jj in range(N_ROUTES):
2434                 uni = VppIpRoute(
2435                     self, "10.0.0.%d" % jj, 32,
2436                     [VppRoutePath(links[ii].remote_hosts[0].ip4,
2437                                   links[ii].sw_if_index),
2438                      VppRoutePath(links[ii].remote_hosts[1].ip4,
2439                                   links[ii].sw_if_index)],
2440                     table_id=t.table_id).add_vpp_config()
2441                 multi = VppIpMRoute(
2442                     self, "0.0.0.0",
2443                     "239.0.0.%d" % jj, 32,
2444                     MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
2445                     [VppMRoutePath(self.pg0.sw_if_index,
2446                                    MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT),
2447                      VppMRoutePath(self.pg1.sw_if_index,
2448                                    MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
2449                      VppMRoutePath(self.pg2.sw_if_index,
2450                                    MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
2451                      VppMRoutePath(self.pg3.sw_if_index,
2452                                    MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD)],
2453                     table_id=t.table_id).add_vpp_config()
2454                 routes[ii].append({'uni': uni,
2455                                    'multi': multi})
2456
2457         #
2458         # replace the tables a few times
2459         #
2460         for kk in range(3):
2461             # replace_begin each table
2462             for t in self.tables:
2463                 t.replace_begin()
2464
2465             # all the routes are still there
2466             for ii, t in enumerate(self.tables):
2467                 dump = t.dump()
2468                 mdump = t.mdump()
2469                 for r in routes[ii]:
2470                     self.assertTrue(find_route_in_dump(dump, r['uni'], t))
2471                     self.assertTrue(find_mroute_in_dump(mdump, r['multi'], t))
2472
2473             # redownload the even numbered routes
2474             for ii, t in enumerate(self.tables):
2475                 for jj in range(0, N_ROUTES, 2):
2476                     routes[ii][jj]['uni'].add_vpp_config()
2477                     routes[ii][jj]['multi'].add_vpp_config()
2478
2479             # signal each table replace_end
2480             for t in self.tables:
2481                 t.replace_end()
2482
2483             # we should find the even routes, but not the odd
2484             for ii, t in enumerate(self.tables):
2485                 dump = t.dump()
2486                 mdump = t.mdump()
2487                 for jj in range(0, N_ROUTES, 2):
2488                     self.assertTrue(find_route_in_dump(
2489                         dump, routes[ii][jj]['uni'], t))
2490                     self.assertTrue(find_mroute_in_dump(
2491                         mdump, routes[ii][jj]['multi'], t))
2492                 for jj in range(1, N_ROUTES - 1, 2):
2493                     self.assertFalse(find_route_in_dump(
2494                         dump, routes[ii][jj]['uni'], t))
2495                     self.assertFalse(find_mroute_in_dump(
2496                         mdump, routes[ii][jj]['multi'], t))
2497
2498             # reload all the routes
2499             for ii, t in enumerate(self.tables):
2500                 for r in routes[ii]:
2501                     r['uni'].add_vpp_config()
2502                     r['multi'].add_vpp_config()
2503
2504             # all the routes are still there
2505             for ii, t in enumerate(self.tables):
2506                 dump = t.dump()
2507                 mdump = t.mdump()
2508                 for r in routes[ii]:
2509                     self.assertTrue(find_route_in_dump(dump, r['uni'], t))
2510                     self.assertTrue(find_mroute_in_dump(mdump, r['multi'], t))
2511
2512         #
2513         # finally flush the tables for good measure
2514         #
2515         for t in self.tables:
2516             t.flush()
2517             self.assertEqual(len(t.dump()), 5)
2518             self.assertEqual(len(t.mdump()), 3)
2519
2520
2521 class TestIPCover(VppTestCase):
2522     """ IPv4 Table Cover """
2523
2524     @classmethod
2525     def setUpClass(cls):
2526         super(TestIPCover, cls).setUpClass()
2527
2528     @classmethod
2529     def tearDownClass(cls):
2530         super(TestIPCover, cls).tearDownClass()
2531
2532     def setUp(self):
2533         super(TestIPCover, self).setUp()
2534
2535         self.create_pg_interfaces(range(4))
2536
2537         table_id = 1
2538         self.tables = []
2539
2540         for i in self.pg_interfaces:
2541             i.admin_up()
2542             i.config_ip4()
2543             i.resolve_arp()
2544             i.generate_remote_hosts(2)
2545             self.tables.append(VppIpTable(self, table_id).add_vpp_config())
2546             table_id += 1
2547
2548     def tearDown(self):
2549         super(TestIPCover, self).tearDown()
2550         for i in self.pg_interfaces:
2551             i.admin_down()
2552             i.unconfig_ip4()
2553
2554     def test_cover(self):
2555         """ IP Table Cover """
2556
2557         # add a loop back with a /32 prefix
2558         lo = VppLoInterface(self)
2559         lo.admin_up()
2560         a = VppIpInterfaceAddress(self, lo, "127.0.0.1", 32).add_vpp_config()
2561
2562         # add a neighbour that matches the loopback's /32
2563         nbr = VppNeighbor(self,
2564                           lo.sw_if_index,
2565                           lo.remote_mac,
2566                           "127.0.0.1").add_vpp_config()
2567
2568         # add the default route which will be the cover for /32
2569         r = VppIpRoute(self, "0.0.0.0", 0,
2570                        [VppRoutePath("127.0.0.1",
2571                                      lo.sw_if_index)],
2572                        register=False).add_vpp_config()
2573
2574         # add/remove/add a longer mask cover
2575         r8 = VppIpRoute(self, "127.0.0.0", 8,
2576                         [VppRoutePath("127.0.0.1",
2577                                       lo.sw_if_index)]).add_vpp_config()
2578         r8.remove_vpp_config()
2579         r8.add_vpp_config()
2580         r8.remove_vpp_config()
2581
2582         # remove the default route
2583         r.remove_vpp_config()
2584
2585         # remove the interface prefix
2586         a.remove_vpp_config()
2587
2588
2589 class TestIP4Replace(VppTestCase):
2590     """ IPv4 Interface Address Replace """
2591
2592     @classmethod
2593     def setUpClass(cls):
2594         super(TestIP4Replace, cls).setUpClass()
2595
2596     @classmethod
2597     def tearDownClass(cls):
2598         super(TestIP4Replace, cls).tearDownClass()
2599
2600     def setUp(self):
2601         super(TestIP4Replace, self).setUp()
2602
2603         self.create_pg_interfaces(range(4))
2604
2605         for i in self.pg_interfaces:
2606             i.admin_up()
2607
2608     def tearDown(self):
2609         super(TestIP4Replace, self).tearDown()
2610         for i in self.pg_interfaces:
2611             i.admin_down()
2612
2613     def get_n_pfxs(self, intf):
2614         return len(self.vapi.ip_address_dump(intf.sw_if_index))
2615
2616     def test_replace(self):
2617         """ IP interface address replace """
2618
2619         intf_pfxs = [[], [], [], []]
2620
2621         # add prefixes to each of the interfaces
2622         for i in range(len(self.pg_interfaces)):
2623             intf = self.pg_interfaces[i]
2624
2625             # 172.16.x.1/24
2626             addr = "172.16.%d.1" % intf.sw_if_index
2627             a = VppIpInterfaceAddress(self, intf, addr, 24).add_vpp_config()
2628             intf_pfxs[i].append(a)
2629
2630             # 172.16.x.2/24 - a different address in the same subnet as above
2631             addr = "172.16.%d.2" % intf.sw_if_index
2632             a = VppIpInterfaceAddress(self, intf, addr, 24).add_vpp_config()
2633             intf_pfxs[i].append(a)
2634
2635             # 172.15.x.2/24 - a different address and subnet
2636             addr = "172.15.%d.2" % intf.sw_if_index
2637             a = VppIpInterfaceAddress(self, intf, addr, 24).add_vpp_config()
2638             intf_pfxs[i].append(a)
2639
2640         # a dump should n_address in it
2641         for intf in self.pg_interfaces:
2642             self.assertEqual(self.get_n_pfxs(intf), 3)
2643
2644         #
2645         # remove all the address thru a replace
2646         #
2647         self.vapi.sw_interface_address_replace_begin()
2648         self.vapi.sw_interface_address_replace_end()
2649         for intf in self.pg_interfaces:
2650             self.assertEqual(self.get_n_pfxs(intf), 0)
2651
2652         #
2653         # add all the interface addresses back
2654         #
2655         for p in intf_pfxs:
2656             for v in p:
2657                 v.add_vpp_config()
2658         for intf in self.pg_interfaces:
2659             self.assertEqual(self.get_n_pfxs(intf), 3)
2660
2661         #
2662         # replace again, but this time update/re-add the address on the first
2663         # two interfaces
2664         #
2665         self.vapi.sw_interface_address_replace_begin()
2666
2667         for p in intf_pfxs[:2]:
2668             for v in p:
2669                 v.add_vpp_config()
2670
2671         self.vapi.sw_interface_address_replace_end()
2672
2673         # on the first two the address still exist,
2674         # on the other two they do not
2675         for intf in self.pg_interfaces[:2]:
2676             self.assertEqual(self.get_n_pfxs(intf), 3)
2677         for p in intf_pfxs[:2]:
2678             for v in p:
2679                 self.assertTrue(v.query_vpp_config())
2680         for intf in self.pg_interfaces[2:]:
2681             self.assertEqual(self.get_n_pfxs(intf), 0)
2682
2683         #
2684         # add all the interface addresses back on the last two
2685         #
2686         for p in intf_pfxs[2:]:
2687             for v in p:
2688                 v.add_vpp_config()
2689         for intf in self.pg_interfaces:
2690             self.assertEqual(self.get_n_pfxs(intf), 3)
2691
2692         #
2693         # replace again, this time add different prefixes on all the interfaces
2694         #
2695         self.vapi.sw_interface_address_replace_begin()
2696
2697         pfxs = []
2698         for intf in self.pg_interfaces:
2699             # 172.18.x.1/24
2700             addr = "172.18.%d.1" % intf.sw_if_index
2701             pfxs.append(VppIpInterfaceAddress(self, intf, addr,
2702                                               24).add_vpp_config())
2703
2704         self.vapi.sw_interface_address_replace_end()
2705
2706         # only .18 should exist on each interface
2707         for intf in self.pg_interfaces:
2708             self.assertEqual(self.get_n_pfxs(intf), 1)
2709         for pfx in pfxs:
2710             self.assertTrue(pfx.query_vpp_config())
2711
2712         #
2713         # remove everything
2714         #
2715         self.vapi.sw_interface_address_replace_begin()
2716         self.vapi.sw_interface_address_replace_end()
2717         for intf in self.pg_interfaces:
2718             self.assertEqual(self.get_n_pfxs(intf), 0)
2719
2720         #
2721         # add prefixes to each interface. post-begin add the prefix from
2722         # interface X onto interface Y. this would normally be an error
2723         # since it would generate a 'duplicate address' warning. but in
2724         # this case, since what is newly downloaded is sane, it's ok
2725         #
2726         for intf in self.pg_interfaces:
2727             # 172.18.x.1/24
2728             addr = "172.18.%d.1" % intf.sw_if_index
2729             VppIpInterfaceAddress(self, intf, addr, 24).add_vpp_config()
2730
2731         self.vapi.sw_interface_address_replace_begin()
2732
2733         pfxs = []
2734         for intf in self.pg_interfaces:
2735             # 172.18.x.1/24
2736             addr = "172.18.%d.1" % (intf.sw_if_index + 1)
2737             pfxs.append(VppIpInterfaceAddress(self, intf,
2738                                               addr, 24).add_vpp_config())
2739
2740         self.vapi.sw_interface_address_replace_end()
2741
2742         self.logger.info(self.vapi.cli("sh int addr"))
2743
2744         for intf in self.pg_interfaces:
2745             self.assertEqual(self.get_n_pfxs(intf), 1)
2746         for pfx in pfxs:
2747             self.assertTrue(pfx.query_vpp_config())
2748
2749
2750 class TestIPv4PathMTU(VppTestCase):
2751     """ IPv4 Path MTU """
2752
2753     @classmethod
2754     def setUpClass(cls):
2755         super(TestIPv4PathMTU, cls).setUpClass()
2756
2757         cls.create_pg_interfaces(range(2))
2758
2759         # setup all interfaces
2760         for i in cls.pg_interfaces:
2761             i.admin_up()
2762             i.config_ip4()
2763             i.resolve_arp()
2764
2765     @classmethod
2766     def tearDownClass(cls):
2767         super(TestIPv4PathMTU, cls).tearDownClass()
2768
2769     def test_path_mtu(self):
2770         """ Path MTU """
2771
2772         #
2773         # The goal here is not to test that fragmentation works correctly,
2774         # that's done elsewhere, the intent is to ensure that the Path MTU
2775         # settings are honoured.
2776         #
2777         self.vapi.cli("adjacency counters enable")
2778
2779         # set the interface MTU to a reasonable value
2780         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index,
2781                                        [1800, 0, 0, 0])
2782
2783         self.pg1.generate_remote_hosts(4)
2784
2785         p_2k = (Ether(dst=self.pg0.local_mac,
2786                       src=self.pg0.remote_mac) /
2787                 IP(src=self.pg0.remote_ip4,
2788                    dst=self.pg1.remote_ip4) /
2789                 UDP(sport=1234, dport=5678) /
2790                 Raw(b'0xa' * 640))
2791         p_1k = (Ether(dst=self.pg0.local_mac,
2792                       src=self.pg0.remote_mac) /
2793                 IP(src=self.pg0.remote_ip4,
2794                    dst=self.pg1.remote_ip4) /
2795                 UDP(sport=1234, dport=5678) /
2796                 Raw(b'0xa' * 320))
2797
2798         nbr = VppNeighbor(self,
2799                           self.pg1.sw_if_index,
2800                           self.pg1.remote_mac,
2801                           self.pg1.remote_ip4).add_vpp_config()
2802
2803         # this is now the interface MTU frags
2804         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=2)
2805         self.send_and_expect(self.pg0, [p_1k], self.pg1)
2806
2807         # drop the path MTU for this neighbour to below the interface MTU
2808         # expect more frags
2809         pmtu = VppIpPathMtu(self, self.pg1.remote_ip4, 900).add_vpp_config()
2810
2811         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=3)
2812         self.send_and_expect(self.pg0, [p_1k], self.pg1, n_rx=2)
2813
2814         # print/format the adj delegate
2815         self.logger.info(self.vapi.cli("sh adj 5"))
2816
2817         # increase the path MTU to more than the interface
2818         # expect to use the interface MTU
2819         pmtu.modify(8192)
2820
2821         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=2)
2822         self.send_and_expect(self.pg0, [p_1k], self.pg1)
2823
2824         # go back to an MTU from the path
2825         # wrap the call around mark-n-sweep to enusre updates clear stale
2826         self.vapi.ip_path_mtu_replace_begin()
2827         pmtu.modify(900)
2828         self.vapi.ip_path_mtu_replace_end()
2829
2830         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=3)
2831         self.send_and_expect(self.pg0, [p_1k], self.pg1, n_rx=2)
2832
2833         # raise the interface's MTU
2834         # should still use that of the path
2835         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index,
2836                                        [2000, 0, 0, 0])
2837         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=3)
2838         self.send_and_expect(self.pg0, [p_1k], self.pg1, n_rx=2)
2839
2840         # set path high and interface low
2841         pmtu.modify(2000)
2842         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index,
2843                                        [900, 0, 0, 0])
2844         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=3)
2845         self.send_and_expect(self.pg0, [p_1k], self.pg1, n_rx=2)
2846
2847         # remove the path MTU using the mark-n-sweep semantics
2848         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index,
2849                                        [1800, 0, 0, 0])
2850         self.vapi.ip_path_mtu_replace_begin()
2851         self.vapi.ip_path_mtu_replace_end()
2852
2853         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=2)
2854         self.send_and_expect(self.pg0, [p_1k], self.pg1)
2855
2856         #
2857         # set path MTU for a neighbour that doesn't exist, yet
2858         #
2859         pmtu2 = VppIpPathMtu(self,
2860                              self.pg1.remote_hosts[2].ip4,
2861                              900).add_vpp_config()
2862
2863         p_2k = (Ether(dst=self.pg0.local_mac,
2864                       src=self.pg0.remote_mac) /
2865                 IP(src=self.pg0.remote_ip4,
2866                    dst=self.pg1.remote_hosts[2].ip4) /
2867                 UDP(sport=1234, dport=5678) /
2868                 Raw(b'0xa' * 640))
2869         p_1k = (Ether(dst=self.pg0.local_mac,
2870                       src=self.pg0.remote_mac) /
2871                 IP(src=self.pg0.remote_ip4,
2872                    dst=self.pg1.remote_hosts[2].ip4) /
2873                 UDP(sport=1234, dport=5678) /
2874                 Raw(b'0xa' * 320))
2875
2876         nbr2 = VppNeighbor(self,
2877                            self.pg1.sw_if_index,
2878                            self.pg1.remote_hosts[2].mac,
2879                            self.pg1.remote_hosts[2].ip4).add_vpp_config()
2880
2881         # should frag to the path MTU
2882         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=3)
2883         self.send_and_expect(self.pg0, [p_1k], self.pg1, n_rx=2)
2884
2885         # remove and re-add the neighbour
2886         nbr2.remove_vpp_config()
2887         nbr2.add_vpp_config()
2888
2889         # should frag to the path MTU
2890         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=3)
2891         self.send_and_expect(self.pg0, [p_1k], self.pg1, n_rx=2)
2892
2893         #
2894         # set PMTUs for many peers
2895         #
2896         N_HOSTS = 16
2897         self.pg1.generate_remote_hosts(16)
2898         self.pg1.configure_ipv4_neighbors()
2899
2900         for h in range(N_HOSTS):
2901             pmtu = VppIpPathMtu(self, self.pg1.remote_hosts[h].ip4, 900)
2902             pmtu.add_vpp_config()
2903             self.assertTrue(pmtu.query_vpp_config())
2904
2905         self.logger.info(self.vapi.cli("sh ip pmtu"))
2906         dump = list(self.vapi.vpp.details_iter(self.vapi.ip_path_mtu_get))
2907         self.assertEqual(N_HOSTS, len(dump))
2908
2909         for h in range(N_HOSTS):
2910             p_2k[IP].dst = self.pg1.remote_hosts[h].ip4
2911             p_1k[IP].dst = self.pg1.remote_hosts[h].ip4
2912
2913             # should frag to the path MTU
2914             self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=3)
2915             self.send_and_expect(self.pg0, [p_1k], self.pg1, n_rx=2)
2916
2917
2918 class TestIPv4ItfRebind(VppTestCase):
2919     """ IPv4 Interface Bind w/ attached routes """
2920
2921     def setUp(self):
2922         super(TestIPv4ItfRebind, self).setUp()
2923
2924         self.create_pg_interfaces(range(3))
2925
2926     def tearDown(self):
2927         super(TestIPv4ItfRebind, self).tearDown()
2928
2929     def test_rebind(self):
2930         """ Import to no import """
2931
2932         TABLE_ID = 1
2933         tbl = VppIpTable(self, TABLE_ID).add_vpp_config()
2934         self.pg1.set_table_ip4(TABLE_ID)
2935
2936         for i in self.pg_interfaces:
2937             i.admin_up()
2938             i.config_ip4()
2939             i.resolve_arp()
2940
2941         # add an attached route via an pg0
2942         # in a different table. this prefix should import
2943         rt = VppIpRoute(self, self.pg0.local_ip4, 24,
2944                         [VppRoutePath("0.0.0.0",
2945                                       self.pg0.sw_if_index)],
2946                         table_id=TABLE_ID).add_vpp_config()
2947
2948         p = (Ether(dst=self.pg1.local_mac,
2949                    src=self.pg1.remote_mac) /
2950              IP(src=self.pg1.remote_ip4,
2951                 dst=self.pg0.remote_ip4) /
2952              UDP(sport=1234, dport=5678) /
2953              Raw(b'0xa' * 640))
2954
2955         rx = self.send_and_expect(self.pg1, [p], self.pg0)
2956         self.assertFalse(rx[0].haslayer(ARP))
2957
2958         # then bind pg0 to a new table
2959         # so the prefix no longer imports
2960         self.pg0.unconfig_ip4()
2961         self.pg0.set_table_ip4(TABLE_ID)
2962         self.pg0.config_ip4()
2963         self.pg0.resolve_arp()
2964
2965         rx = self.send_and_expect(self.pg1, [p], self.pg0)
2966         self.assertFalse(rx[0].haslayer(ARP))
2967
2968         # revert back to imported
2969         self.pg0.unconfig_ip4()
2970         self.pg0.set_table_ip4(0)
2971         self.pg0.config_ip4()
2972         self.pg0.resolve_arp()
2973
2974         rx = self.send_and_expect(self.pg1, [p], self.pg0)
2975         self.assertFalse(rx[0].haslayer(ARP))
2976
2977         # cleanup
2978         for i in self.pg_interfaces:
2979             i.unconfig_ip4()
2980             i.set_table_ip4(0)
2981             i.admin_down()
2982
2983         rt.remove_vpp_config()
2984         tbl.remove_vpp_config()
2985
2986     def test_delete(self):
2987         """ Swap import tables """
2988
2989         TABLE_ID1 = 1
2990         tbl1_4 = VppIpTable(self, TABLE_ID1).add_vpp_config()
2991         tbl1_6 = VppIpTable(self, TABLE_ID1, True).add_vpp_config()
2992         TABLE_ID2 = 2
2993         tbl2_4 = VppIpTable(self, TABLE_ID2).add_vpp_config()
2994         tbl2_6 = VppIpTable(self, TABLE_ID2, True).add_vpp_config()
2995
2996         # table mappings
2997         self.pg1.set_table_ip4(TABLE_ID1)
2998         self.pg1.set_table_ip6(TABLE_ID1)
2999         self.pg2.set_table_ip4(TABLE_ID2)
3000         self.pg2.set_table_ip6(TABLE_ID2)
3001
3002         for i in self.pg_interfaces:
3003             i.admin_up()
3004             i.config_ip4()
3005             i.resolve_arp()
3006
3007         # add an attached route in the default table via pg0
3008         # this should import to table 1
3009         rt4 = VppIpRoute(self, self.pg1.local_ip4, 24,
3010                          [VppRoutePath("0.0.0.0",
3011                                        self.pg1.sw_if_index)]).add_vpp_config()
3012         rt6 = VppIpRoute(self, self.pg1.local_ip6, 64,
3013                          [VppRoutePath("0.0.0.0",
3014                                        self.pg1.sw_if_index)]).add_vpp_config()
3015
3016         p1 = (Ether(dst=self.pg0.local_mac,
3017                     src=self.pg0.remote_mac) /
3018               IP(src=self.pg1.remote_ip4,
3019                  dst=self.pg1.remote_ip4) /
3020               UDP(sport=1234, dport=5678) /
3021               Raw(b'0xa' * 640))
3022
3023         # inject into table 0
3024         rx = self.send_and_expect(self.pg0, [p1], self.pg1)
3025         self.assertFalse(rx[0].haslayer(ARP))
3026
3027         # swap the attached interface to table 2
3028         self.pg1.unconfig_ip4()
3029         self.pg1.unconfig_ip6()
3030         self.pg1.set_table_ip4(TABLE_ID2)
3031         self.pg1.set_table_ip6(TABLE_ID2)
3032         self.pg1.config_ip4()
3033         self.pg1.config_ip6()
3034         self.pg1.resolve_arp()
3035
3036         # delete table 1
3037         tbl1_4.flush()
3038         tbl1_6.flush()
3039         tbl1_4.remove_vpp_config()
3040         tbl1_6.remove_vpp_config()
3041
3042         rx = self.send_and_expect(self.pg0, [p1], self.pg1)
3043         self.assertFalse(rx[0].haslayer(ARP))
3044
3045         for i in self.pg_interfaces:
3046             i.unconfig_ip4()
3047             i.unconfig_ip6()
3048             i.set_table_ip4(0)
3049             i.set_table_ip6(0)
3050             i.admin_down()
3051
3052
3053 if __name__ == '__main__':
3054     unittest.main(testRunner=VppTestRunner)