Tests: Refactor payload_to_info()
[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(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(p[Raw]) == \
355                     self.payload_to_info(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 {!s} 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 {!s} 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(
808             sw_if_index=self.pg0.sw_if_index, address=ip_addr_n,
809             address_length=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(
827             sw_if_index=self.pg0.sw_if_index, address=ip_addr_n,
828             address_length=16, is_add=0)
829         self.pg1.add_stream(pn)
830         self.pg_enable_capture(self.pg_interfaces)
831         self.pg_start()
832         rx = self.pg1.get_capture(1)
833         self.pg1.add_stream(pb)
834         self.pg_enable_capture(self.pg_interfaces)
835         self.pg_start()
836         rx = self.pg1.get_capture(1)
837
838         #
839         # A /31 is a special case where the 'other-side' is an attached host
840         # packets to that peer generate ARP requests
841         #
842         ip_addr_n = socket.inet_pton(socket.AF_INET, "10.10.10.10")
843
844         self.vapi.sw_interface_add_del_address(
845             sw_if_index=self.pg0.sw_if_index, address=ip_addr_n,
846             address_length=31)
847
848         pn = (Ether(src=self.pg1.remote_mac,
849                     dst=self.pg1.local_mac) /
850               IP(dst="10.10.10.11", src=self.pg0.local_ip4) /
851               UDP(sport=1234, dport=1234) /
852               Raw('\xa5' * 100))
853
854         self.pg1.add_stream(pn)
855         self.pg_enable_capture(self.pg_interfaces)
856         self.pg_start()
857         rx = self.pg0.get_capture(1)
858         rx[ARP]
859
860         # remove the sub-net and we are forwarding via the cover again
861         self.vapi.sw_interface_add_del_address(
862             sw_if_index=self.pg0.sw_if_index, address=ip_addr_n,
863             address_length=31, is_add=0)
864         self.pg1.add_stream(pn)
865         self.pg_enable_capture(self.pg_interfaces)
866         self.pg_start()
867         rx = self.pg1.get_capture(1)
868
869
870 class TestIPLoadBalance(VppTestCase):
871     """ IPv4 Load-Balancing """
872
873     def setUp(self):
874         super(TestIPLoadBalance, self).setUp()
875
876         self.create_pg_interfaces(range(5))
877         mpls_tbl = VppMplsTable(self, 0)
878         mpls_tbl.add_vpp_config()
879
880         for i in self.pg_interfaces:
881             i.admin_up()
882             i.config_ip4()
883             i.resolve_arp()
884             i.enable_mpls()
885
886     def tearDown(self):
887         for i in self.pg_interfaces:
888             i.disable_mpls()
889             i.unconfig_ip4()
890             i.admin_down()
891         super(TestIPLoadBalance, self).tearDown()
892
893     def send_and_expect_load_balancing(self, input, pkts, outputs):
894         input.add_stream(pkts)
895         self.pg_enable_capture(self.pg_interfaces)
896         self.pg_start()
897         for oo in outputs:
898             rx = oo._get_capture(1)
899             self.assertNotEqual(0, len(rx))
900
901     def send_and_expect_one_itf(self, input, pkts, itf):
902         input.add_stream(pkts)
903         self.pg_enable_capture(self.pg_interfaces)
904         self.pg_start()
905         rx = itf.get_capture(len(pkts))
906
907     def test_ip_load_balance(self):
908         """ IP Load-Balancing """
909
910         #
911         # An array of packets that differ only in the destination port
912         #
913         port_ip_pkts = []
914         port_mpls_pkts = []
915
916         #
917         # An array of packets that differ only in the source address
918         #
919         src_ip_pkts = []
920         src_mpls_pkts = []
921
922         for ii in range(65):
923             port_ip_hdr = (IP(dst="10.0.0.1", src="20.0.0.1") /
924                            UDP(sport=1234, dport=1234 + ii) /
925                            Raw('\xa5' * 100))
926             port_ip_pkts.append((Ether(src=self.pg0.remote_mac,
927                                        dst=self.pg0.local_mac) /
928                                  port_ip_hdr))
929             port_mpls_pkts.append((Ether(src=self.pg0.remote_mac,
930                                          dst=self.pg0.local_mac) /
931                                    MPLS(label=66, ttl=2) /
932                                    port_ip_hdr))
933
934             src_ip_hdr = (IP(dst="10.0.0.1", src="20.0.0.%d" % ii) /
935                           UDP(sport=1234, dport=1234) /
936                           Raw('\xa5' * 100))
937             src_ip_pkts.append((Ether(src=self.pg0.remote_mac,
938                                       dst=self.pg0.local_mac) /
939                                 src_ip_hdr))
940             src_mpls_pkts.append((Ether(src=self.pg0.remote_mac,
941                                         dst=self.pg0.local_mac) /
942                                   MPLS(label=66, ttl=2) /
943                                   src_ip_hdr))
944
945         route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
946                                     [VppRoutePath(self.pg1.remote_ip4,
947                                                   self.pg1.sw_if_index),
948                                      VppRoutePath(self.pg2.remote_ip4,
949                                                   self.pg2.sw_if_index)])
950         route_10_0_0_1.add_vpp_config()
951
952         binding = VppMplsIpBind(self, 66, "10.0.0.1", 32)
953         binding.add_vpp_config()
954
955         #
956         # inject the packet on pg0 - expect load-balancing across the 2 paths
957         #  - since the default hash config is to use IP src,dst and port
958         #    src,dst
959         # We are not going to ensure equal amounts of packets across each link,
960         # since the hash algorithm is statistical and therefore this can never
961         # be guaranteed. But wuth 64 different packets we do expect some
962         # balancing. So instead just ensure there is traffic on each link.
963         #
964         self.send_and_expect_load_balancing(self.pg0, port_ip_pkts,
965                                             [self.pg1, self.pg2])
966         self.send_and_expect_load_balancing(self.pg0, src_ip_pkts,
967                                             [self.pg1, self.pg2])
968         self.send_and_expect_load_balancing(self.pg0, port_mpls_pkts,
969                                             [self.pg1, self.pg2])
970         self.send_and_expect_load_balancing(self.pg0, src_mpls_pkts,
971                                             [self.pg1, self.pg2])
972
973         #
974         # change the flow hash config so it's only IP src,dst
975         #  - now only the stream with differing source address will
976         #    load-balance
977         #
978         self.vapi.set_ip_flow_hash(0, src=1, dst=1, sport=0, dport=0)
979
980         self.send_and_expect_load_balancing(self.pg0, src_ip_pkts,
981                                             [self.pg1, self.pg2])
982         self.send_and_expect_load_balancing(self.pg0, src_mpls_pkts,
983                                             [self.pg1, self.pg2])
984
985         self.send_and_expect_one_itf(self.pg0, port_ip_pkts, self.pg2)
986
987         #
988         # change the flow hash config back to defaults
989         #
990         self.vapi.set_ip_flow_hash(0, src=1, dst=1, sport=1, dport=1)
991
992         #
993         # Recursive prefixes
994         #  - testing that 2 stages of load-balancing occurs and there is no
995         #    polarisation (i.e. only 2 of 4 paths are used)
996         #
997         port_pkts = []
998         src_pkts = []
999
1000         for ii in range(257):
1001             port_pkts.append((Ether(src=self.pg0.remote_mac,
1002                                     dst=self.pg0.local_mac) /
1003                               IP(dst="1.1.1.1", src="20.0.0.1") /
1004                               UDP(sport=1234, dport=1234 + ii) /
1005                               Raw('\xa5' * 100)))
1006             src_pkts.append((Ether(src=self.pg0.remote_mac,
1007                                    dst=self.pg0.local_mac) /
1008                              IP(dst="1.1.1.1", src="20.0.0.%d" % ii) /
1009                              UDP(sport=1234, dport=1234) /
1010                              Raw('\xa5' * 100)))
1011
1012         route_10_0_0_2 = VppIpRoute(self, "10.0.0.2", 32,
1013                                     [VppRoutePath(self.pg3.remote_ip4,
1014                                                   self.pg3.sw_if_index),
1015                                      VppRoutePath(self.pg4.remote_ip4,
1016                                                   self.pg4.sw_if_index)])
1017         route_10_0_0_2.add_vpp_config()
1018
1019         route_1_1_1_1 = VppIpRoute(self, "1.1.1.1", 32,
1020                                    [VppRoutePath("10.0.0.2", 0xffffffff),
1021                                     VppRoutePath("10.0.0.1", 0xffffffff)])
1022         route_1_1_1_1.add_vpp_config()
1023
1024         #
1025         # inject the packet on pg0 - expect load-balancing across all 4 paths
1026         #
1027         self.vapi.cli("clear trace")
1028         self.send_and_expect_load_balancing(self.pg0, port_pkts,
1029                                             [self.pg1, self.pg2,
1030                                              self.pg3, self.pg4])
1031         self.send_and_expect_load_balancing(self.pg0, src_pkts,
1032                                             [self.pg1, self.pg2,
1033                                              self.pg3, self.pg4])
1034
1035         #
1036         # Recursive prefixes
1037         #  - testing that 2 stages of load-balancing, no choices
1038         #
1039         port_pkts = []
1040
1041         for ii in range(257):
1042             port_pkts.append((Ether(src=self.pg0.remote_mac,
1043                                     dst=self.pg0.local_mac) /
1044                               IP(dst="1.1.1.2", src="20.0.0.2") /
1045                               UDP(sport=1234, dport=1234 + ii) /
1046                               Raw('\xa5' * 100)))
1047
1048         route_10_0_0_3 = VppIpRoute(self, "10.0.0.3", 32,
1049                                     [VppRoutePath(self.pg3.remote_ip4,
1050                                                   self.pg3.sw_if_index)])
1051         route_10_0_0_3.add_vpp_config()
1052
1053         route_1_1_1_2 = VppIpRoute(self, "1.1.1.2", 32,
1054                                    [VppRoutePath("10.0.0.3", 0xffffffff)])
1055         route_1_1_1_2.add_vpp_config()
1056
1057         #
1058         # inject the packet on pg0 - expect load-balancing across all 4 paths
1059         #
1060         self.vapi.cli("clear trace")
1061         self.send_and_expect_one_itf(self.pg0, port_pkts, self.pg3)
1062
1063
1064 class TestIPVlan0(VppTestCase):
1065     """ IPv4 VLAN-0 """
1066
1067     def setUp(self):
1068         super(TestIPVlan0, self).setUp()
1069
1070         self.create_pg_interfaces(range(2))
1071         mpls_tbl = VppMplsTable(self, 0)
1072         mpls_tbl.add_vpp_config()
1073
1074         for i in self.pg_interfaces:
1075             i.admin_up()
1076             i.config_ip4()
1077             i.resolve_arp()
1078             i.enable_mpls()
1079
1080     def tearDown(self):
1081         for i in self.pg_interfaces:
1082             i.disable_mpls()
1083             i.unconfig_ip4()
1084             i.admin_down()
1085         super(TestIPVlan0, self).tearDown()
1086
1087     def test_ip_vlan_0(self):
1088         """ IP VLAN-0 """
1089
1090         pkts = (Ether(src=self.pg0.remote_mac,
1091                       dst=self.pg0.local_mac) /
1092                 Dot1Q(vlan=0) /
1093                 IP(dst=self.pg1.remote_ip4,
1094                    src=self.pg0.remote_ip4) /
1095                 UDP(sport=1234, dport=1234) /
1096                 Raw('\xa5' * 100)) * 65
1097
1098         #
1099         # Expect that packets sent on VLAN-0 are forwarded on the
1100         # main interface.
1101         #
1102         self.send_and_expect(self.pg0, pkts, self.pg1)
1103
1104
1105 class TestIPPunt(VppTestCase):
1106     """ IPv4 Punt Police/Redirect """
1107
1108     def setUp(self):
1109         super(TestIPPunt, self).setUp()
1110
1111         self.create_pg_interfaces(range(4))
1112
1113         for i in self.pg_interfaces:
1114             i.admin_up()
1115             i.config_ip4()
1116             i.resolve_arp()
1117
1118     def tearDown(self):
1119         super(TestIPPunt, self).tearDown()
1120         for i in self.pg_interfaces:
1121             i.unconfig_ip4()
1122             i.admin_down()
1123
1124     def test_ip_punt(self):
1125         """ IP punt police and redirect """
1126
1127         p = (Ether(src=self.pg0.remote_mac,
1128                    dst=self.pg0.local_mac) /
1129              IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
1130              TCP(sport=1234, dport=1234) /
1131              Raw('\xa5' * 100))
1132
1133         pkts = p * 1025
1134
1135         #
1136         # Configure a punt redirect via pg1.
1137         #
1138         nh_addr = self.pg1.remote_ip4
1139         self.vapi.ip_punt_redirect(self.pg0.sw_if_index,
1140                                    self.pg1.sw_if_index,
1141                                    nh_addr)
1142
1143         self.send_and_expect(self.pg0, pkts, self.pg1)
1144
1145         #
1146         # add a policer
1147         #
1148         policer = self.vapi.policer_add_del("ip4-punt", 400, 0, 10, 0,
1149                                             rate_type=1)
1150         self.vapi.ip_punt_police(policer.policer_index)
1151
1152         self.vapi.cli("clear trace")
1153         self.pg0.add_stream(pkts)
1154         self.pg_enable_capture(self.pg_interfaces)
1155         self.pg_start()
1156
1157         #
1158         # the number of packet recieved should be greater than 0,
1159         # but not equal to the number sent, since some were policed
1160         #
1161         rx = self.pg1._get_capture(1)
1162         self.assertGreater(len(rx), 0)
1163         self.assertLess(len(rx), len(pkts))
1164
1165         #
1166         # remove the poilcer. back to full rx
1167         #
1168         self.vapi.ip_punt_police(policer.policer_index, is_add=0)
1169         self.vapi.policer_add_del("ip4-punt", 400, 0, 10, 0,
1170                                   rate_type=1, is_add=0)
1171         self.send_and_expect(self.pg0, pkts, self.pg1)
1172
1173         #
1174         # remove the redirect. expect full drop.
1175         #
1176         self.vapi.ip_punt_redirect(self.pg0.sw_if_index,
1177                                    self.pg1.sw_if_index,
1178                                    nh_addr,
1179                                    is_add=0)
1180         self.send_and_assert_no_replies(self.pg0, pkts,
1181                                         "IP no punt config")
1182
1183         #
1184         # Add a redirect that is not input port selective
1185         #
1186         self.vapi.ip_punt_redirect(0xffffffff,
1187                                    self.pg1.sw_if_index,
1188                                    nh_addr)
1189         self.send_and_expect(self.pg0, pkts, self.pg1)
1190
1191         self.vapi.ip_punt_redirect(0xffffffff,
1192                                    self.pg1.sw_if_index,
1193                                    nh_addr,
1194                                    is_add=0)
1195
1196     def test_ip_punt_dump(self):
1197         """ IP4 punt redirect dump"""
1198
1199         #
1200         # Configure a punt redirects
1201         #
1202         nh_address = self.pg3.remote_ip4
1203         self.vapi.ip_punt_redirect(self.pg0.sw_if_index,
1204                                    self.pg3.sw_if_index,
1205                                    nh_address)
1206         self.vapi.ip_punt_redirect(self.pg1.sw_if_index,
1207                                    self.pg3.sw_if_index,
1208                                    nh_address)
1209         self.vapi.ip_punt_redirect(self.pg2.sw_if_index,
1210                                    self.pg3.sw_if_index,
1211                                    '0.0.0.0')
1212
1213         #
1214         # Dump pg0 punt redirects
1215         #
1216         punts = self.vapi.ip_punt_redirect_dump(self.pg0.sw_if_index)
1217         for p in punts:
1218             self.assertEqual(p.punt.rx_sw_if_index, self.pg0.sw_if_index)
1219
1220         #
1221         # Dump punt redirects for all interfaces
1222         #
1223         punts = self.vapi.ip_punt_redirect_dump(0xffffffff)
1224         self.assertEqual(len(punts), 3)
1225         for p in punts:
1226             self.assertEqual(p.punt.tx_sw_if_index, self.pg3.sw_if_index)
1227         self.assertNotEqual(punts[1].punt.nh, self.pg3.remote_ip4)
1228         self.assertEqual(str(punts[2].punt.nh), '0.0.0.0')
1229
1230
1231 class TestIPDeag(VppTestCase):
1232     """ IPv4 Deaggregate Routes """
1233
1234     def setUp(self):
1235         super(TestIPDeag, self).setUp()
1236
1237         self.create_pg_interfaces(range(3))
1238
1239         for i in self.pg_interfaces:
1240             i.admin_up()
1241             i.config_ip4()
1242             i.resolve_arp()
1243
1244     def tearDown(self):
1245         super(TestIPDeag, self).tearDown()
1246         for i in self.pg_interfaces:
1247             i.unconfig_ip4()
1248             i.admin_down()
1249
1250     def test_ip_deag(self):
1251         """ IP Deag Routes """
1252
1253         #
1254         # Create a table to be used for:
1255         #  1 - another destination address lookup
1256         #  2 - a source address lookup
1257         #
1258         table_dst = VppIpTable(self, 1)
1259         table_src = VppIpTable(self, 2)
1260         table_dst.add_vpp_config()
1261         table_src.add_vpp_config()
1262
1263         #
1264         # Add a route in the default table to point to a deag/
1265         # second lookup in each of these tables
1266         #
1267         route_to_dst = VppIpRoute(self, "1.1.1.1", 32,
1268                                   [VppRoutePath("0.0.0.0",
1269                                                 0xffffffff,
1270                                                 nh_table_id=1)])
1271         route_to_src = VppIpRoute(self, "1.1.1.2", 32,
1272                                   [VppRoutePath("0.0.0.0",
1273                                                 0xffffffff,
1274                                                 nh_table_id=2,
1275                                                 is_source_lookup=1)])
1276         route_to_dst.add_vpp_config()
1277         route_to_src.add_vpp_config()
1278
1279         #
1280         # packets to these destination are dropped, since they'll
1281         # hit the respective default routes in the second table
1282         #
1283         p_dst = (Ether(src=self.pg0.remote_mac,
1284                        dst=self.pg0.local_mac) /
1285                  IP(src="5.5.5.5", dst="1.1.1.1") /
1286                  TCP(sport=1234, dport=1234) /
1287                  Raw('\xa5' * 100))
1288         p_src = (Ether(src=self.pg0.remote_mac,
1289                        dst=self.pg0.local_mac) /
1290                  IP(src="2.2.2.2", dst="1.1.1.2") /
1291                  TCP(sport=1234, dport=1234) /
1292                  Raw('\xa5' * 100))
1293         pkts_dst = p_dst * 257
1294         pkts_src = p_src * 257
1295
1296         self.send_and_assert_no_replies(self.pg0, pkts_dst,
1297                                         "IP in dst table")
1298         self.send_and_assert_no_replies(self.pg0, pkts_src,
1299                                         "IP in src table")
1300
1301         #
1302         # add a route in the dst table to forward via pg1
1303         #
1304         route_in_dst = VppIpRoute(self, "1.1.1.1", 32,
1305                                   [VppRoutePath(self.pg1.remote_ip4,
1306                                                 self.pg1.sw_if_index)],
1307                                   table_id=1)
1308         route_in_dst.add_vpp_config()
1309         self.send_and_expect(self.pg0, pkts_dst, self.pg1)
1310
1311         #
1312         # add a route in the src table to forward via pg2
1313         #
1314         route_in_src = VppIpRoute(self, "2.2.2.2", 32,
1315                                   [VppRoutePath(self.pg2.remote_ip4,
1316                                                 self.pg2.sw_if_index)],
1317                                   table_id=2)
1318         route_in_src.add_vpp_config()
1319         self.send_and_expect(self.pg0, pkts_src, self.pg2)
1320
1321         #
1322         # loop in the lookup DP
1323         #
1324         route_loop = VppIpRoute(self, "2.2.2.3", 32,
1325                                 [VppRoutePath("0.0.0.0",
1326                                               0xffffffff,
1327                                               nh_table_id=0)])
1328         route_loop.add_vpp_config()
1329
1330         p_l = (Ether(src=self.pg0.remote_mac,
1331                      dst=self.pg0.local_mac) /
1332                IP(src="2.2.2.4", dst="2.2.2.3") /
1333                TCP(sport=1234, dport=1234) /
1334                Raw('\xa5' * 100))
1335
1336         self.send_and_assert_no_replies(self.pg0, p_l * 257,
1337                                         "IP lookup loop")
1338
1339
1340 class TestIPInput(VppTestCase):
1341     """ IPv4 Input Exceptions """
1342
1343     def setUp(self):
1344         super(TestIPInput, self).setUp()
1345
1346         self.create_pg_interfaces(range(2))
1347
1348         for i in self.pg_interfaces:
1349             i.admin_up()
1350             i.config_ip4()
1351             i.resolve_arp()
1352
1353     def tearDown(self):
1354         super(TestIPInput, self).tearDown()
1355         for i in self.pg_interfaces:
1356             i.unconfig_ip4()
1357             i.admin_down()
1358
1359     def test_ip_input(self):
1360         """ IP Input Exceptions """
1361
1362         # i can't find a way in scapy to construct an IP packet
1363         # with a length less than the IP header length
1364
1365         #
1366         # Packet too short - this is forwarded
1367         #
1368         p_short = (Ether(src=self.pg0.remote_mac,
1369                          dst=self.pg0.local_mac) /
1370                    IP(src=self.pg0.remote_ip4,
1371                       dst=self.pg1.remote_ip4,
1372                       len=40) /
1373                    UDP(sport=1234, dport=1234) /
1374                    Raw('\xa5' * 100))
1375
1376         rx = self.send_and_expect(self.pg0, p_short * 65, self.pg1)
1377
1378         #
1379         # Packet too long - this is dropped
1380         #
1381         p_long = (Ether(src=self.pg0.remote_mac,
1382                         dst=self.pg0.local_mac) /
1383                   IP(src=self.pg0.remote_ip4,
1384                      dst=self.pg1.remote_ip4,
1385                      len=400) /
1386                   UDP(sport=1234, dport=1234) /
1387                   Raw('\xa5' * 100))
1388
1389         rx = self.send_and_assert_no_replies(self.pg0, p_long * 65,
1390                                              "too long")
1391
1392         #
1393         # bad chksum - this is dropped
1394         #
1395         p_chksum = (Ether(src=self.pg0.remote_mac,
1396                           dst=self.pg0.local_mac) /
1397                     IP(src=self.pg0.remote_ip4,
1398                        dst=self.pg1.remote_ip4,
1399                        chksum=400) /
1400                     UDP(sport=1234, dport=1234) /
1401                     Raw('\xa5' * 100))
1402
1403         rx = self.send_and_assert_no_replies(self.pg0, p_chksum * 65,
1404                                              "bad checksum")
1405
1406         #
1407         # bad version - this is dropped
1408         #
1409         p_ver = (Ether(src=self.pg0.remote_mac,
1410                        dst=self.pg0.local_mac) /
1411                  IP(src=self.pg0.remote_ip4,
1412                     dst=self.pg1.remote_ip4,
1413                     version=3) /
1414                  UDP(sport=1234, dport=1234) /
1415                  Raw('\xa5' * 100))
1416
1417         rx = self.send_and_assert_no_replies(self.pg0, p_ver * 65,
1418                                              "funky version")
1419
1420         #
1421         # fragment offset 1 - this is dropped
1422         #
1423         p_frag = (Ether(src=self.pg0.remote_mac,
1424                         dst=self.pg0.local_mac) /
1425                   IP(src=self.pg0.remote_ip4,
1426                      dst=self.pg1.remote_ip4,
1427                      frag=1) /
1428                   UDP(sport=1234, dport=1234) /
1429                   Raw('\xa5' * 100))
1430
1431         rx = self.send_and_assert_no_replies(self.pg0, p_frag * 65,
1432                                              "frag offset")
1433
1434         #
1435         # TTL expired packet
1436         #
1437         p_ttl = (Ether(src=self.pg0.remote_mac,
1438                        dst=self.pg0.local_mac) /
1439                  IP(src=self.pg0.remote_ip4,
1440                     dst=self.pg1.remote_ip4,
1441                     ttl=1) /
1442                  UDP(sport=1234, dport=1234) /
1443                  Raw('\xa5' * 100))
1444
1445         rx = self.send_and_expect(self.pg0, p_ttl * 65, self.pg0)
1446
1447         rx = rx[0]
1448         icmp = rx[ICMP]
1449
1450         self.assertEqual(icmptypes[icmp.type], "time-exceeded")
1451         self.assertEqual(icmpcodes[icmp.type][icmp.code],
1452                          "ttl-zero-during-transit")
1453         self.assertEqual(icmp.src, self.pg0.remote_ip4)
1454         self.assertEqual(icmp.dst, self.pg1.remote_ip4)
1455
1456         #
1457         # MTU exceeded
1458         #
1459         p_mtu = (Ether(src=self.pg0.remote_mac,
1460                        dst=self.pg0.local_mac) /
1461                  IP(src=self.pg0.remote_ip4,
1462                     dst=self.pg1.remote_ip4,
1463                     ttl=10, flags='DF') /
1464                  UDP(sport=1234, dport=1234) /
1465                  Raw('\xa5' * 2000))
1466
1467         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [1500, 0, 0, 0])
1468
1469         rx = self.send_and_expect(self.pg0, p_mtu * 65, self.pg0)
1470         rx = rx[0]
1471         icmp = rx[ICMP]
1472
1473         self.assertEqual(icmptypes[icmp.type], "dest-unreach")
1474         self.assertEqual(icmpcodes[icmp.type][icmp.code],
1475                          "fragmentation-needed")
1476         self.assertEqual(icmp.src, self.pg0.remote_ip4)
1477         self.assertEqual(icmp.dst, self.pg1.remote_ip4)
1478
1479         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [2500, 0, 0, 0])
1480         rx = self.send_and_expect(self.pg0, p_mtu * 65, self.pg1)
1481
1482         # Reset MTU for subsequent tests
1483         self.vapi.sw_interface_set_mtu(self.pg1.sw_if_index, [9000, 0, 0, 0])
1484
1485         #
1486         # source address 0.0.0.0 and 25.255.255.255 and for-us
1487         #
1488         p_s0 = (Ether(src=self.pg0.remote_mac,
1489                       dst=self.pg0.local_mac) /
1490                 IP(src="0.0.0.0",
1491                    dst=self.pg0.local_ip4) /
1492                 ICMP(id=4, seq=4) /
1493                 Raw(load='\x0a' * 18))
1494         rx = self.send_and_assert_no_replies(self.pg0, p_s0 * 17)
1495
1496         p_s0 = (Ether(src=self.pg0.remote_mac,
1497                       dst=self.pg0.local_mac) /
1498                 IP(src="255.255.255.255",
1499                    dst=self.pg0.local_ip4) /
1500                 ICMP(id=4, seq=4) /
1501                 Raw(load='\x0a' * 18))
1502         rx = self.send_and_assert_no_replies(self.pg0, p_s0 * 17)
1503
1504
1505 class TestIPDirectedBroadcast(VppTestCase):
1506     """ IPv4 Directed Broadcast """
1507
1508     def setUp(self):
1509         super(TestIPDirectedBroadcast, self).setUp()
1510
1511         self.create_pg_interfaces(range(2))
1512
1513         for i in self.pg_interfaces:
1514             i.admin_up()
1515
1516     def tearDown(self):
1517         super(TestIPDirectedBroadcast, self).tearDown()
1518         for i in self.pg_interfaces:
1519             i.admin_down()
1520
1521     def test_ip_input(self):
1522         """ IP Directed Broadcast """
1523
1524         #
1525         # set the directed broadcast on pg0 first, then config IP4 addresses
1526         # for pg1 directed broadcast is always disabled
1527         self.vapi.sw_interface_set_ip_directed_broadcast(
1528             self.pg0.sw_if_index, 1)
1529
1530         p0 = (Ether(src=self.pg1.remote_mac,
1531                     dst=self.pg1.local_mac) /
1532               IP(src="1.1.1.1",
1533                  dst=self.pg0._local_ip4_bcast) /
1534               UDP(sport=1234, dport=1234) /
1535               Raw('\xa5' * 2000))
1536         p1 = (Ether(src=self.pg0.remote_mac,
1537                     dst=self.pg0.local_mac) /
1538               IP(src="1.1.1.1",
1539                  dst=self.pg1._local_ip4_bcast) /
1540               UDP(sport=1234, dport=1234) /
1541               Raw('\xa5' * 2000))
1542
1543         self.pg0.config_ip4()
1544         self.pg0.resolve_arp()
1545         self.pg1.config_ip4()
1546         self.pg1.resolve_arp()
1547
1548         #
1549         # test packet is L2 broadcast
1550         #
1551         rx = self.send_and_expect(self.pg1, p0 * 65, self.pg0)
1552         self.assertTrue(rx[0][Ether].dst, "ff:ff:ff:ff:ff:ff")
1553
1554         self.send_and_assert_no_replies(self.pg0, p1 * 65,
1555                                         "directed broadcast disabled")
1556
1557         #
1558         # toggle directed broadcast on pg0
1559         #
1560         self.vapi.sw_interface_set_ip_directed_broadcast(
1561             self.pg0.sw_if_index, 0)
1562         self.send_and_assert_no_replies(self.pg1, p0 * 65,
1563                                         "directed broadcast disabled")
1564
1565         self.vapi.sw_interface_set_ip_directed_broadcast(
1566             self.pg0.sw_if_index, 1)
1567         rx = self.send_and_expect(self.pg1, p0 * 65, self.pg0)
1568
1569         self.pg0.unconfig_ip4()
1570         self.pg1.unconfig_ip4()
1571
1572
1573 class TestIPLPM(VppTestCase):
1574     """ IPv4 longest Prefix Match """
1575
1576     def setUp(self):
1577         super(TestIPLPM, self).setUp()
1578
1579         self.create_pg_interfaces(range(4))
1580
1581         for i in self.pg_interfaces:
1582             i.admin_up()
1583             i.config_ip4()
1584             i.resolve_arp()
1585
1586     def tearDown(self):
1587         super(TestIPLPM, self).tearDown()
1588         for i in self.pg_interfaces:
1589             i.admin_down()
1590             i.unconfig_ip4()
1591
1592     def test_ip_lpm(self):
1593         """ IP longest Prefix Match """
1594
1595         s_24 = VppIpRoute(self, "10.1.2.0", 24,
1596                           [VppRoutePath(self.pg1.remote_ip4,
1597                                         self.pg1.sw_if_index)])
1598         s_24.add_vpp_config()
1599         s_8 = VppIpRoute(self, "10.0.0.0", 8,
1600                          [VppRoutePath(self.pg2.remote_ip4,
1601                                        self.pg2.sw_if_index)])
1602         s_8.add_vpp_config()
1603
1604         p_8 = (Ether(src=self.pg0.remote_mac,
1605                      dst=self.pg0.local_mac) /
1606                IP(src="1.1.1.1",
1607                   dst="10.1.1.1") /
1608                UDP(sport=1234, dport=1234) /
1609                Raw('\xa5' * 2000))
1610         p_24 = (Ether(src=self.pg0.remote_mac,
1611                       dst=self.pg0.local_mac) /
1612                 IP(src="1.1.1.1",
1613                    dst="10.1.2.1") /
1614                 UDP(sport=1234, dport=1234) /
1615                 Raw('\xa5' * 2000))
1616
1617         self.logger.info(self.vapi.cli("sh ip fib mtrie"))
1618         rx = self.send_and_expect(self.pg0, p_8 * 65, self.pg2)
1619         rx = self.send_and_expect(self.pg0, p_24 * 65, self.pg1)
1620
1621
1622 class TestIPv4Frag(VppTestCase):
1623     """ IPv4 fragmentation """
1624
1625     @classmethod
1626     def setUpClass(cls):
1627         super(TestIPv4Frag, cls).setUpClass()
1628
1629         cls.create_pg_interfaces([0, 1])
1630         cls.src_if = cls.pg0
1631         cls.dst_if = cls.pg1
1632
1633         # setup all interfaces
1634         for i in cls.pg_interfaces:
1635             i.admin_up()
1636             i.config_ip4()
1637             i.resolve_arp()
1638
1639     def test_frag_large_packets(self):
1640         """ Fragmentation of large packets """
1641
1642         p = (Ether(dst=self.src_if.local_mac, src=self.src_if.remote_mac) /
1643              IP(src=self.src_if.remote_ip4, dst=self.dst_if.remote_ip4) /
1644              UDP(sport=1234, dport=5678) / Raw())
1645         self.extend_packet(p, 6000, "abcde")
1646         saved_payload = p[Raw].load
1647
1648         # Force fragmentation by setting MTU of output interface
1649         # lower than packet size
1650         self.vapi.sw_interface_set_mtu(self.dst_if.sw_if_index,
1651                                        [5000, 0, 0, 0])
1652
1653         self.pg_enable_capture()
1654         self.src_if.add_stream(p)
1655         self.pg_start()
1656
1657         # Expecting 3 fragments because size of created fragments currently
1658         # cannot be larger then VPP buffer size (which is 2048)
1659         packets = self.dst_if.get_capture(3)
1660
1661         # Assume VPP sends the fragments in order
1662         payload = ''
1663         for p in packets:
1664             payload_offset = p.frag * 8
1665             if payload_offset > 0:
1666                 payload_offset -= 8  # UDP header is not in payload
1667             self.assert_equal(payload_offset, len(payload))
1668             payload += p[Raw].load
1669         self.assert_equal(payload, saved_payload, "payload")
1670
1671
1672 if __name__ == '__main__':
1673     unittest.main(testRunner=VppTestRunner)