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