ip: rate-limit the sending of ICMP error messages
[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         rxs = self.send_and_expect_some(self.pg0, p_ttl * NUM_PKTS, self.pg0)
1951
1952         for rx in rxs:
1953             icmp = rx[ICMP]
1954             self.assertEqual(icmptypes[icmp.type], "time-exceeded")
1955             self.assertEqual(icmpcodes[icmp.type][icmp.code],
1956                              "ttl-zero-during-transit")
1957             self.assertEqual(icmp.src, self.pg0.remote_ip4)
1958             self.assertEqual(icmp.dst, self.pg1.remote_ip4)
1959
1960         #
1961         # MTU exceeded
1962         #
1963         p_mtu = (Ether(src=self.pg0.remote_mac,
1964                        dst=self.pg0.local_mac) /
1965                  IP(src=self.pg0.remote_ip4,
1966                     dst=self.pg1.remote_ip4,
1967                     ttl=10, flags='DF') /
1968                  UDP(sport=1234, dport=1234) /
1969                  Raw(b'\xa5' * 2000))
1970
1971         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [1500, 0, 0, 0])
1972
1973         rxs = self.send_and_expect_some(self.pg0, p_mtu * NUM_PKTS, self.pg0)
1974
1975         for rx in rxs:
1976             icmp = rx[ICMP]
1977             self.assertEqual(icmptypes[icmp.type], "dest-unreach")
1978             self.assertEqual(icmpcodes[icmp.type][icmp.code],
1979                              "fragmentation-needed")
1980             self.assertEqual(icmp.src, self.pg0.remote_ip4)
1981             self.assertEqual(icmp.dst, self.pg1.remote_ip4)
1982
1983         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [2500, 0, 0, 0])
1984         rx = self.send_and_expect(self.pg0, p_mtu * NUM_PKTS, self.pg1)
1985
1986         # Reset MTU for subsequent tests
1987         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [9000, 0, 0, 0])
1988
1989         #
1990         # source address 0.0.0.0 and 25.255.255.255 and for-us
1991         #
1992         p_s0 = (Ether(src=self.pg0.remote_mac,
1993                       dst=self.pg0.local_mac) /
1994                 IP(src="0.0.0.0",
1995                    dst=self.pg0.local_ip4) /
1996                 ICMP(id=4, seq=4) /
1997                 Raw(load=b'\x0a' * 18))
1998         rx = self.send_and_assert_no_replies(self.pg0, p_s0 * 17)
1999
2000         p_s0 = (Ether(src=self.pg0.remote_mac,
2001                       dst=self.pg0.local_mac) /
2002                 IP(src="255.255.255.255",
2003                    dst=self.pg0.local_ip4) /
2004                 ICMP(id=4, seq=4) /
2005                 Raw(load=b'\x0a' * 18))
2006         rx = self.send_and_assert_no_replies(self.pg0, p_s0 * 17)
2007
2008
2009 class TestIPDirectedBroadcast(VppTestCase):
2010     """ IPv4 Directed Broadcast """
2011
2012     @classmethod
2013     def setUpClass(cls):
2014         super(TestIPDirectedBroadcast, cls).setUpClass()
2015
2016     @classmethod
2017     def tearDownClass(cls):
2018         super(TestIPDirectedBroadcast, cls).tearDownClass()
2019
2020     def setUp(self):
2021         super(TestIPDirectedBroadcast, self).setUp()
2022
2023         self.create_pg_interfaces(range(2))
2024
2025         for i in self.pg_interfaces:
2026             i.admin_up()
2027
2028     def tearDown(self):
2029         super(TestIPDirectedBroadcast, self).tearDown()
2030         for i in self.pg_interfaces:
2031             i.admin_down()
2032
2033     def test_ip_input(self):
2034         """ IP Directed Broadcast """
2035
2036         #
2037         # set the directed broadcast on pg0 first, then config IP4 addresses
2038         # for pg1 directed broadcast is always disabled
2039         self.vapi.sw_interface_set_ip_directed_broadcast(
2040             self.pg0.sw_if_index, 1)
2041
2042         p0 = (Ether(src=self.pg1.remote_mac,
2043                     dst=self.pg1.local_mac) /
2044               IP(src="1.1.1.1",
2045                  dst=self.pg0._local_ip4_bcast) /
2046               UDP(sport=1234, dport=1234) /
2047               Raw(b'\xa5' * 2000))
2048         p1 = (Ether(src=self.pg0.remote_mac,
2049                     dst=self.pg0.local_mac) /
2050               IP(src="1.1.1.1",
2051                  dst=self.pg1._local_ip4_bcast) /
2052               UDP(sport=1234, dport=1234) /
2053               Raw(b'\xa5' * 2000))
2054
2055         self.pg0.config_ip4()
2056         self.pg0.resolve_arp()
2057         self.pg1.config_ip4()
2058         self.pg1.resolve_arp()
2059
2060         #
2061         # test packet is L2 broadcast
2062         #
2063         rx = self.send_and_expect(self.pg1, p0 * NUM_PKTS, self.pg0)
2064         self.assertTrue(rx[0][Ether].dst, "ff:ff:ff:ff:ff:ff")
2065
2066         self.send_and_assert_no_replies(self.pg0, p1 * NUM_PKTS,
2067                                         "directed broadcast disabled")
2068
2069         #
2070         # toggle directed broadcast on pg0
2071         #
2072         self.vapi.sw_interface_set_ip_directed_broadcast(
2073             self.pg0.sw_if_index, 0)
2074         self.send_and_assert_no_replies(self.pg1, p0 * NUM_PKTS,
2075                                         "directed broadcast disabled")
2076
2077         self.vapi.sw_interface_set_ip_directed_broadcast(
2078             self.pg0.sw_if_index, 1)
2079         rx = self.send_and_expect(self.pg1, p0 * NUM_PKTS, self.pg0)
2080
2081         self.pg0.unconfig_ip4()
2082         self.pg1.unconfig_ip4()
2083
2084
2085 class TestIPLPM(VppTestCase):
2086     """ IPv4 longest Prefix Match """
2087
2088     @classmethod
2089     def setUpClass(cls):
2090         super(TestIPLPM, cls).setUpClass()
2091
2092     @classmethod
2093     def tearDownClass(cls):
2094         super(TestIPLPM, cls).tearDownClass()
2095
2096     def setUp(self):
2097         super(TestIPLPM, self).setUp()
2098
2099         self.create_pg_interfaces(range(4))
2100
2101         for i in self.pg_interfaces:
2102             i.admin_up()
2103             i.config_ip4()
2104             i.resolve_arp()
2105
2106     def tearDown(self):
2107         super(TestIPLPM, self).tearDown()
2108         for i in self.pg_interfaces:
2109             i.admin_down()
2110             i.unconfig_ip4()
2111
2112     def test_ip_lpm(self):
2113         """ IP longest Prefix Match """
2114
2115         s_24 = VppIpRoute(self, "10.1.2.0", 24,
2116                           [VppRoutePath(self.pg1.remote_ip4,
2117                                         self.pg1.sw_if_index)])
2118         s_24.add_vpp_config()
2119         s_8 = VppIpRoute(self, "10.0.0.0", 8,
2120                          [VppRoutePath(self.pg2.remote_ip4,
2121                                        self.pg2.sw_if_index)])
2122         s_8.add_vpp_config()
2123
2124         p_8 = (Ether(src=self.pg0.remote_mac,
2125                      dst=self.pg0.local_mac) /
2126                IP(src="1.1.1.1",
2127                   dst="10.1.1.1") /
2128                UDP(sport=1234, dport=1234) /
2129                Raw(b'\xa5' * 2000))
2130         p_24 = (Ether(src=self.pg0.remote_mac,
2131                       dst=self.pg0.local_mac) /
2132                 IP(src="1.1.1.1",
2133                    dst="10.1.2.1") /
2134                 UDP(sport=1234, dport=1234) /
2135                 Raw(b'\xa5' * 2000))
2136
2137         self.logger.info(self.vapi.cli("sh ip fib mtrie"))
2138         rx = self.send_and_expect(self.pg0, p_8 * NUM_PKTS, self.pg2)
2139         rx = self.send_and_expect(self.pg0, p_24 * NUM_PKTS, self.pg1)
2140
2141
2142 @tag_fixme_vpp_workers
2143 class TestIPv4Frag(VppTestCase):
2144     """ IPv4 fragmentation """
2145
2146     @classmethod
2147     def setUpClass(cls):
2148         super(TestIPv4Frag, cls).setUpClass()
2149
2150         cls.create_pg_interfaces([0, 1])
2151         cls.src_if = cls.pg0
2152         cls.dst_if = cls.pg1
2153
2154         # setup all interfaces
2155         for i in cls.pg_interfaces:
2156             i.admin_up()
2157             i.config_ip4()
2158             i.resolve_arp()
2159
2160     @classmethod
2161     def tearDownClass(cls):
2162         super(TestIPv4Frag, cls).tearDownClass()
2163
2164     def test_frag_large_packets(self):
2165         """ Fragmentation of large packets """
2166
2167         self.vapi.cli("adjacency counters enable")
2168
2169         p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
2170              IP(src=self.src_if.remote_ip4, dst=self.dst_if.remote_ip4) /
2171              UDP(sport=1234, dport=5678) / Raw())
2172         self.extend_packet(p, 6000, "abcde")
2173         saved_payload = p[Raw].load
2174
2175         nbr = VppNeighbor(self,
2176                           self.dst_if.sw_if_index,
2177                           self.dst_if.remote_mac,
2178                           self.dst_if.remote_ip4).add_vpp_config()
2179
2180         # Force fragmentation by setting MTU of output interface
2181         # lower than packet size
2182         self.vapi.sw_interface_set_mtu(self.dst_if.sw_if_index,
2183                                        [5000, 0, 0, 0])
2184
2185         self.pg_enable_capture()
2186         self.src_if.add_stream(p)
2187         self.pg_start()
2188
2189         # Expecting 3 fragments because size of created fragments currently
2190         # cannot be larger then VPP buffer size (which is 2048)
2191         packets = self.dst_if.get_capture(3)
2192
2193         # we should show 3 packets thru the neighbor
2194         self.assertEqual(3, nbr.get_stats()['packets'])
2195
2196         # Assume VPP sends the fragments in order
2197         payload = b''
2198         for p in packets:
2199             payload_offset = p.frag * 8
2200             if payload_offset > 0:
2201                 payload_offset -= 8  # UDP header is not in payload
2202             self.assert_equal(payload_offset, len(payload))
2203             payload += p[Raw].load
2204         self.assert_equal(payload, saved_payload, "payload")
2205
2206
2207 class TestIPReplace(VppTestCase):
2208     """ IPv4 Table Replace """
2209
2210     @classmethod
2211     def setUpClass(cls):
2212         super(TestIPReplace, cls).setUpClass()
2213
2214     @classmethod
2215     def tearDownClass(cls):
2216         super(TestIPReplace, cls).tearDownClass()
2217
2218     def setUp(self):
2219         super(TestIPReplace, self).setUp()
2220
2221         self.create_pg_interfaces(range(4))
2222
2223         table_id = 1
2224         self.tables = []
2225
2226         for i in self.pg_interfaces:
2227             i.admin_up()
2228             i.config_ip4()
2229             i.resolve_arp()
2230             i.generate_remote_hosts(2)
2231             self.tables.append(VppIpTable(self, table_id).add_vpp_config())
2232             table_id += 1
2233
2234     def tearDown(self):
2235         super(TestIPReplace, self).tearDown()
2236         for i in self.pg_interfaces:
2237             i.admin_down()
2238             i.unconfig_ip4()
2239
2240     def test_replace(self):
2241         """ IP Table Replace """
2242
2243         MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
2244         MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
2245         N_ROUTES = 20
2246         links = [self.pg0, self.pg1, self.pg2, self.pg3]
2247         routes = [[], [], [], []]
2248
2249         # load up the tables with some routes
2250         for ii, t in enumerate(self.tables):
2251             for jj in range(N_ROUTES):
2252                 uni = VppIpRoute(
2253                     self, "10.0.0.%d" % jj, 32,
2254                     [VppRoutePath(links[ii].remote_hosts[0].ip4,
2255                                   links[ii].sw_if_index),
2256                      VppRoutePath(links[ii].remote_hosts[1].ip4,
2257                                   links[ii].sw_if_index)],
2258                     table_id=t.table_id).add_vpp_config()
2259                 multi = VppIpMRoute(
2260                     self, "0.0.0.0",
2261                     "239.0.0.%d" % jj, 32,
2262                     MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
2263                     [VppMRoutePath(self.pg0.sw_if_index,
2264                                    MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT),
2265                      VppMRoutePath(self.pg1.sw_if_index,
2266                                    MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
2267                      VppMRoutePath(self.pg2.sw_if_index,
2268                                    MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
2269                      VppMRoutePath(self.pg3.sw_if_index,
2270                                    MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD)],
2271                     table_id=t.table_id).add_vpp_config()
2272                 routes[ii].append({'uni': uni,
2273                                    'multi': multi})
2274
2275         #
2276         # replace the tables a few times
2277         #
2278         for kk in range(3):
2279             # replace_begin each table
2280             for t in self.tables:
2281                 t.replace_begin()
2282
2283             # all the routes are still there
2284             for ii, t in enumerate(self.tables):
2285                 dump = t.dump()
2286                 mdump = t.mdump()
2287                 for r in routes[ii]:
2288                     self.assertTrue(find_route_in_dump(dump, r['uni'], t))
2289                     self.assertTrue(find_mroute_in_dump(mdump, r['multi'], t))
2290
2291             # redownload the even numbered routes
2292             for ii, t in enumerate(self.tables):
2293                 for jj in range(0, N_ROUTES, 2):
2294                     routes[ii][jj]['uni'].add_vpp_config()
2295                     routes[ii][jj]['multi'].add_vpp_config()
2296
2297             # signal each table replace_end
2298             for t in self.tables:
2299                 t.replace_end()
2300
2301             # we should find the even routes, but not the odd
2302             for ii, t in enumerate(self.tables):
2303                 dump = t.dump()
2304                 mdump = t.mdump()
2305                 for jj in range(0, N_ROUTES, 2):
2306                     self.assertTrue(find_route_in_dump(
2307                         dump, routes[ii][jj]['uni'], t))
2308                     self.assertTrue(find_mroute_in_dump(
2309                         mdump, routes[ii][jj]['multi'], t))
2310                 for jj in range(1, N_ROUTES - 1, 2):
2311                     self.assertFalse(find_route_in_dump(
2312                         dump, routes[ii][jj]['uni'], t))
2313                     self.assertFalse(find_mroute_in_dump(
2314                         mdump, routes[ii][jj]['multi'], t))
2315
2316             # reload all the routes
2317             for ii, t in enumerate(self.tables):
2318                 for r in routes[ii]:
2319                     r['uni'].add_vpp_config()
2320                     r['multi'].add_vpp_config()
2321
2322             # all the routes are still there
2323             for ii, t in enumerate(self.tables):
2324                 dump = t.dump()
2325                 mdump = t.mdump()
2326                 for r in routes[ii]:
2327                     self.assertTrue(find_route_in_dump(dump, r['uni'], t))
2328                     self.assertTrue(find_mroute_in_dump(mdump, r['multi'], t))
2329
2330         #
2331         # finally flush the tables for good measure
2332         #
2333         for t in self.tables:
2334             t.flush()
2335             self.assertEqual(len(t.dump()), 5)
2336             self.assertEqual(len(t.mdump()), 3)
2337
2338
2339 class TestIPCover(VppTestCase):
2340     """ IPv4 Table Cover """
2341
2342     @classmethod
2343     def setUpClass(cls):
2344         super(TestIPCover, cls).setUpClass()
2345
2346     @classmethod
2347     def tearDownClass(cls):
2348         super(TestIPCover, cls).tearDownClass()
2349
2350     def setUp(self):
2351         super(TestIPCover, self).setUp()
2352
2353         self.create_pg_interfaces(range(4))
2354
2355         table_id = 1
2356         self.tables = []
2357
2358         for i in self.pg_interfaces:
2359             i.admin_up()
2360             i.config_ip4()
2361             i.resolve_arp()
2362             i.generate_remote_hosts(2)
2363             self.tables.append(VppIpTable(self, table_id).add_vpp_config())
2364             table_id += 1
2365
2366     def tearDown(self):
2367         super(TestIPCover, self).tearDown()
2368         for i in self.pg_interfaces:
2369             i.admin_down()
2370             i.unconfig_ip4()
2371
2372     def test_cover(self):
2373         """ IP Table Cover """
2374
2375         # add a loop back with a /32 prefix
2376         lo = VppLoInterface(self)
2377         lo.admin_up()
2378         a = VppIpInterfaceAddress(self, lo, "127.0.0.1", 32).add_vpp_config()
2379
2380         # add a neighbour that matches the loopback's /32
2381         nbr = VppNeighbor(self,
2382                           lo.sw_if_index,
2383                           lo.remote_mac,
2384                           "127.0.0.1").add_vpp_config()
2385
2386         # add the default route which will be the cover for /32
2387         r = VppIpRoute(self, "0.0.0.0", 0,
2388                        [VppRoutePath("127.0.0.1",
2389                                      lo.sw_if_index)],
2390                        register=False).add_vpp_config()
2391
2392         # add/remove/add a longer mask cover
2393         r8 = VppIpRoute(self, "127.0.0.0", 8,
2394                         [VppRoutePath("127.0.0.1",
2395                                       lo.sw_if_index)]).add_vpp_config()
2396         r8.remove_vpp_config()
2397         r8.add_vpp_config()
2398         r8.remove_vpp_config()
2399
2400         # remove the default route
2401         r.remove_vpp_config()
2402
2403         # remove the interface prefix
2404         a.remove_vpp_config()
2405
2406
2407 class TestIP4Replace(VppTestCase):
2408     """ IPv4 Interface Address Replace """
2409
2410     @classmethod
2411     def setUpClass(cls):
2412         super(TestIP4Replace, cls).setUpClass()
2413
2414     @classmethod
2415     def tearDownClass(cls):
2416         super(TestIP4Replace, cls).tearDownClass()
2417
2418     def setUp(self):
2419         super(TestIP4Replace, self).setUp()
2420
2421         self.create_pg_interfaces(range(4))
2422
2423         for i in self.pg_interfaces:
2424             i.admin_up()
2425
2426     def tearDown(self):
2427         super(TestIP4Replace, self).tearDown()
2428         for i in self.pg_interfaces:
2429             i.admin_down()
2430
2431     def get_n_pfxs(self, intf):
2432         return len(self.vapi.ip_address_dump(intf.sw_if_index))
2433
2434     def test_replace(self):
2435         """ IP interface address replace """
2436
2437         intf_pfxs = [[], [], [], []]
2438
2439         # add prefixes to each of the interfaces
2440         for i in range(len(self.pg_interfaces)):
2441             intf = self.pg_interfaces[i]
2442
2443             # 172.16.x.1/24
2444             addr = "172.16.%d.1" % intf.sw_if_index
2445             a = VppIpInterfaceAddress(self, intf, addr, 24).add_vpp_config()
2446             intf_pfxs[i].append(a)
2447
2448             # 172.16.x.2/24 - a different address in the same subnet as above
2449             addr = "172.16.%d.2" % intf.sw_if_index
2450             a = VppIpInterfaceAddress(self, intf, addr, 24).add_vpp_config()
2451             intf_pfxs[i].append(a)
2452
2453             # 172.15.x.2/24 - a different address and subnet
2454             addr = "172.15.%d.2" % intf.sw_if_index
2455             a = VppIpInterfaceAddress(self, intf, addr, 24).add_vpp_config()
2456             intf_pfxs[i].append(a)
2457
2458         # a dump should n_address in it
2459         for intf in self.pg_interfaces:
2460             self.assertEqual(self.get_n_pfxs(intf), 3)
2461
2462         #
2463         # remove all the address thru a replace
2464         #
2465         self.vapi.sw_interface_address_replace_begin()
2466         self.vapi.sw_interface_address_replace_end()
2467         for intf in self.pg_interfaces:
2468             self.assertEqual(self.get_n_pfxs(intf), 0)
2469
2470         #
2471         # add all the interface addresses back
2472         #
2473         for p in intf_pfxs:
2474             for v in p:
2475                 v.add_vpp_config()
2476         for intf in self.pg_interfaces:
2477             self.assertEqual(self.get_n_pfxs(intf), 3)
2478
2479         #
2480         # replace again, but this time update/re-add the address on the first
2481         # two interfaces
2482         #
2483         self.vapi.sw_interface_address_replace_begin()
2484
2485         for p in intf_pfxs[:2]:
2486             for v in p:
2487                 v.add_vpp_config()
2488
2489         self.vapi.sw_interface_address_replace_end()
2490
2491         # on the first two the address still exist,
2492         # on the other two they do not
2493         for intf in self.pg_interfaces[:2]:
2494             self.assertEqual(self.get_n_pfxs(intf), 3)
2495         for p in intf_pfxs[:2]:
2496             for v in p:
2497                 self.assertTrue(v.query_vpp_config())
2498         for intf in self.pg_interfaces[2:]:
2499             self.assertEqual(self.get_n_pfxs(intf), 0)
2500
2501         #
2502         # add all the interface addresses back on the last two
2503         #
2504         for p in intf_pfxs[2:]:
2505             for v in p:
2506                 v.add_vpp_config()
2507         for intf in self.pg_interfaces:
2508             self.assertEqual(self.get_n_pfxs(intf), 3)
2509
2510         #
2511         # replace again, this time add different prefixes on all the interfaces
2512         #
2513         self.vapi.sw_interface_address_replace_begin()
2514
2515         pfxs = []
2516         for intf in self.pg_interfaces:
2517             # 172.18.x.1/24
2518             addr = "172.18.%d.1" % intf.sw_if_index
2519             pfxs.append(VppIpInterfaceAddress(self, intf, addr,
2520                                               24).add_vpp_config())
2521
2522         self.vapi.sw_interface_address_replace_end()
2523
2524         # only .18 should exist on each interface
2525         for intf in self.pg_interfaces:
2526             self.assertEqual(self.get_n_pfxs(intf), 1)
2527         for pfx in pfxs:
2528             self.assertTrue(pfx.query_vpp_config())
2529
2530         #
2531         # remove everything
2532         #
2533         self.vapi.sw_interface_address_replace_begin()
2534         self.vapi.sw_interface_address_replace_end()
2535         for intf in self.pg_interfaces:
2536             self.assertEqual(self.get_n_pfxs(intf), 0)
2537
2538         #
2539         # add prefixes to each interface. post-begin add the prefix from
2540         # interface X onto interface Y. this would normally be an error
2541         # since it would generate a 'duplicate address' warning. but in
2542         # this case, since what is newly downloaded is sane, it's ok
2543         #
2544         for intf in self.pg_interfaces:
2545             # 172.18.x.1/24
2546             addr = "172.18.%d.1" % intf.sw_if_index
2547             VppIpInterfaceAddress(self, intf, addr, 24).add_vpp_config()
2548
2549         self.vapi.sw_interface_address_replace_begin()
2550
2551         pfxs = []
2552         for intf in self.pg_interfaces:
2553             # 172.18.x.1/24
2554             addr = "172.18.%d.1" % (intf.sw_if_index + 1)
2555             pfxs.append(VppIpInterfaceAddress(self, intf,
2556                                               addr, 24).add_vpp_config())
2557
2558         self.vapi.sw_interface_address_replace_end()
2559
2560         self.logger.info(self.vapi.cli("sh int addr"))
2561
2562         for intf in self.pg_interfaces:
2563             self.assertEqual(self.get_n_pfxs(intf), 1)
2564         for pfx in pfxs:
2565             self.assertTrue(pfx.query_vpp_config())
2566
2567
2568 class TestIPv4PathMTU(VppTestCase):
2569     """ IPv4 Path MTU """
2570
2571     @classmethod
2572     def setUpClass(cls):
2573         super(TestIPv4PathMTU, cls).setUpClass()
2574
2575         cls.create_pg_interfaces(range(2))
2576
2577         # setup all interfaces
2578         for i in cls.pg_interfaces:
2579             i.admin_up()
2580             i.config_ip4()
2581             i.resolve_arp()
2582
2583     @classmethod
2584     def tearDownClass(cls):
2585         super(TestIPv4PathMTU, cls).tearDownClass()
2586
2587     def test_path_mtu(self):
2588         """ Path MTU """
2589
2590         #
2591         # The goal here is not to test that fragmentation works correctly,
2592         # that's done elsewhere, the intent is to ensure that the Path MTU
2593         # settings are honoured.
2594         #
2595         self.vapi.cli("adjacency counters enable")
2596
2597         # set the interface MTU to a reasonable value
2598         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index,
2599                                        [1800, 0, 0, 0])
2600
2601         self.pg1.generate_remote_hosts(4)
2602
2603         p_2k = (Ether(dst=self.pg0.local_mac,
2604                       src=self.pg0.remote_mac) /
2605                 IP(src=self.pg0.remote_ip4,
2606                    dst=self.pg1.remote_ip4) /
2607                 UDP(sport=1234, dport=5678) /
2608                 Raw(b'0xa' * 640))
2609         p_1k = (Ether(dst=self.pg0.local_mac,
2610                       src=self.pg0.remote_mac) /
2611                 IP(src=self.pg0.remote_ip4,
2612                    dst=self.pg1.remote_ip4) /
2613                 UDP(sport=1234, dport=5678) /
2614                 Raw(b'0xa' * 320))
2615
2616         nbr = VppNeighbor(self,
2617                           self.pg1.sw_if_index,
2618                           self.pg1.remote_mac,
2619                           self.pg1.remote_ip4).add_vpp_config()
2620
2621         # this is now the interface MTU frags
2622         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=2)
2623         self.send_and_expect(self.pg0, [p_1k], self.pg1)
2624
2625         # drop the path MTU for this neighbour to below the interface MTU
2626         # expect more frags
2627         pmtu = VppIpPathMtu(self, self.pg1.remote_ip4, 900).add_vpp_config()
2628
2629         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=3)
2630         self.send_and_expect(self.pg0, [p_1k], self.pg1, n_rx=2)
2631
2632         # print/format the adj delegate
2633         self.logger.info(self.vapi.cli("sh adj 5"))
2634
2635         # increase the path MTU to more than the interface
2636         # expect to use the interface MTU
2637         pmtu.modify(8192)
2638
2639         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=2)
2640         self.send_and_expect(self.pg0, [p_1k], self.pg1)
2641
2642         # go back to an MTU from the path
2643         # wrap the call around mark-n-sweep to enusre updates clear stale
2644         self.vapi.ip_path_mtu_replace_begin()
2645         pmtu.modify(900)
2646         self.vapi.ip_path_mtu_replace_end()
2647
2648         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=3)
2649         self.send_and_expect(self.pg0, [p_1k], self.pg1, n_rx=2)
2650
2651         # raise the interface's MTU
2652         # should still use that of the path
2653         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index,
2654                                        [2000, 0, 0, 0])
2655         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=3)
2656         self.send_and_expect(self.pg0, [p_1k], self.pg1, n_rx=2)
2657
2658         # set path high and interface low
2659         pmtu.modify(2000)
2660         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index,
2661                                        [900, 0, 0, 0])
2662         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=3)
2663         self.send_and_expect(self.pg0, [p_1k], self.pg1, n_rx=2)
2664
2665         # remove the path MTU using the mark-n-sweep semantics
2666         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index,
2667                                        [1800, 0, 0, 0])
2668         self.vapi.ip_path_mtu_replace_begin()
2669         self.vapi.ip_path_mtu_replace_end()
2670
2671         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=2)
2672         self.send_and_expect(self.pg0, [p_1k], self.pg1)
2673
2674         #
2675         # set path MTU for a neighbour that doesn't exist, yet
2676         #
2677         pmtu2 = VppIpPathMtu(self,
2678                              self.pg1.remote_hosts[2].ip4,
2679                              900).add_vpp_config()
2680
2681         p_2k = (Ether(dst=self.pg0.local_mac,
2682                       src=self.pg0.remote_mac) /
2683                 IP(src=self.pg0.remote_ip4,
2684                    dst=self.pg1.remote_hosts[2].ip4) /
2685                 UDP(sport=1234, dport=5678) /
2686                 Raw(b'0xa' * 640))
2687         p_1k = (Ether(dst=self.pg0.local_mac,
2688                       src=self.pg0.remote_mac) /
2689                 IP(src=self.pg0.remote_ip4,
2690                    dst=self.pg1.remote_hosts[2].ip4) /
2691                 UDP(sport=1234, dport=5678) /
2692                 Raw(b'0xa' * 320))
2693
2694         nbr2 = VppNeighbor(self,
2695                            self.pg1.sw_if_index,
2696                            self.pg1.remote_hosts[2].mac,
2697                            self.pg1.remote_hosts[2].ip4).add_vpp_config()
2698
2699         # should frag to the path MTU
2700         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=3)
2701         self.send_and_expect(self.pg0, [p_1k], self.pg1, n_rx=2)
2702
2703         # remove and re-add the neighbour
2704         nbr2.remove_vpp_config()
2705         nbr2.add_vpp_config()
2706
2707         # should frag to the path MTU
2708         self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=3)
2709         self.send_and_expect(self.pg0, [p_1k], self.pg1, n_rx=2)
2710
2711         #
2712         # set PMTUs for many peers
2713         #
2714         N_HOSTS = 16
2715         self.pg1.generate_remote_hosts(16)
2716         self.pg1.configure_ipv4_neighbors()
2717
2718         for h in range(N_HOSTS):
2719             pmtu = VppIpPathMtu(self, self.pg1.remote_hosts[h].ip4, 900)
2720             pmtu.add_vpp_config()
2721             self.assertTrue(pmtu.query_vpp_config())
2722
2723         self.logger.info(self.vapi.cli("sh ip pmtu"))
2724         dump = list(self.vapi.vpp.details_iter(self.vapi.ip_path_mtu_get))
2725         self.assertEqual(N_HOSTS, len(dump))
2726
2727         for h in range(N_HOSTS):
2728             p_2k[IP].dst = self.pg1.remote_hosts[h].ip4
2729             p_1k[IP].dst = self.pg1.remote_hosts[h].ip4
2730
2731             # should frag to the path MTU
2732             self.send_and_expect(self.pg0, [p_2k], self.pg1, n_rx=3)
2733             self.send_and_expect(self.pg0, [p_1k], self.pg1, n_rx=2)
2734
2735
2736 class TestIPv4ItfRebind(VppTestCase):
2737     """ IPv4 Interface Bind w/ attached routes """
2738
2739     def setUp(self):
2740         super(TestIPv4ItfRebind, self).setUp()
2741
2742         self.create_pg_interfaces(range(3))
2743
2744     def tearDown(self):
2745         super(TestIPv4ItfRebind, self).tearDown()
2746
2747     def test_rebind(self):
2748         """ Import to no import """
2749
2750         TABLE_ID = 1
2751         tbl = VppIpTable(self, TABLE_ID).add_vpp_config()
2752         self.pg1.set_table_ip4(TABLE_ID)
2753
2754         for i in self.pg_interfaces:
2755             i.admin_up()
2756             i.config_ip4()
2757             i.resolve_arp()
2758
2759         # add an attached route via an pg0
2760         # in a different table. this prefix should import
2761         rt = VppIpRoute(self, self.pg0.local_ip4, 24,
2762                         [VppRoutePath("0.0.0.0",
2763                                       self.pg0.sw_if_index)],
2764                         table_id=TABLE_ID).add_vpp_config()
2765
2766         p = (Ether(dst=self.pg1.local_mac,
2767                    src=self.pg1.remote_mac) /
2768              IP(src=self.pg1.remote_ip4,
2769                 dst=self.pg0.remote_ip4) /
2770              UDP(sport=1234, dport=5678) /
2771              Raw(b'0xa' * 640))
2772
2773         rx = self.send_and_expect(self.pg1, [p], self.pg0)
2774         self.assertFalse(rx[0].haslayer(ARP))
2775
2776         # then bind pg0 to a new table
2777         # so the prefix no longer imports
2778         self.pg0.unconfig_ip4()
2779         self.pg0.set_table_ip4(TABLE_ID)
2780         self.pg0.config_ip4()
2781         self.pg0.resolve_arp()
2782
2783         rx = self.send_and_expect(self.pg1, [p], self.pg0)
2784         self.assertFalse(rx[0].haslayer(ARP))
2785
2786         # revert back to imported
2787         self.pg0.unconfig_ip4()
2788         self.pg0.set_table_ip4(0)
2789         self.pg0.config_ip4()
2790         self.pg0.resolve_arp()
2791
2792         rx = self.send_and_expect(self.pg1, [p], self.pg0)
2793         self.assertFalse(rx[0].haslayer(ARP))
2794
2795         # cleanup
2796         for i in self.pg_interfaces:
2797             i.unconfig_ip4()
2798             i.set_table_ip4(0)
2799             i.admin_down()
2800
2801         rt.remove_vpp_config()
2802         tbl.remove_vpp_config()
2803
2804     def test_delete(self):
2805         """ Swap import tables """
2806
2807         TABLE_ID1 = 1
2808         tbl1_4 = VppIpTable(self, TABLE_ID1).add_vpp_config()
2809         tbl1_6 = VppIpTable(self, TABLE_ID1, True).add_vpp_config()
2810         TABLE_ID2 = 2
2811         tbl2_4 = VppIpTable(self, TABLE_ID2).add_vpp_config()
2812         tbl2_6 = VppIpTable(self, TABLE_ID2, True).add_vpp_config()
2813
2814         # table mappings
2815         self.pg1.set_table_ip4(TABLE_ID1)
2816         self.pg1.set_table_ip6(TABLE_ID1)
2817         self.pg2.set_table_ip4(TABLE_ID2)
2818         self.pg2.set_table_ip6(TABLE_ID2)
2819
2820         for i in self.pg_interfaces:
2821             i.admin_up()
2822             i.config_ip4()
2823             i.resolve_arp()
2824
2825         # add an attached route in the default table via pg0
2826         # this should import to table 1
2827         rt4 = VppIpRoute(self, self.pg1.local_ip4, 24,
2828                          [VppRoutePath("0.0.0.0",
2829                                        self.pg1.sw_if_index)]).add_vpp_config()
2830         rt6 = VppIpRoute(self, self.pg1.local_ip6, 64,
2831                          [VppRoutePath("0.0.0.0",
2832                                        self.pg1.sw_if_index)]).add_vpp_config()
2833
2834         p1 = (Ether(dst=self.pg0.local_mac,
2835                     src=self.pg0.remote_mac) /
2836               IP(src=self.pg1.remote_ip4,
2837                  dst=self.pg1.remote_ip4) /
2838               UDP(sport=1234, dport=5678) /
2839               Raw(b'0xa' * 640))
2840
2841         # inject into table 0
2842         rx = self.send_and_expect(self.pg0, [p1], self.pg1)
2843         self.assertFalse(rx[0].haslayer(ARP))
2844
2845         # swap the attached interface to table 2
2846         self.pg1.unconfig_ip4()
2847         self.pg1.unconfig_ip6()
2848         self.pg1.set_table_ip4(TABLE_ID2)
2849         self.pg1.set_table_ip6(TABLE_ID2)
2850         self.pg1.config_ip4()
2851         self.pg1.config_ip6()
2852         self.pg1.resolve_arp()
2853
2854         # delete table 1
2855         tbl1_4.flush()
2856         tbl1_6.flush()
2857         tbl1_4.remove_vpp_config()
2858         tbl1_6.remove_vpp_config()
2859
2860         rx = self.send_and_expect(self.pg0, [p1], self.pg1)
2861         self.assertFalse(rx[0].haslayer(ARP))
2862
2863         for i in self.pg_interfaces:
2864             i.unconfig_ip4()
2865             i.unconfig_ip6()
2866             i.set_table_ip4(0)
2867             i.set_table_ip6(0)
2868             i.admin_down()
2869
2870
2871 if __name__ == '__main__':
2872     unittest.main(testRunner=VppTestRunner)