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