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