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