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