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