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