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