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