Fix TestIPv4FibCrud interdependencies.
[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
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         self.configured_routes = []
445         self.deleted_routes = []
446
447     def test_1_add_routes(self):
448         """ Add 1k routes
449
450         - add 100 routes check with traffic script.
451         """
452         # config 1M FIB entries
453         self.configured_routes.extend(self.config_fib_many_to_one(
454             "10.0.0.0", self.pg0.remote_ip4, 100))
455
456         fib_dump = self.vapi.ip_fib_dump()
457         self.verify_route_dump(fib_dump, self.configured_routes)
458
459         self.stream_1 = self.create_stream(
460             self.pg1, self.pg0, self.configured_routes, 100)
461         self.stream_2 = self.create_stream(
462             self.pg2, self.pg0, self.configured_routes, 100)
463         self.pg1.add_stream(self.stream_1)
464         self.pg2.add_stream(self.stream_2)
465
466         self.pg_enable_capture(self.pg_interfaces)
467         self.pg_start()
468
469         pkts = self.pg0.get_capture(len(self.stream_1) + len(self.stream_2))
470         self.verify_capture(self.pg0, pkts, self.stream_1 + self.stream_2)
471
472     def test_2_del_routes(self):
473         """ Delete 100 routes
474
475         - delete 10 routes check with traffic script.
476         """
477         # config 1M FIB entries
478         self.configured_routes.extend(self.config_fib_many_to_one(
479             "10.0.0.0", self.pg0.remote_ip4, 100))
480         self.deleted_routes.extend(self.unconfig_fib_many_to_one(
481             "10.0.0.10", self.pg0.remote_ip4, 10))
482         for x in self.deleted_routes:
483             self.configured_routes.remove(x)
484
485         fib_dump = self.vapi.ip_fib_dump()
486         self.verify_route_dump(fib_dump, self.configured_routes)
487
488         self.stream_1 = self.create_stream(
489             self.pg1, self.pg0, self.configured_routes, 100)
490         self.stream_2 = self.create_stream(
491             self.pg2, self.pg0, self.configured_routes, 100)
492         self.stream_3 = self.create_stream(
493             self.pg1, self.pg0, self.deleted_routes, 100)
494         self.stream_4 = self.create_stream(
495             self.pg2, self.pg0, self.deleted_routes, 100)
496         self.pg1.add_stream(self.stream_1 + self.stream_3)
497         self.pg2.add_stream(self.stream_2 + self.stream_4)
498         self.pg_enable_capture(self.pg_interfaces)
499         self.pg_start()
500
501         pkts = self.pg0.get_capture(len(self.stream_1) + len(self.stream_2))
502         self.verify_capture(self.pg0, pkts, self.stream_1 + self.stream_2)
503
504     def test_3_add_new_routes(self):
505         """ Add 1k routes
506
507         - re-add 5 routes check with traffic script.
508         - add 100 routes check with traffic script.
509         """
510         # config 1M FIB entries
511         self.configured_routes.extend(self.config_fib_many_to_one(
512             "10.0.0.0", self.pg0.remote_ip4, 100))
513         self.deleted_routes.extend(self.unconfig_fib_many_to_one(
514             "10.0.0.10", self.pg0.remote_ip4, 10))
515         for x in self.deleted_routes:
516             self.configured_routes.remove(x)
517
518         tmp = self.config_fib_many_to_one(
519             "10.0.0.10", self.pg0.remote_ip4, 5)
520         self.configured_routes.extend(tmp)
521         for x in tmp:
522             self.deleted_routes.remove(x)
523
524         self.configured_routes.extend(self.config_fib_many_to_one(
525             "10.0.1.0", self.pg0.remote_ip4, 100))
526
527         fib_dump = self.vapi.ip_fib_dump()
528         self.verify_route_dump(fib_dump, self.configured_routes)
529
530         self.stream_1 = self.create_stream(
531             self.pg1, self.pg0, self.configured_routes, 300)
532         self.stream_2 = self.create_stream(
533             self.pg2, self.pg0, self.configured_routes, 300)
534         self.stream_3 = self.create_stream(
535             self.pg1, self.pg0, self.deleted_routes, 100)
536         self.stream_4 = self.create_stream(
537             self.pg2, self.pg0, self.deleted_routes, 100)
538
539         self.pg1.add_stream(self.stream_1 + self.stream_3)
540         self.pg2.add_stream(self.stream_2 + self.stream_4)
541         self.pg_enable_capture(self.pg_interfaces)
542         self.pg_start()
543
544         pkts = self.pg0.get_capture(len(self.stream_1) + len(self.stream_2))
545         self.verify_capture(self.pg0, pkts, self.stream_1 + self.stream_2)
546
547     def test_4_del_routes(self):
548         """ Delete 1.5k routes
549
550         - delete 5 routes check with traffic script.
551         - add 100 routes check with traffic script.
552         """
553         self.deleted_routes.extend(self.unconfig_fib_many_to_one(
554             "10.0.0.0", self.pg0.remote_ip4, 15))
555         self.deleted_routes.extend(self.unconfig_fib_many_to_one(
556             "10.0.0.20", self.pg0.remote_ip4, 85))
557         self.deleted_routes.extend(self.unconfig_fib_many_to_one(
558             "10.0.1.0", self.pg0.remote_ip4, 100))
559         fib_dump = self.vapi.ip_fib_dump()
560         self.verify_not_in_route_dump(fib_dump, self.deleted_routes)
561
562
563 class TestIPNull(VppTestCase):
564     """ IPv4 routes via NULL """
565
566     def setUp(self):
567         super(TestIPNull, self).setUp()
568
569         # create 2 pg interfaces
570         self.create_pg_interfaces(range(2))
571
572         for i in self.pg_interfaces:
573             i.admin_up()
574             i.config_ip4()
575             i.resolve_arp()
576
577     def tearDown(self):
578         super(TestIPNull, self).tearDown()
579         for i in self.pg_interfaces:
580             i.unconfig_ip4()
581             i.admin_down()
582
583     def test_ip_null(self):
584         """ IP NULL route """
585
586         #
587         # A route via IP NULL that will reply with ICMP unreachables
588         #
589         ip_unreach = VppIpRoute(self, "10.0.0.1", 32, [], is_unreach=1)
590         ip_unreach.add_vpp_config()
591
592         p_unreach = (Ether(src=self.pg0.remote_mac,
593                            dst=self.pg0.local_mac) /
594                      IP(src=self.pg0.remote_ip4, dst="10.0.0.1") /
595                      UDP(sport=1234, dport=1234) /
596                      Raw('\xa5' * 100))
597
598         self.pg0.add_stream(p_unreach)
599         self.pg_enable_capture(self.pg_interfaces)
600         self.pg_start()
601
602         rx = self.pg0.get_capture(1)
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-unreachable")
608         self.assertEqual(icmp.src, self.pg0.remote_ip4)
609         self.assertEqual(icmp.dst, "10.0.0.1")
610
611         #
612         # ICMP replies are rate limited. so sit and spin.
613         #
614         self.sleep(1)
615
616         #
617         # A route via IP NULL that will reply with ICMP prohibited
618         #
619         ip_prohibit = VppIpRoute(self, "10.0.0.2", 32, [], is_prohibit=1)
620         ip_prohibit.add_vpp_config()
621
622         p_prohibit = (Ether(src=self.pg0.remote_mac,
623                             dst=self.pg0.local_mac) /
624                       IP(src=self.pg0.remote_ip4, dst="10.0.0.2") /
625                       UDP(sport=1234, dport=1234) /
626                       Raw('\xa5' * 100))
627
628         self.pg0.add_stream(p_prohibit)
629         self.pg_enable_capture(self.pg_interfaces)
630         self.pg_start()
631
632         rx = self.pg0.get_capture(1)
633
634         rx = rx[0]
635         icmp = rx[ICMP]
636
637         self.assertEqual(icmptypes[icmp.type], "dest-unreach")
638         self.assertEqual(icmpcodes[icmp.type][icmp.code], "host-prohibited")
639         self.assertEqual(icmp.src, self.pg0.remote_ip4)
640         self.assertEqual(icmp.dst, "10.0.0.2")
641
642     def test_ip_drop(self):
643         """ IP Drop Routes """
644
645         p = (Ether(src=self.pg0.remote_mac,
646                    dst=self.pg0.local_mac) /
647              IP(src=self.pg0.remote_ip4, dst="1.1.1.1") /
648              UDP(sport=1234, dport=1234) /
649              Raw('\xa5' * 100))
650
651         r1 = VppIpRoute(self, "1.1.1.0", 24,
652                         [VppRoutePath(self.pg1.remote_ip4,
653                                       self.pg1.sw_if_index)])
654         r1.add_vpp_config()
655
656         rx = self.send_and_expect(self.pg0, p * 65, self.pg1)
657
658         #
659         # insert a more specific as a drop
660         #
661         r2 = VppIpRoute(self, "1.1.1.1", 32, [], is_drop=1)
662         r2.add_vpp_config()
663
664         self.send_and_assert_no_replies(self.pg0, p * 65, "Drop Route")
665         r2.remove_vpp_config()
666         rx = self.send_and_expect(self.pg0, p * 65, self.pg1)
667
668
669 class TestIPDisabled(VppTestCase):
670     """ IPv4 disabled """
671
672     def setUp(self):
673         super(TestIPDisabled, self).setUp()
674
675         # create 2 pg interfaces
676         self.create_pg_interfaces(range(2))
677
678         # PG0 is IP enalbed
679         self.pg0.admin_up()
680         self.pg0.config_ip4()
681         self.pg0.resolve_arp()
682
683         # PG 1 is not IP enabled
684         self.pg1.admin_up()
685
686     def tearDown(self):
687         super(TestIPDisabled, self).tearDown()
688         for i in self.pg_interfaces:
689             i.unconfig_ip4()
690             i.admin_down()
691
692     def test_ip_disabled(self):
693         """ IP Disabled """
694
695         #
696         # An (S,G).
697         # one accepting interface, pg0, 2 forwarding interfaces
698         #
699         route_232_1_1_1 = VppIpMRoute(
700             self,
701             "0.0.0.0",
702             "232.1.1.1", 32,
703             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
704             [VppMRoutePath(self.pg1.sw_if_index,
705                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
706              VppMRoutePath(self.pg0.sw_if_index,
707                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
708         route_232_1_1_1.add_vpp_config()
709
710         pu = (Ether(src=self.pg1.remote_mac,
711                     dst=self.pg1.local_mac) /
712               IP(src="10.10.10.10", dst=self.pg0.remote_ip4) /
713               UDP(sport=1234, dport=1234) /
714               Raw('\xa5' * 100))
715         pm = (Ether(src=self.pg1.remote_mac,
716                     dst=self.pg1.local_mac) /
717               IP(src="10.10.10.10", dst="232.1.1.1") /
718               UDP(sport=1234, dport=1234) /
719               Raw('\xa5' * 100))
720
721         #
722         # PG1 does not forward IP traffic
723         #
724         self.send_and_assert_no_replies(self.pg1, pu, "IP disabled")
725         self.send_and_assert_no_replies(self.pg1, pm, "IP disabled")
726
727         #
728         # IP enable PG1
729         #
730         self.pg1.config_ip4()
731
732         #
733         # Now we get packets through
734         #
735         self.pg1.add_stream(pu)
736         self.pg_enable_capture(self.pg_interfaces)
737         self.pg_start()
738         rx = self.pg0.get_capture(1)
739
740         self.pg1.add_stream(pm)
741         self.pg_enable_capture(self.pg_interfaces)
742         self.pg_start()
743         rx = self.pg0.get_capture(1)
744
745         #
746         # Disable PG1
747         #
748         self.pg1.unconfig_ip4()
749
750         #
751         # PG1 does not forward IP traffic
752         #
753         self.send_and_assert_no_replies(self.pg1, pu, "IP disabled")
754         self.send_and_assert_no_replies(self.pg1, pm, "IP disabled")
755
756
757 class TestIPSubNets(VppTestCase):
758     """ IPv4 Subnets """
759
760     def setUp(self):
761         super(TestIPSubNets, self).setUp()
762
763         # create a 2 pg interfaces
764         self.create_pg_interfaces(range(2))
765
766         # pg0 we will use to experiemnt
767         self.pg0.admin_up()
768
769         # pg1 is setup normally
770         self.pg1.admin_up()
771         self.pg1.config_ip4()
772         self.pg1.resolve_arp()
773
774     def tearDown(self):
775         super(TestIPSubNets, self).tearDown()
776         for i in self.pg_interfaces:
777             i.admin_down()
778
779     def test_ip_sub_nets(self):
780         """ IP Sub Nets """
781
782         #
783         # Configure a covering route to forward so we know
784         # when we are dropping
785         #
786         cover_route = VppIpRoute(self, "10.0.0.0", 8,
787                                  [VppRoutePath(self.pg1.remote_ip4,
788                                                self.pg1.sw_if_index)])
789         cover_route.add_vpp_config()
790
791         p = (Ether(src=self.pg1.remote_mac,
792                    dst=self.pg1.local_mac) /
793              IP(dst="10.10.10.10", src=self.pg0.local_ip4) /
794              UDP(sport=1234, dport=1234) /
795              Raw('\xa5' * 100))
796
797         self.pg1.add_stream(p)
798         self.pg_enable_capture(self.pg_interfaces)
799         self.pg_start()
800         rx = self.pg1.get_capture(1)
801
802         #
803         # Configure some non-/24 subnets on an IP interface
804         #
805         ip_addr_n = socket.inet_pton(socket.AF_INET, "10.10.10.10")
806
807         self.vapi.sw_interface_add_del_address(self.pg0.sw_if_index,
808                                                ip_addr_n,
809                                                16)
810
811         pn = (Ether(src=self.pg1.remote_mac,
812                     dst=self.pg1.local_mac) /
813               IP(dst="10.10.0.0", src=self.pg0.local_ip4) /
814               UDP(sport=1234, dport=1234) /
815               Raw('\xa5' * 100))
816         pb = (Ether(src=self.pg1.remote_mac,
817                     dst=self.pg1.local_mac) /
818               IP(dst="10.10.255.255", src=self.pg0.local_ip4) /
819               UDP(sport=1234, dport=1234) /
820               Raw('\xa5' * 100))
821
822         self.send_and_assert_no_replies(self.pg1, pn, "IP Network address")
823         self.send_and_assert_no_replies(self.pg1, pb, "IP Broadcast address")
824
825         # remove the sub-net and we are forwarding via the cover again
826         self.vapi.sw_interface_add_del_address(self.pg0.sw_if_index,
827                                                ip_addr_n,
828                                                16,
829                                                is_add=0)
830         self.pg1.add_stream(pn)
831         self.pg_enable_capture(self.pg_interfaces)
832         self.pg_start()
833         rx = self.pg1.get_capture(1)
834         self.pg1.add_stream(pb)
835         self.pg_enable_capture(self.pg_interfaces)
836         self.pg_start()
837         rx = self.pg1.get_capture(1)
838
839         #
840         # A /31 is a special case where the 'other-side' is an attached host
841         # packets to that peer generate ARP requests
842         #
843         ip_addr_n = socket.inet_pton(socket.AF_INET, "10.10.10.10")
844
845         self.vapi.sw_interface_add_del_address(self.pg0.sw_if_index,
846                                                ip_addr_n,
847                                                31)
848
849         pn = (Ether(src=self.pg1.remote_mac,
850                     dst=self.pg1.local_mac) /
851               IP(dst="10.10.10.11", src=self.pg0.local_ip4) /
852               UDP(sport=1234, dport=1234) /
853               Raw('\xa5' * 100))
854
855         self.pg1.add_stream(pn)
856         self.pg_enable_capture(self.pg_interfaces)
857         self.pg_start()
858         rx = self.pg0.get_capture(1)
859         rx[ARP]
860
861         # remove the sub-net and we are forwarding via the cover again
862         self.vapi.sw_interface_add_del_address(self.pg0.sw_if_index,
863                                                ip_addr_n,
864                                                31,
865                                                is_add=0)
866         self.pg1.add_stream(pn)
867         self.pg_enable_capture(self.pg_interfaces)
868         self.pg_start()
869         rx = self.pg1.get_capture(1)
870
871
872 class TestIPLoadBalance(VppTestCase):
873     """ IPv4 Load-Balancing """
874
875     def setUp(self):
876         super(TestIPLoadBalance, self).setUp()
877
878         self.create_pg_interfaces(range(5))
879         mpls_tbl = VppMplsTable(self, 0)
880         mpls_tbl.add_vpp_config()
881
882         for i in self.pg_interfaces:
883             i.admin_up()
884             i.config_ip4()
885             i.resolve_arp()
886             i.enable_mpls()
887
888     def tearDown(self):
889         for i in self.pg_interfaces:
890             i.disable_mpls()
891             i.unconfig_ip4()
892             i.admin_down()
893         super(TestIPLoadBalance, self).tearDown()
894
895     def send_and_expect_load_balancing(self, input, pkts, outputs):
896         input.add_stream(pkts)
897         self.pg_enable_capture(self.pg_interfaces)
898         self.pg_start()
899         for oo in outputs:
900             rx = oo._get_capture(1)
901             self.assertNotEqual(0, len(rx))
902
903     def send_and_expect_one_itf(self, input, pkts, itf):
904         input.add_stream(pkts)
905         self.pg_enable_capture(self.pg_interfaces)
906         self.pg_start()
907         rx = itf.get_capture(len(pkts))
908
909     def test_ip_load_balance(self):
910         """ IP Load-Balancing """
911
912         #
913         # An array of packets that differ only in the destination port
914         #
915         port_ip_pkts = []
916         port_mpls_pkts = []
917
918         #
919         # An array of packets that differ only in the source address
920         #
921         src_ip_pkts = []
922         src_mpls_pkts = []
923
924         for ii in range(65):
925             port_ip_hdr = (IP(dst="10.0.0.1", src="20.0.0.1") /
926                            UDP(sport=1234, dport=1234 + ii) /
927                            Raw('\xa5' * 100))
928             port_ip_pkts.append((Ether(src=self.pg0.remote_mac,
929                                        dst=self.pg0.local_mac) /
930                                  port_ip_hdr))
931             port_mpls_pkts.append((Ether(src=self.pg0.remote_mac,
932                                          dst=self.pg0.local_mac) /
933                                    MPLS(label=66, ttl=2) /
934                                    port_ip_hdr))
935
936             src_ip_hdr = (IP(dst="10.0.0.1", src="20.0.0.%d" % ii) /
937                           UDP(sport=1234, dport=1234) /
938                           Raw('\xa5' * 100))
939             src_ip_pkts.append((Ether(src=self.pg0.remote_mac,
940                                       dst=self.pg0.local_mac) /
941                                 src_ip_hdr))
942             src_mpls_pkts.append((Ether(src=self.pg0.remote_mac,
943                                         dst=self.pg0.local_mac) /
944                                   MPLS(label=66, ttl=2) /
945                                   src_ip_hdr))
946
947         route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
948                                     [VppRoutePath(self.pg1.remote_ip4,
949                                                   self.pg1.sw_if_index),
950                                      VppRoutePath(self.pg2.remote_ip4,
951                                                   self.pg2.sw_if_index)])
952         route_10_0_0_1.add_vpp_config()
953
954         binding = VppMplsIpBind(self, 66, "10.0.0.1", 32)
955         binding.add_vpp_config()
956
957         #
958         # inject the packet on pg0 - expect load-balancing across the 2 paths
959         #  - since the default hash config is to use IP src,dst and port
960         #    src,dst
961         # We are not going to ensure equal amounts of packets across each link,
962         # since the hash algorithm is statistical and therefore this can never
963         # be guaranteed. But wuth 64 different packets we do expect some
964         # balancing. So instead just ensure there is traffic on each link.
965         #
966         self.send_and_expect_load_balancing(self.pg0, port_ip_pkts,
967                                             [self.pg1, self.pg2])
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, port_mpls_pkts,
971                                             [self.pg1, self.pg2])
972         self.send_and_expect_load_balancing(self.pg0, src_mpls_pkts,
973                                             [self.pg1, self.pg2])
974
975         #
976         # change the flow hash config so it's only IP src,dst
977         #  - now only the stream with differing source address will
978         #    load-balance
979         #
980         self.vapi.set_ip_flow_hash(0, src=1, dst=1, sport=0, dport=0)
981
982         self.send_and_expect_load_balancing(self.pg0, src_ip_pkts,
983                                             [self.pg1, self.pg2])
984         self.send_and_expect_load_balancing(self.pg0, src_mpls_pkts,
985                                             [self.pg1, self.pg2])
986
987         self.send_and_expect_one_itf(self.pg0, port_ip_pkts, self.pg2)
988
989         #
990         # change the flow hash config back to defaults
991         #
992         self.vapi.set_ip_flow_hash(0, src=1, dst=1, sport=1, dport=1)
993
994         #
995         # Recursive prefixes
996         #  - testing that 2 stages of load-balancing occurs and there is no
997         #    polarisation (i.e. only 2 of 4 paths are used)
998         #
999         port_pkts = []
1000         src_pkts = []
1001
1002         for ii in range(257):
1003             port_pkts.append((Ether(src=self.pg0.remote_mac,
1004                                     dst=self.pg0.local_mac) /
1005                               IP(dst="1.1.1.1", src="20.0.0.1") /
1006                               UDP(sport=1234, dport=1234 + ii) /
1007                               Raw('\xa5' * 100)))
1008             src_pkts.append((Ether(src=self.pg0.remote_mac,
1009                                    dst=self.pg0.local_mac) /
1010                              IP(dst="1.1.1.1", src="20.0.0.%d" % ii) /
1011                              UDP(sport=1234, dport=1234) /
1012                              Raw('\xa5' * 100)))
1013
1014         route_10_0_0_2 = VppIpRoute(self, "10.0.0.2", 32,
1015                                     [VppRoutePath(self.pg3.remote_ip4,
1016                                                   self.pg3.sw_if_index),
1017                                      VppRoutePath(self.pg4.remote_ip4,
1018                                                   self.pg4.sw_if_index)])
1019         route_10_0_0_2.add_vpp_config()
1020
1021         route_1_1_1_1 = VppIpRoute(self, "1.1.1.1", 32,
1022                                    [VppRoutePath("10.0.0.2", 0xffffffff),
1023                                     VppRoutePath("10.0.0.1", 0xffffffff)])
1024         route_1_1_1_1.add_vpp_config()
1025
1026         #
1027         # inject the packet on pg0 - expect load-balancing across all 4 paths
1028         #
1029         self.vapi.cli("clear trace")
1030         self.send_and_expect_load_balancing(self.pg0, port_pkts,
1031                                             [self.pg1, self.pg2,
1032                                              self.pg3, self.pg4])
1033         self.send_and_expect_load_balancing(self.pg0, src_pkts,
1034                                             [self.pg1, self.pg2,
1035                                              self.pg3, self.pg4])
1036
1037         #
1038         # Recursive prefixes
1039         #  - testing that 2 stages of load-balancing, no choices
1040         #
1041         port_pkts = []
1042
1043         for ii in range(257):
1044             port_pkts.append((Ether(src=self.pg0.remote_mac,
1045                                     dst=self.pg0.local_mac) /
1046                               IP(dst="1.1.1.2", src="20.0.0.2") /
1047                               UDP(sport=1234, dport=1234 + ii) /
1048                               Raw('\xa5' * 100)))
1049
1050         route_10_0_0_3 = VppIpRoute(self, "10.0.0.3", 32,
1051                                     [VppRoutePath(self.pg3.remote_ip4,
1052                                                   self.pg3.sw_if_index)])
1053         route_10_0_0_3.add_vpp_config()
1054
1055         route_1_1_1_2 = VppIpRoute(self, "1.1.1.2", 32,
1056                                    [VppRoutePath("10.0.0.3", 0xffffffff)])
1057         route_1_1_1_2.add_vpp_config()
1058
1059         #
1060         # inject the packet on pg0 - expect load-balancing across all 4 paths
1061         #
1062         self.vapi.cli("clear trace")
1063         self.send_and_expect_one_itf(self.pg0, port_pkts, self.pg3)
1064
1065
1066 class TestIPVlan0(VppTestCase):
1067     """ IPv4 VLAN-0 """
1068
1069     def setUp(self):
1070         super(TestIPVlan0, self).setUp()
1071
1072         self.create_pg_interfaces(range(2))
1073         mpls_tbl = VppMplsTable(self, 0)
1074         mpls_tbl.add_vpp_config()
1075
1076         for i in self.pg_interfaces:
1077             i.admin_up()
1078             i.config_ip4()
1079             i.resolve_arp()
1080             i.enable_mpls()
1081
1082     def tearDown(self):
1083         for i in self.pg_interfaces:
1084             i.disable_mpls()
1085             i.unconfig_ip4()
1086             i.admin_down()
1087         super(TestIPVlan0, self).tearDown()
1088
1089     def test_ip_vlan_0(self):
1090         """ IP VLAN-0 """
1091
1092         pkts = (Ether(src=self.pg0.remote_mac,
1093                       dst=self.pg0.local_mac) /
1094                 Dot1Q(vlan=0) /
1095                 IP(dst=self.pg1.remote_ip4,
1096                    src=self.pg0.remote_ip4) /
1097                 UDP(sport=1234, dport=1234) /
1098                 Raw('\xa5' * 100)) * 65
1099
1100         #
1101         # Expect that packets sent on VLAN-0 are forwarded on the
1102         # main interface.
1103         #
1104         self.send_and_expect(self.pg0, pkts, self.pg1)
1105
1106
1107 class TestIPPunt(VppTestCase):
1108     """ IPv4 Punt Police/Redirect """
1109
1110     def setUp(self):
1111         super(TestIPPunt, self).setUp()
1112
1113         self.create_pg_interfaces(range(4))
1114
1115         for i in self.pg_interfaces:
1116             i.admin_up()
1117             i.config_ip4()
1118             i.resolve_arp()
1119
1120     def tearDown(self):
1121         super(TestIPPunt, self).tearDown()
1122         for i in self.pg_interfaces:
1123             i.unconfig_ip4()
1124             i.admin_down()
1125
1126     def test_ip_punt(self):
1127         """ IP punt police and redirect """
1128
1129         p = (Ether(src=self.pg0.remote_mac,
1130                    dst=self.pg0.local_mac) /
1131              IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
1132              TCP(sport=1234, dport=1234) /
1133              Raw('\xa5' * 100))
1134
1135         pkts = p * 1025
1136
1137         #
1138         # Configure a punt redirect via pg1.
1139         #
1140         nh_addr = self.pg1.remote_ip4
1141         self.vapi.ip_punt_redirect(self.pg0.sw_if_index,
1142                                    self.pg1.sw_if_index,
1143                                    nh_addr)
1144
1145         self.send_and_expect(self.pg0, pkts, self.pg1)
1146
1147         #
1148         # add a policer
1149         #
1150         policer = self.vapi.policer_add_del("ip4-punt", 400, 0, 10, 0,
1151                                             rate_type=1)
1152         self.vapi.ip_punt_police(policer.policer_index)
1153
1154         self.vapi.cli("clear trace")
1155         self.pg0.add_stream(pkts)
1156         self.pg_enable_capture(self.pg_interfaces)
1157         self.pg_start()
1158
1159         #
1160         # the number of packet recieved should be greater than 0,
1161         # but not equal to the number sent, since some were policed
1162         #
1163         rx = self.pg1._get_capture(1)
1164         self.assertGreater(len(rx), 0)
1165         self.assertLess(len(rx), len(pkts))
1166
1167         #
1168         # remove the poilcer. back to full rx
1169         #
1170         self.vapi.ip_punt_police(policer.policer_index, is_add=0)
1171         self.vapi.policer_add_del("ip4-punt", 400, 0, 10, 0,
1172                                   rate_type=1, is_add=0)
1173         self.send_and_expect(self.pg0, pkts, self.pg1)
1174
1175         #
1176         # remove the redirect. expect full drop.
1177         #
1178         self.vapi.ip_punt_redirect(self.pg0.sw_if_index,
1179                                    self.pg1.sw_if_index,
1180                                    nh_addr,
1181                                    is_add=0)
1182         self.send_and_assert_no_replies(self.pg0, pkts,
1183                                         "IP no punt config")
1184
1185         #
1186         # Add a redirect that is not input port selective
1187         #
1188         self.vapi.ip_punt_redirect(0xffffffff,
1189                                    self.pg1.sw_if_index,
1190                                    nh_addr)
1191         self.send_and_expect(self.pg0, pkts, self.pg1)
1192
1193         self.vapi.ip_punt_redirect(0xffffffff,
1194                                    self.pg1.sw_if_index,
1195                                    nh_addr,
1196                                    is_add=0)
1197
1198     def test_ip_punt_dump(self):
1199         """ IP4 punt redirect dump"""
1200
1201         #
1202         # Configure a punt redirects
1203         #
1204         nh_address = self.pg3.remote_ip4
1205         self.vapi.ip_punt_redirect(self.pg0.sw_if_index,
1206                                    self.pg3.sw_if_index,
1207                                    nh_address)
1208         self.vapi.ip_punt_redirect(self.pg1.sw_if_index,
1209                                    self.pg3.sw_if_index,
1210                                    nh_address)
1211         self.vapi.ip_punt_redirect(self.pg2.sw_if_index,
1212                                    self.pg3.sw_if_index,
1213                                    '0.0.0.0')
1214
1215         #
1216         # Dump pg0 punt redirects
1217         #
1218         punts = self.vapi.ip_punt_redirect_dump(self.pg0.sw_if_index)
1219         for p in punts:
1220             self.assertEqual(p.punt.rx_sw_if_index, self.pg0.sw_if_index)
1221
1222         #
1223         # Dump punt redirects for all interfaces
1224         #
1225         punts = self.vapi.ip_punt_redirect_dump(0xffffffff)
1226         self.assertEqual(len(punts), 3)
1227         for p in punts:
1228             self.assertEqual(p.punt.tx_sw_if_index, self.pg3.sw_if_index)
1229         self.assertNotEqual(punts[1].punt.nh, self.pg3.remote_ip4)
1230         self.assertEqual(str(punts[2].punt.nh), '0.0.0.0')
1231
1232
1233 class TestIPDeag(VppTestCase):
1234     """ IPv4 Deaggregate Routes """
1235
1236     def setUp(self):
1237         super(TestIPDeag, self).setUp()
1238
1239         self.create_pg_interfaces(range(3))
1240
1241         for i in self.pg_interfaces:
1242             i.admin_up()
1243             i.config_ip4()
1244             i.resolve_arp()
1245
1246     def tearDown(self):
1247         super(TestIPDeag, self).tearDown()
1248         for i in self.pg_interfaces:
1249             i.unconfig_ip4()
1250             i.admin_down()
1251
1252     def test_ip_deag(self):
1253         """ IP Deag Routes """
1254
1255         #
1256         # Create a table to be used for:
1257         #  1 - another destination address lookup
1258         #  2 - a source address lookup
1259         #
1260         table_dst = VppIpTable(self, 1)
1261         table_src = VppIpTable(self, 2)
1262         table_dst.add_vpp_config()
1263         table_src.add_vpp_config()
1264
1265         #
1266         # Add a route in the default table to point to a deag/
1267         # second lookup in each of these tables
1268         #
1269         route_to_dst = VppIpRoute(self, "1.1.1.1", 32,
1270                                   [VppRoutePath("0.0.0.0",
1271                                                 0xffffffff,
1272                                                 nh_table_id=1)])
1273         route_to_src = VppIpRoute(self, "1.1.1.2", 32,
1274                                   [VppRoutePath("0.0.0.0",
1275                                                 0xffffffff,
1276                                                 nh_table_id=2,
1277                                                 is_source_lookup=1)])
1278         route_to_dst.add_vpp_config()
1279         route_to_src.add_vpp_config()
1280
1281         #
1282         # packets to these destination are dropped, since they'll
1283         # hit the respective default routes in the second table
1284         #
1285         p_dst = (Ether(src=self.pg0.remote_mac,
1286                        dst=self.pg0.local_mac) /
1287                  IP(src="5.5.5.5", dst="1.1.1.1") /
1288                  TCP(sport=1234, dport=1234) /
1289                  Raw('\xa5' * 100))
1290         p_src = (Ether(src=self.pg0.remote_mac,
1291                        dst=self.pg0.local_mac) /
1292                  IP(src="2.2.2.2", dst="1.1.1.2") /
1293                  TCP(sport=1234, dport=1234) /
1294                  Raw('\xa5' * 100))
1295         pkts_dst = p_dst * 257
1296         pkts_src = p_src * 257
1297
1298         self.send_and_assert_no_replies(self.pg0, pkts_dst,
1299                                         "IP in dst table")
1300         self.send_and_assert_no_replies(self.pg0, pkts_src,
1301                                         "IP in src table")
1302
1303         #
1304         # add a route in the dst table to forward via pg1
1305         #
1306         route_in_dst = VppIpRoute(self, "1.1.1.1", 32,
1307                                   [VppRoutePath(self.pg1.remote_ip4,
1308                                                 self.pg1.sw_if_index)],
1309                                   table_id=1)
1310         route_in_dst.add_vpp_config()
1311         self.send_and_expect(self.pg0, pkts_dst, self.pg1)
1312
1313         #
1314         # add a route in the src table to forward via pg2
1315         #
1316         route_in_src = VppIpRoute(self, "2.2.2.2", 32,
1317                                   [VppRoutePath(self.pg2.remote_ip4,
1318                                                 self.pg2.sw_if_index)],
1319                                   table_id=2)
1320         route_in_src.add_vpp_config()
1321         self.send_and_expect(self.pg0, pkts_src, self.pg2)
1322
1323         #
1324         # loop in the lookup DP
1325         #
1326         route_loop = VppIpRoute(self, "2.2.2.3", 32,
1327                                 [VppRoutePath("0.0.0.0",
1328                                               0xffffffff,
1329                                               nh_table_id=0)])
1330         route_loop.add_vpp_config()
1331
1332         p_l = (Ether(src=self.pg0.remote_mac,
1333                      dst=self.pg0.local_mac) /
1334                IP(src="2.2.2.4", dst="2.2.2.3") /
1335                TCP(sport=1234, dport=1234) /
1336                Raw('\xa5' * 100))
1337
1338         self.send_and_assert_no_replies(self.pg0, p_l * 257,
1339                                         "IP lookup loop")
1340
1341
1342 class TestIPInput(VppTestCase):
1343     """ IPv4 Input Exceptions """
1344
1345     def setUp(self):
1346         super(TestIPInput, self).setUp()
1347
1348         self.create_pg_interfaces(range(2))
1349
1350         for i in self.pg_interfaces:
1351             i.admin_up()
1352             i.config_ip4()
1353             i.resolve_arp()
1354
1355     def tearDown(self):
1356         super(TestIPInput, self).tearDown()
1357         for i in self.pg_interfaces:
1358             i.unconfig_ip4()
1359             i.admin_down()
1360
1361     def test_ip_input(self):
1362         """ IP Input Exceptions """
1363
1364         # i can't find a way in scapy to construct an IP packet
1365         # with a length less than the IP header length
1366
1367         #
1368         # Packet too short - this is forwarded
1369         #
1370         p_short = (Ether(src=self.pg0.remote_mac,
1371                          dst=self.pg0.local_mac) /
1372                    IP(src=self.pg0.remote_ip4,
1373                       dst=self.pg1.remote_ip4,
1374                       len=40) /
1375                    UDP(sport=1234, dport=1234) /
1376                    Raw('\xa5' * 100))
1377
1378         rx = self.send_and_expect(self.pg0, p_short * 65, self.pg1)
1379
1380         #
1381         # Packet too long - this is dropped
1382         #
1383         p_long = (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                      len=400) /
1388                   UDP(sport=1234, dport=1234) /
1389                   Raw('\xa5' * 100))
1390
1391         rx = self.send_and_assert_no_replies(self.pg0, p_long * 65,
1392                                              "too long")
1393
1394         #
1395         # bad chksum - this is dropped
1396         #
1397         p_chksum = (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                        chksum=400) /
1402                     UDP(sport=1234, dport=1234) /
1403                     Raw('\xa5' * 100))
1404
1405         rx = self.send_and_assert_no_replies(self.pg0, p_chksum * 65,
1406                                              "bad checksum")
1407
1408         #
1409         # bad version - this is dropped
1410         #
1411         p_ver = (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                     version=3) /
1416                  UDP(sport=1234, dport=1234) /
1417                  Raw('\xa5' * 100))
1418
1419         rx = self.send_and_assert_no_replies(self.pg0, p_ver * 65,
1420                                              "funky version")
1421
1422         #
1423         # fragment offset 1 - this is dropped
1424         #
1425         p_frag = (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                      frag=1) /
1430                   UDP(sport=1234, dport=1234) /
1431                   Raw('\xa5' * 100))
1432
1433         rx = self.send_and_assert_no_replies(self.pg0, p_frag * 65,
1434                                              "frag offset")
1435
1436         #
1437         # TTL expired packet
1438         #
1439         p_ttl = (Ether(src=self.pg0.remote_mac,
1440                        dst=self.pg0.local_mac) /
1441                  IP(src=self.pg0.remote_ip4,
1442                     dst=self.pg1.remote_ip4,
1443                     ttl=1) /
1444                  UDP(sport=1234, dport=1234) /
1445                  Raw('\xa5' * 100))
1446
1447         rx = self.send_and_expect(self.pg0, p_ttl * 65, self.pg0)
1448
1449         rx = rx[0]
1450         icmp = rx[ICMP]
1451
1452         self.assertEqual(icmptypes[icmp.type], "time-exceeded")
1453         self.assertEqual(icmpcodes[icmp.type][icmp.code],
1454                          "ttl-zero-during-transit")
1455         self.assertEqual(icmp.src, self.pg0.remote_ip4)
1456         self.assertEqual(icmp.dst, self.pg1.remote_ip4)
1457
1458         #
1459         # MTU exceeded
1460         #
1461         p_mtu = (Ether(src=self.pg0.remote_mac,
1462                        dst=self.pg0.local_mac) /
1463                  IP(src=self.pg0.remote_ip4,
1464                     dst=self.pg1.remote_ip4,
1465                     ttl=10, flags='DF') /
1466                  UDP(sport=1234, dport=1234) /
1467                  Raw('\xa5' * 2000))
1468
1469         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [1500, 0, 0, 0])
1470
1471         rx = self.send_and_expect(self.pg0, p_mtu * 65, self.pg0)
1472         rx = rx[0]
1473         icmp = rx[ICMP]
1474
1475         self.assertEqual(icmptypes[icmp.type], "dest-unreach")
1476         self.assertEqual(icmpcodes[icmp.type][icmp.code],
1477                          "fragmentation-needed")
1478         self.assertEqual(icmp.src, self.pg0.remote_ip4)
1479         self.assertEqual(icmp.dst, self.pg1.remote_ip4)
1480
1481         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [2500, 0, 0, 0])
1482         rx = self.send_and_expect(self.pg0, p_mtu * 65, self.pg1)
1483
1484         # Reset MTU for subsequent tests
1485         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [9000, 0, 0, 0])
1486
1487         #
1488         # source address 0.0.0.0 and 25.255.255.255 and for-us
1489         #
1490         p_s0 = (Ether(src=self.pg0.remote_mac,
1491                       dst=self.pg0.local_mac) /
1492                 IP(src="0.0.0.0",
1493                    dst=self.pg0.local_ip4) /
1494                 ICMP(id=4, seq=4) /
1495                 Raw(load='\x0a' * 18))
1496         rx = self.send_and_assert_no_replies(self.pg0, p_s0 * 17)
1497
1498         p_s0 = (Ether(src=self.pg0.remote_mac,
1499                       dst=self.pg0.local_mac) /
1500                 IP(src="255.255.255.255",
1501                    dst=self.pg0.local_ip4) /
1502                 ICMP(id=4, seq=4) /
1503                 Raw(load='\x0a' * 18))
1504         rx = self.send_and_assert_no_replies(self.pg0, p_s0 * 17)
1505
1506
1507 class TestIPDirectedBroadcast(VppTestCase):
1508     """ IPv4 Directed Broadcast """
1509
1510     def setUp(self):
1511         super(TestIPDirectedBroadcast, self).setUp()
1512
1513         self.create_pg_interfaces(range(2))
1514
1515         for i in self.pg_interfaces:
1516             i.admin_up()
1517
1518     def tearDown(self):
1519         super(TestIPDirectedBroadcast, self).tearDown()
1520         for i in self.pg_interfaces:
1521             i.admin_down()
1522
1523     def test_ip_input(self):
1524         """ IP Directed Broadcast """
1525
1526         #
1527         # set the directed broadcast on pg0 first, then config IP4 addresses
1528         # for pg1 directed broadcast is always disabled
1529         self.vapi.sw_interface_set_ip_directed_broadcast(
1530             self.pg0.sw_if_index, 1)
1531
1532         p0 = (Ether(src=self.pg1.remote_mac,
1533                     dst=self.pg1.local_mac) /
1534               IP(src="1.1.1.1",
1535                  dst=self.pg0._local_ip4_bcast) /
1536               UDP(sport=1234, dport=1234) /
1537               Raw('\xa5' * 2000))
1538         p1 = (Ether(src=self.pg0.remote_mac,
1539                     dst=self.pg0.local_mac) /
1540               IP(src="1.1.1.1",
1541                  dst=self.pg1._local_ip4_bcast) /
1542               UDP(sport=1234, dport=1234) /
1543               Raw('\xa5' * 2000))
1544
1545         self.pg0.config_ip4()
1546         self.pg0.resolve_arp()
1547         self.pg1.config_ip4()
1548         self.pg1.resolve_arp()
1549
1550         #
1551         # test packet is L2 broadcast
1552         #
1553         rx = self.send_and_expect(self.pg1, p0 * 65, self.pg0)
1554         self.assertTrue(rx[0][Ether].dst, "ff:ff:ff:ff:ff:ff")
1555
1556         self.send_and_assert_no_replies(self.pg0, p1 * 65,
1557                                         "directed broadcast disabled")
1558
1559         #
1560         # toggle directed broadcast on pg0
1561         #
1562         self.vapi.sw_interface_set_ip_directed_broadcast(
1563             self.pg0.sw_if_index, 0)
1564         self.send_and_assert_no_replies(self.pg1, p0 * 65,
1565                                         "directed broadcast disabled")
1566
1567         self.vapi.sw_interface_set_ip_directed_broadcast(
1568             self.pg0.sw_if_index, 1)
1569         rx = self.send_and_expect(self.pg1, p0 * 65, self.pg0)
1570
1571         self.pg0.unconfig_ip4()
1572         self.pg1.unconfig_ip4()
1573
1574
1575 class TestIPLPM(VppTestCase):
1576     """ IPv4 longest Prefix Match """
1577
1578     def setUp(self):
1579         super(TestIPLPM, self).setUp()
1580
1581         self.create_pg_interfaces(range(4))
1582
1583         for i in self.pg_interfaces:
1584             i.admin_up()
1585             i.config_ip4()
1586             i.resolve_arp()
1587
1588     def tearDown(self):
1589         super(TestIPLPM, self).tearDown()
1590         for i in self.pg_interfaces:
1591             i.admin_down()
1592             i.unconfig_ip4()
1593
1594     def test_ip_lpm(self):
1595         """ IP longest Prefix Match """
1596
1597         s_24 = VppIpRoute(self, "10.1.2.0", 24,
1598                           [VppRoutePath(self.pg1.remote_ip4,
1599                                         self.pg1.sw_if_index)])
1600         s_24.add_vpp_config()
1601         s_8 = VppIpRoute(self, "10.0.0.0", 8,
1602                          [VppRoutePath(self.pg2.remote_ip4,
1603                                        self.pg2.sw_if_index)])
1604         s_8.add_vpp_config()
1605
1606         p_8 = (Ether(src=self.pg0.remote_mac,
1607                      dst=self.pg0.local_mac) /
1608                IP(src="1.1.1.1",
1609                   dst="10.1.1.1") /
1610                UDP(sport=1234, dport=1234) /
1611                Raw('\xa5' * 2000))
1612         p_24 = (Ether(src=self.pg0.remote_mac,
1613                       dst=self.pg0.local_mac) /
1614                 IP(src="1.1.1.1",
1615                    dst="10.1.2.1") /
1616                 UDP(sport=1234, dport=1234) /
1617                 Raw('\xa5' * 2000))
1618
1619         self.logger.info(self.vapi.cli("sh ip fib mtrie"))
1620         rx = self.send_and_expect(self.pg0, p_8 * 65, self.pg2)
1621         rx = self.send_and_expect(self.pg0, p_24 * 65, self.pg1)
1622
1623
1624 class TestIPv4Frag(VppTestCase):
1625     """ IPv4 fragmentation """
1626
1627     @classmethod
1628     def setUpClass(cls):
1629         super(TestIPv4Frag, cls).setUpClass()
1630
1631         cls.create_pg_interfaces([0, 1])
1632         cls.src_if = cls.pg0
1633         cls.dst_if = cls.pg1
1634
1635         # setup all interfaces
1636         for i in cls.pg_interfaces:
1637             i.admin_up()
1638             i.config_ip4()
1639             i.resolve_arp()
1640
1641     def test_frag_large_packets(self):
1642         """ Fragmentation of large packets """
1643
1644         p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1645              IP(src=self.src_if.remote_ip4, dst=self.dst_if.remote_ip4) /
1646              UDP(sport=1234, dport=5678) / Raw())
1647         self.extend_packet(p, 6000, "abcde")
1648         saved_payload = p[Raw].load
1649
1650         # Force fragmentation by setting MTU of output interface
1651         # lower than packet size
1652         self.vapi.sw_interface_set_mtu(self.dst_if.sw_if_index,
1653                                        [5000, 0, 0, 0])
1654
1655         self.pg_enable_capture()
1656         self.src_if.add_stream(p)
1657         self.pg_start()
1658
1659         # Expecting 3 fragments because size of created fragments currently
1660         # cannot be larger then VPP buffer size (which is 2048)
1661         packets = self.dst_if.get_capture(3)
1662
1663         # Assume VPP sends the fragments in order
1664         payload = ''
1665         for p in packets:
1666             payload_offset = p.frag * 8
1667             if payload_offset > 0:
1668                 payload_offset -= 8  # UDP header is not in payload
1669             self.assert_equal(payload_offset, len(payload))
1670             payload += p[Raw].load
1671         self.assert_equal(payload, saved_payload, "payload")
1672
1673
1674 if __name__ == '__main__':
1675     unittest.main(testRunner=VppTestRunner)