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