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