Tests Cleanup: Fix missing calls to setUpClass/tearDownClass.
[vpp.git] / test / test_ip4.py
1 #!/usr/bin/env python
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
19 from vpp_sub_interface import VppSubInterface, VppDot1QSubint, VppDot1ADSubint
20
21
22 class TestIPv4(VppTestCase):
23     """ IPv4 Test Case """
24
25     @classmethod
26     def setUpClass(cls):
27         super(TestIPv4, cls).setUpClass()
28
29     @classmethod
30     def tearDownClass(cls):
31         super(TestIPv4, cls).tearDownClass()
32
33     def setUp(self):
34         """
35         Perform test setup before test case.
36
37         **Config:**
38             - create 3 pg interfaces
39                 - untagged pg0 interface
40                 - Dot1Q subinterface on pg1
41                 - Dot1AD subinterface on pg2
42             - setup interfaces:
43                 - put it into UP state
44                 - set IPv4 addresses
45                 - resolve neighbor address using ARP
46             - configure 200 fib entries
47
48         :ivar list interfaces: pg interfaces and subinterfaces.
49         :ivar dict flows: IPv4 packet flows in test.
50         """
51         super(TestIPv4, self).setUp()
52
53         # create 3 pg interfaces
54         self.create_pg_interfaces(range(3))
55
56         # create 2 subinterfaces for pg1 and pg2
57         self.sub_interfaces = [
58             VppDot1QSubint(self, self.pg1, 100),
59             VppDot1ADSubint(self, self.pg2, 200, 300, 400)]
60
61         # packet flows mapping pg0 -> pg1.sub, pg2.sub, etc.
62         self.flows = dict()
63         self.flows[self.pg0] = [self.pg1.sub_if, self.pg2.sub_if]
64         self.flows[self.pg1.sub_if] = [self.pg0, self.pg2.sub_if]
65         self.flows[self.pg2.sub_if] = [self.pg0, self.pg1.sub_if]
66
67         # packet sizes
68         self.pg_if_packet_sizes = [64, 1500, 9020]
69
70         self.interfaces = list(self.pg_interfaces)
71         self.interfaces.extend(self.sub_interfaces)
72
73         # setup all interfaces
74         for i in self.interfaces:
75             i.admin_up()
76             i.config_ip4()
77             i.resolve_arp()
78
79         # config 2M FIB entries
80         self.config_fib_entries(200)
81
82     def tearDown(self):
83         """Run standard test teardown and log ``show ip arp``."""
84         super(TestIPv4, self).tearDown()
85         if not self.vpp_dead:
86             self.logger.info(self.vapi.cli("show ip arp"))
87             # info(self.vapi.cli("show ip fib"))  # many entries
88
89     def config_fib_entries(self, count):
90         """For each interface add to the FIB table *count* routes to
91         "10.0.0.1/32" destination with interface's local address as next-hop
92         address.
93
94         :param int count: Number of FIB entries.
95
96         - *TODO:* check if the next-hop address shouldn't be remote address
97           instead of local address.
98         """
99         n_int = len(self.interfaces)
100         percent = 0
101         counter = 0.0
102         dest_addr = socket.inet_pton(socket.AF_INET, "10.0.0.1")
103         dest_addr_len = 32
104         for i in self.interfaces:
105             next_hop_address = i.local_ip4n
106             for j in range(count / n_int):
107                 self.vapi.ip_add_del_route(dst_address=dest_addr,
108                                            dst_address_length=dest_addr_len,
109                                            next_hop_address=next_hop_address)
110                 counter += 1
111                 if counter / count * 100 > percent:
112                     self.logger.info("Configure %d FIB entries .. %d%% done" %
113                                      (count, percent))
114                     percent += 1
115
116     def modify_packet(self, src_if, packet_size, pkt):
117         """Add load, set destination IP and extend packet to required packet
118         size for defined interface.
119
120         :param VppInterface src_if: Interface to create packet for.
121         :param int packet_size: Required packet size.
122         :param Scapy pkt: Packet to be modified.
123         """
124         dst_if_idx = packet_size / 10 % 2
125         dst_if = self.flows[src_if][dst_if_idx]
126         info = self.create_packet_info(src_if, dst_if)
127         payload = self.info_to_payload(info)
128         p = pkt/Raw(payload)
129         p[IP].dst = dst_if.remote_ip4
130         info.data = p.copy()
131         if isinstance(src_if, VppSubInterface):
132             p = src_if.add_dot1_layer(p)
133         self.extend_packet(p, packet_size)
134
135         return p
136
137     def create_stream(self, src_if):
138         """Create input packet stream for defined interface.
139
140         :param VppInterface src_if: Interface to create packet stream for.
141         """
142         hdr_ext = 4 if isinstance(src_if, VppSubInterface) else 0
143         pkt_tmpl = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
144                     IP(src=src_if.remote_ip4) /
145                     UDP(sport=1234, dport=1234))
146
147         pkts = [self.modify_packet(src_if, i, pkt_tmpl)
148                 for i in moves.range(self.pg_if_packet_sizes[0],
149                                      self.pg_if_packet_sizes[1], 10)]
150         pkts_b = [self.modify_packet(src_if, i, pkt_tmpl)
151                   for i in moves.range(self.pg_if_packet_sizes[1] + hdr_ext,
152                                        self.pg_if_packet_sizes[2] + hdr_ext,
153                                        50)]
154         pkts.extend(pkts_b)
155
156         return pkts
157
158     def verify_capture(self, dst_if, capture):
159         """Verify captured input packet stream for defined interface.
160
161         :param VppInterface dst_if: Interface to verify captured packet stream
162             for.
163         :param list capture: Captured packet stream.
164         """
165         self.logger.info("Verifying capture on interface %s" % dst_if.name)
166         last_info = dict()
167         for i in self.interfaces:
168             last_info[i.sw_if_index] = None
169         is_sub_if = False
170         dst_sw_if_index = dst_if.sw_if_index
171         if hasattr(dst_if, 'parent'):
172             is_sub_if = True
173         for packet in capture:
174             if is_sub_if:
175                 # Check VLAN tags and Ethernet header
176                 packet = dst_if.remove_dot1_layer(packet)
177             self.assertTrue(Dot1Q not in packet)
178             try:
179                 ip = packet[IP]
180                 udp = packet[UDP]
181                 payload_info = self.payload_to_info(packet[Raw])
182                 packet_index = payload_info.index
183                 self.assertEqual(payload_info.dst, dst_sw_if_index)
184                 self.logger.debug(
185                     "Got packet on port %s: src=%u (id=%u)" %
186                     (dst_if.name, payload_info.src, packet_index))
187                 next_info = self.get_next_packet_info_for_interface2(
188                     payload_info.src, dst_sw_if_index,
189                     last_info[payload_info.src])
190                 last_info[payload_info.src] = next_info
191                 self.assertTrue(next_info is not None)
192                 self.assertEqual(packet_index, next_info.index)
193                 saved_packet = next_info.data
194                 # Check standard fields
195                 self.assertEqual(ip.src, saved_packet[IP].src)
196                 self.assertEqual(ip.dst, saved_packet[IP].dst)
197                 self.assertEqual(udp.sport, saved_packet[UDP].sport)
198                 self.assertEqual(udp.dport, saved_packet[UDP].dport)
199             except:
200                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
201                 raise
202         for i in self.interfaces:
203             remaining_packet = self.get_next_packet_info_for_interface2(
204                 i.sw_if_index, dst_sw_if_index, last_info[i.sw_if_index])
205             self.assertTrue(remaining_packet is None,
206                             "Interface %s: Packet expected from interface %s "
207                             "didn't arrive" % (dst_if.name, i.name))
208
209     def test_fib(self):
210         """ IPv4 FIB test
211
212         Test scenario:
213
214             - Create IPv4 stream for pg0 interface
215             - Create IPv4 tagged streams for pg1's and pg2's sub-interface.
216             - Send and verify received packets on each interface.
217         """
218
219         pkts = self.create_stream(self.pg0)
220         self.pg0.add_stream(pkts)
221
222         for i in self.sub_interfaces:
223             pkts = self.create_stream(i)
224             i.parent.add_stream(pkts)
225
226         self.pg_enable_capture(self.pg_interfaces)
227         self.pg_start()
228
229         pkts = self.pg0.get_capture()
230         self.verify_capture(self.pg0, pkts)
231
232         for i in self.sub_interfaces:
233             pkts = i.parent.get_capture()
234             self.verify_capture(i, pkts)
235
236
237 class TestICMPEcho(VppTestCase):
238     """ ICMP Echo Test Case """
239
240     @classmethod
241     def setUpClass(cls):
242         super(TestICMPEcho, cls).setUpClass()
243
244     @classmethod
245     def tearDownClass(cls):
246         super(TestICMPEcho, cls).tearDownClass()
247
248     def setUp(self):
249         super(TestICMPEcho, self).setUp()
250
251         # create 1 pg interface
252         self.create_pg_interfaces(range(1))
253
254         for i in self.pg_interfaces:
255             i.admin_up()
256             i.config_ip4()
257             i.resolve_arp()
258
259     def tearDown(self):
260         super(TestICMPEcho, self).tearDown()
261         for i in self.pg_interfaces:
262             i.unconfig_ip4()
263             i.admin_down()
264
265     def test_icmp_echo(self):
266         """ VPP replies to ICMP Echo Request
267
268         Test scenario:
269
270             - Receive ICMP Echo Request message on pg0 interface.
271             - Check outgoing ICMP Echo Reply message on pg0 interface.
272         """
273
274         icmp_id = 0xb
275         icmp_seq = 5
276         icmp_load = '\x0a' * 18
277         p_echo_request = (Ether(src=self.pg0.remote_mac,
278                                 dst=self.pg0.local_mac) /
279                           IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
280                           ICMP(id=icmp_id, seq=icmp_seq) /
281                           Raw(load=icmp_load))
282
283         self.pg0.add_stream(p_echo_request)
284         self.pg_enable_capture(self.pg_interfaces)
285         self.pg_start()
286
287         rx = self.pg0.get_capture(1)
288         rx = rx[0]
289         ether = rx[Ether]
290         ipv4 = rx[IP]
291         icmp = rx[ICMP]
292
293         self.assertEqual(ether.src, self.pg0.local_mac)
294         self.assertEqual(ether.dst, self.pg0.remote_mac)
295
296         self.assertEqual(ipv4.src, self.pg0.local_ip4)
297         self.assertEqual(ipv4.dst, self.pg0.remote_ip4)
298
299         self.assertEqual(icmptypes[icmp.type], "echo-reply")
300         self.assertEqual(icmp.id, icmp_id)
301         self.assertEqual(icmp.seq, icmp_seq)
302         self.assertEqual(icmp[Raw].load, icmp_load)
303
304
305 class TestIPv4FibCrud(VppTestCase):
306     """ FIB - add/update/delete - ip4 routes
307
308     Test scenario:
309         - add 1k,
310         - del 100,
311         - add new 1k,
312         - del 1.5k
313
314     ..note:: Python API is too slow to add many routes, needs replacement.
315     """
316
317     def config_fib_many_to_one(self, start_dest_addr, next_hop_addr, count):
318         """
319
320         :param start_dest_addr:
321         :param next_hop_addr:
322         :param count:
323         :return list: added ips with 32 prefix
324         """
325         added_ips = []
326         dest_addr = int(binascii.hexlify(socket.inet_pton(socket.AF_INET,
327                                          start_dest_addr)), 16)
328         dest_addr_len = 32
329         n_next_hop_addr = socket.inet_pton(socket.AF_INET, next_hop_addr)
330         for _ in range(count):
331             n_dest_addr = binascii.unhexlify('{:08x}'.format(dest_addr))
332             self.vapi.ip_add_del_route(dst_address=n_dest_addr,
333                                        dst_address_length=dest_addr_len,
334                                        next_hop_address=n_next_hop_addr)
335             added_ips.append(socket.inet_ntoa(n_dest_addr))
336             dest_addr += 1
337         return added_ips
338
339     def unconfig_fib_many_to_one(self, start_dest_addr, next_hop_addr, count):
340
341         removed_ips = []
342         dest_addr = int(binascii.hexlify(socket.inet_pton(socket.AF_INET,
343                                          start_dest_addr)), 16)
344         dest_addr_len = 32
345         n_next_hop_addr = socket.inet_pton(socket.AF_INET, next_hop_addr)
346         for _ in range(count):
347             n_dest_addr = binascii.unhexlify('{:08x}'.format(dest_addr))
348             self.vapi.ip_add_del_route(dst_address=n_dest_addr,
349                                        dst_address_length=dest_addr_len,
350                                        next_hop_address=n_next_hop_addr,
351                                        is_add=0)
352             removed_ips.append(socket.inet_ntoa(n_dest_addr))
353             dest_addr += 1
354         return removed_ips
355
356     def create_stream(self, src_if, dst_if, dst_ips, count):
357         pkts = []
358
359         for _ in range(count):
360             dst_addr = random.choice(dst_ips)
361             info = self.create_packet_info(src_if, dst_if)
362             payload = self.info_to_payload(info)
363             p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
364                  IP(src=src_if.remote_ip4, dst=dst_addr) /
365                  UDP(sport=1234, dport=1234) /
366                  Raw(payload))
367             info.data = p.copy()
368             self.extend_packet(p, random.choice(self.pg_if_packet_sizes))
369             pkts.append(p)
370
371         return pkts
372
373     def _find_ip_match(self, find_in, pkt):
374         for p in find_in:
375             if self.payload_to_info(p[Raw]) == \
376                     self.payload_to_info(pkt[Raw]):
377                 if p[IP].src != pkt[IP].src:
378                     break
379                 if p[IP].dst != pkt[IP].dst:
380                     break
381                 if p[UDP].sport != pkt[UDP].sport:
382                     break
383                 if p[UDP].dport != pkt[UDP].dport:
384                     break
385                 return p
386         return None
387
388     @staticmethod
389     def _match_route_detail(route_detail, ip, address_length=32, table_id=0):
390         if route_detail.address == socket.inet_pton(socket.AF_INET, ip):
391             if route_detail.table_id != table_id:
392                 return False
393             elif route_detail.address_length != address_length:
394                 return False
395             else:
396                 return True
397         else:
398             return False
399
400     def verify_capture(self, dst_interface, received_pkts, expected_pkts):
401         self.assertEqual(len(received_pkts), len(expected_pkts))
402         to_verify = list(expected_pkts)
403         for p in received_pkts:
404             self.assertEqual(p.src, dst_interface.local_mac)
405             self.assertEqual(p.dst, dst_interface.remote_mac)
406             x = self._find_ip_match(to_verify, p)
407             to_verify.remove(x)
408         self.assertListEqual(to_verify, [])
409
410     def verify_route_dump(self, fib_dump, ips):
411
412         def _ip_in_route_dump(ip, fib_dump):
413             return next((route for route in fib_dump
414                          if self._match_route_detail(route, ip)),
415                         False)
416
417         for ip in ips:
418             self.assertTrue(_ip_in_route_dump(ip, fib_dump),
419                             'IP {!s} is not in fib dump.'.format(ip))
420
421     def verify_not_in_route_dump(self, fib_dump, ips):
422
423         def _ip_in_route_dump(ip, fib_dump):
424             return next((route for route in fib_dump
425                          if self._match_route_detail(route, ip)),
426                         False)
427
428         for ip in ips:
429             self.assertFalse(_ip_in_route_dump(ip, fib_dump),
430                              'IP {!s} is in fib dump.'.format(ip))
431
432     @classmethod
433     def setUpClass(cls):
434         """
435         #. Create and initialize 3 pg interfaces.
436         #. initialize class attributes configured_routes and deleted_routes
437            to store information between tests.
438         """
439         super(TestIPv4FibCrud, cls).setUpClass()
440
441         try:
442             # create 3 pg interfaces
443             cls.create_pg_interfaces(range(3))
444
445             cls.interfaces = list(cls.pg_interfaces)
446
447             # setup all interfaces
448             for i in cls.interfaces:
449                 i.admin_up()
450                 i.config_ip4()
451                 i.resolve_arp()
452
453             cls.configured_routes = []
454             cls.deleted_routes = []
455             cls.pg_if_packet_sizes = [64, 512, 1518, 9018]
456
457         except Exception:
458             super(TestIPv4FibCrud, cls).tearDownClass()
459             raise
460
461     @classmethod
462     def tearDownClass(cls):
463         super(TestIPv4FibCrud, cls).tearDownClass()
464
465     def setUp(self):
466         super(TestIPv4FibCrud, self).setUp()
467         self.reset_packet_infos()
468
469         self.configured_routes = []
470         self.deleted_routes = []
471
472     def test_1_add_routes(self):
473         """ Add 1k routes
474
475         - add 100 routes check with traffic script.
476         """
477         # config 1M FIB entries
478         self.configured_routes.extend(self.config_fib_many_to_one(
479             "10.0.0.0", self.pg0.remote_ip4, 100))
480
481         fib_dump = self.vapi.ip_fib_dump()
482         self.verify_route_dump(fib_dump, self.configured_routes)
483
484         self.stream_1 = self.create_stream(
485             self.pg1, self.pg0, self.configured_routes, 100)
486         self.stream_2 = self.create_stream(
487             self.pg2, self.pg0, self.configured_routes, 100)
488         self.pg1.add_stream(self.stream_1)
489         self.pg2.add_stream(self.stream_2)
490
491         self.pg_enable_capture(self.pg_interfaces)
492         self.pg_start()
493
494         pkts = self.pg0.get_capture(len(self.stream_1) + len(self.stream_2))
495         self.verify_capture(self.pg0, pkts, self.stream_1 + self.stream_2)
496
497     def test_2_del_routes(self):
498         """ Delete 100 routes
499
500         - delete 10 routes check with traffic script.
501         """
502         # config 1M FIB entries
503         self.configured_routes.extend(self.config_fib_many_to_one(
504             "10.0.0.0", self.pg0.remote_ip4, 100))
505         self.deleted_routes.extend(self.unconfig_fib_many_to_one(
506             "10.0.0.10", self.pg0.remote_ip4, 10))
507         for x in self.deleted_routes:
508             self.configured_routes.remove(x)
509
510         fib_dump = self.vapi.ip_fib_dump()
511         self.verify_route_dump(fib_dump, self.configured_routes)
512
513         self.stream_1 = self.create_stream(
514             self.pg1, self.pg0, self.configured_routes, 100)
515         self.stream_2 = self.create_stream(
516             self.pg2, self.pg0, self.configured_routes, 100)
517         self.stream_3 = self.create_stream(
518             self.pg1, self.pg0, self.deleted_routes, 100)
519         self.stream_4 = self.create_stream(
520             self.pg2, self.pg0, self.deleted_routes, 100)
521         self.pg1.add_stream(self.stream_1 + self.stream_3)
522         self.pg2.add_stream(self.stream_2 + self.stream_4)
523         self.pg_enable_capture(self.pg_interfaces)
524         self.pg_start()
525
526         pkts = self.pg0.get_capture(len(self.stream_1) + len(self.stream_2))
527         self.verify_capture(self.pg0, pkts, self.stream_1 + self.stream_2)
528
529     def test_3_add_new_routes(self):
530         """ Add 1k routes
531
532         - re-add 5 routes check with traffic script.
533         - add 100 routes check with traffic script.
534         """
535         # config 1M FIB entries
536         self.configured_routes.extend(self.config_fib_many_to_one(
537             "10.0.0.0", self.pg0.remote_ip4, 100))
538         self.deleted_routes.extend(self.unconfig_fib_many_to_one(
539             "10.0.0.10", self.pg0.remote_ip4, 10))
540         for x in self.deleted_routes:
541             self.configured_routes.remove(x)
542
543         tmp = self.config_fib_many_to_one(
544             "10.0.0.10", self.pg0.remote_ip4, 5)
545         self.configured_routes.extend(tmp)
546         for x in tmp:
547             self.deleted_routes.remove(x)
548
549         self.configured_routes.extend(self.config_fib_many_to_one(
550             "10.0.1.0", self.pg0.remote_ip4, 100))
551
552         fib_dump = self.vapi.ip_fib_dump()
553         self.verify_route_dump(fib_dump, self.configured_routes)
554
555         self.stream_1 = self.create_stream(
556             self.pg1, self.pg0, self.configured_routes, 300)
557         self.stream_2 = self.create_stream(
558             self.pg2, self.pg0, self.configured_routes, 300)
559         self.stream_3 = self.create_stream(
560             self.pg1, self.pg0, self.deleted_routes, 100)
561         self.stream_4 = self.create_stream(
562             self.pg2, self.pg0, self.deleted_routes, 100)
563
564         self.pg1.add_stream(self.stream_1 + self.stream_3)
565         self.pg2.add_stream(self.stream_2 + self.stream_4)
566         self.pg_enable_capture(self.pg_interfaces)
567         self.pg_start()
568
569         pkts = self.pg0.get_capture(len(self.stream_1) + len(self.stream_2))
570         self.verify_capture(self.pg0, pkts, self.stream_1 + self.stream_2)
571
572     def test_4_del_routes(self):
573         """ Delete 1.5k routes
574
575         - delete 5 routes check with traffic script.
576         - add 100 routes check with traffic script.
577         """
578         self.deleted_routes.extend(self.unconfig_fib_many_to_one(
579             "10.0.0.0", self.pg0.remote_ip4, 15))
580         self.deleted_routes.extend(self.unconfig_fib_many_to_one(
581             "10.0.0.20", self.pg0.remote_ip4, 85))
582         self.deleted_routes.extend(self.unconfig_fib_many_to_one(
583             "10.0.1.0", self.pg0.remote_ip4, 100))
584         fib_dump = self.vapi.ip_fib_dump()
585         self.verify_not_in_route_dump(fib_dump, self.deleted_routes)
586
587
588 class TestIPNull(VppTestCase):
589     """ IPv4 routes via NULL """
590
591     @classmethod
592     def setUpClass(cls):
593         super(TestIPNull, cls).setUpClass()
594
595     @classmethod
596     def tearDownClass(cls):
597         super(TestIPNull, cls).tearDownClass()
598
599     def setUp(self):
600         super(TestIPNull, self).setUp()
601
602         # create 2 pg interfaces
603         self.create_pg_interfaces(range(2))
604
605         for i in self.pg_interfaces:
606             i.admin_up()
607             i.config_ip4()
608             i.resolve_arp()
609
610     def tearDown(self):
611         super(TestIPNull, self).tearDown()
612         for i in self.pg_interfaces:
613             i.unconfig_ip4()
614             i.admin_down()
615
616     def test_ip_null(self):
617         """ IP NULL route """
618
619         #
620         # A route via IP NULL that will reply with ICMP unreachables
621         #
622         ip_unreach = VppIpRoute(self, "10.0.0.1", 32, [], is_unreach=1)
623         ip_unreach.add_vpp_config()
624
625         p_unreach = (Ether(src=self.pg0.remote_mac,
626                            dst=self.pg0.local_mac) /
627                      IP(src=self.pg0.remote_ip4, dst="10.0.0.1") /
628                      UDP(sport=1234, dport=1234) /
629                      Raw('\xa5' * 100))
630
631         self.pg0.add_stream(p_unreach)
632         self.pg_enable_capture(self.pg_interfaces)
633         self.pg_start()
634
635         rx = self.pg0.get_capture(1)
636         rx = rx[0]
637         icmp = rx[ICMP]
638
639         self.assertEqual(icmptypes[icmp.type], "dest-unreach")
640         self.assertEqual(icmpcodes[icmp.type][icmp.code], "host-unreachable")
641         self.assertEqual(icmp.src, self.pg0.remote_ip4)
642         self.assertEqual(icmp.dst, "10.0.0.1")
643
644         #
645         # ICMP replies are rate limited. so sit and spin.
646         #
647         self.sleep(1)
648
649         #
650         # A route via IP NULL that will reply with ICMP prohibited
651         #
652         ip_prohibit = VppIpRoute(self, "10.0.0.2", 32, [], is_prohibit=1)
653         ip_prohibit.add_vpp_config()
654
655         p_prohibit = (Ether(src=self.pg0.remote_mac,
656                             dst=self.pg0.local_mac) /
657                       IP(src=self.pg0.remote_ip4, dst="10.0.0.2") /
658                       UDP(sport=1234, dport=1234) /
659                       Raw('\xa5' * 100))
660
661         self.pg0.add_stream(p_prohibit)
662         self.pg_enable_capture(self.pg_interfaces)
663         self.pg_start()
664
665         rx = self.pg0.get_capture(1)
666
667         rx = rx[0]
668         icmp = rx[ICMP]
669
670         self.assertEqual(icmptypes[icmp.type], "dest-unreach")
671         self.assertEqual(icmpcodes[icmp.type][icmp.code], "host-prohibited")
672         self.assertEqual(icmp.src, self.pg0.remote_ip4)
673         self.assertEqual(icmp.dst, "10.0.0.2")
674
675     def test_ip_drop(self):
676         """ IP Drop Routes """
677
678         p = (Ether(src=self.pg0.remote_mac,
679                    dst=self.pg0.local_mac) /
680              IP(src=self.pg0.remote_ip4, dst="1.1.1.1") /
681              UDP(sport=1234, dport=1234) /
682              Raw('\xa5' * 100))
683
684         r1 = VppIpRoute(self, "1.1.1.0", 24,
685                         [VppRoutePath(self.pg1.remote_ip4,
686                                       self.pg1.sw_if_index)])
687         r1.add_vpp_config()
688
689         rx = self.send_and_expect(self.pg0, p * 65, self.pg1)
690
691         #
692         # insert a more specific as a drop
693         #
694         r2 = VppIpRoute(self, "1.1.1.1", 32, [], is_drop=1)
695         r2.add_vpp_config()
696
697         self.send_and_assert_no_replies(self.pg0, p * 65, "Drop Route")
698         r2.remove_vpp_config()
699         rx = self.send_and_expect(self.pg0, p * 65, self.pg1)
700
701
702 class TestIPDisabled(VppTestCase):
703     """ IPv4 disabled """
704
705     @classmethod
706     def setUpClass(cls):
707         super(TestIPDisabled, cls).setUpClass()
708
709     @classmethod
710     def tearDownClass(cls):
711         super(TestIPDisabled, cls).tearDownClass()
712
713     def setUp(self):
714         super(TestIPDisabled, self).setUp()
715
716         # create 2 pg interfaces
717         self.create_pg_interfaces(range(2))
718
719         # PG0 is IP enalbed
720         self.pg0.admin_up()
721         self.pg0.config_ip4()
722         self.pg0.resolve_arp()
723
724         # PG 1 is not IP enabled
725         self.pg1.admin_up()
726
727     def tearDown(self):
728         super(TestIPDisabled, self).tearDown()
729         for i in self.pg_interfaces:
730             i.unconfig_ip4()
731             i.admin_down()
732
733     def test_ip_disabled(self):
734         """ IP Disabled """
735
736         #
737         # An (S,G).
738         # one accepting interface, pg0, 2 forwarding interfaces
739         #
740         route_232_1_1_1 = VppIpMRoute(
741             self,
742             "0.0.0.0",
743             "232.1.1.1", 32,
744             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
745             [VppMRoutePath(self.pg1.sw_if_index,
746                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
747              VppMRoutePath(self.pg0.sw_if_index,
748                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
749         route_232_1_1_1.add_vpp_config()
750
751         pu = (Ether(src=self.pg1.remote_mac,
752                     dst=self.pg1.local_mac) /
753               IP(src="10.10.10.10", dst=self.pg0.remote_ip4) /
754               UDP(sport=1234, dport=1234) /
755               Raw('\xa5' * 100))
756         pm = (Ether(src=self.pg1.remote_mac,
757                     dst=self.pg1.local_mac) /
758               IP(src="10.10.10.10", dst="232.1.1.1") /
759               UDP(sport=1234, dport=1234) /
760               Raw('\xa5' * 100))
761
762         #
763         # PG1 does not forward IP traffic
764         #
765         self.send_and_assert_no_replies(self.pg1, pu, "IP disabled")
766         self.send_and_assert_no_replies(self.pg1, pm, "IP disabled")
767
768         #
769         # IP enable PG1
770         #
771         self.pg1.config_ip4()
772
773         #
774         # Now we get packets through
775         #
776         self.pg1.add_stream(pu)
777         self.pg_enable_capture(self.pg_interfaces)
778         self.pg_start()
779         rx = self.pg0.get_capture(1)
780
781         self.pg1.add_stream(pm)
782         self.pg_enable_capture(self.pg_interfaces)
783         self.pg_start()
784         rx = self.pg0.get_capture(1)
785
786         #
787         # Disable PG1
788         #
789         self.pg1.unconfig_ip4()
790
791         #
792         # PG1 does not forward IP traffic
793         #
794         self.send_and_assert_no_replies(self.pg1, pu, "IP disabled")
795         self.send_and_assert_no_replies(self.pg1, pm, "IP disabled")
796
797
798 class TestIPSubNets(VppTestCase):
799     """ IPv4 Subnets """
800
801     @classmethod
802     def setUpClass(cls):
803         super(TestIPSubNets, cls).setUpClass()
804
805     @classmethod
806     def tearDownClass(cls):
807         super(TestIPSubNets, cls).tearDownClass()
808
809     def setUp(self):
810         super(TestIPSubNets, self).setUp()
811
812         # create a 2 pg interfaces
813         self.create_pg_interfaces(range(2))
814
815         # pg0 we will use to experiment
816         self.pg0.admin_up()
817
818         # pg1 is setup normally
819         self.pg1.admin_up()
820         self.pg1.config_ip4()
821         self.pg1.resolve_arp()
822
823     def tearDown(self):
824         super(TestIPSubNets, self).tearDown()
825         for i in self.pg_interfaces:
826             i.admin_down()
827
828     def test_ip_sub_nets(self):
829         """ IP Sub Nets """
830
831         #
832         # Configure a covering route to forward so we know
833         # when we are dropping
834         #
835         cover_route = VppIpRoute(self, "10.0.0.0", 8,
836                                  [VppRoutePath(self.pg1.remote_ip4,
837                                                self.pg1.sw_if_index)])
838         cover_route.add_vpp_config()
839
840         p = (Ether(src=self.pg1.remote_mac,
841                    dst=self.pg1.local_mac) /
842              IP(dst="10.10.10.10", src=self.pg0.local_ip4) /
843              UDP(sport=1234, dport=1234) /
844              Raw('\xa5' * 100))
845
846         self.pg1.add_stream(p)
847         self.pg_enable_capture(self.pg_interfaces)
848         self.pg_start()
849         rx = self.pg1.get_capture(1)
850
851         #
852         # Configure some non-/24 subnets on an IP interface
853         #
854         ip_addr_n = socket.inet_pton(socket.AF_INET, "10.10.10.10")
855
856         self.vapi.sw_interface_add_del_address(
857             sw_if_index=self.pg0.sw_if_index, address=ip_addr_n,
858             address_length=16)
859
860         pn = (Ether(src=self.pg1.remote_mac,
861                     dst=self.pg1.local_mac) /
862               IP(dst="10.10.0.0", src=self.pg0.local_ip4) /
863               UDP(sport=1234, dport=1234) /
864               Raw('\xa5' * 100))
865         pb = (Ether(src=self.pg1.remote_mac,
866                     dst=self.pg1.local_mac) /
867               IP(dst="10.10.255.255", src=self.pg0.local_ip4) /
868               UDP(sport=1234, dport=1234) /
869               Raw('\xa5' * 100))
870
871         self.send_and_assert_no_replies(self.pg1, pn, "IP Network address")
872         self.send_and_assert_no_replies(self.pg1, pb, "IP Broadcast address")
873
874         # remove the sub-net and we are forwarding via the cover again
875         self.vapi.sw_interface_add_del_address(
876             sw_if_index=self.pg0.sw_if_index, address=ip_addr_n,
877             address_length=16, is_add=0)
878         self.pg1.add_stream(pn)
879         self.pg_enable_capture(self.pg_interfaces)
880         self.pg_start()
881         rx = self.pg1.get_capture(1)
882         self.pg1.add_stream(pb)
883         self.pg_enable_capture(self.pg_interfaces)
884         self.pg_start()
885         rx = self.pg1.get_capture(1)
886
887         #
888         # A /31 is a special case where the 'other-side' is an attached host
889         # packets to that peer generate ARP requests
890         #
891         ip_addr_n = socket.inet_pton(socket.AF_INET, "10.10.10.10")
892
893         self.vapi.sw_interface_add_del_address(
894             sw_if_index=self.pg0.sw_if_index, address=ip_addr_n,
895             address_length=31)
896
897         pn = (Ether(src=self.pg1.remote_mac,
898                     dst=self.pg1.local_mac) /
899               IP(dst="10.10.10.11", src=self.pg0.local_ip4) /
900               UDP(sport=1234, dport=1234) /
901               Raw('\xa5' * 100))
902
903         self.pg1.add_stream(pn)
904         self.pg_enable_capture(self.pg_interfaces)
905         self.pg_start()
906         rx = self.pg0.get_capture(1)
907         rx[ARP]
908
909         # remove the sub-net and we are forwarding via the cover again
910         self.vapi.sw_interface_add_del_address(
911             sw_if_index=self.pg0.sw_if_index, address=ip_addr_n,
912             address_length=31, is_add=0)
913         self.pg1.add_stream(pn)
914         self.pg_enable_capture(self.pg_interfaces)
915         self.pg_start()
916         rx = self.pg1.get_capture(1)
917
918
919 class TestIPLoadBalance(VppTestCase):
920     """ IPv4 Load-Balancing """
921
922     @classmethod
923     def setUpClass(cls):
924         super(TestIPLoadBalance, cls).setUpClass()
925
926     @classmethod
927     def tearDownClass(cls):
928         super(TestIPLoadBalance, cls).tearDownClass()
929
930     def setUp(self):
931         super(TestIPLoadBalance, self).setUp()
932
933         self.create_pg_interfaces(range(5))
934         mpls_tbl = VppMplsTable(self, 0)
935         mpls_tbl.add_vpp_config()
936
937         for i in self.pg_interfaces:
938             i.admin_up()
939             i.config_ip4()
940             i.resolve_arp()
941             i.enable_mpls()
942
943     def tearDown(self):
944         for i in self.pg_interfaces:
945             i.disable_mpls()
946             i.unconfig_ip4()
947             i.admin_down()
948         super(TestIPLoadBalance, self).tearDown()
949
950     def send_and_expect_load_balancing(self, input, pkts, outputs):
951         input.add_stream(pkts)
952         self.pg_enable_capture(self.pg_interfaces)
953         self.pg_start()
954         rxs = []
955         for oo in outputs:
956             rx = oo._get_capture(1)
957             self.assertNotEqual(0, len(rx))
958             for r in rx:
959                 rxs.append(r)
960         return rxs
961
962     def send_and_expect_one_itf(self, input, pkts, itf):
963         input.add_stream(pkts)
964         self.pg_enable_capture(self.pg_interfaces)
965         self.pg_start()
966         rx = itf.get_capture(len(pkts))
967
968     def test_ip_load_balance(self):
969         """ IP Load-Balancing """
970
971         #
972         # An array of packets that differ only in the destination port
973         #
974         port_ip_pkts = []
975         port_mpls_pkts = []
976
977         #
978         # An array of packets that differ only in the source address
979         #
980         src_ip_pkts = []
981         src_mpls_pkts = []
982
983         for ii in range(65):
984             port_ip_hdr = (IP(dst="10.0.0.1", src="20.0.0.1") /
985                            UDP(sport=1234, dport=1234 + ii) /
986                            Raw('\xa5' * 100))
987             port_ip_pkts.append((Ether(src=self.pg0.remote_mac,
988                                        dst=self.pg0.local_mac) /
989                                  port_ip_hdr))
990             port_mpls_pkts.append((Ether(src=self.pg0.remote_mac,
991                                          dst=self.pg0.local_mac) /
992                                    MPLS(label=66, ttl=2) /
993                                    port_ip_hdr))
994
995             src_ip_hdr = (IP(dst="10.0.0.1", src="20.0.0.%d" % ii) /
996                           UDP(sport=1234, dport=1234) /
997                           Raw('\xa5' * 100))
998             src_ip_pkts.append((Ether(src=self.pg0.remote_mac,
999                                       dst=self.pg0.local_mac) /
1000                                 src_ip_hdr))
1001             src_mpls_pkts.append((Ether(src=self.pg0.remote_mac,
1002                                         dst=self.pg0.local_mac) /
1003                                   MPLS(label=66, ttl=2) /
1004                                   src_ip_hdr))
1005
1006         route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
1007                                     [VppRoutePath(self.pg1.remote_ip4,
1008                                                   self.pg1.sw_if_index),
1009                                      VppRoutePath(self.pg2.remote_ip4,
1010                                                   self.pg2.sw_if_index)])
1011         route_10_0_0_1.add_vpp_config()
1012
1013         binding = VppMplsIpBind(self, 66, "10.0.0.1", 32)
1014         binding.add_vpp_config()
1015
1016         #
1017         # inject the packet on pg0 - expect load-balancing across the 2 paths
1018         #  - since the default hash config is to use IP src,dst and port
1019         #    src,dst
1020         # We are not going to ensure equal amounts of packets across each link,
1021         # since the hash algorithm is statistical and therefore this can never
1022         # be guaranteed. But with 64 different packets we do expect some
1023         # balancing. So instead just ensure there is traffic on each link.
1024         #
1025         self.send_and_expect_load_balancing(self.pg0, port_ip_pkts,
1026                                             [self.pg1, self.pg2])
1027         self.send_and_expect_load_balancing(self.pg0, src_ip_pkts,
1028                                             [self.pg1, self.pg2])
1029         self.send_and_expect_load_balancing(self.pg0, port_mpls_pkts,
1030                                             [self.pg1, self.pg2])
1031         self.send_and_expect_load_balancing(self.pg0, src_mpls_pkts,
1032                                             [self.pg1, self.pg2])
1033
1034         #
1035         # change the flow hash config so it's only IP src,dst
1036         #  - now only the stream with differing source address will
1037         #    load-balance
1038         #
1039         self.vapi.set_ip_flow_hash(vrf_id=0, src=1, dst=1, sport=0, dport=0)
1040
1041         self.send_and_expect_load_balancing(self.pg0, src_ip_pkts,
1042                                             [self.pg1, self.pg2])
1043         self.send_and_expect_load_balancing(self.pg0, src_mpls_pkts,
1044                                             [self.pg1, self.pg2])
1045
1046         self.send_and_expect_one_itf(self.pg0, port_ip_pkts, self.pg2)
1047
1048         #
1049         # change the flow hash config back to defaults
1050         #
1051         self.vapi.set_ip_flow_hash(vrf_id=0, src=1, dst=1, sport=1, dport=1)
1052
1053         #
1054         # Recursive prefixes
1055         #  - testing that 2 stages of load-balancing occurs and there is no
1056         #    polarisation (i.e. only 2 of 4 paths are used)
1057         #
1058         port_pkts = []
1059         src_pkts = []
1060
1061         for ii in range(257):
1062             port_pkts.append((Ether(src=self.pg0.remote_mac,
1063                                     dst=self.pg0.local_mac) /
1064                               IP(dst="1.1.1.1", src="20.0.0.1") /
1065                               UDP(sport=1234, dport=1234 + ii) /
1066                               Raw('\xa5' * 100)))
1067             src_pkts.append((Ether(src=self.pg0.remote_mac,
1068                                    dst=self.pg0.local_mac) /
1069                              IP(dst="1.1.1.1", src="20.0.0.%d" % ii) /
1070                              UDP(sport=1234, dport=1234) /
1071                              Raw('\xa5' * 100)))
1072
1073         route_10_0_0_2 = VppIpRoute(self, "10.0.0.2", 32,
1074                                     [VppRoutePath(self.pg3.remote_ip4,
1075                                                   self.pg3.sw_if_index),
1076                                      VppRoutePath(self.pg4.remote_ip4,
1077                                                   self.pg4.sw_if_index)])
1078         route_10_0_0_2.add_vpp_config()
1079
1080         route_1_1_1_1 = VppIpRoute(self, "1.1.1.1", 32,
1081                                    [VppRoutePath("10.0.0.2", 0xffffffff),
1082                                     VppRoutePath("10.0.0.1", 0xffffffff)])
1083         route_1_1_1_1.add_vpp_config()
1084
1085         #
1086         # inject the packet on pg0 - expect load-balancing across all 4 paths
1087         #
1088         self.vapi.cli("clear trace")
1089         self.send_and_expect_load_balancing(self.pg0, port_pkts,
1090                                             [self.pg1, self.pg2,
1091                                              self.pg3, self.pg4])
1092         self.send_and_expect_load_balancing(self.pg0, src_pkts,
1093                                             [self.pg1, self.pg2,
1094                                              self.pg3, self.pg4])
1095
1096         #
1097         # bring down pg1 expect LB to adjust to use only those that are pu
1098         #
1099         self.pg1.link_down()
1100
1101         rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
1102                                                  [self.pg2, self.pg3,
1103                                                   self.pg4])
1104         self.assertEqual(len(src_pkts), len(rx))
1105
1106         #
1107         # bring down pg2 expect LB to adjust to use only those that are pu
1108         #
1109         self.pg2.link_down()
1110
1111         rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
1112                                                  [self.pg3, self.pg4])
1113         self.assertEqual(len(src_pkts), len(rx))
1114
1115         #
1116         # bring the links back up - expect LB over all again
1117         #
1118         self.pg1.link_up()
1119         self.pg2.link_up()
1120
1121         rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
1122                                                  [self.pg1, self.pg2,
1123                                                   self.pg3, self.pg4])
1124         self.assertEqual(len(src_pkts), len(rx))
1125
1126         #
1127         # The same link-up/down but this time admin state
1128         #
1129         self.pg1.admin_down()
1130         self.pg2.admin_down()
1131         rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
1132                                                  [self.pg3, self.pg4])
1133         self.assertEqual(len(src_pkts), len(rx))
1134         self.pg1.admin_up()
1135         self.pg2.admin_up()
1136         self.pg1.resolve_arp()
1137         self.pg2.resolve_arp()
1138         rx = self.send_and_expect_load_balancing(self.pg0, src_pkts,
1139                                                  [self.pg1, self.pg2,
1140                                                   self.pg3, self.pg4])
1141         self.assertEqual(len(src_pkts), len(rx))
1142
1143         #
1144         # Recursive prefixes
1145         #  - testing that 2 stages of load-balancing, no choices
1146         #
1147         port_pkts = []
1148
1149         for ii in range(257):
1150             port_pkts.append((Ether(src=self.pg0.remote_mac,
1151                                     dst=self.pg0.local_mac) /
1152                               IP(dst="1.1.1.2", src="20.0.0.2") /
1153                               UDP(sport=1234, dport=1234 + ii) /
1154                               Raw('\xa5' * 100)))
1155
1156         route_10_0_0_3 = VppIpRoute(self, "10.0.0.3", 32,
1157                                     [VppRoutePath(self.pg3.remote_ip4,
1158                                                   self.pg3.sw_if_index)])
1159         route_10_0_0_3.add_vpp_config()
1160
1161         route_1_1_1_2 = VppIpRoute(self, "1.1.1.2", 32,
1162                                    [VppRoutePath("10.0.0.3", 0xffffffff)])
1163         route_1_1_1_2.add_vpp_config()
1164
1165         #
1166         # inject the packet on pg0 - rx only on via routes output interface
1167         #
1168         self.vapi.cli("clear trace")
1169         self.send_and_expect_one_itf(self.pg0, port_pkts, self.pg3)
1170
1171         #
1172         # Add a LB route in the presence of a down link - expect no
1173         # packets over the down link
1174         #
1175         self.pg3.link_down()
1176
1177         route_10_0_0_3 = VppIpRoute(self, "10.0.0.3", 32,
1178                                     [VppRoutePath(self.pg3.remote_ip4,
1179                                                   self.pg3.sw_if_index),
1180                                      VppRoutePath(self.pg4.remote_ip4,
1181                                                   self.pg4.sw_if_index)])
1182         route_10_0_0_3.add_vpp_config()
1183
1184         port_pkts = []
1185         for ii in range(257):
1186             port_pkts.append(Ether(src=self.pg0.remote_mac,
1187                                    dst=self.pg0.local_mac) /
1188                              IP(dst="10.0.0.3", src="20.0.0.2") /
1189                              UDP(sport=1234, dport=1234 + ii) /
1190                              Raw('\xa5' * 100))
1191
1192         self.send_and_expect_one_itf(self.pg0, port_pkts, self.pg4)
1193
1194         # bring the link back up
1195         self.pg3.link_up()
1196
1197         rx = self.send_and_expect_load_balancing(self.pg0, port_pkts,
1198                                                  [self.pg3, self.pg4])
1199         self.assertEqual(len(src_pkts), len(rx))
1200
1201
1202 class TestIPVlan0(VppTestCase):
1203     """ IPv4 VLAN-0 """
1204
1205     @classmethod
1206     def setUpClass(cls):
1207         super(TestIPVlan0, cls).setUpClass()
1208
1209     @classmethod
1210     def tearDownClass(cls):
1211         super(TestIPVlan0, cls).tearDownClass()
1212
1213     def setUp(self):
1214         super(TestIPVlan0, self).setUp()
1215
1216         self.create_pg_interfaces(range(2))
1217         mpls_tbl = VppMplsTable(self, 0)
1218         mpls_tbl.add_vpp_config()
1219
1220         for i in self.pg_interfaces:
1221             i.admin_up()
1222             i.config_ip4()
1223             i.resolve_arp()
1224             i.enable_mpls()
1225
1226     def tearDown(self):
1227         for i in self.pg_interfaces:
1228             i.disable_mpls()
1229             i.unconfig_ip4()
1230             i.admin_down()
1231         super(TestIPVlan0, self).tearDown()
1232
1233     def test_ip_vlan_0(self):
1234         """ IP VLAN-0 """
1235
1236         pkts = (Ether(src=self.pg0.remote_mac,
1237                       dst=self.pg0.local_mac) /
1238                 Dot1Q(vlan=0) /
1239                 IP(dst=self.pg1.remote_ip4,
1240                    src=self.pg0.remote_ip4) /
1241                 UDP(sport=1234, dport=1234) /
1242                 Raw('\xa5' * 100)) * 65
1243
1244         #
1245         # Expect that packets sent on VLAN-0 are forwarded on the
1246         # main interface.
1247         #
1248         self.send_and_expect(self.pg0, pkts, self.pg1)
1249
1250
1251 class TestIPPunt(VppTestCase):
1252     """ IPv4 Punt Police/Redirect """
1253
1254     @classmethod
1255     def setUpClass(cls):
1256         super(TestIPPunt, cls).setUpClass()
1257
1258     @classmethod
1259     def tearDownClass(cls):
1260         super(TestIPPunt, cls).tearDownClass()
1261
1262     def setUp(self):
1263         super(TestIPPunt, self).setUp()
1264
1265         self.create_pg_interfaces(range(4))
1266
1267         for i in self.pg_interfaces:
1268             i.admin_up()
1269             i.config_ip4()
1270             i.resolve_arp()
1271
1272     def tearDown(self):
1273         super(TestIPPunt, self).tearDown()
1274         for i in self.pg_interfaces:
1275             i.unconfig_ip4()
1276             i.admin_down()
1277
1278     def test_ip_punt(self):
1279         """ IP punt police and redirect """
1280
1281         p = (Ether(src=self.pg0.remote_mac,
1282                    dst=self.pg0.local_mac) /
1283              IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
1284              TCP(sport=1234, dport=1234) /
1285              Raw('\xa5' * 100))
1286
1287         pkts = p * 1025
1288
1289         #
1290         # Configure a punt redirect via pg1.
1291         #
1292         nh_addr = self.pg1.remote_ip4
1293         self.vapi.ip_punt_redirect(self.pg0.sw_if_index,
1294                                    self.pg1.sw_if_index,
1295                                    nh_addr)
1296
1297         self.send_and_expect(self.pg0, pkts, self.pg1)
1298
1299         #
1300         # add a policer
1301         #
1302         policer = self.vapi.policer_add_del(b"ip4-punt", 400, 0, 10, 0,
1303                                             rate_type=1)
1304         self.vapi.ip_punt_police(policer.policer_index)
1305
1306         self.vapi.cli("clear trace")
1307         self.pg0.add_stream(pkts)
1308         self.pg_enable_capture(self.pg_interfaces)
1309         self.pg_start()
1310
1311         #
1312         # the number of packet received should be greater than 0,
1313         # but not equal to the number sent, since some were policed
1314         #
1315         rx = self.pg1._get_capture(1)
1316         self.assertGreater(len(rx), 0)
1317         self.assertLess(len(rx), len(pkts))
1318
1319         #
1320         # remove the policer. back to full rx
1321         #
1322         self.vapi.ip_punt_police(policer.policer_index, is_add=0)
1323         self.vapi.policer_add_del(b"ip4-punt", 400, 0, 10, 0,
1324                                   rate_type=1, is_add=0)
1325         self.send_and_expect(self.pg0, pkts, self.pg1)
1326
1327         #
1328         # remove the redirect. expect full drop.
1329         #
1330         self.vapi.ip_punt_redirect(self.pg0.sw_if_index,
1331                                    self.pg1.sw_if_index,
1332                                    nh_addr,
1333                                    is_add=0)
1334         self.send_and_assert_no_replies(self.pg0, pkts,
1335                                         "IP no punt config")
1336
1337         #
1338         # Add a redirect that is not input port selective
1339         #
1340         self.vapi.ip_punt_redirect(0xffffffff,
1341                                    self.pg1.sw_if_index,
1342                                    nh_addr)
1343         self.send_and_expect(self.pg0, pkts, self.pg1)
1344
1345         self.vapi.ip_punt_redirect(0xffffffff,
1346                                    self.pg1.sw_if_index,
1347                                    nh_addr,
1348                                    is_add=0)
1349
1350     def test_ip_punt_dump(self):
1351         """ IP4 punt redirect dump"""
1352
1353         #
1354         # Configure a punt redirects
1355         #
1356         nh_address = self.pg3.remote_ip4
1357         self.vapi.ip_punt_redirect(self.pg0.sw_if_index,
1358                                    self.pg3.sw_if_index,
1359                                    nh_address)
1360         self.vapi.ip_punt_redirect(self.pg1.sw_if_index,
1361                                    self.pg3.sw_if_index,
1362                                    nh_address)
1363         self.vapi.ip_punt_redirect(self.pg2.sw_if_index,
1364                                    self.pg3.sw_if_index,
1365                                    '0.0.0.0')
1366
1367         #
1368         # Dump pg0 punt redirects
1369         #
1370         punts = self.vapi.ip_punt_redirect_dump(self.pg0.sw_if_index)
1371         for p in punts:
1372             self.assertEqual(p.punt.rx_sw_if_index, self.pg0.sw_if_index)
1373
1374         #
1375         # Dump punt redirects for all interfaces
1376         #
1377         punts = self.vapi.ip_punt_redirect_dump(0xffffffff)
1378         self.assertEqual(len(punts), 3)
1379         for p in punts:
1380             self.assertEqual(p.punt.tx_sw_if_index, self.pg3.sw_if_index)
1381         self.assertNotEqual(punts[1].punt.nh, self.pg3.remote_ip4)
1382         self.assertEqual(str(punts[2].punt.nh), '0.0.0.0')
1383
1384
1385 class TestIPDeag(VppTestCase):
1386     """ IPv4 Deaggregate Routes """
1387
1388     @classmethod
1389     def setUpClass(cls):
1390         super(TestIPDeag, cls).setUpClass()
1391
1392     @classmethod
1393     def tearDownClass(cls):
1394         super(TestIPDeag, cls).tearDownClass()
1395
1396     def setUp(self):
1397         super(TestIPDeag, self).setUp()
1398
1399         self.create_pg_interfaces(range(3))
1400
1401         for i in self.pg_interfaces:
1402             i.admin_up()
1403             i.config_ip4()
1404             i.resolve_arp()
1405
1406     def tearDown(self):
1407         super(TestIPDeag, self).tearDown()
1408         for i in self.pg_interfaces:
1409             i.unconfig_ip4()
1410             i.admin_down()
1411
1412     def test_ip_deag(self):
1413         """ IP Deag Routes """
1414
1415         #
1416         # Create a table to be used for:
1417         #  1 - another destination address lookup
1418         #  2 - a source address lookup
1419         #
1420         table_dst = VppIpTable(self, 1)
1421         table_src = VppIpTable(self, 2)
1422         table_dst.add_vpp_config()
1423         table_src.add_vpp_config()
1424
1425         #
1426         # Add a route in the default table to point to a deag/
1427         # second lookup in each of these tables
1428         #
1429         route_to_dst = VppIpRoute(self, "1.1.1.1", 32,
1430                                   [VppRoutePath("0.0.0.0",
1431                                                 0xffffffff,
1432                                                 nh_table_id=1)])
1433         route_to_src = VppIpRoute(self, "1.1.1.2", 32,
1434                                   [VppRoutePath("0.0.0.0",
1435                                                 0xffffffff,
1436                                                 nh_table_id=2,
1437                                                 is_source_lookup=1)])
1438         route_to_dst.add_vpp_config()
1439         route_to_src.add_vpp_config()
1440
1441         #
1442         # packets to these destination are dropped, since they'll
1443         # hit the respective default routes in the second table
1444         #
1445         p_dst = (Ether(src=self.pg0.remote_mac,
1446                        dst=self.pg0.local_mac) /
1447                  IP(src="5.5.5.5", dst="1.1.1.1") /
1448                  TCP(sport=1234, dport=1234) /
1449                  Raw('\xa5' * 100))
1450         p_src = (Ether(src=self.pg0.remote_mac,
1451                        dst=self.pg0.local_mac) /
1452                  IP(src="2.2.2.2", dst="1.1.1.2") /
1453                  TCP(sport=1234, dport=1234) /
1454                  Raw('\xa5' * 100))
1455         pkts_dst = p_dst * 257
1456         pkts_src = p_src * 257
1457
1458         self.send_and_assert_no_replies(self.pg0, pkts_dst,
1459                                         "IP in dst table")
1460         self.send_and_assert_no_replies(self.pg0, pkts_src,
1461                                         "IP in src table")
1462
1463         #
1464         # add a route in the dst table to forward via pg1
1465         #
1466         route_in_dst = VppIpRoute(self, "1.1.1.1", 32,
1467                                   [VppRoutePath(self.pg1.remote_ip4,
1468                                                 self.pg1.sw_if_index)],
1469                                   table_id=1)
1470         route_in_dst.add_vpp_config()
1471         self.send_and_expect(self.pg0, pkts_dst, self.pg1)
1472
1473         #
1474         # add a route in the src table to forward via pg2
1475         #
1476         route_in_src = VppIpRoute(self, "2.2.2.2", 32,
1477                                   [VppRoutePath(self.pg2.remote_ip4,
1478                                                 self.pg2.sw_if_index)],
1479                                   table_id=2)
1480         route_in_src.add_vpp_config()
1481         self.send_and_expect(self.pg0, pkts_src, self.pg2)
1482
1483         #
1484         # loop in the lookup DP
1485         #
1486         route_loop = VppIpRoute(self, "2.2.2.3", 32,
1487                                 [VppRoutePath("0.0.0.0",
1488                                               0xffffffff,
1489                                               nh_table_id=0)])
1490         route_loop.add_vpp_config()
1491
1492         p_l = (Ether(src=self.pg0.remote_mac,
1493                      dst=self.pg0.local_mac) /
1494                IP(src="2.2.2.4", dst="2.2.2.3") /
1495                TCP(sport=1234, dport=1234) /
1496                Raw('\xa5' * 100))
1497
1498         self.send_and_assert_no_replies(self.pg0, p_l * 257,
1499                                         "IP lookup loop")
1500
1501
1502 class TestIPInput(VppTestCase):
1503     """ IPv4 Input Exceptions """
1504
1505     @classmethod
1506     def setUpClass(cls):
1507         super(TestIPInput, cls).setUpClass()
1508
1509     @classmethod
1510     def tearDownClass(cls):
1511         super(TestIPInput, cls).tearDownClass()
1512
1513     def setUp(self):
1514         super(TestIPInput, self).setUp()
1515
1516         self.create_pg_interfaces(range(2))
1517
1518         for i in self.pg_interfaces:
1519             i.admin_up()
1520             i.config_ip4()
1521             i.resolve_arp()
1522
1523     def tearDown(self):
1524         super(TestIPInput, self).tearDown()
1525         for i in self.pg_interfaces:
1526             i.unconfig_ip4()
1527             i.admin_down()
1528
1529     def test_ip_input(self):
1530         """ IP Input Exceptions """
1531
1532         # i can't find a way in scapy to construct an IP packet
1533         # with a length less than the IP header length
1534
1535         #
1536         # Packet too short - this is forwarded
1537         #
1538         p_short = (Ether(src=self.pg0.remote_mac,
1539                          dst=self.pg0.local_mac) /
1540                    IP(src=self.pg0.remote_ip4,
1541                       dst=self.pg1.remote_ip4,
1542                       len=40) /
1543                    UDP(sport=1234, dport=1234) /
1544                    Raw('\xa5' * 100))
1545
1546         rx = self.send_and_expect(self.pg0, p_short * 65, self.pg1)
1547
1548         #
1549         # Packet too long - this is dropped
1550         #
1551         p_long = (Ether(src=self.pg0.remote_mac,
1552                         dst=self.pg0.local_mac) /
1553                   IP(src=self.pg0.remote_ip4,
1554                      dst=self.pg1.remote_ip4,
1555                      len=400) /
1556                   UDP(sport=1234, dport=1234) /
1557                   Raw('\xa5' * 100))
1558
1559         rx = self.send_and_assert_no_replies(self.pg0, p_long * 65,
1560                                              "too long")
1561
1562         #
1563         # bad chksum - this is dropped
1564         #
1565         p_chksum = (Ether(src=self.pg0.remote_mac,
1566                           dst=self.pg0.local_mac) /
1567                     IP(src=self.pg0.remote_ip4,
1568                        dst=self.pg1.remote_ip4,
1569                        chksum=400) /
1570                     UDP(sport=1234, dport=1234) /
1571                     Raw('\xa5' * 100))
1572
1573         rx = self.send_and_assert_no_replies(self.pg0, p_chksum * 65,
1574                                              "bad checksum")
1575
1576         #
1577         # bad version - this is dropped
1578         #
1579         p_ver = (Ether(src=self.pg0.remote_mac,
1580                        dst=self.pg0.local_mac) /
1581                  IP(src=self.pg0.remote_ip4,
1582                     dst=self.pg1.remote_ip4,
1583                     version=3) /
1584                  UDP(sport=1234, dport=1234) /
1585                  Raw('\xa5' * 100))
1586
1587         rx = self.send_and_assert_no_replies(self.pg0, p_ver * 65,
1588                                              "funky version")
1589
1590         #
1591         # fragment offset 1 - this is dropped
1592         #
1593         p_frag = (Ether(src=self.pg0.remote_mac,
1594                         dst=self.pg0.local_mac) /
1595                   IP(src=self.pg0.remote_ip4,
1596                      dst=self.pg1.remote_ip4,
1597                      frag=1) /
1598                   UDP(sport=1234, dport=1234) /
1599                   Raw('\xa5' * 100))
1600
1601         rx = self.send_and_assert_no_replies(self.pg0, p_frag * 65,
1602                                              "frag offset")
1603
1604         #
1605         # TTL expired packet
1606         #
1607         p_ttl = (Ether(src=self.pg0.remote_mac,
1608                        dst=self.pg0.local_mac) /
1609                  IP(src=self.pg0.remote_ip4,
1610                     dst=self.pg1.remote_ip4,
1611                     ttl=1) /
1612                  UDP(sport=1234, dport=1234) /
1613                  Raw('\xa5' * 100))
1614
1615         rx = self.send_and_expect(self.pg0, p_ttl * 65, self.pg0)
1616
1617         rx = rx[0]
1618         icmp = rx[ICMP]
1619
1620         self.assertEqual(icmptypes[icmp.type], "time-exceeded")
1621         self.assertEqual(icmpcodes[icmp.type][icmp.code],
1622                          "ttl-zero-during-transit")
1623         self.assertEqual(icmp.src, self.pg0.remote_ip4)
1624         self.assertEqual(icmp.dst, self.pg1.remote_ip4)
1625
1626         #
1627         # MTU exceeded
1628         #
1629         p_mtu = (Ether(src=self.pg0.remote_mac,
1630                        dst=self.pg0.local_mac) /
1631                  IP(src=self.pg0.remote_ip4,
1632                     dst=self.pg1.remote_ip4,
1633                     ttl=10, flags='DF') /
1634                  UDP(sport=1234, dport=1234) /
1635                  Raw('\xa5' * 2000))
1636
1637         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [1500, 0, 0, 0])
1638
1639         rx = self.send_and_expect(self.pg0, p_mtu * 65, self.pg0)
1640         rx = rx[0]
1641         icmp = rx[ICMP]
1642
1643         self.assertEqual(icmptypes[icmp.type], "dest-unreach")
1644         self.assertEqual(icmpcodes[icmp.type][icmp.code],
1645                          "fragmentation-needed")
1646         self.assertEqual(icmp.src, self.pg0.remote_ip4)
1647         self.assertEqual(icmp.dst, self.pg1.remote_ip4)
1648
1649         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [2500, 0, 0, 0])
1650         rx = self.send_and_expect(self.pg0, p_mtu * 65, self.pg1)
1651
1652         # Reset MTU for subsequent tests
1653         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [9000, 0, 0, 0])
1654
1655         #
1656         # source address 0.0.0.0 and 25.255.255.255 and for-us
1657         #
1658         p_s0 = (Ether(src=self.pg0.remote_mac,
1659                       dst=self.pg0.local_mac) /
1660                 IP(src="0.0.0.0",
1661                    dst=self.pg0.local_ip4) /
1662                 ICMP(id=4, seq=4) /
1663                 Raw(load='\x0a' * 18))
1664         rx = self.send_and_assert_no_replies(self.pg0, p_s0 * 17)
1665
1666         p_s0 = (Ether(src=self.pg0.remote_mac,
1667                       dst=self.pg0.local_mac) /
1668                 IP(src="255.255.255.255",
1669                    dst=self.pg0.local_ip4) /
1670                 ICMP(id=4, seq=4) /
1671                 Raw(load='\x0a' * 18))
1672         rx = self.send_and_assert_no_replies(self.pg0, p_s0 * 17)
1673
1674
1675 class TestIPDirectedBroadcast(VppTestCase):
1676     """ IPv4 Directed Broadcast """
1677
1678     @classmethod
1679     def setUpClass(cls):
1680         super(TestIPDirectedBroadcast, cls).setUpClass()
1681
1682     @classmethod
1683     def tearDownClass(cls):
1684         super(TestIPDirectedBroadcast, cls).tearDownClass()
1685
1686     def setUp(self):
1687         super(TestIPDirectedBroadcast, self).setUp()
1688
1689         self.create_pg_interfaces(range(2))
1690
1691         for i in self.pg_interfaces:
1692             i.admin_up()
1693
1694     def tearDown(self):
1695         super(TestIPDirectedBroadcast, self).tearDown()
1696         for i in self.pg_interfaces:
1697             i.admin_down()
1698
1699     def test_ip_input(self):
1700         """ IP Directed Broadcast """
1701
1702         #
1703         # set the directed broadcast on pg0 first, then config IP4 addresses
1704         # for pg1 directed broadcast is always disabled
1705         self.vapi.sw_interface_set_ip_directed_broadcast(
1706             self.pg0.sw_if_index, 1)
1707
1708         p0 = (Ether(src=self.pg1.remote_mac,
1709                     dst=self.pg1.local_mac) /
1710               IP(src="1.1.1.1",
1711                  dst=self.pg0._local_ip4_bcast) /
1712               UDP(sport=1234, dport=1234) /
1713               Raw('\xa5' * 2000))
1714         p1 = (Ether(src=self.pg0.remote_mac,
1715                     dst=self.pg0.local_mac) /
1716               IP(src="1.1.1.1",
1717                  dst=self.pg1._local_ip4_bcast) /
1718               UDP(sport=1234, dport=1234) /
1719               Raw('\xa5' * 2000))
1720
1721         self.pg0.config_ip4()
1722         self.pg0.resolve_arp()
1723         self.pg1.config_ip4()
1724         self.pg1.resolve_arp()
1725
1726         #
1727         # test packet is L2 broadcast
1728         #
1729         rx = self.send_and_expect(self.pg1, p0 * 65, self.pg0)
1730         self.assertTrue(rx[0][Ether].dst, "ff:ff:ff:ff:ff:ff")
1731
1732         self.send_and_assert_no_replies(self.pg0, p1 * 65,
1733                                         "directed broadcast disabled")
1734
1735         #
1736         # toggle directed broadcast on pg0
1737         #
1738         self.vapi.sw_interface_set_ip_directed_broadcast(
1739             self.pg0.sw_if_index, 0)
1740         self.send_and_assert_no_replies(self.pg1, p0 * 65,
1741                                         "directed broadcast disabled")
1742
1743         self.vapi.sw_interface_set_ip_directed_broadcast(
1744             self.pg0.sw_if_index, 1)
1745         rx = self.send_and_expect(self.pg1, p0 * 65, self.pg0)
1746
1747         self.pg0.unconfig_ip4()
1748         self.pg1.unconfig_ip4()
1749
1750
1751 class TestIPLPM(VppTestCase):
1752     """ IPv4 longest Prefix Match """
1753
1754     @classmethod
1755     def setUpClass(cls):
1756         super(TestIPLPM, cls).setUpClass()
1757
1758     @classmethod
1759     def tearDownClass(cls):
1760         super(TestIPLPM, cls).tearDownClass()
1761
1762     def setUp(self):
1763         super(TestIPLPM, self).setUp()
1764
1765         self.create_pg_interfaces(range(4))
1766
1767         for i in self.pg_interfaces:
1768             i.admin_up()
1769             i.config_ip4()
1770             i.resolve_arp()
1771
1772     def tearDown(self):
1773         super(TestIPLPM, self).tearDown()
1774         for i in self.pg_interfaces:
1775             i.admin_down()
1776             i.unconfig_ip4()
1777
1778     def test_ip_lpm(self):
1779         """ IP longest Prefix Match """
1780
1781         s_24 = VppIpRoute(self, "10.1.2.0", 24,
1782                           [VppRoutePath(self.pg1.remote_ip4,
1783                                         self.pg1.sw_if_index)])
1784         s_24.add_vpp_config()
1785         s_8 = VppIpRoute(self, "10.0.0.0", 8,
1786                          [VppRoutePath(self.pg2.remote_ip4,
1787                                        self.pg2.sw_if_index)])
1788         s_8.add_vpp_config()
1789
1790         p_8 = (Ether(src=self.pg0.remote_mac,
1791                      dst=self.pg0.local_mac) /
1792                IP(src="1.1.1.1",
1793                   dst="10.1.1.1") /
1794                UDP(sport=1234, dport=1234) /
1795                Raw('\xa5' * 2000))
1796         p_24 = (Ether(src=self.pg0.remote_mac,
1797                       dst=self.pg0.local_mac) /
1798                 IP(src="1.1.1.1",
1799                    dst="10.1.2.1") /
1800                 UDP(sport=1234, dport=1234) /
1801                 Raw('\xa5' * 2000))
1802
1803         self.logger.info(self.vapi.cli("sh ip fib mtrie"))
1804         rx = self.send_and_expect(self.pg0, p_8 * 65, self.pg2)
1805         rx = self.send_and_expect(self.pg0, p_24 * 65, self.pg1)
1806
1807
1808 class TestIPv4Frag(VppTestCase):
1809     """ IPv4 fragmentation """
1810
1811     @classmethod
1812     def setUpClass(cls):
1813         super(TestIPv4Frag, cls).setUpClass()
1814
1815         cls.create_pg_interfaces([0, 1])
1816         cls.src_if = cls.pg0
1817         cls.dst_if = cls.pg1
1818
1819         # setup all interfaces
1820         for i in cls.pg_interfaces:
1821             i.admin_up()
1822             i.config_ip4()
1823             i.resolve_arp()
1824
1825     @classmethod
1826     def tearDownClass(cls):
1827         super(TestIPv4Frag, cls).tearDownClass()
1828
1829     def test_frag_large_packets(self):
1830         """ Fragmentation of large packets """
1831
1832         p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1833              IP(src=self.src_if.remote_ip4, dst=self.dst_if.remote_ip4) /
1834              UDP(sport=1234, dport=5678) / Raw())
1835         self.extend_packet(p, 6000, "abcde")
1836         saved_payload = p[Raw].load
1837
1838         # Force fragmentation by setting MTU of output interface
1839         # lower than packet size
1840         self.vapi.sw_interface_set_mtu(self.dst_if.sw_if_index,
1841                                        [5000, 0, 0, 0])
1842
1843         self.pg_enable_capture()
1844         self.src_if.add_stream(p)
1845         self.pg_start()
1846
1847         # Expecting 3 fragments because size of created fragments currently
1848         # cannot be larger then VPP buffer size (which is 2048)
1849         packets = self.dst_if.get_capture(3)
1850
1851         # Assume VPP sends the fragments in order
1852         payload = ''
1853         for p in packets:
1854             payload_offset = p.frag * 8
1855             if payload_offset > 0:
1856                 payload_offset -= 8  # UDP header is not in payload
1857             self.assert_equal(payload_offset, len(payload))
1858             payload += p[Raw].load
1859         self.assert_equal(payload, saved_payload, "payload")
1860
1861
1862 if __name__ == '__main__':
1863     unittest.main(testRunner=VppTestRunner)