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