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