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