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