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