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