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