ip: Path MTU
[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 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 send_and_expect_load_balancing(self, input, pkts, outputs):
1122         self.vapi.cli("clear trace")
1123         input.add_stream(pkts)
1124         self.pg_enable_capture(self.pg_interfaces)
1125         self.pg_start()
1126         rxs = []
1127         for oo in outputs:
1128             rx = oo._get_capture(1)
1129             self.assertNotEqual(0, len(rx))
1130             rxs.append(rx)
1131         return rxs
1132
1133     def send_and_expect_one_itf(self, input, pkts, itf):
1134         input.add_stream(pkts)
1135         self.pg_enable_capture(self.pg_interfaces)
1136         self.pg_start()
1137         rx = itf.get_capture(len(pkts))
1138
1139     def total_len(self, rxs):
1140         n = 0
1141         for rx in rxs:
1142             n += len(rx)
1143         return n
1144
1145     def test_ip_load_balance(self):
1146         """ IP Load-Balancing """
1147
1148         fhc = VppEnum.vl_api_ip_flow_hash_config_t
1149         af = VppEnum.vl_api_address_family_t
1150
1151         #
1152         # An array of packets that differ only in the destination port
1153         #
1154         port_ip_pkts = []
1155         port_mpls_pkts = []
1156
1157         #
1158         # An array of packets that differ only in the source address
1159         #
1160         src_ip_pkts = []
1161         src_mpls_pkts = []
1162
1163         for ii in range(NUM_PKTS):
1164             port_ip_hdr = (IP(dst="10.0.0.1", src="20.0.0.1") /
1165                            UDP(sport=1234, dport=1234 + ii) /
1166                            Raw(b'\xa5' * 100))
1167             port_ip_pkts.append((Ether(src=self.pg0.remote_mac,
1168                                        dst=self.pg0.local_mac) /
1169                                  port_ip_hdr))
1170             port_mpls_pkts.append((Ether(src=self.pg0.remote_mac,
1171                                          dst=self.pg0.local_mac) /
1172                                    MPLS(label=66, ttl=2) /
1173                                    port_ip_hdr))
1174
1175             src_ip_hdr = (IP(dst="10.0.0.1", src="20.0.0.%d" % ii) /
1176                           UDP(sport=1234, dport=1234) /
1177                           Raw(b'\xa5' * 100))
1178             src_ip_pkts.append((Ether(src=self.pg0.remote_mac,
1179                                       dst=self.pg0.local_mac) /
1180                                 src_ip_hdr))
1181             src_mpls_pkts.append((Ether(src=self.pg0.remote_mac,
1182                                         dst=self.pg0.local_mac) /
1183                                   MPLS(label=66, ttl=2) /
1184                                   src_ip_hdr))
1185
1186         route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
1187                                     [VppRoutePath(self.pg1.remote_ip4,
1188                                                   self.pg1.sw_if_index),
1189                                      VppRoutePath(self.pg2.remote_ip4,
1190                                                   self.pg2.sw_if_index)])
1191         route_10_0_0_1.add_vpp_config()
1192
1193         binding = VppMplsIpBind(self, 66, "10.0.0.1", 32)
1194         binding.add_vpp_config()
1195
1196         #
1197         # inject the packet on pg0 - expect load-balancing across the 2 paths
1198         #  - since the default hash config is to use IP src,dst and port
1199         #    src,dst
1200         # We are not going to ensure equal amounts of packets across each link,
1201         # since the hash algorithm is statistical and therefore this can never
1202         # be guaranteed. But with 64 different packets we do expect some
1203         # balancing. So instead just ensure there is traffic on each link.
1204         #
1205         rx = self.send_and_expect_load_balancing(self.pg0, port_ip_pkts,
1206                                                  [self.pg1, self.pg2])
1207         n_ip_pg0 = len(rx[0])
1208         self.send_and_expect_load_balancing(self.pg0, src_ip_pkts,
1209                                             [self.pg1, self.pg2])
1210         self.send_and_expect_load_balancing(self.pg0, port_mpls_pkts,
1211                                             [self.pg1, self.pg2])
1212         rx = self.send_and_expect_load_balancing(self.pg0, src_mpls_pkts,
1213                                                  [self.pg1, self.pg2])
1214         n_mpls_pg0 = len(rx[0])
1215
1216         #
1217         # change the router ID and expect the distribution changes
1218         #
1219         self.vapi.set_ip_flow_hash_router_id(router_id=0x11111111)
1220
1221         rx = self.send_and_expect_load_balancing(self.pg0, port_ip_pkts,
1222                                                  [self.pg1, self.pg2])
1223         self.assertNotEqual(n_ip_pg0, len(rx[0]))
1224
1225         rx = self.send_and_expect_load_balancing(self.pg0, src_mpls_pkts,
1226                                                  [self.pg1, self.pg2])
1227         self.assertNotEqual(n_mpls_pg0, len(rx[0]))
1228
1229         #
1230         # change the flow hash config so it's only IP src,dst
1231         #  - now only the stream with differing source address will
1232         #    load-balance
1233         #
1234         self.vapi.set_ip_flow_hash_v2(
1235             af=af.ADDRESS_IP4,
1236             table_id=0,
1237             flow_hash_config=(fhc.IP_API_FLOW_HASH_SRC_IP |
1238                               fhc.IP_API_FLOW_HASH_DST_IP |
1239                               fhc.IP_API_FLOW_HASH_PROTO))
1240
1241         self.send_and_expect_load_balancing(self.pg0, src_ip_pkts,
1242                                             [self.pg1, self.pg2])
1243         self.send_and_expect_load_balancing(self.pg0, src_mpls_pkts,
1244                                             [self.pg1, self.pg2])
1245
1246         self.send_and_expect_one_itf(self.pg0, port_ip_pkts, self.pg2)
1247
1248         #
1249         # change the flow hash config back to defaults
1250         #
1251         self.vapi.set_ip_flow_hash(vrf_id=0, src=1, dst=1,
1252                                    proto=1, sport=1, dport=1)
1253
1254         #
1255         # Recursive prefixes
1256         #  - testing that 2 stages of load-balancing occurs and there is no
1257         #    polarisation (i.e. only 2 of 4 paths are used)
1258         #
1259         port_pkts = []
1260         src_pkts = []
1261
1262         for ii in range(257):
1263             port_pkts.append((Ether(src=self.pg0.remote_mac,
1264                                     dst=self.pg0.local_mac) /
1265                               IP(dst="1.1.1.1", src="20.0.0.1") /
1266                               UDP(sport=1234, dport=1234 + ii) /
1267                               Raw(b'\xa5' * 100)))
1268             src_pkts.append((Ether(src=self.pg0.remote_mac,
1269                                    dst=self.pg0.local_mac) /
1270                              IP(dst="1.1.1.1", src="20.0.0.%d" % ii) /
1271                              UDP(sport=1234, dport=1234) /
1272                              Raw(b'\xa5' * 100)))
1273
1274         route_10_0_0_2 = VppIpRoute(self, "10.0.0.2", 32,
1275                                     [VppRoutePath(self.pg3.remote_ip4,
1276                                                   self.pg3.sw_if_index),
1277                                      VppRoutePath(self.pg4.remote_ip4,
1278                                                   self.pg4.sw_if_index)])
1279         route_10_0_0_2.add_vpp_config()
1280
1281         route_1_1_1_1 = VppIpRoute(self, "1.1.1.1", 32,
1282                                    [VppRoutePath("10.0.0.2", 0xffffffff),
1283                                     VppRoutePath("10.0.0.1", 0xffffffff)])
1284         route_1_1_1_1.add_vpp_config()
1285
1286         #
1287         # inject the packet on pg0 - expect load-balancing across all 4 paths
1288         #
1289         self.vapi.cli("clear trace")
1290         self.send_and_expect_load_balancing(self.pg0, port_pkts,
1291                                             [self.pg1, self.pg2,
1292                                              self.pg3, self.pg4])
1293         self.send_and_expect_load_balancing(self.pg0, src_pkts,
1294                                             [self.pg1, self.pg2,
1295                                              self.pg3, self.pg4])
1296
1297         #
1298         # bring down pg1 expect LB to adjust to use only those that are up
1299         #
1300         self.pg1.link_down()
1301
1302         rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
1303                                                  [self.pg2, self.pg3,
1304                                                   self.pg4])
1305         self.assertEqual(len(src_pkts), self.total_len(rx))
1306
1307         #
1308         # bring down pg2 expect LB to adjust to use only those that are up
1309         #
1310         self.pg2.link_down()
1311
1312         rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
1313                                                  [self.pg3, self.pg4])
1314         self.assertEqual(len(src_pkts), self.total_len(rx))
1315
1316         #
1317         # bring the links back up - expect LB over all again
1318         #
1319         self.pg1.link_up()
1320         self.pg2.link_up()
1321
1322         rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
1323                                                  [self.pg1, self.pg2,
1324                                                   self.pg3, self.pg4])
1325         self.assertEqual(len(src_pkts), self.total_len(rx))
1326
1327         #
1328         # The same link-up/down but this time admin state
1329         #
1330         self.pg1.admin_down()
1331         self.pg2.admin_down()
1332         rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
1333                                                  [self.pg3, self.pg4])
1334         self.assertEqual(len(src_pkts), self.total_len(rx))
1335         self.pg1.admin_up()
1336         self.pg2.admin_up()
1337         self.pg1.resolve_arp()
1338         self.pg2.resolve_arp()
1339         rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
1340                                                  [self.pg1, self.pg2,
1341                                                   self.pg3, self.pg4])
1342         self.assertEqual(len(src_pkts), self.total_len(rx))
1343
1344         #
1345         # Recursive prefixes
1346         #  - testing that 2 stages of load-balancing, no choices
1347         #
1348         port_pkts = []
1349
1350         for ii in range(257):
1351             port_pkts.append((Ether(src=self.pg0.remote_mac,
1352                                     dst=self.pg0.local_mac) /
1353                               IP(dst="1.1.1.2", src="20.0.0.2") /
1354                               UDP(sport=1234, dport=1234 + ii) /
1355                               Raw(b'\xa5' * 100)))
1356
1357         route_10_0_0_3 = VppIpRoute(self, "10.0.0.3", 32,
1358                                     [VppRoutePath(self.pg3.remote_ip4,
1359                                                   self.pg3.sw_if_index)])
1360         route_10_0_0_3.add_vpp_config()
1361
1362         route_1_1_1_2 = VppIpRoute(self, "1.1.1.2", 32,
1363                                    [VppRoutePath("10.0.0.3", 0xffffffff)])
1364         route_1_1_1_2.add_vpp_config()
1365
1366         #
1367         # inject the packet on pg0 - rx only on via routes output interface
1368         #
1369         self.vapi.cli("clear trace")
1370         self.send_and_expect_one_itf(self.pg0, port_pkts, self.pg3)
1371
1372         #
1373         # Add a LB route in the presence of a down link - expect no
1374         # packets over the down link
1375         #
1376         self.pg3.link_down()
1377
1378         route_10_0_0_3 = VppIpRoute(self, "10.0.0.3", 32,
1379                                     [VppRoutePath(self.pg3.remote_ip4,
1380                                                   self.pg3.sw_if_index),
1381                                      VppRoutePath(self.pg4.remote_ip4,
1382                                                   self.pg4.sw_if_index)])
1383         route_10_0_0_3.add_vpp_config()
1384
1385         port_pkts = []
1386         for ii in range(257):
1387             port_pkts.append(Ether(src=self.pg0.remote_mac,
1388                                    dst=self.pg0.local_mac) /
1389                              IP(dst="10.0.0.3", src="20.0.0.2") /
1390                              UDP(sport=1234, dport=1234 + ii) /
1391                              Raw(b'\xa5' * 100))
1392
1393         self.send_and_expect_one_itf(self.pg0, port_pkts, self.pg4)
1394
1395         # bring the link back up
1396         self.pg3.link_up()
1397
1398         rx = self.send_and_expect_load_balancing(self.pg0, port_pkts,
1399                                                  [self.pg3, self.pg4])
1400         self.assertEqual(len(src_pkts), self.total_len(rx))
1401
1402
1403 class TestIPVlan0(VppTestCase):
1404     """ IPv4 VLAN-0 """
1405
1406     @classmethod
1407     def setUpClass(cls):
1408         super(TestIPVlan0, cls).setUpClass()
1409
1410     @classmethod
1411     def tearDownClass(cls):
1412         super(TestIPVlan0, cls).tearDownClass()
1413
1414     def setUp(self):
1415         super(TestIPVlan0, self).setUp()
1416
1417         self.create_pg_interfaces(range(2))
1418         mpls_tbl = VppMplsTable(self, 0)
1419         mpls_tbl.add_vpp_config()
1420
1421         for i in self.pg_interfaces:
1422             i.admin_up()
1423             i.config_ip4()
1424             i.resolve_arp()
1425             i.enable_mpls()
1426
1427     def tearDown(self):
1428         for i in self.pg_interfaces:
1429             i.disable_mpls()
1430             i.unconfig_ip4()
1431             i.admin_down()
1432         super(TestIPVlan0, self).tearDown()
1433
1434     def test_ip_vlan_0(self):
1435         """ IP VLAN-0 """
1436
1437         pkts = (Ether(src=self.pg0.remote_mac,
1438                       dst=self.pg0.local_mac) /
1439                 Dot1Q(vlan=0) /
1440                 IP(dst=self.pg1.remote_ip4,
1441                    src=self.pg0.remote_ip4) /
1442                 UDP(sport=1234, dport=1234) /
1443                 Raw(b'\xa5' * 100)) * NUM_PKTS
1444
1445         #
1446         # Expect that packets sent on VLAN-0 are forwarded on the
1447         # main interface.
1448         #
1449         self.send_and_expect(self.pg0, pkts, self.pg1)
1450
1451
1452 class IPPuntSetup(object):
1453     """ Setup for IPv4 Punt Police/Redirect """
1454
1455     def punt_setup(self):
1456         self.create_pg_interfaces(range(4))
1457
1458         for i in self.pg_interfaces:
1459             i.admin_up()
1460             i.config_ip4()
1461             i.resolve_arp()
1462
1463         # use UDP packet that have a port we need to explicitly
1464         # register to get punted.
1465         pt_l4 = VppEnum.vl_api_punt_type_t.PUNT_API_TYPE_L4
1466         af_ip4 = VppEnum.vl_api_address_family_t.ADDRESS_IP4
1467         udp_proto = VppEnum.vl_api_ip_proto_t.IP_API_PROTO_UDP
1468         punt_udp = {
1469             'type': pt_l4,
1470             'punt': {
1471                 'l4': {
1472                     'af': af_ip4,
1473                     'protocol': udp_proto,
1474                     'port': 1234,
1475                 }
1476             }
1477         }
1478
1479         self.vapi.set_punt(is_add=1, punt=punt_udp)
1480
1481         self.pkt = (Ether(src=self.pg0.remote_mac,
1482                           dst=self.pg0.local_mac) /
1483                     IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
1484                     UDP(sport=1234, dport=1234) /
1485                     Raw(b'\xa5' * 100))
1486
1487     def punt_teardown(self):
1488         for i in self.pg_interfaces:
1489             i.unconfig_ip4()
1490             i.admin_down()
1491
1492
1493 class TestIPPunt(IPPuntSetup, VppTestCase):
1494     """ IPv4 Punt Police/Redirect """
1495
1496     def setUp(self):
1497         super(TestIPPunt, self).setUp()
1498         super(TestIPPunt, self).punt_setup()
1499
1500     def tearDown(self):
1501         super(TestIPPunt, self).punt_teardown()
1502         super(TestIPPunt, self).tearDown()
1503
1504     def test_ip_punt(self):
1505         """ IP punt police and redirect """
1506
1507         pkts = self.pkt * 1025
1508
1509         #
1510         # Configure a punt redirect via pg1.
1511         #
1512         nh_addr = self.pg1.remote_ip4
1513         ip_punt_redirect = VppIpPuntRedirect(self, self.pg0.sw_if_index,
1514                                              self.pg1.sw_if_index, nh_addr)
1515         ip_punt_redirect.add_vpp_config()
1516
1517         self.send_and_expect(self.pg0, pkts, self.pg1)
1518
1519         #
1520         # add a policer
1521         #
1522         policer = VppPolicer(self, "ip4-punt", 400, 0, 10, 0, rate_type=1)
1523         policer.add_vpp_config()
1524         ip_punt_policer = VppIpPuntPolicer(self, policer.policer_index)
1525         ip_punt_policer.add_vpp_config()
1526
1527         self.vapi.cli("clear trace")
1528         self.pg0.add_stream(pkts)
1529         self.pg_enable_capture(self.pg_interfaces)
1530         self.pg_start()
1531
1532         #
1533         # the number of packet received should be greater than 0,
1534         # but not equal to the number sent, since some were policed
1535         #
1536         rx = self.pg1._get_capture(1)
1537
1538         stats = policer.get_stats()
1539
1540         # Single rate policer - expect conform, violate but no exceed
1541         self.assertGreater(stats['conform_packets'], 0)
1542         self.assertEqual(stats['exceed_packets'], 0)
1543         self.assertGreater(stats['violate_packets'], 0)
1544
1545         self.assertGreater(len(rx), 0)
1546         self.assertLess(len(rx), len(pkts))
1547
1548         #
1549         # remove the policer. back to full rx
1550         #
1551         ip_punt_policer.remove_vpp_config()
1552         policer.remove_vpp_config()
1553         self.send_and_expect(self.pg0, pkts, self.pg1)
1554
1555         #
1556         # remove the redirect. expect full drop.
1557         #
1558         ip_punt_redirect.remove_vpp_config()
1559         self.send_and_assert_no_replies(self.pg0, pkts,
1560                                         "IP no punt config")
1561
1562         #
1563         # Add a redirect that is not input port selective
1564         #
1565         ip_punt_redirect = VppIpPuntRedirect(self, 0xffffffff,
1566                                              self.pg1.sw_if_index, nh_addr)
1567         ip_punt_redirect.add_vpp_config()
1568         self.send_and_expect(self.pg0, pkts, self.pg1)
1569         ip_punt_redirect.remove_vpp_config()
1570
1571     def test_ip_punt_dump(self):
1572         """ IP4 punt redirect dump"""
1573
1574         #
1575         # Configure a punt redirects
1576         #
1577         nh_address = self.pg3.remote_ip4
1578         ipr_03 = VppIpPuntRedirect(self, self.pg0.sw_if_index,
1579                                    self.pg3.sw_if_index, nh_address)
1580         ipr_13 = VppIpPuntRedirect(self, self.pg1.sw_if_index,
1581                                    self.pg3.sw_if_index, nh_address)
1582         ipr_23 = VppIpPuntRedirect(self, self.pg2.sw_if_index,
1583                                    self.pg3.sw_if_index, "0.0.0.0")
1584         ipr_03.add_vpp_config()
1585         ipr_13.add_vpp_config()
1586         ipr_23.add_vpp_config()
1587
1588         #
1589         # Dump pg0 punt redirects
1590         #
1591         self.assertTrue(ipr_03.query_vpp_config())
1592         self.assertTrue(ipr_13.query_vpp_config())
1593         self.assertTrue(ipr_23.query_vpp_config())
1594
1595         #
1596         # Dump punt redirects for all interfaces
1597         #
1598         punts = self.vapi.ip_punt_redirect_dump(0xffffffff)
1599         self.assertEqual(len(punts), 3)
1600         for p in punts:
1601             self.assertEqual(p.punt.tx_sw_if_index, self.pg3.sw_if_index)
1602         self.assertNotEqual(punts[1].punt.nh, self.pg3.remote_ip4)
1603         self.assertEqual(str(punts[2].punt.nh), '0.0.0.0')
1604
1605
1606 class TestIPPuntHandoff(IPPuntSetup, VppTestCase):
1607     """ IPv4 Punt Policer thread handoff """
1608     worker_config = "workers 2"
1609
1610     def setUp(self):
1611         super(TestIPPuntHandoff, self).setUp()
1612         super(TestIPPuntHandoff, self).punt_setup()
1613
1614     def tearDown(self):
1615         super(TestIPPuntHandoff, self).punt_teardown()
1616         super(TestIPPuntHandoff, self).tearDown()
1617
1618     def test_ip_punt_policer_handoff(self):
1619         """ IP4 punt policer thread handoff """
1620         pkts = self.pkt * NUM_PKTS
1621
1622         #
1623         # Configure a punt redirect via pg1.
1624         #
1625         nh_addr = self.pg1.remote_ip4
1626         ip_punt_redirect = VppIpPuntRedirect(self, self.pg0.sw_if_index,
1627                                              self.pg1.sw_if_index, nh_addr)
1628         ip_punt_redirect.add_vpp_config()
1629
1630         action_tx = PolicerAction(
1631             VppEnum.vl_api_sse2_qos_action_type_t.SSE2_QOS_ACTION_API_TRANSMIT,
1632             0)
1633         #
1634         # This policer drops no packets, we are just
1635         # testing that they get to the right thread.
1636         #
1637         policer = VppPolicer(self, "ip4-punt", 400, 0, 10, 0, 1,
1638                              0, 0, False, action_tx, action_tx, action_tx)
1639         policer.add_vpp_config()
1640         ip_punt_policer = VppIpPuntPolicer(self, policer.policer_index)
1641         ip_punt_policer.add_vpp_config()
1642
1643         for worker in [0, 1]:
1644             self.send_and_expect(self.pg0, pkts, self.pg1, worker=worker)
1645             self.logger.debug(self.vapi.cli("show trace max 100"))
1646
1647         # Combined stats, all threads
1648         stats = policer.get_stats()
1649
1650         # Single rate policer - expect conform, violate but no exceed
1651         self.assertGreater(stats['conform_packets'], 0)
1652         self.assertEqual(stats['exceed_packets'], 0)
1653         self.assertGreater(stats['violate_packets'], 0)
1654
1655         # Worker 0, should have done all the policing
1656         stats0 = policer.get_stats(worker=0)
1657         self.assertEqual(stats, stats0)
1658
1659         # Worker 1, should have handed everything off
1660         stats1 = policer.get_stats(worker=1)
1661         self.assertEqual(stats1['conform_packets'], 0)
1662         self.assertEqual(stats1['exceed_packets'], 0)
1663         self.assertEqual(stats1['violate_packets'], 0)
1664
1665         # Bind the policer to worker 1 and repeat
1666         policer.bind_vpp_config(1, True)
1667         for worker in [0, 1]:
1668             self.send_and_expect(self.pg0, pkts, self.pg1, worker=worker)
1669             self.logger.debug(self.vapi.cli("show trace max 100"))
1670
1671         # The 2 workers should now have policed the same amount
1672         stats = policer.get_stats()
1673         stats0 = policer.get_stats(worker=0)
1674         stats1 = policer.get_stats(worker=1)
1675
1676         self.assertGreater(stats0['conform_packets'], 0)
1677         self.assertEqual(stats0['exceed_packets'], 0)
1678         self.assertGreater(stats0['violate_packets'], 0)
1679
1680         self.assertGreater(stats1['conform_packets'], 0)
1681         self.assertEqual(stats1['exceed_packets'], 0)
1682         self.assertGreater(stats1['violate_packets'], 0)
1683
1684         self.assertEqual(stats0['conform_packets'] + stats1['conform_packets'],
1685                          stats['conform_packets'])
1686
1687         self.assertEqual(stats0['violate_packets'] + stats1['violate_packets'],
1688                          stats['violate_packets'])
1689
1690         # Unbind the policer and repeat
1691         policer.bind_vpp_config(1, False)
1692         for worker in [0, 1]:
1693             self.send_and_expect(self.pg0, pkts, self.pg1, worker=worker)
1694             self.logger.debug(self.vapi.cli("show trace max 100"))
1695
1696         # The policer should auto-bind to worker 0 when packets arrive
1697         stats = policer.get_stats()
1698         stats0new = policer.get_stats(worker=0)
1699         stats1new = policer.get_stats(worker=1)
1700
1701         self.assertGreater(stats0new['conform_packets'],
1702                            stats0['conform_packets'])
1703         self.assertEqual(stats0new['exceed_packets'], 0)
1704         self.assertGreater(stats0new['violate_packets'],
1705                            stats0['violate_packets'])
1706
1707         self.assertEqual(stats1, stats1new)
1708
1709         #
1710         # Clean up
1711         #
1712         ip_punt_policer.remove_vpp_config()
1713         policer.remove_vpp_config()
1714         ip_punt_redirect.remove_vpp_config()
1715
1716
1717 class TestIPDeag(VppTestCase):
1718     """ IPv4 Deaggregate Routes """
1719
1720     @classmethod
1721     def setUpClass(cls):
1722         super(TestIPDeag, cls).setUpClass()
1723
1724     @classmethod
1725     def tearDownClass(cls):
1726         super(TestIPDeag, cls).tearDownClass()
1727
1728     def setUp(self):
1729         super(TestIPDeag, self).setUp()
1730
1731         self.create_pg_interfaces(range(3))
1732
1733         for i in self.pg_interfaces:
1734             i.admin_up()
1735             i.config_ip4()
1736             i.resolve_arp()
1737
1738     def tearDown(self):
1739         super(TestIPDeag, self).tearDown()
1740         for i in self.pg_interfaces:
1741             i.unconfig_ip4()
1742             i.admin_down()
1743
1744     def test_ip_deag(self):
1745         """ IP Deag Routes """
1746
1747         #
1748         # Create a table to be used for:
1749         #  1 - another destination address lookup
1750         #  2 - a source address lookup
1751         #
1752         table_dst = VppIpTable(self, 1)
1753         table_src = VppIpTable(self, 2)
1754         table_dst.add_vpp_config()
1755         table_src.add_vpp_config()
1756
1757         #
1758         # Add a route in the default table to point to a deag/
1759         # second lookup in each of these tables
1760         #
1761         route_to_dst = VppIpRoute(self, "1.1.1.1", 32,
1762                                   [VppRoutePath("0.0.0.0",
1763                                                 0xffffffff,
1764                                                 nh_table_id=1)])
1765         route_to_src = VppIpRoute(
1766             self, "1.1.1.2", 32,
1767             [VppRoutePath("0.0.0.0",
1768                           0xffffffff,
1769                           nh_table_id=2,
1770                           type=FibPathType.FIB_PATH_TYPE_SOURCE_LOOKUP)])
1771         route_to_dst.add_vpp_config()
1772         route_to_src.add_vpp_config()
1773
1774         #
1775         # packets to these destination are dropped, since they'll
1776         # hit the respective default routes in the second table
1777         #
1778         p_dst = (Ether(src=self.pg0.remote_mac,
1779                        dst=self.pg0.local_mac) /
1780                  IP(src="5.5.5.5", dst="1.1.1.1") /
1781                  TCP(sport=1234, dport=1234) /
1782                  Raw(b'\xa5' * 100))
1783         p_src = (Ether(src=self.pg0.remote_mac,
1784                        dst=self.pg0.local_mac) /
1785                  IP(src="2.2.2.2", dst="1.1.1.2") /
1786                  TCP(sport=1234, dport=1234) /
1787                  Raw(b'\xa5' * 100))
1788         pkts_dst = p_dst * 257
1789         pkts_src = p_src * 257
1790
1791         self.send_and_assert_no_replies(self.pg0, pkts_dst,
1792                                         "IP in dst table")
1793         self.send_and_assert_no_replies(self.pg0, pkts_src,
1794                                         "IP in src table")
1795
1796         #
1797         # add a route in the dst table to forward via pg1
1798         #
1799         route_in_dst = VppIpRoute(self, "1.1.1.1", 32,
1800                                   [VppRoutePath(self.pg1.remote_ip4,
1801                                                 self.pg1.sw_if_index)],
1802                                   table_id=1)
1803         route_in_dst.add_vpp_config()
1804
1805         self.send_and_expect(self.pg0, pkts_dst, self.pg1)
1806
1807         #
1808         # add a route in the src table to forward via pg2
1809         #
1810         route_in_src = VppIpRoute(self, "2.2.2.2", 32,
1811                                   [VppRoutePath(self.pg2.remote_ip4,
1812                                                 self.pg2.sw_if_index)],
1813                                   table_id=2)
1814         route_in_src.add_vpp_config()
1815         self.send_and_expect(self.pg0, pkts_src, self.pg2)
1816
1817         #
1818         # loop in the lookup DP
1819         #
1820         route_loop = VppIpRoute(self, "2.2.2.3", 32,
1821                                 [VppRoutePath("0.0.0.0",
1822                                               0xffffffff,
1823                                               nh_table_id=0)])
1824         route_loop.add_vpp_config()
1825
1826         p_l = (Ether(src=self.pg0.remote_mac,
1827                      dst=self.pg0.local_mac) /
1828                IP(src="2.2.2.4", dst="2.2.2.3") /
1829                TCP(sport=1234, dport=1234) /
1830                Raw(b'\xa5' * 100))
1831
1832         self.send_and_assert_no_replies(self.pg0, p_l * 257,
1833                                         "IP lookup loop")
1834
1835
1836 class TestIPInput(VppTestCase):
1837     """ IPv4 Input Exceptions """
1838
1839     @classmethod
1840     def setUpClass(cls):
1841         super(TestIPInput, cls).setUpClass()
1842
1843     @classmethod
1844     def tearDownClass(cls):
1845         super(TestIPInput, cls).tearDownClass()
1846
1847     def setUp(self):
1848         super(TestIPInput, self).setUp()
1849
1850         self.create_pg_interfaces(range(2))
1851
1852         for i in self.pg_interfaces:
1853             i.admin_up()
1854             i.config_ip4()
1855             i.resolve_arp()
1856
1857     def tearDown(self):
1858         super(TestIPInput, self).tearDown()
1859         for i in self.pg_interfaces:
1860             i.unconfig_ip4()
1861             i.admin_down()
1862
1863     def test_ip_input(self):
1864         """ IP Input Exceptions """
1865
1866         # i can't find a way in scapy to construct an IP packet
1867         # with a length less than the IP header length
1868
1869         #
1870         # Packet too short - this is forwarded
1871         #
1872         p_short = (Ether(src=self.pg0.remote_mac,
1873                          dst=self.pg0.local_mac) /
1874                    IP(src=self.pg0.remote_ip4,
1875                       dst=self.pg1.remote_ip4,
1876                       len=40) /
1877                    UDP(sport=1234, dport=1234) /
1878                    Raw(b'\xa5' * 100))
1879
1880         rx = self.send_and_expect(self.pg0, p_short * NUM_PKTS, self.pg1)
1881
1882         #
1883         # Packet too long - this is dropped
1884         #
1885         p_long = (Ether(src=self.pg0.remote_mac,
1886                         dst=self.pg0.local_mac) /
1887                   IP(src=self.pg0.remote_ip4,
1888                      dst=self.pg1.remote_ip4,
1889                      len=400) /
1890                   UDP(sport=1234, dport=1234) /
1891                   Raw(b'\xa5' * 100))
1892
1893         rx = self.send_and_assert_no_replies(self.pg0, p_long * NUM_PKTS,
1894                                              "too long")
1895
1896         #
1897         # bad chksum - this is dropped
1898         #
1899         p_chksum = (Ether(src=self.pg0.remote_mac,
1900                           dst=self.pg0.local_mac) /
1901                     IP(src=self.pg0.remote_ip4,
1902                        dst=self.pg1.remote_ip4,
1903                        chksum=400) /
1904                     UDP(sport=1234, dport=1234) /
1905                     Raw(b'\xa5' * 100))
1906
1907         rx = self.send_and_assert_no_replies(self.pg0, p_chksum * NUM_PKTS,
1908                                              "bad checksum")
1909
1910         #
1911         # bad version - this is dropped
1912         #
1913         p_ver = (Ether(src=self.pg0.remote_mac,
1914                        dst=self.pg0.local_mac) /
1915                  IP(src=self.pg0.remote_ip4,
1916                     dst=self.pg1.remote_ip4,
1917                     version=3) /
1918                  UDP(sport=1234, dport=1234) /
1919                  Raw(b'\xa5' * 100))
1920
1921         rx = self.send_and_assert_no_replies(self.pg0, p_ver * NUM_PKTS,
1922                                              "funky version")
1923
1924         #
1925         # fragment offset 1 - this is dropped
1926         #
1927         p_frag = (Ether(src=self.pg0.remote_mac,
1928                         dst=self.pg0.local_mac) /
1929                   IP(src=self.pg0.remote_ip4,
1930                      dst=self.pg1.remote_ip4,
1931                      frag=1) /
1932                   UDP(sport=1234, dport=1234) /
1933                   Raw(b'\xa5' * 100))
1934
1935         rx = self.send_and_assert_no_replies(self.pg0, p_frag * NUM_PKTS,
1936                                              "frag offset")
1937
1938         #
1939         # TTL expired packet
1940         #
1941         p_ttl = (Ether(src=self.pg0.remote_mac,
1942                        dst=self.pg0.local_mac) /
1943                  IP(src=self.pg0.remote_ip4,
1944                     dst=self.pg1.remote_ip4,
1945                     ttl=1) /
1946                  UDP(sport=1234, dport=1234) /
1947                  Raw(b'\xa5' * 100))
1948
1949         rx = self.send_and_expect(self.pg0, p_ttl * NUM_PKTS, self.pg0)
1950
1951         rx = rx[0]
1952         icmp = rx[ICMP]
1953
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         rx = self.send_and_expect(self.pg0, p_mtu * NUM_PKTS, self.pg0)
1974         rx = rx[0]
1975         icmp = rx[ICMP]
1976
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 if __name__ == '__main__':
2737     unittest.main(testRunner=VppTestRunner)