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