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