ip: fix interface ip address del sw_if_index check
[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, MRouteItfFlags, MRouteEntryFlags, 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         #
900         # An (S,G).
901         # one accepting interface, pg0, 2 forwarding interfaces
902         #
903         route_232_1_1_1 = VppIpMRoute(
904             self,
905             "0.0.0.0",
906             "232.1.1.1", 32,
907             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
908             [VppMRoutePath(self.pg1.sw_if_index,
909                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
910              VppMRoutePath(self.pg0.sw_if_index,
911                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
912         route_232_1_1_1.add_vpp_config()
913
914         pu = (Ether(src=self.pg1.remote_mac,
915                     dst=self.pg1.local_mac) /
916               IP(src="10.10.10.10", dst=self.pg0.remote_ip4) /
917               UDP(sport=1234, dport=1234) /
918               Raw(b'\xa5' * 100))
919         pm = (Ether(src=self.pg1.remote_mac,
920                     dst=self.pg1.local_mac) /
921               IP(src="10.10.10.10", dst="232.1.1.1") /
922               UDP(sport=1234, dport=1234) /
923               Raw(b'\xa5' * 100))
924
925         #
926         # PG1 does not forward IP traffic
927         #
928         self.send_and_assert_no_replies(self.pg1, pu, "IP disabled")
929         self.send_and_assert_no_replies(self.pg1, pm, "IP disabled")
930
931         #
932         # IP enable PG1
933         #
934         self.pg1.config_ip4()
935
936         #
937         # Now we get packets through
938         #
939         self.pg1.add_stream(pu)
940         self.pg_enable_capture(self.pg_interfaces)
941         self.pg_start()
942         rx = self.pg0.get_capture(1)
943
944         self.pg1.add_stream(pm)
945         self.pg_enable_capture(self.pg_interfaces)
946         self.pg_start()
947         rx = self.pg0.get_capture(1)
948
949         #
950         # Disable PG1
951         #
952         self.pg1.unconfig_ip4()
953
954         #
955         # PG1 does not forward IP traffic
956         #
957         self.send_and_assert_no_replies(self.pg1, pu, "IP disabled")
958         self.send_and_assert_no_replies(self.pg1, pm, "IP disabled")
959
960
961 class TestIPSubNets(VppTestCase):
962     """ IPv4 Subnets """
963
964     @classmethod
965     def setUpClass(cls):
966         super(TestIPSubNets, cls).setUpClass()
967
968     @classmethod
969     def tearDownClass(cls):
970         super(TestIPSubNets, cls).tearDownClass()
971
972     def setUp(self):
973         super(TestIPSubNets, self).setUp()
974
975         # create a 2 pg interfaces
976         self.create_pg_interfaces(range(2))
977
978         # pg0 we will use to experiment
979         self.pg0.admin_up()
980
981         # pg1 is setup normally
982         self.pg1.admin_up()
983         self.pg1.config_ip4()
984         self.pg1.resolve_arp()
985
986     def tearDown(self):
987         super(TestIPSubNets, self).tearDown()
988         for i in self.pg_interfaces:
989             i.admin_down()
990
991     def test_ip_sub_nets(self):
992         """ IP Sub Nets """
993
994         #
995         # Configure a covering route to forward so we know
996         # when we are dropping
997         #
998         cover_route = VppIpRoute(self, "10.0.0.0", 8,
999                                  [VppRoutePath(self.pg1.remote_ip4,
1000                                                self.pg1.sw_if_index)])
1001         cover_route.add_vpp_config()
1002
1003         p = (Ether(src=self.pg1.remote_mac,
1004                    dst=self.pg1.local_mac) /
1005              IP(dst="10.10.10.10", src=self.pg0.local_ip4) /
1006              UDP(sport=1234, dport=1234) /
1007              Raw(b'\xa5' * 100))
1008
1009         self.pg1.add_stream(p)
1010         self.pg_enable_capture(self.pg_interfaces)
1011         self.pg_start()
1012         rx = self.pg1.get_capture(1)
1013
1014         #
1015         # Configure some non-/24 subnets on an IP interface
1016         #
1017         ip_addr_n = socket.inet_pton(socket.AF_INET, "10.10.10.10")
1018
1019         self.vapi.sw_interface_add_del_address(
1020             sw_if_index=self.pg0.sw_if_index,
1021             prefix="10.10.10.10/16")
1022
1023         pn = (Ether(src=self.pg1.remote_mac,
1024                     dst=self.pg1.local_mac) /
1025               IP(dst="10.10.0.0", src=self.pg0.local_ip4) /
1026               UDP(sport=1234, dport=1234) /
1027               Raw(b'\xa5' * 100))
1028         pb = (Ether(src=self.pg1.remote_mac,
1029                     dst=self.pg1.local_mac) /
1030               IP(dst="10.10.255.255", src=self.pg0.local_ip4) /
1031               UDP(sport=1234, dport=1234) /
1032               Raw(b'\xa5' * 100))
1033
1034         self.send_and_assert_no_replies(self.pg1, pn, "IP Network address")
1035         self.send_and_assert_no_replies(self.pg1, pb, "IP Broadcast address")
1036
1037         # remove the sub-net and we are forwarding via the cover again
1038         self.vapi.sw_interface_add_del_address(
1039             sw_if_index=self.pg0.sw_if_index,
1040             prefix="10.10.10.10/16",
1041             is_add=0)
1042
1043         self.pg1.add_stream(pn)
1044         self.pg_enable_capture(self.pg_interfaces)
1045         self.pg_start()
1046         rx = self.pg1.get_capture(1)
1047         self.pg1.add_stream(pb)
1048         self.pg_enable_capture(self.pg_interfaces)
1049         self.pg_start()
1050         rx = self.pg1.get_capture(1)
1051
1052         #
1053         # A /31 is a special case where the 'other-side' is an attached host
1054         # packets to that peer generate ARP requests
1055         #
1056         ip_addr_n = socket.inet_pton(socket.AF_INET, "10.10.10.10")
1057
1058         self.vapi.sw_interface_add_del_address(
1059             sw_if_index=self.pg0.sw_if_index,
1060             prefix="10.10.10.10/31")
1061
1062         pn = (Ether(src=self.pg1.remote_mac,
1063                     dst=self.pg1.local_mac) /
1064               IP(dst="10.10.10.11", src=self.pg0.local_ip4) /
1065               UDP(sport=1234, dport=1234) /
1066               Raw(b'\xa5' * 100))
1067
1068         self.pg1.add_stream(pn)
1069         self.pg_enable_capture(self.pg_interfaces)
1070         self.pg_start()
1071         rx = self.pg0.get_capture(1)
1072         rx[ARP]
1073
1074         # remove the sub-net and we are forwarding via the cover again
1075         self.vapi.sw_interface_add_del_address(
1076             sw_if_index=self.pg0.sw_if_index,
1077             prefix="10.10.10.10/31", is_add=0)
1078
1079         self.pg1.add_stream(pn)
1080         self.pg_enable_capture(self.pg_interfaces)
1081         self.pg_start()
1082         rx = self.pg1.get_capture(1)
1083
1084
1085 class TestIPLoadBalance(VppTestCase):
1086     """ IPv4 Load-Balancing """
1087
1088     @classmethod
1089     def setUpClass(cls):
1090         super(TestIPLoadBalance, cls).setUpClass()
1091
1092     @classmethod
1093     def tearDownClass(cls):
1094         super(TestIPLoadBalance, cls).tearDownClass()
1095
1096     def setUp(self):
1097         super(TestIPLoadBalance, self).setUp()
1098
1099         self.create_pg_interfaces(range(5))
1100         mpls_tbl = VppMplsTable(self, 0)
1101         mpls_tbl.add_vpp_config()
1102
1103         for i in self.pg_interfaces:
1104             i.admin_up()
1105             i.config_ip4()
1106             i.resolve_arp()
1107             i.enable_mpls()
1108
1109     def tearDown(self):
1110         for i in self.pg_interfaces:
1111             i.disable_mpls()
1112             i.unconfig_ip4()
1113             i.admin_down()
1114         super(TestIPLoadBalance, self).tearDown()
1115
1116     def send_and_expect_load_balancing(self, input, pkts, outputs):
1117         input.add_stream(pkts)
1118         self.pg_enable_capture(self.pg_interfaces)
1119         self.pg_start()
1120         rxs = []
1121         for oo in outputs:
1122             rx = oo._get_capture(1)
1123             self.assertNotEqual(0, len(rx))
1124             for r in rx:
1125                 rxs.append(r)
1126         return rxs
1127
1128     def send_and_expect_one_itf(self, input, pkts, itf):
1129         input.add_stream(pkts)
1130         self.pg_enable_capture(self.pg_interfaces)
1131         self.pg_start()
1132         rx = itf.get_capture(len(pkts))
1133
1134     def test_ip_load_balance(self):
1135         """ IP Load-Balancing """
1136
1137         #
1138         # An array of packets that differ only in the destination port
1139         #
1140         port_ip_pkts = []
1141         port_mpls_pkts = []
1142
1143         #
1144         # An array of packets that differ only in the source address
1145         #
1146         src_ip_pkts = []
1147         src_mpls_pkts = []
1148
1149         for ii in range(NUM_PKTS):
1150             port_ip_hdr = (IP(dst="10.0.0.1", src="20.0.0.1") /
1151                            UDP(sport=1234, dport=1234 + ii) /
1152                            Raw(b'\xa5' * 100))
1153             port_ip_pkts.append((Ether(src=self.pg0.remote_mac,
1154                                        dst=self.pg0.local_mac) /
1155                                  port_ip_hdr))
1156             port_mpls_pkts.append((Ether(src=self.pg0.remote_mac,
1157                                          dst=self.pg0.local_mac) /
1158                                    MPLS(label=66, ttl=2) /
1159                                    port_ip_hdr))
1160
1161             src_ip_hdr = (IP(dst="10.0.0.1", src="20.0.0.%d" % ii) /
1162                           UDP(sport=1234, dport=1234) /
1163                           Raw(b'\xa5' * 100))
1164             src_ip_pkts.append((Ether(src=self.pg0.remote_mac,
1165                                       dst=self.pg0.local_mac) /
1166                                 src_ip_hdr))
1167             src_mpls_pkts.append((Ether(src=self.pg0.remote_mac,
1168                                         dst=self.pg0.local_mac) /
1169                                   MPLS(label=66, ttl=2) /
1170                                   src_ip_hdr))
1171
1172         route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
1173                                     [VppRoutePath(self.pg1.remote_ip4,
1174                                                   self.pg1.sw_if_index),
1175                                      VppRoutePath(self.pg2.remote_ip4,
1176                                                   self.pg2.sw_if_index)])
1177         route_10_0_0_1.add_vpp_config()
1178
1179         binding = VppMplsIpBind(self, 66, "10.0.0.1", 32)
1180         binding.add_vpp_config()
1181
1182         #
1183         # inject the packet on pg0 - expect load-balancing across the 2 paths
1184         #  - since the default hash config is to use IP src,dst and port
1185         #    src,dst
1186         # We are not going to ensure equal amounts of packets across each link,
1187         # since the hash algorithm is statistical and therefore this can never
1188         # be guaranteed. But with 64 different packets we do expect some
1189         # balancing. So instead just ensure there is traffic on each link.
1190         #
1191         self.send_and_expect_load_balancing(self.pg0, port_ip_pkts,
1192                                             [self.pg1, self.pg2])
1193         self.send_and_expect_load_balancing(self.pg0, src_ip_pkts,
1194                                             [self.pg1, self.pg2])
1195         self.send_and_expect_load_balancing(self.pg0, port_mpls_pkts,
1196                                             [self.pg1, self.pg2])
1197         self.send_and_expect_load_balancing(self.pg0, src_mpls_pkts,
1198                                             [self.pg1, self.pg2])
1199
1200         #
1201         # change the flow hash config so it's only IP src,dst
1202         #  - now only the stream with differing source address will
1203         #    load-balance
1204         #
1205         self.vapi.set_ip_flow_hash(vrf_id=0, src=1, dst=1, sport=0, dport=0)
1206
1207         self.send_and_expect_load_balancing(self.pg0, src_ip_pkts,
1208                                             [self.pg1, self.pg2])
1209         self.send_and_expect_load_balancing(self.pg0, src_mpls_pkts,
1210                                             [self.pg1, self.pg2])
1211
1212         self.send_and_expect_one_itf(self.pg0, port_ip_pkts, self.pg2)
1213
1214         #
1215         # change the flow hash config back to defaults
1216         #
1217         self.vapi.set_ip_flow_hash(vrf_id=0, src=1, dst=1, sport=1, dport=1)
1218
1219         #
1220         # Recursive prefixes
1221         #  - testing that 2 stages of load-balancing occurs and there is no
1222         #    polarisation (i.e. only 2 of 4 paths are used)
1223         #
1224         port_pkts = []
1225         src_pkts = []
1226
1227         for ii in range(257):
1228             port_pkts.append((Ether(src=self.pg0.remote_mac,
1229                                     dst=self.pg0.local_mac) /
1230                               IP(dst="1.1.1.1", src="20.0.0.1") /
1231                               UDP(sport=1234, dport=1234 + ii) /
1232                               Raw(b'\xa5' * 100)))
1233             src_pkts.append((Ether(src=self.pg0.remote_mac,
1234                                    dst=self.pg0.local_mac) /
1235                              IP(dst="1.1.1.1", src="20.0.0.%d" % ii) /
1236                              UDP(sport=1234, dport=1234) /
1237                              Raw(b'\xa5' * 100)))
1238
1239         route_10_0_0_2 = VppIpRoute(self, "10.0.0.2", 32,
1240                                     [VppRoutePath(self.pg3.remote_ip4,
1241                                                   self.pg3.sw_if_index),
1242                                      VppRoutePath(self.pg4.remote_ip4,
1243                                                   self.pg4.sw_if_index)])
1244         route_10_0_0_2.add_vpp_config()
1245
1246         route_1_1_1_1 = VppIpRoute(self, "1.1.1.1", 32,
1247                                    [VppRoutePath("10.0.0.2", 0xffffffff),
1248                                     VppRoutePath("10.0.0.1", 0xffffffff)])
1249         route_1_1_1_1.add_vpp_config()
1250
1251         #
1252         # inject the packet on pg0 - expect load-balancing across all 4 paths
1253         #
1254         self.vapi.cli("clear trace")
1255         self.send_and_expect_load_balancing(self.pg0, port_pkts,
1256                                             [self.pg1, self.pg2,
1257                                              self.pg3, self.pg4])
1258         self.send_and_expect_load_balancing(self.pg0, src_pkts,
1259                                             [self.pg1, self.pg2,
1260                                              self.pg3, self.pg4])
1261
1262         #
1263         # bring down pg1 expect LB to adjust to use only those that are pu
1264         #
1265         self.pg1.link_down()
1266
1267         rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
1268                                                  [self.pg2, self.pg3,
1269                                                   self.pg4])
1270         self.assertEqual(len(src_pkts), len(rx))
1271
1272         #
1273         # bring down pg2 expect LB to adjust to use only those that are pu
1274         #
1275         self.pg2.link_down()
1276
1277         rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
1278                                                  [self.pg3, self.pg4])
1279         self.assertEqual(len(src_pkts), len(rx))
1280
1281         #
1282         # bring the links back up - expect LB over all again
1283         #
1284         self.pg1.link_up()
1285         self.pg2.link_up()
1286
1287         rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
1288                                                  [self.pg1, self.pg2,
1289                                                   self.pg3, self.pg4])
1290         self.assertEqual(len(src_pkts), len(rx))
1291
1292         #
1293         # The same link-up/down but this time admin state
1294         #
1295         self.pg1.admin_down()
1296         self.pg2.admin_down()
1297         rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
1298                                                  [self.pg3, self.pg4])
1299         self.assertEqual(len(src_pkts), len(rx))
1300         self.pg1.admin_up()
1301         self.pg2.admin_up()
1302         self.pg1.resolve_arp()
1303         self.pg2.resolve_arp()
1304         rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
1305                                                  [self.pg1, self.pg2,
1306                                                   self.pg3, self.pg4])
1307         self.assertEqual(len(src_pkts), len(rx))
1308
1309         #
1310         # Recursive prefixes
1311         #  - testing that 2 stages of load-balancing, no choices
1312         #
1313         port_pkts = []
1314
1315         for ii in range(257):
1316             port_pkts.append((Ether(src=self.pg0.remote_mac,
1317                                     dst=self.pg0.local_mac) /
1318                               IP(dst="1.1.1.2", src="20.0.0.2") /
1319                               UDP(sport=1234, dport=1234 + ii) /
1320                               Raw(b'\xa5' * 100)))
1321
1322         route_10_0_0_3 = VppIpRoute(self, "10.0.0.3", 32,
1323                                     [VppRoutePath(self.pg3.remote_ip4,
1324                                                   self.pg3.sw_if_index)])
1325         route_10_0_0_3.add_vpp_config()
1326
1327         route_1_1_1_2 = VppIpRoute(self, "1.1.1.2", 32,
1328                                    [VppRoutePath("10.0.0.3", 0xffffffff)])
1329         route_1_1_1_2.add_vpp_config()
1330
1331         #
1332         # inject the packet on pg0 - rx only on via routes output interface
1333         #
1334         self.vapi.cli("clear trace")
1335         self.send_and_expect_one_itf(self.pg0, port_pkts, self.pg3)
1336
1337         #
1338         # Add a LB route in the presence of a down link - expect no
1339         # packets over the down link
1340         #
1341         self.pg3.link_down()
1342
1343         route_10_0_0_3 = VppIpRoute(self, "10.0.0.3", 32,
1344                                     [VppRoutePath(self.pg3.remote_ip4,
1345                                                   self.pg3.sw_if_index),
1346                                      VppRoutePath(self.pg4.remote_ip4,
1347                                                   self.pg4.sw_if_index)])
1348         route_10_0_0_3.add_vpp_config()
1349
1350         port_pkts = []
1351         for ii in range(257):
1352             port_pkts.append(Ether(src=self.pg0.remote_mac,
1353                                    dst=self.pg0.local_mac) /
1354                              IP(dst="10.0.0.3", src="20.0.0.2") /
1355                              UDP(sport=1234, dport=1234 + ii) /
1356                              Raw(b'\xa5' * 100))
1357
1358         self.send_and_expect_one_itf(self.pg0, port_pkts, self.pg4)
1359
1360         # bring the link back up
1361         self.pg3.link_up()
1362
1363         rx = self.send_and_expect_load_balancing(self.pg0, port_pkts,
1364                                                  [self.pg3, self.pg4])
1365         self.assertEqual(len(src_pkts), len(rx))
1366
1367
1368 class TestIPVlan0(VppTestCase):
1369     """ IPv4 VLAN-0 """
1370
1371     @classmethod
1372     def setUpClass(cls):
1373         super(TestIPVlan0, cls).setUpClass()
1374
1375     @classmethod
1376     def tearDownClass(cls):
1377         super(TestIPVlan0, cls).tearDownClass()
1378
1379     def setUp(self):
1380         super(TestIPVlan0, self).setUp()
1381
1382         self.create_pg_interfaces(range(2))
1383         mpls_tbl = VppMplsTable(self, 0)
1384         mpls_tbl.add_vpp_config()
1385
1386         for i in self.pg_interfaces:
1387             i.admin_up()
1388             i.config_ip4()
1389             i.resolve_arp()
1390             i.enable_mpls()
1391
1392     def tearDown(self):
1393         for i in self.pg_interfaces:
1394             i.disable_mpls()
1395             i.unconfig_ip4()
1396             i.admin_down()
1397         super(TestIPVlan0, self).tearDown()
1398
1399     def test_ip_vlan_0(self):
1400         """ IP VLAN-0 """
1401
1402         pkts = (Ether(src=self.pg0.remote_mac,
1403                       dst=self.pg0.local_mac) /
1404                 Dot1Q(vlan=0) /
1405                 IP(dst=self.pg1.remote_ip4,
1406                    src=self.pg0.remote_ip4) /
1407                 UDP(sport=1234, dport=1234) /
1408                 Raw(b'\xa5' * 100)) * NUM_PKTS
1409
1410         #
1411         # Expect that packets sent on VLAN-0 are forwarded on the
1412         # main interface.
1413         #
1414         self.send_and_expect(self.pg0, pkts, self.pg1)
1415
1416
1417 class TestIPPunt(VppTestCase):
1418     """ IPv4 Punt Police/Redirect """
1419
1420     @classmethod
1421     def setUpClass(cls):
1422         super(TestIPPunt, cls).setUpClass()
1423
1424     @classmethod
1425     def tearDownClass(cls):
1426         super(TestIPPunt, cls).tearDownClass()
1427
1428     def setUp(self):
1429         super(TestIPPunt, self).setUp()
1430
1431         self.create_pg_interfaces(range(4))
1432
1433         for i in self.pg_interfaces:
1434             i.admin_up()
1435             i.config_ip4()
1436             i.resolve_arp()
1437
1438     def tearDown(self):
1439         super(TestIPPunt, self).tearDown()
1440         for i in self.pg_interfaces:
1441             i.unconfig_ip4()
1442             i.admin_down()
1443
1444     def test_ip_punt(self):
1445         """ IP punt police and redirect """
1446
1447         # use UDP packet that have a port we need to explicitly
1448         # register to get punted.
1449         pt_l4 = VppEnum.vl_api_punt_type_t.PUNT_API_TYPE_L4
1450         af_ip4 = VppEnum.vl_api_address_family_t.ADDRESS_IP4
1451         udp_proto = VppEnum.vl_api_ip_proto_t.IP_API_PROTO_UDP
1452         punt_udp = {
1453             'type': pt_l4,
1454             'punt': {
1455                 'l4': {
1456                     'af': af_ip4,
1457                     'protocol': udp_proto,
1458                     'port': 1234,
1459                 }
1460             }
1461         }
1462
1463         self.vapi.set_punt(is_add=1, punt=punt_udp)
1464
1465         p = (Ether(src=self.pg0.remote_mac,
1466                    dst=self.pg0.local_mac) /
1467              IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
1468              UDP(sport=1234, dport=1234) /
1469              Raw(b'\xa5' * 100))
1470
1471         pkts = p * 1025
1472
1473         #
1474         # Configure a punt redirect via pg1.
1475         #
1476         nh_addr = self.pg1.remote_ip4
1477         self.vapi.ip_punt_redirect(self.pg0.sw_if_index,
1478                                    self.pg1.sw_if_index,
1479                                    nh_addr)
1480
1481         self.send_and_expect(self.pg0, pkts, self.pg1)
1482
1483         #
1484         # add a policer
1485         #
1486         policer = VppPolicer(self, "ip4-punt", 400, 0, 10, 0, rate_type=1)
1487         policer.add_vpp_config()
1488         self.vapi.ip_punt_police(policer.policer_index)
1489
1490         self.vapi.cli("clear trace")
1491         self.pg0.add_stream(pkts)
1492         self.pg_enable_capture(self.pg_interfaces)
1493         self.pg_start()
1494
1495         #
1496         # the number of packet received should be greater than 0,
1497         # but not equal to the number sent, since some were policed
1498         #
1499         rx = self.pg1._get_capture(1)
1500         self.assertGreater(len(rx), 0)
1501         self.assertLess(len(rx), len(pkts))
1502
1503         #
1504         # remove the policer. back to full rx
1505         #
1506         self.vapi.ip_punt_police(policer.policer_index, is_add=0)
1507         policer.remove_vpp_config()
1508         self.send_and_expect(self.pg0, pkts, self.pg1)
1509
1510         #
1511         # remove the redirect. expect full drop.
1512         #
1513         self.vapi.ip_punt_redirect(self.pg0.sw_if_index,
1514                                    self.pg1.sw_if_index,
1515                                    nh_addr,
1516                                    is_add=0)
1517         self.send_and_assert_no_replies(self.pg0, pkts,
1518                                         "IP no punt config")
1519
1520         #
1521         # Add a redirect that is not input port selective
1522         #
1523         self.vapi.ip_punt_redirect(0xffffffff,
1524                                    self.pg1.sw_if_index,
1525                                    nh_addr)
1526         self.send_and_expect(self.pg0, pkts, self.pg1)
1527
1528         self.vapi.ip_punt_redirect(0xffffffff,
1529                                    self.pg1.sw_if_index,
1530                                    nh_addr,
1531                                    is_add=0)
1532
1533     def test_ip_punt_dump(self):
1534         """ IP4 punt redirect dump"""
1535
1536         #
1537         # Configure a punt redirects
1538         #
1539         nh_address = self.pg3.remote_ip4
1540         self.vapi.ip_punt_redirect(self.pg0.sw_if_index,
1541                                    self.pg3.sw_if_index,
1542                                    nh_address)
1543         self.vapi.ip_punt_redirect(self.pg1.sw_if_index,
1544                                    self.pg3.sw_if_index,
1545                                    nh_address)
1546         self.vapi.ip_punt_redirect(self.pg2.sw_if_index,
1547                                    self.pg3.sw_if_index,
1548                                    '0.0.0.0')
1549
1550         #
1551         # Dump pg0 punt redirects
1552         #
1553         punts = self.vapi.ip_punt_redirect_dump(self.pg0.sw_if_index)
1554         for p in punts:
1555             self.assertEqual(p.punt.rx_sw_if_index, self.pg0.sw_if_index)
1556
1557         #
1558         # Dump punt redirects for all interfaces
1559         #
1560         punts = self.vapi.ip_punt_redirect_dump(0xffffffff)
1561         self.assertEqual(len(punts), 3)
1562         for p in punts:
1563             self.assertEqual(p.punt.tx_sw_if_index, self.pg3.sw_if_index)
1564         self.assertNotEqual(punts[1].punt.nh, self.pg3.remote_ip4)
1565         self.assertEqual(str(punts[2].punt.nh), '0.0.0.0')
1566
1567
1568 class TestIPDeag(VppTestCase):
1569     """ IPv4 Deaggregate Routes """
1570
1571     @classmethod
1572     def setUpClass(cls):
1573         super(TestIPDeag, cls).setUpClass()
1574
1575     @classmethod
1576     def tearDownClass(cls):
1577         super(TestIPDeag, cls).tearDownClass()
1578
1579     def setUp(self):
1580         super(TestIPDeag, self).setUp()
1581
1582         self.create_pg_interfaces(range(3))
1583
1584         for i in self.pg_interfaces:
1585             i.admin_up()
1586             i.config_ip4()
1587             i.resolve_arp()
1588
1589     def tearDown(self):
1590         super(TestIPDeag, self).tearDown()
1591         for i in self.pg_interfaces:
1592             i.unconfig_ip4()
1593             i.admin_down()
1594
1595     def test_ip_deag(self):
1596         """ IP Deag Routes """
1597
1598         #
1599         # Create a table to be used for:
1600         #  1 - another destination address lookup
1601         #  2 - a source address lookup
1602         #
1603         table_dst = VppIpTable(self, 1)
1604         table_src = VppIpTable(self, 2)
1605         table_dst.add_vpp_config()
1606         table_src.add_vpp_config()
1607
1608         #
1609         # Add a route in the default table to point to a deag/
1610         # second lookup in each of these tables
1611         #
1612         route_to_dst = VppIpRoute(self, "1.1.1.1", 32,
1613                                   [VppRoutePath("0.0.0.0",
1614                                                 0xffffffff,
1615                                                 nh_table_id=1)])
1616         route_to_src = VppIpRoute(
1617             self, "1.1.1.2", 32,
1618             [VppRoutePath("0.0.0.0",
1619                           0xffffffff,
1620                           nh_table_id=2,
1621                           type=FibPathType.FIB_PATH_TYPE_SOURCE_LOOKUP)])
1622         route_to_dst.add_vpp_config()
1623         route_to_src.add_vpp_config()
1624
1625         #
1626         # packets to these destination are dropped, since they'll
1627         # hit the respective default routes in the second table
1628         #
1629         p_dst = (Ether(src=self.pg0.remote_mac,
1630                        dst=self.pg0.local_mac) /
1631                  IP(src="5.5.5.5", dst="1.1.1.1") /
1632                  TCP(sport=1234, dport=1234) /
1633                  Raw(b'\xa5' * 100))
1634         p_src = (Ether(src=self.pg0.remote_mac,
1635                        dst=self.pg0.local_mac) /
1636                  IP(src="2.2.2.2", dst="1.1.1.2") /
1637                  TCP(sport=1234, dport=1234) /
1638                  Raw(b'\xa5' * 100))
1639         pkts_dst = p_dst * 257
1640         pkts_src = p_src * 257
1641
1642         self.send_and_assert_no_replies(self.pg0, pkts_dst,
1643                                         "IP in dst table")
1644         self.send_and_assert_no_replies(self.pg0, pkts_src,
1645                                         "IP in src table")
1646
1647         #
1648         # add a route in the dst table to forward via pg1
1649         #
1650         route_in_dst = VppIpRoute(self, "1.1.1.1", 32,
1651                                   [VppRoutePath(self.pg1.remote_ip4,
1652                                                 self.pg1.sw_if_index)],
1653                                   table_id=1)
1654         route_in_dst.add_vpp_config()
1655
1656         self.send_and_expect(self.pg0, pkts_dst, self.pg1)
1657
1658         #
1659         # add a route in the src table to forward via pg2
1660         #
1661         route_in_src = VppIpRoute(self, "2.2.2.2", 32,
1662                                   [VppRoutePath(self.pg2.remote_ip4,
1663                                                 self.pg2.sw_if_index)],
1664                                   table_id=2)
1665         route_in_src.add_vpp_config()
1666         self.send_and_expect(self.pg0, pkts_src, self.pg2)
1667
1668         #
1669         # loop in the lookup DP
1670         #
1671         route_loop = VppIpRoute(self, "2.2.2.3", 32,
1672                                 [VppRoutePath("0.0.0.0",
1673                                               0xffffffff,
1674                                               nh_table_id=0)])
1675         route_loop.add_vpp_config()
1676
1677         p_l = (Ether(src=self.pg0.remote_mac,
1678                      dst=self.pg0.local_mac) /
1679                IP(src="2.2.2.4", dst="2.2.2.3") /
1680                TCP(sport=1234, dport=1234) /
1681                Raw(b'\xa5' * 100))
1682
1683         self.send_and_assert_no_replies(self.pg0, p_l * 257,
1684                                         "IP lookup loop")
1685
1686
1687 class TestIPInput(VppTestCase):
1688     """ IPv4 Input Exceptions """
1689
1690     @classmethod
1691     def setUpClass(cls):
1692         super(TestIPInput, cls).setUpClass()
1693
1694     @classmethod
1695     def tearDownClass(cls):
1696         super(TestIPInput, cls).tearDownClass()
1697
1698     def setUp(self):
1699         super(TestIPInput, self).setUp()
1700
1701         self.create_pg_interfaces(range(2))
1702
1703         for i in self.pg_interfaces:
1704             i.admin_up()
1705             i.config_ip4()
1706             i.resolve_arp()
1707
1708     def tearDown(self):
1709         super(TestIPInput, self).tearDown()
1710         for i in self.pg_interfaces:
1711             i.unconfig_ip4()
1712             i.admin_down()
1713
1714     def test_ip_input(self):
1715         """ IP Input Exceptions """
1716
1717         # i can't find a way in scapy to construct an IP packet
1718         # with a length less than the IP header length
1719
1720         #
1721         # Packet too short - this is forwarded
1722         #
1723         p_short = (Ether(src=self.pg0.remote_mac,
1724                          dst=self.pg0.local_mac) /
1725                    IP(src=self.pg0.remote_ip4,
1726                       dst=self.pg1.remote_ip4,
1727                       len=40) /
1728                    UDP(sport=1234, dport=1234) /
1729                    Raw(b'\xa5' * 100))
1730
1731         rx = self.send_and_expect(self.pg0, p_short * NUM_PKTS, self.pg1)
1732
1733         #
1734         # Packet too long - this is dropped
1735         #
1736         p_long = (Ether(src=self.pg0.remote_mac,
1737                         dst=self.pg0.local_mac) /
1738                   IP(src=self.pg0.remote_ip4,
1739                      dst=self.pg1.remote_ip4,
1740                      len=400) /
1741                   UDP(sport=1234, dport=1234) /
1742                   Raw(b'\xa5' * 100))
1743
1744         rx = self.send_and_assert_no_replies(self.pg0, p_long * NUM_PKTS,
1745                                              "too long")
1746
1747         #
1748         # bad chksum - this is dropped
1749         #
1750         p_chksum = (Ether(src=self.pg0.remote_mac,
1751                           dst=self.pg0.local_mac) /
1752                     IP(src=self.pg0.remote_ip4,
1753                        dst=self.pg1.remote_ip4,
1754                        chksum=400) /
1755                     UDP(sport=1234, dport=1234) /
1756                     Raw(b'\xa5' * 100))
1757
1758         rx = self.send_and_assert_no_replies(self.pg0, p_chksum * NUM_PKTS,
1759                                              "bad checksum")
1760
1761         #
1762         # bad version - this is dropped
1763         #
1764         p_ver = (Ether(src=self.pg0.remote_mac,
1765                        dst=self.pg0.local_mac) /
1766                  IP(src=self.pg0.remote_ip4,
1767                     dst=self.pg1.remote_ip4,
1768                     version=3) /
1769                  UDP(sport=1234, dport=1234) /
1770                  Raw(b'\xa5' * 100))
1771
1772         rx = self.send_and_assert_no_replies(self.pg0, p_ver * NUM_PKTS,
1773                                              "funky version")
1774
1775         #
1776         # fragment offset 1 - this is dropped
1777         #
1778         p_frag = (Ether(src=self.pg0.remote_mac,
1779                         dst=self.pg0.local_mac) /
1780                   IP(src=self.pg0.remote_ip4,
1781                      dst=self.pg1.remote_ip4,
1782                      frag=1) /
1783                   UDP(sport=1234, dport=1234) /
1784                   Raw(b'\xa5' * 100))
1785
1786         rx = self.send_and_assert_no_replies(self.pg0, p_frag * NUM_PKTS,
1787                                              "frag offset")
1788
1789         #
1790         # TTL expired packet
1791         #
1792         p_ttl = (Ether(src=self.pg0.remote_mac,
1793                        dst=self.pg0.local_mac) /
1794                  IP(src=self.pg0.remote_ip4,
1795                     dst=self.pg1.remote_ip4,
1796                     ttl=1) /
1797                  UDP(sport=1234, dport=1234) /
1798                  Raw(b'\xa5' * 100))
1799
1800         rx = self.send_and_expect(self.pg0, p_ttl * NUM_PKTS, self.pg0)
1801
1802         rx = rx[0]
1803         icmp = rx[ICMP]
1804
1805         self.assertEqual(icmptypes[icmp.type], "time-exceeded")
1806         self.assertEqual(icmpcodes[icmp.type][icmp.code],
1807                          "ttl-zero-during-transit")
1808         self.assertEqual(icmp.src, self.pg0.remote_ip4)
1809         self.assertEqual(icmp.dst, self.pg1.remote_ip4)
1810
1811         #
1812         # MTU exceeded
1813         #
1814         p_mtu = (Ether(src=self.pg0.remote_mac,
1815                        dst=self.pg0.local_mac) /
1816                  IP(src=self.pg0.remote_ip4,
1817                     dst=self.pg1.remote_ip4,
1818                     ttl=10, flags='DF') /
1819                  UDP(sport=1234, dport=1234) /
1820                  Raw(b'\xa5' * 2000))
1821
1822         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [1500, 0, 0, 0])
1823
1824         rx = self.send_and_expect(self.pg0, p_mtu * NUM_PKTS, self.pg0)
1825         rx = rx[0]
1826         icmp = rx[ICMP]
1827
1828         self.assertEqual(icmptypes[icmp.type], "dest-unreach")
1829         self.assertEqual(icmpcodes[icmp.type][icmp.code],
1830                          "fragmentation-needed")
1831         self.assertEqual(icmp.src, self.pg0.remote_ip4)
1832         self.assertEqual(icmp.dst, self.pg1.remote_ip4)
1833
1834         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [2500, 0, 0, 0])
1835         rx = self.send_and_expect(self.pg0, p_mtu * NUM_PKTS, self.pg1)
1836
1837         # Reset MTU for subsequent tests
1838         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [9000, 0, 0, 0])
1839
1840         #
1841         # source address 0.0.0.0 and 25.255.255.255 and for-us
1842         #
1843         p_s0 = (Ether(src=self.pg0.remote_mac,
1844                       dst=self.pg0.local_mac) /
1845                 IP(src="0.0.0.0",
1846                    dst=self.pg0.local_ip4) /
1847                 ICMP(id=4, seq=4) /
1848                 Raw(load=b'\x0a' * 18))
1849         rx = self.send_and_assert_no_replies(self.pg0, p_s0 * 17)
1850
1851         p_s0 = (Ether(src=self.pg0.remote_mac,
1852                       dst=self.pg0.local_mac) /
1853                 IP(src="255.255.255.255",
1854                    dst=self.pg0.local_ip4) /
1855                 ICMP(id=4, seq=4) /
1856                 Raw(load=b'\x0a' * 18))
1857         rx = self.send_and_assert_no_replies(self.pg0, p_s0 * 17)
1858
1859
1860 class TestIPDirectedBroadcast(VppTestCase):
1861     """ IPv4 Directed Broadcast """
1862
1863     @classmethod
1864     def setUpClass(cls):
1865         super(TestIPDirectedBroadcast, cls).setUpClass()
1866
1867     @classmethod
1868     def tearDownClass(cls):
1869         super(TestIPDirectedBroadcast, cls).tearDownClass()
1870
1871     def setUp(self):
1872         super(TestIPDirectedBroadcast, self).setUp()
1873
1874         self.create_pg_interfaces(range(2))
1875
1876         for i in self.pg_interfaces:
1877             i.admin_up()
1878
1879     def tearDown(self):
1880         super(TestIPDirectedBroadcast, self).tearDown()
1881         for i in self.pg_interfaces:
1882             i.admin_down()
1883
1884     def test_ip_input(self):
1885         """ IP Directed Broadcast """
1886
1887         #
1888         # set the directed broadcast on pg0 first, then config IP4 addresses
1889         # for pg1 directed broadcast is always disabled
1890         self.vapi.sw_interface_set_ip_directed_broadcast(
1891             self.pg0.sw_if_index, 1)
1892
1893         p0 = (Ether(src=self.pg1.remote_mac,
1894                     dst=self.pg1.local_mac) /
1895               IP(src="1.1.1.1",
1896                  dst=self.pg0._local_ip4_bcast) /
1897               UDP(sport=1234, dport=1234) /
1898               Raw(b'\xa5' * 2000))
1899         p1 = (Ether(src=self.pg0.remote_mac,
1900                     dst=self.pg0.local_mac) /
1901               IP(src="1.1.1.1",
1902                  dst=self.pg1._local_ip4_bcast) /
1903               UDP(sport=1234, dport=1234) /
1904               Raw(b'\xa5' * 2000))
1905
1906         self.pg0.config_ip4()
1907         self.pg0.resolve_arp()
1908         self.pg1.config_ip4()
1909         self.pg1.resolve_arp()
1910
1911         #
1912         # test packet is L2 broadcast
1913         #
1914         rx = self.send_and_expect(self.pg1, p0 * NUM_PKTS, self.pg0)
1915         self.assertTrue(rx[0][Ether].dst, "ff:ff:ff:ff:ff:ff")
1916
1917         self.send_and_assert_no_replies(self.pg0, p1 * NUM_PKTS,
1918                                         "directed broadcast disabled")
1919
1920         #
1921         # toggle directed broadcast on pg0
1922         #
1923         self.vapi.sw_interface_set_ip_directed_broadcast(
1924             self.pg0.sw_if_index, 0)
1925         self.send_and_assert_no_replies(self.pg1, p0 * NUM_PKTS,
1926                                         "directed broadcast disabled")
1927
1928         self.vapi.sw_interface_set_ip_directed_broadcast(
1929             self.pg0.sw_if_index, 1)
1930         rx = self.send_and_expect(self.pg1, p0 * NUM_PKTS, self.pg0)
1931
1932         self.pg0.unconfig_ip4()
1933         self.pg1.unconfig_ip4()
1934
1935
1936 class TestIPLPM(VppTestCase):
1937     """ IPv4 longest Prefix Match """
1938
1939     @classmethod
1940     def setUpClass(cls):
1941         super(TestIPLPM, cls).setUpClass()
1942
1943     @classmethod
1944     def tearDownClass(cls):
1945         super(TestIPLPM, cls).tearDownClass()
1946
1947     def setUp(self):
1948         super(TestIPLPM, self).setUp()
1949
1950         self.create_pg_interfaces(range(4))
1951
1952         for i in self.pg_interfaces:
1953             i.admin_up()
1954             i.config_ip4()
1955             i.resolve_arp()
1956
1957     def tearDown(self):
1958         super(TestIPLPM, self).tearDown()
1959         for i in self.pg_interfaces:
1960             i.admin_down()
1961             i.unconfig_ip4()
1962
1963     def test_ip_lpm(self):
1964         """ IP longest Prefix Match """
1965
1966         s_24 = VppIpRoute(self, "10.1.2.0", 24,
1967                           [VppRoutePath(self.pg1.remote_ip4,
1968                                         self.pg1.sw_if_index)])
1969         s_24.add_vpp_config()
1970         s_8 = VppIpRoute(self, "10.0.0.0", 8,
1971                          [VppRoutePath(self.pg2.remote_ip4,
1972                                        self.pg2.sw_if_index)])
1973         s_8.add_vpp_config()
1974
1975         p_8 = (Ether(src=self.pg0.remote_mac,
1976                      dst=self.pg0.local_mac) /
1977                IP(src="1.1.1.1",
1978                   dst="10.1.1.1") /
1979                UDP(sport=1234, dport=1234) /
1980                Raw(b'\xa5' * 2000))
1981         p_24 = (Ether(src=self.pg0.remote_mac,
1982                       dst=self.pg0.local_mac) /
1983                 IP(src="1.1.1.1",
1984                    dst="10.1.2.1") /
1985                 UDP(sport=1234, dport=1234) /
1986                 Raw(b'\xa5' * 2000))
1987
1988         self.logger.info(self.vapi.cli("sh ip fib mtrie"))
1989         rx = self.send_and_expect(self.pg0, p_8 * NUM_PKTS, self.pg2)
1990         rx = self.send_and_expect(self.pg0, p_24 * NUM_PKTS, self.pg1)
1991
1992
1993 class TestIPv4Frag(VppTestCase):
1994     """ IPv4 fragmentation """
1995
1996     @classmethod
1997     def setUpClass(cls):
1998         super(TestIPv4Frag, cls).setUpClass()
1999
2000         cls.create_pg_interfaces([0, 1])
2001         cls.src_if = cls.pg0
2002         cls.dst_if = cls.pg1
2003
2004         # setup all interfaces
2005         for i in cls.pg_interfaces:
2006             i.admin_up()
2007             i.config_ip4()
2008             i.resolve_arp()
2009
2010     @classmethod
2011     def tearDownClass(cls):
2012         super(TestIPv4Frag, cls).tearDownClass()
2013
2014     def test_frag_large_packets(self):
2015         """ Fragmentation of large packets """
2016
2017         self.vapi.cli("adjacency counters enable")
2018
2019         p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
2020              IP(src=self.src_if.remote_ip4, dst=self.dst_if.remote_ip4) /
2021              UDP(sport=1234, dport=5678) / Raw())
2022         self.extend_packet(p, 6000, "abcde")
2023         saved_payload = p[Raw].load
2024
2025         nbr = VppNeighbor(self,
2026                           self.dst_if.sw_if_index,
2027                           self.dst_if.remote_mac,
2028                           self.dst_if.remote_ip4).add_vpp_config()
2029
2030         # Force fragmentation by setting MTU of output interface
2031         # lower than packet size
2032         self.vapi.sw_interface_set_mtu(self.dst_if.sw_if_index,
2033                                        [5000, 0, 0, 0])
2034
2035         self.pg_enable_capture()
2036         self.src_if.add_stream(p)
2037         self.pg_start()
2038
2039         # Expecting 3 fragments because size of created fragments currently
2040         # cannot be larger then VPP buffer size (which is 2048)
2041         packets = self.dst_if.get_capture(3)
2042
2043         # we should show 3 packets thru the neighbor
2044         self.assertEqual(3, nbr.get_stats()['packets'])
2045
2046         # Assume VPP sends the fragments in order
2047         payload = b''
2048         for p in packets:
2049             payload_offset = p.frag * 8
2050             if payload_offset > 0:
2051                 payload_offset -= 8  # UDP header is not in payload
2052             self.assert_equal(payload_offset, len(payload))
2053             payload += p[Raw].load
2054         self.assert_equal(payload, saved_payload, "payload")
2055
2056
2057 class TestIPReplace(VppTestCase):
2058     """ IPv4 Table Replace """
2059
2060     @classmethod
2061     def setUpClass(cls):
2062         super(TestIPReplace, cls).setUpClass()
2063
2064     @classmethod
2065     def tearDownClass(cls):
2066         super(TestIPReplace, cls).tearDownClass()
2067
2068     def setUp(self):
2069         super(TestIPReplace, self).setUp()
2070
2071         self.create_pg_interfaces(range(4))
2072
2073         table_id = 1
2074         self.tables = []
2075
2076         for i in self.pg_interfaces:
2077             i.admin_up()
2078             i.config_ip4()
2079             i.resolve_arp()
2080             i.generate_remote_hosts(2)
2081             self.tables.append(VppIpTable(self, table_id).add_vpp_config())
2082             table_id += 1
2083
2084     def tearDown(self):
2085         super(TestIPReplace, self).tearDown()
2086         for i in self.pg_interfaces:
2087             i.admin_down()
2088             i.unconfig_ip4()
2089
2090     def test_replace(self):
2091         """ IP Table Replace """
2092
2093         N_ROUTES = 20
2094         links = [self.pg0, self.pg1, self.pg2, self.pg3]
2095         routes = [[], [], [], []]
2096
2097         # load up the tables with some routes
2098         for ii, t in enumerate(self.tables):
2099             for jj in range(N_ROUTES):
2100                 uni = VppIpRoute(
2101                     self, "10.0.0.%d" % jj, 32,
2102                     [VppRoutePath(links[ii].remote_hosts[0].ip4,
2103                                   links[ii].sw_if_index),
2104                      VppRoutePath(links[ii].remote_hosts[1].ip4,
2105                                   links[ii].sw_if_index)],
2106                     table_id=t.table_id).add_vpp_config()
2107                 multi = VppIpMRoute(
2108                     self, "0.0.0.0",
2109                     "239.0.0.%d" % jj, 32,
2110                     MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
2111                     [VppMRoutePath(self.pg0.sw_if_index,
2112                                    MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
2113                      VppMRoutePath(self.pg1.sw_if_index,
2114                                    MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
2115                      VppMRoutePath(self.pg2.sw_if_index,
2116                                    MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
2117                      VppMRoutePath(self.pg3.sw_if_index,
2118                                    MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)],
2119                     table_id=t.table_id).add_vpp_config()
2120                 routes[ii].append({'uni': uni,
2121                                    'multi': multi})
2122
2123         #
2124         # replace the tables a few times
2125         #
2126         for kk in range(3):
2127             # replace_begin each table
2128             for t in self.tables:
2129                 t.replace_begin()
2130
2131             # all the routes are still there
2132             for ii, t in enumerate(self.tables):
2133                 dump = t.dump()
2134                 mdump = t.mdump()
2135                 for r in routes[ii]:
2136                     self.assertTrue(find_route_in_dump(dump, r['uni'], t))
2137                     self.assertTrue(find_mroute_in_dump(mdump, r['multi'], t))
2138
2139             # redownload the even numbered routes
2140             for ii, t in enumerate(self.tables):
2141                 for jj in range(0, N_ROUTES, 2):
2142                     routes[ii][jj]['uni'].add_vpp_config()
2143                     routes[ii][jj]['multi'].add_vpp_config()
2144
2145             # signal each table replace_end
2146             for t in self.tables:
2147                 t.replace_end()
2148
2149             # we should find the even routes, but not the odd
2150             for ii, t in enumerate(self.tables):
2151                 dump = t.dump()
2152                 mdump = t.mdump()
2153                 for jj in range(0, N_ROUTES, 2):
2154                     self.assertTrue(find_route_in_dump(
2155                         dump, routes[ii][jj]['uni'], t))
2156                     self.assertTrue(find_mroute_in_dump(
2157                         mdump, routes[ii][jj]['multi'], t))
2158                 for jj in range(1, N_ROUTES - 1, 2):
2159                     self.assertFalse(find_route_in_dump(
2160                         dump, routes[ii][jj]['uni'], t))
2161                     self.assertFalse(find_mroute_in_dump(
2162                         mdump, routes[ii][jj]['multi'], t))
2163
2164             # reload all the routes
2165             for ii, t in enumerate(self.tables):
2166                 for r in routes[ii]:
2167                     r['uni'].add_vpp_config()
2168                     r['multi'].add_vpp_config()
2169
2170             # all the routes are still there
2171             for ii, t in enumerate(self.tables):
2172                 dump = t.dump()
2173                 mdump = t.mdump()
2174                 for r in routes[ii]:
2175                     self.assertTrue(find_route_in_dump(dump, r['uni'], t))
2176                     self.assertTrue(find_mroute_in_dump(mdump, r['multi'], t))
2177
2178         #
2179         # finally flush the tables for good measure
2180         #
2181         for t in self.tables:
2182             t.flush()
2183             self.assertEqual(len(t.dump()), 5)
2184             self.assertEqual(len(t.mdump()), 3)
2185
2186
2187 class TestIPCover(VppTestCase):
2188     """ IPv4 Table Cover """
2189
2190     @classmethod
2191     def setUpClass(cls):
2192         super(TestIPCover, cls).setUpClass()
2193
2194     @classmethod
2195     def tearDownClass(cls):
2196         super(TestIPCover, cls).tearDownClass()
2197
2198     def setUp(self):
2199         super(TestIPCover, self).setUp()
2200
2201         self.create_pg_interfaces(range(4))
2202
2203         table_id = 1
2204         self.tables = []
2205
2206         for i in self.pg_interfaces:
2207             i.admin_up()
2208             i.config_ip4()
2209             i.resolve_arp()
2210             i.generate_remote_hosts(2)
2211             self.tables.append(VppIpTable(self, table_id).add_vpp_config())
2212             table_id += 1
2213
2214     def tearDown(self):
2215         super(TestIPCover, self).tearDown()
2216         for i in self.pg_interfaces:
2217             i.admin_down()
2218             i.unconfig_ip4()
2219
2220     def test_cover(self):
2221         """ IP Table Cover """
2222
2223         # add a loop back with a /32 prefix
2224         lo = VppLoInterface(self)
2225         lo.admin_up()
2226         a = VppIpInterfaceAddress(self, lo, "127.0.0.1", 32).add_vpp_config()
2227
2228         # add a neighbour that matches the loopback's /32
2229         nbr = VppNeighbor(self,
2230                           lo.sw_if_index,
2231                           lo.remote_mac,
2232                           "127.0.0.1").add_vpp_config()
2233
2234         # add the default route which will be the cover for /32
2235         r = VppIpRoute(self, "0.0.0.0", 0,
2236                        [VppRoutePath("127.0.0.1",
2237                                      lo.sw_if_index)],
2238                        register=False).add_vpp_config()
2239
2240         # add/remove/add a longer mask cover
2241         r = VppIpRoute(self, "127.0.0.0", 8,
2242                        [VppRoutePath("127.0.0.1",
2243                                      lo.sw_if_index)]).add_vpp_config()
2244         r.remove_vpp_config()
2245         r.add_vpp_config()
2246
2247         # remove the default route
2248         r.remove_vpp_config()
2249
2250
2251 class TestIP4Replace(VppTestCase):
2252     """ IPv4 Interface Address Replace """
2253
2254     @classmethod
2255     def setUpClass(cls):
2256         super(TestIP4Replace, cls).setUpClass()
2257
2258     @classmethod
2259     def tearDownClass(cls):
2260         super(TestIP4Replace, cls).tearDownClass()
2261
2262     def setUp(self):
2263         super(TestIP4Replace, self).setUp()
2264
2265         self.create_pg_interfaces(range(4))
2266
2267         for i in self.pg_interfaces:
2268             i.admin_up()
2269
2270     def tearDown(self):
2271         super(TestIP4Replace, self).tearDown()
2272         for i in self.pg_interfaces:
2273             i.admin_down()
2274
2275     def get_n_pfxs(self, intf):
2276         return len(self.vapi.ip_address_dump(intf.sw_if_index))
2277
2278     def test_replace(self):
2279         """ IP interface address replace """
2280
2281         intf_pfxs = [[], [], [], []]
2282
2283         # add prefixes to each of the interfaces
2284         for i in range(len(self.pg_interfaces)):
2285             intf = self.pg_interfaces[i]
2286
2287             # 172.16.x.1/24
2288             addr = "172.16.%d.1" % intf.sw_if_index
2289             a = VppIpInterfaceAddress(self, intf, addr, 24).add_vpp_config()
2290             intf_pfxs[i].append(a)
2291
2292             # 172.16.x.2/24 - a different address in the same subnet as above
2293             addr = "172.16.%d.2" % intf.sw_if_index
2294             a = VppIpInterfaceAddress(self, intf, addr, 24).add_vpp_config()
2295             intf_pfxs[i].append(a)
2296
2297             # 172.15.x.2/24 - a different address and subnet
2298             addr = "172.15.%d.2" % intf.sw_if_index
2299             a = VppIpInterfaceAddress(self, intf, addr, 24).add_vpp_config()
2300             intf_pfxs[i].append(a)
2301
2302         # a dump should n_address in it
2303         for intf in self.pg_interfaces:
2304             self.assertEqual(self.get_n_pfxs(intf), 3)
2305
2306         #
2307         # remove all the address thru a replace
2308         #
2309         self.vapi.sw_interface_address_replace_begin()
2310         self.vapi.sw_interface_address_replace_end()
2311         for intf in self.pg_interfaces:
2312             self.assertEqual(self.get_n_pfxs(intf), 0)
2313
2314         #
2315         # add all the interface addresses back
2316         #
2317         for p in intf_pfxs:
2318             for v in p:
2319                 v.add_vpp_config()
2320         for intf in self.pg_interfaces:
2321             self.assertEqual(self.get_n_pfxs(intf), 3)
2322
2323         #
2324         # replace again, but this time update/re-add the address on the first
2325         # two interfaces
2326         #
2327         self.vapi.sw_interface_address_replace_begin()
2328
2329         for p in intf_pfxs[:2]:
2330             for v in p:
2331                 v.add_vpp_config()
2332
2333         self.vapi.sw_interface_address_replace_end()
2334
2335         # on the first two the address still exist,
2336         # on the other two they do not
2337         for intf in self.pg_interfaces[:2]:
2338             self.assertEqual(self.get_n_pfxs(intf), 3)
2339         for p in intf_pfxs[:2]:
2340             for v in p:
2341                 self.assertTrue(v.query_vpp_config())
2342         for intf in self.pg_interfaces[2:]:
2343             self.assertEqual(self.get_n_pfxs(intf), 0)
2344
2345         #
2346         # add all the interface addresses back on the last two
2347         #
2348         for p in intf_pfxs[2:]:
2349             for v in p:
2350                 v.add_vpp_config()
2351         for intf in self.pg_interfaces:
2352             self.assertEqual(self.get_n_pfxs(intf), 3)
2353
2354         #
2355         # replace again, this time add different prefixes on all the interfaces
2356         #
2357         self.vapi.sw_interface_address_replace_begin()
2358
2359         pfxs = []
2360         for intf in self.pg_interfaces:
2361             # 172.18.x.1/24
2362             addr = "172.18.%d.1" % intf.sw_if_index
2363             pfxs.append(VppIpInterfaceAddress(self, intf, addr,
2364                                               24).add_vpp_config())
2365
2366         self.vapi.sw_interface_address_replace_end()
2367
2368         # only .18 should exist on each interface
2369         for intf in self.pg_interfaces:
2370             self.assertEqual(self.get_n_pfxs(intf), 1)
2371         for pfx in pfxs:
2372             self.assertTrue(pfx.query_vpp_config())
2373
2374         #
2375         # remove everything
2376         #
2377         self.vapi.sw_interface_address_replace_begin()
2378         self.vapi.sw_interface_address_replace_end()
2379         for intf in self.pg_interfaces:
2380             self.assertEqual(self.get_n_pfxs(intf), 0)
2381
2382         #
2383         # add prefixes to each interface. post-begin add the prefix from
2384         # interface X onto interface Y. this would normally be an error
2385         # since it would generate a 'duplicate address' warning. but in
2386         # this case, since what is newly downloaded is sane, it's ok
2387         #
2388         for intf in self.pg_interfaces:
2389             # 172.18.x.1/24
2390             addr = "172.18.%d.1" % intf.sw_if_index
2391             VppIpInterfaceAddress(self, intf, addr, 24).add_vpp_config()
2392
2393         self.vapi.sw_interface_address_replace_begin()
2394
2395         pfxs = []
2396         for intf in self.pg_interfaces:
2397             # 172.18.x.1/24
2398             addr = "172.18.%d.1" % (intf.sw_if_index + 1)
2399             pfxs.append(VppIpInterfaceAddress(self, intf,
2400                                               addr, 24).add_vpp_config())
2401
2402         self.vapi.sw_interface_address_replace_end()
2403
2404         self.logger.info(self.vapi.cli("sh int addr"))
2405
2406         for intf in self.pg_interfaces:
2407             self.assertEqual(self.get_n_pfxs(intf), 1)
2408         for pfx in pfxs:
2409             self.assertTrue(pfx.query_vpp_config())
2410
2411
2412 if __name__ == '__main__':
2413     unittest.main(testRunner=VppTestRunner)