Source Lookup progammable via API
[vpp.git] / test / test_ip4.py
1 #!/usr/bin/env python
2 import random
3 import socket
4 import unittest
5
6 from framework import VppTestCase, VppTestRunner
7 from vpp_sub_interface import VppSubInterface, VppDot1QSubint, VppDot1ADSubint
8 from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpMRoute, \
9     VppMRoutePath, MRouteItfFlags, MRouteEntryFlags, VppMplsIpBind, \
10     VppMplsTable, VppIpTable
11
12 from scapy.packet import Raw
13 from scapy.layers.l2 import Ether, Dot1Q, ARP
14 from scapy.layers.inet import IP, UDP, TCP, ICMP, icmptypes, icmpcodes
15 from util import ppp
16 from scapy.contrib.mpls import MPLS
17
18
19 class TestIPv4(VppTestCase):
20     """ IPv4 Test Case """
21
22     def setUp(self):
23         """
24         Perform test setup before test case.
25
26         **Config:**
27             - create 3 pg interfaces
28                 - untagged pg0 interface
29                 - Dot1Q subinterface on pg1
30                 - Dot1AD subinterface on pg2
31             - setup interfaces:
32                 - put it into UP state
33                 - set IPv4 addresses
34                 - resolve neighbor address using ARP
35             - configure 200 fib entries
36
37         :ivar list interfaces: pg interfaces and subinterfaces.
38         :ivar dict flows: IPv4 packet flows in test.
39         :ivar list pg_if_packet_sizes: packet sizes in test.
40         """
41         super(TestIPv4, self).setUp()
42
43         # create 3 pg interfaces
44         self.create_pg_interfaces(range(3))
45
46         # create 2 subinterfaces for pg1 and pg2
47         self.sub_interfaces = [
48             VppDot1QSubint(self, self.pg1, 100),
49             VppDot1ADSubint(self, self.pg2, 200, 300, 400)]
50
51         # packet flows mapping pg0 -> pg1.sub, pg2.sub, etc.
52         self.flows = dict()
53         self.flows[self.pg0] = [self.pg1.sub_if, self.pg2.sub_if]
54         self.flows[self.pg1.sub_if] = [self.pg0, self.pg2.sub_if]
55         self.flows[self.pg2.sub_if] = [self.pg0, self.pg1.sub_if]
56
57         # packet sizes
58         self.pg_if_packet_sizes = [64, 512, 1518, 9018]
59         self.sub_if_packet_sizes = [64, 512, 1518 + 4, 9018 + 4]
60
61         self.interfaces = list(self.pg_interfaces)
62         self.interfaces.extend(self.sub_interfaces)
63
64         # setup all interfaces
65         for i in self.interfaces:
66             i.admin_up()
67             i.config_ip4()
68             i.resolve_arp()
69
70         # config 2M FIB entries
71         self.config_fib_entries(200)
72
73     def tearDown(self):
74         """Run standard test teardown and log ``show ip arp``."""
75         super(TestIPv4, self).tearDown()
76         if not self.vpp_dead:
77             self.logger.info(self.vapi.cli("show ip arp"))
78             # info(self.vapi.cli("show ip fib"))  # many entries
79
80     def config_fib_entries(self, count):
81         """For each interface add to the FIB table *count* routes to
82         "10.0.0.1/32" destination with interface's local address as next-hop
83         address.
84
85         :param int count: Number of FIB entries.
86
87         - *TODO:* check if the next-hop address shouldn't be remote address
88           instead of local address.
89         """
90         n_int = len(self.interfaces)
91         percent = 0
92         counter = 0.0
93         dest_addr = socket.inet_pton(socket.AF_INET, "10.0.0.1")
94         dest_addr_len = 32
95         for i in self.interfaces:
96             next_hop_address = i.local_ip4n
97             for j in range(count / n_int):
98                 self.vapi.ip_add_del_route(
99                     dest_addr, dest_addr_len, next_hop_address)
100                 counter += 1
101                 if counter / count * 100 > percent:
102                     self.logger.info("Configure %d FIB entries .. %d%% done" %
103                                      (count, percent))
104                     percent += 1
105
106     def create_stream(self, src_if, packet_sizes):
107         """Create input packet stream for defined interface.
108
109         :param VppInterface src_if: Interface to create packet stream for.
110         :param list packet_sizes: Required packet sizes.
111         """
112         pkts = []
113         for i in range(0, 257):
114             dst_if = self.flows[src_if][i % 2]
115             info = self.create_packet_info(src_if, dst_if)
116             payload = self.info_to_payload(info)
117             p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
118                  IP(src=src_if.remote_ip4, dst=dst_if.remote_ip4) /
119                  UDP(sport=1234, dport=1234) /
120                  Raw(payload))
121             info.data = p.copy()
122             if isinstance(src_if, VppSubInterface):
123                 p = src_if.add_dot1_layer(p)
124             size = packet_sizes[(i // 2) % len(packet_sizes)]
125             self.extend_packet(p, size)
126             pkts.append(p)
127         return pkts
128
129     def verify_capture(self, dst_if, capture):
130         """Verify captured input packet stream for defined interface.
131
132         :param VppInterface dst_if: Interface to verify captured packet stream
133                                     for.
134         :param list capture: Captured packet stream.
135         """
136         self.logger.info("Verifying capture on interface %s" % dst_if.name)
137         last_info = dict()
138         for i in self.interfaces:
139             last_info[i.sw_if_index] = None
140         is_sub_if = False
141         dst_sw_if_index = dst_if.sw_if_index
142         if hasattr(dst_if, 'parent'):
143             is_sub_if = True
144         for packet in capture:
145             if is_sub_if:
146                 # Check VLAN tags and Ethernet header
147                 packet = dst_if.remove_dot1_layer(packet)
148             self.assertTrue(Dot1Q not in packet)
149             try:
150                 ip = packet[IP]
151                 udp = packet[UDP]
152                 payload_info = self.payload_to_info(str(packet[Raw]))
153                 packet_index = payload_info.index
154                 self.assertEqual(payload_info.dst, dst_sw_if_index)
155                 self.logger.debug(
156                     "Got packet on port %s: src=%u (id=%u)" %
157                     (dst_if.name, payload_info.src, packet_index))
158                 next_info = self.get_next_packet_info_for_interface2(
159                     payload_info.src, dst_sw_if_index,
160                     last_info[payload_info.src])
161                 last_info[payload_info.src] = next_info
162                 self.assertTrue(next_info is not None)
163                 self.assertEqual(packet_index, next_info.index)
164                 saved_packet = next_info.data
165                 # Check standard fields
166                 self.assertEqual(ip.src, saved_packet[IP].src)
167                 self.assertEqual(ip.dst, saved_packet[IP].dst)
168                 self.assertEqual(udp.sport, saved_packet[UDP].sport)
169                 self.assertEqual(udp.dport, saved_packet[UDP].dport)
170             except:
171                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
172                 raise
173         for i in self.interfaces:
174             remaining_packet = self.get_next_packet_info_for_interface2(
175                 i.sw_if_index, dst_sw_if_index, last_info[i.sw_if_index])
176             self.assertTrue(remaining_packet is None,
177                             "Interface %s: Packet expected from interface %s "
178                             "didn't arrive" % (dst_if.name, i.name))
179
180     def test_fib(self):
181         """ IPv4 FIB test
182
183         Test scenario:
184
185             - Create IPv4 stream for pg0 interface
186             - Create IPv4 tagged streams for pg1's and pg2's subinterface.
187             - Send and verify received packets on each interface.
188         """
189
190         pkts = self.create_stream(self.pg0, self.pg_if_packet_sizes)
191         self.pg0.add_stream(pkts)
192
193         for i in self.sub_interfaces:
194             pkts = self.create_stream(i, self.sub_if_packet_sizes)
195             i.parent.add_stream(pkts)
196
197         self.pg_enable_capture(self.pg_interfaces)
198         self.pg_start()
199
200         pkts = self.pg0.get_capture()
201         self.verify_capture(self.pg0, pkts)
202
203         for i in self.sub_interfaces:
204             pkts = i.parent.get_capture()
205             self.verify_capture(i, pkts)
206
207
208 class TestIPv4FibCrud(VppTestCase):
209     """ FIB - add/update/delete - ip4 routes
210
211     Test scenario:
212         - add 1k,
213         - del 100,
214         - add new 1k,
215         - del 1.5k
216
217     ..note:: Python API is too slow to add many routes, needs replacement.
218     """
219
220     def config_fib_many_to_one(self, start_dest_addr, next_hop_addr, count):
221         """
222
223         :param start_dest_addr:
224         :param next_hop_addr:
225         :param count:
226         :return list: added ips with 32 prefix
227         """
228         added_ips = []
229         dest_addr = int(socket.inet_pton(socket.AF_INET,
230                                          start_dest_addr).encode('hex'),
231                         16)
232         dest_addr_len = 32
233         n_next_hop_addr = socket.inet_pton(socket.AF_INET, next_hop_addr)
234         for _ in range(count):
235             n_dest_addr = '{:08x}'.format(dest_addr).decode('hex')
236             self.vapi.ip_add_del_route(n_dest_addr, dest_addr_len,
237                                        n_next_hop_addr)
238             added_ips.append(socket.inet_ntoa(n_dest_addr))
239             dest_addr += 1
240         return added_ips
241
242     def unconfig_fib_many_to_one(self, start_dest_addr, next_hop_addr, count):
243
244         removed_ips = []
245         dest_addr = int(socket.inet_pton(socket.AF_INET,
246                                          start_dest_addr).encode('hex'),
247                         16)
248         dest_addr_len = 32
249         n_next_hop_addr = socket.inet_pton(socket.AF_INET, next_hop_addr)
250         for _ in range(count):
251             n_dest_addr = '{:08x}'.format(dest_addr).decode('hex')
252             self.vapi.ip_add_del_route(n_dest_addr, dest_addr_len,
253                                        n_next_hop_addr, is_add=0)
254             removed_ips.append(socket.inet_ntoa(n_dest_addr))
255             dest_addr += 1
256         return removed_ips
257
258     def create_stream(self, src_if, dst_if, dst_ips, count):
259         pkts = []
260
261         for _ in range(count):
262             dst_addr = random.choice(dst_ips)
263             info = self.create_packet_info(src_if, dst_if)
264             payload = self.info_to_payload(info)
265             p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
266                  IP(src=src_if.remote_ip4, dst=dst_addr) /
267                  UDP(sport=1234, dport=1234) /
268                  Raw(payload))
269             info.data = p.copy()
270             self.extend_packet(p, random.choice(self.pg_if_packet_sizes))
271             pkts.append(p)
272
273         return pkts
274
275     def _find_ip_match(self, find_in, pkt):
276         for p in find_in:
277             if self.payload_to_info(str(p[Raw])) == \
278                     self.payload_to_info(str(pkt[Raw])):
279                 if p[IP].src != pkt[IP].src:
280                     break
281                 if p[IP].dst != pkt[IP].dst:
282                     break
283                 if p[UDP].sport != pkt[UDP].sport:
284                     break
285                 if p[UDP].dport != pkt[UDP].dport:
286                     break
287                 return p
288         return None
289
290     @staticmethod
291     def _match_route_detail(route_detail, ip, address_length=32, table_id=0):
292         if route_detail.address == socket.inet_pton(socket.AF_INET, ip):
293             if route_detail.table_id != table_id:
294                 return False
295             elif route_detail.address_length != address_length:
296                 return False
297             else:
298                 return True
299         else:
300             return False
301
302     def verify_capture(self, dst_interface, received_pkts, expected_pkts):
303         self.assertEqual(len(received_pkts), len(expected_pkts))
304         to_verify = list(expected_pkts)
305         for p in received_pkts:
306             self.assertEqual(p.src, dst_interface.local_mac)
307             self.assertEqual(p.dst, dst_interface.remote_mac)
308             x = self._find_ip_match(to_verify, p)
309             to_verify.remove(x)
310         self.assertListEqual(to_verify, [])
311
312     def verify_route_dump(self, fib_dump, ips):
313
314         def _ip_in_route_dump(ip, fib_dump):
315             return next((route for route in fib_dump
316                          if self._match_route_detail(route, ip)),
317                         False)
318
319         for ip in ips:
320             self.assertTrue(_ip_in_route_dump(ip, fib_dump),
321                             'IP {} is not in fib dump.'.format(ip))
322
323     def verify_not_in_route_dump(self, fib_dump, ips):
324
325         def _ip_in_route_dump(ip, fib_dump):
326             return next((route for route in fib_dump
327                          if self._match_route_detail(route, ip)),
328                         False)
329
330         for ip in ips:
331             self.assertFalse(_ip_in_route_dump(ip, fib_dump),
332                              'IP {} is in fib dump.'.format(ip))
333
334     @classmethod
335     def setUpClass(cls):
336         """
337         #. Create and initialize 3 pg interfaces.
338         #. initialize class attributes configured_routes and deleted_routes
339            to store information between tests.
340         """
341         super(TestIPv4FibCrud, cls).setUpClass()
342
343         try:
344             # create 3 pg interfaces
345             cls.create_pg_interfaces(range(3))
346
347             cls.interfaces = list(cls.pg_interfaces)
348
349             # setup all interfaces
350             for i in cls.interfaces:
351                 i.admin_up()
352                 i.config_ip4()
353                 i.resolve_arp()
354
355             cls.configured_routes = []
356             cls.deleted_routes = []
357             cls.pg_if_packet_sizes = [64, 512, 1518, 9018]
358
359         except Exception:
360             super(TestIPv4FibCrud, cls).tearDownClass()
361             raise
362
363     def setUp(self):
364         super(TestIPv4FibCrud, self).setUp()
365         self.reset_packet_infos()
366
367     def test_1_add_routes(self):
368         """ Add 1k routes
369
370         - add 100 routes check with traffic script.
371         """
372         # config 1M FIB entries
373         self.configured_routes.extend(self.config_fib_many_to_one(
374             "10.0.0.0", self.pg0.remote_ip4, 100))
375
376         fib_dump = self.vapi.ip_fib_dump()
377         self.verify_route_dump(fib_dump, self.configured_routes)
378
379         self.stream_1 = self.create_stream(
380             self.pg1, self.pg0, self.configured_routes, 100)
381         self.stream_2 = self.create_stream(
382             self.pg2, self.pg0, self.configured_routes, 100)
383         self.pg1.add_stream(self.stream_1)
384         self.pg2.add_stream(self.stream_2)
385
386         self.pg_enable_capture(self.pg_interfaces)
387         self.pg_start()
388
389         pkts = self.pg0.get_capture(len(self.stream_1) + len(self.stream_2))
390         self.verify_capture(self.pg0, pkts, self.stream_1 + self.stream_2)
391
392     def test_2_del_routes(self):
393         """ Delete 100 routes
394
395         - delete 10 routes check with traffic script.
396         """
397         self.deleted_routes.extend(self.unconfig_fib_many_to_one(
398             "10.0.0.10", self.pg0.remote_ip4, 10))
399         for x in self.deleted_routes:
400             self.configured_routes.remove(x)
401
402         fib_dump = self.vapi.ip_fib_dump()
403         self.verify_route_dump(fib_dump, self.configured_routes)
404
405         self.stream_1 = self.create_stream(
406             self.pg1, self.pg0, self.configured_routes, 100)
407         self.stream_2 = self.create_stream(
408             self.pg2, self.pg0, self.configured_routes, 100)
409         self.stream_3 = self.create_stream(
410             self.pg1, self.pg0, self.deleted_routes, 100)
411         self.stream_4 = self.create_stream(
412             self.pg2, self.pg0, self.deleted_routes, 100)
413         self.pg1.add_stream(self.stream_1 + self.stream_3)
414         self.pg2.add_stream(self.stream_2 + self.stream_4)
415         self.pg_enable_capture(self.pg_interfaces)
416         self.pg_start()
417
418         pkts = self.pg0.get_capture(len(self.stream_1) + len(self.stream_2))
419         self.verify_capture(self.pg0, pkts, self.stream_1 + self.stream_2)
420
421     def test_3_add_new_routes(self):
422         """ Add 1k routes
423
424         - re-add 5 routes check with traffic script.
425         - add 100 routes check with traffic script.
426         """
427         tmp = self.config_fib_many_to_one(
428             "10.0.0.10", self.pg0.remote_ip4, 5)
429         self.configured_routes.extend(tmp)
430         for x in tmp:
431             self.deleted_routes.remove(x)
432
433         self.configured_routes.extend(self.config_fib_many_to_one(
434             "10.0.1.0", self.pg0.remote_ip4, 100))
435
436         fib_dump = self.vapi.ip_fib_dump()
437         self.verify_route_dump(fib_dump, self.configured_routes)
438
439         self.stream_1 = self.create_stream(
440             self.pg1, self.pg0, self.configured_routes, 300)
441         self.stream_2 = self.create_stream(
442             self.pg2, self.pg0, self.configured_routes, 300)
443         self.stream_3 = self.create_stream(
444             self.pg1, self.pg0, self.deleted_routes, 100)
445         self.stream_4 = self.create_stream(
446             self.pg2, self.pg0, self.deleted_routes, 100)
447
448         self.pg1.add_stream(self.stream_1 + self.stream_3)
449         self.pg2.add_stream(self.stream_2 + self.stream_4)
450         self.pg_enable_capture(self.pg_interfaces)
451         self.pg_start()
452
453         pkts = self.pg0.get_capture(len(self.stream_1) + len(self.stream_2))
454         self.verify_capture(self.pg0, pkts, self.stream_1 + self.stream_2)
455
456     def test_4_del_routes(self):
457         """ Delete 1.5k routes
458
459         - delete 5 routes check with traffic script.
460         - add 100 routes check with traffic script.
461         """
462         self.deleted_routes.extend(self.unconfig_fib_many_to_one(
463             "10.0.0.0", self.pg0.remote_ip4, 15))
464         self.deleted_routes.extend(self.unconfig_fib_many_to_one(
465             "10.0.0.20", self.pg0.remote_ip4, 85))
466         self.deleted_routes.extend(self.unconfig_fib_many_to_one(
467             "10.0.1.0", self.pg0.remote_ip4, 100))
468         fib_dump = self.vapi.ip_fib_dump()
469         self.verify_not_in_route_dump(fib_dump, self.deleted_routes)
470
471
472 class TestIPNull(VppTestCase):
473     """ IPv4 routes via NULL """
474
475     def setUp(self):
476         super(TestIPNull, self).setUp()
477
478         # create 2 pg interfaces
479         self.create_pg_interfaces(range(1))
480
481         for i in self.pg_interfaces:
482             i.admin_up()
483             i.config_ip4()
484             i.resolve_arp()
485
486     def tearDown(self):
487         super(TestIPNull, self).tearDown()
488         for i in self.pg_interfaces:
489             i.unconfig_ip4()
490             i.admin_down()
491
492     def test_ip_null(self):
493         """ IP NULL route """
494
495         #
496         # A route via IP NULL that will reply with ICMP unreachables
497         #
498         ip_unreach = VppIpRoute(self, "10.0.0.1", 32, [], is_unreach=1)
499         ip_unreach.add_vpp_config()
500
501         p_unreach = (Ether(src=self.pg0.remote_mac,
502                            dst=self.pg0.local_mac) /
503                      IP(src=self.pg0.remote_ip4, dst="10.0.0.1") /
504                      UDP(sport=1234, dport=1234) /
505                      Raw('\xa5' * 100))
506
507         self.pg0.add_stream(p_unreach)
508         self.pg_enable_capture(self.pg_interfaces)
509         self.pg_start()
510
511         rx = self.pg0.get_capture(1)
512         rx = rx[0]
513         icmp = rx[ICMP]
514
515         self.assertEqual(icmptypes[icmp.type], "dest-unreach")
516         self.assertEqual(icmpcodes[icmp.type][icmp.code], "host-unreachable")
517         self.assertEqual(icmp.src, self.pg0.remote_ip4)
518         self.assertEqual(icmp.dst, "10.0.0.1")
519
520         #
521         # ICMP replies are rate limited. so sit and spin.
522         #
523         self.sleep(1)
524
525         #
526         # A route via IP NULL that will reply with ICMP prohibited
527         #
528         ip_prohibit = VppIpRoute(self, "10.0.0.2", 32, [], is_prohibit=1)
529         ip_prohibit.add_vpp_config()
530
531         p_prohibit = (Ether(src=self.pg0.remote_mac,
532                             dst=self.pg0.local_mac) /
533                       IP(src=self.pg0.remote_ip4, dst="10.0.0.2") /
534                       UDP(sport=1234, dport=1234) /
535                       Raw('\xa5' * 100))
536
537         self.pg0.add_stream(p_prohibit)
538         self.pg_enable_capture(self.pg_interfaces)
539         self.pg_start()
540
541         rx = self.pg0.get_capture(1)
542
543         rx = rx[0]
544         icmp = rx[ICMP]
545
546         self.assertEqual(icmptypes[icmp.type], "dest-unreach")
547         self.assertEqual(icmpcodes[icmp.type][icmp.code], "host-prohibited")
548         self.assertEqual(icmp.src, self.pg0.remote_ip4)
549         self.assertEqual(icmp.dst, "10.0.0.2")
550
551
552 class TestIPDisabled(VppTestCase):
553     """ IPv4 disabled """
554
555     def setUp(self):
556         super(TestIPDisabled, self).setUp()
557
558         # create 2 pg interfaces
559         self.create_pg_interfaces(range(2))
560
561         # PG0 is IP enalbed
562         self.pg0.admin_up()
563         self.pg0.config_ip4()
564         self.pg0.resolve_arp()
565
566         # PG 1 is not IP enabled
567         self.pg1.admin_up()
568
569     def tearDown(self):
570         super(TestIPDisabled, self).tearDown()
571         for i in self.pg_interfaces:
572             i.unconfig_ip4()
573             i.admin_down()
574
575     def send_and_assert_no_replies(self, intf, pkts, remark):
576         intf.add_stream(pkts)
577         self.pg_enable_capture(self.pg_interfaces)
578         self.pg_start()
579         for i in self.pg_interfaces:
580             i.get_capture(0)
581             i.assert_nothing_captured(remark=remark)
582
583     def test_ip_disabled(self):
584         """ IP Disabled """
585
586         #
587         # An (S,G).
588         # one accepting interface, pg0, 2 forwarding interfaces
589         #
590         route_232_1_1_1 = VppIpMRoute(
591             self,
592             "0.0.0.0",
593             "232.1.1.1", 32,
594             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
595             [VppMRoutePath(self.pg1.sw_if_index,
596                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
597              VppMRoutePath(self.pg0.sw_if_index,
598                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
599         route_232_1_1_1.add_vpp_config()
600
601         pu = (Ether(src=self.pg1.remote_mac,
602                     dst=self.pg1.local_mac) /
603               IP(src="10.10.10.10", dst=self.pg0.remote_ip4) /
604               UDP(sport=1234, dport=1234) /
605               Raw('\xa5' * 100))
606         pm = (Ether(src=self.pg1.remote_mac,
607                     dst=self.pg1.local_mac) /
608               IP(src="10.10.10.10", dst="232.1.1.1") /
609               UDP(sport=1234, dport=1234) /
610               Raw('\xa5' * 100))
611
612         #
613         # PG1 does not forward IP traffic
614         #
615         self.send_and_assert_no_replies(self.pg1, pu, "IP disabled")
616         self.send_and_assert_no_replies(self.pg1, pm, "IP disabled")
617
618         #
619         # IP enable PG1
620         #
621         self.pg1.config_ip4()
622
623         #
624         # Now we get packets through
625         #
626         self.pg1.add_stream(pu)
627         self.pg_enable_capture(self.pg_interfaces)
628         self.pg_start()
629         rx = self.pg0.get_capture(1)
630
631         self.pg1.add_stream(pm)
632         self.pg_enable_capture(self.pg_interfaces)
633         self.pg_start()
634         rx = self.pg0.get_capture(1)
635
636         #
637         # Disable PG1
638         #
639         self.pg1.unconfig_ip4()
640
641         #
642         # PG1 does not forward IP traffic
643         #
644         self.send_and_assert_no_replies(self.pg1, pu, "IP disabled")
645         self.send_and_assert_no_replies(self.pg1, pm, "IP disabled")
646
647
648 class TestIPSubNets(VppTestCase):
649     """ IPv4 Subnets """
650
651     def setUp(self):
652         super(TestIPSubNets, self).setUp()
653
654         # create a 2 pg interfaces
655         self.create_pg_interfaces(range(2))
656
657         # pg0 we will use to experiemnt
658         self.pg0.admin_up()
659
660         # pg1 is setup normally
661         self.pg1.admin_up()
662         self.pg1.config_ip4()
663         self.pg1.resolve_arp()
664
665     def tearDown(self):
666         super(TestIPSubNets, self).tearDown()
667         for i in self.pg_interfaces:
668             i.admin_down()
669
670     def send_and_assert_no_replies(self, intf, pkts, remark):
671         intf.add_stream(pkts)
672         self.pg_enable_capture(self.pg_interfaces)
673         self.pg_start()
674         for i in self.pg_interfaces:
675             i.get_capture(0)
676             i.assert_nothing_captured(remark=remark)
677
678     def test_ip_sub_nets(self):
679         """ IP Sub Nets """
680
681         #
682         # Configure a covering route to forward so we know
683         # when we are dropping
684         #
685         cover_route = VppIpRoute(self, "10.0.0.0", 8,
686                                  [VppRoutePath(self.pg1.remote_ip4,
687                                                self.pg1.sw_if_index)])
688         cover_route.add_vpp_config()
689
690         p = (Ether(src=self.pg1.remote_mac,
691                    dst=self.pg1.local_mac) /
692              IP(dst="10.10.10.10", src=self.pg0.local_ip4) /
693              UDP(sport=1234, dport=1234) /
694              Raw('\xa5' * 100))
695
696         self.pg1.add_stream(p)
697         self.pg_enable_capture(self.pg_interfaces)
698         self.pg_start()
699         rx = self.pg1.get_capture(1)
700
701         #
702         # Configure some non-/24 subnets on an IP interface
703         #
704         ip_addr_n = socket.inet_pton(socket.AF_INET, "10.10.10.10")
705
706         self.vapi.sw_interface_add_del_address(self.pg0.sw_if_index,
707                                                ip_addr_n,
708                                                16)
709
710         pn = (Ether(src=self.pg1.remote_mac,
711                     dst=self.pg1.local_mac) /
712               IP(dst="10.10.0.0", src=self.pg0.local_ip4) /
713               UDP(sport=1234, dport=1234) /
714               Raw('\xa5' * 100))
715         pb = (Ether(src=self.pg1.remote_mac,
716                     dst=self.pg1.local_mac) /
717               IP(dst="10.10.255.255", src=self.pg0.local_ip4) /
718               UDP(sport=1234, dport=1234) /
719               Raw('\xa5' * 100))
720
721         self.send_and_assert_no_replies(self.pg1, pn, "IP Network address")
722         self.send_and_assert_no_replies(self.pg1, pb, "IP Broadcast address")
723
724         # remove the sub-net and we are forwarding via the cover again
725         self.vapi.sw_interface_add_del_address(self.pg0.sw_if_index,
726                                                ip_addr_n,
727                                                16,
728                                                is_add=0)
729         self.pg1.add_stream(pn)
730         self.pg_enable_capture(self.pg_interfaces)
731         self.pg_start()
732         rx = self.pg1.get_capture(1)
733         self.pg1.add_stream(pb)
734         self.pg_enable_capture(self.pg_interfaces)
735         self.pg_start()
736         rx = self.pg1.get_capture(1)
737
738         #
739         # A /31 is a special case where the 'other-side' is an attached host
740         # packets to that peer generate ARP requests
741         #
742         ip_addr_n = socket.inet_pton(socket.AF_INET, "10.10.10.10")
743
744         self.vapi.sw_interface_add_del_address(self.pg0.sw_if_index,
745                                                ip_addr_n,
746                                                31)
747
748         pn = (Ether(src=self.pg1.remote_mac,
749                     dst=self.pg1.local_mac) /
750               IP(dst="10.10.10.11", src=self.pg0.local_ip4) /
751               UDP(sport=1234, dport=1234) /
752               Raw('\xa5' * 100))
753
754         self.pg1.add_stream(pn)
755         self.pg_enable_capture(self.pg_interfaces)
756         self.pg_start()
757         rx = self.pg0.get_capture(1)
758         rx[ARP]
759
760         # remove the sub-net and we are forwarding via the cover again
761         self.vapi.sw_interface_add_del_address(self.pg0.sw_if_index,
762                                                ip_addr_n,
763                                                31,
764                                                is_add=0)
765         self.pg1.add_stream(pn)
766         self.pg_enable_capture(self.pg_interfaces)
767         self.pg_start()
768         rx = self.pg1.get_capture(1)
769
770
771 class TestIPLoadBalance(VppTestCase):
772     """ IPv4 Load-Balancing """
773
774     def setUp(self):
775         super(TestIPLoadBalance, self).setUp()
776
777         self.create_pg_interfaces(range(5))
778         mpls_tbl = VppMplsTable(self, 0)
779         mpls_tbl.add_vpp_config()
780
781         for i in self.pg_interfaces:
782             i.admin_up()
783             i.config_ip4()
784             i.resolve_arp()
785             i.enable_mpls()
786
787     def tearDown(self):
788         for i in self.pg_interfaces:
789             i.disable_mpls()
790             i.unconfig_ip4()
791             i.admin_down()
792         super(TestIPLoadBalance, self).tearDown()
793
794     def send_and_expect_load_balancing(self, input, pkts, outputs):
795         input.add_stream(pkts)
796         self.pg_enable_capture(self.pg_interfaces)
797         self.pg_start()
798         for oo in outputs:
799             rx = oo._get_capture(1)
800             self.assertNotEqual(0, len(rx))
801
802     def send_and_expect_one_itf(self, input, pkts, itf):
803         input.add_stream(pkts)
804         self.pg_enable_capture(self.pg_interfaces)
805         self.pg_start()
806         rx = itf.get_capture(len(pkts))
807
808     def test_ip_load_balance(self):
809         """ IP Load-Balancing """
810
811         #
812         # An array of packets that differ only in the destination port
813         #
814         port_ip_pkts = []
815         port_mpls_pkts = []
816
817         #
818         # An array of packets that differ only in the source address
819         #
820         src_ip_pkts = []
821         src_mpls_pkts = []
822
823         for ii in range(65):
824             port_ip_hdr = (IP(dst="10.0.0.1", src="20.0.0.1") /
825                            UDP(sport=1234, dport=1234 + ii) /
826                            Raw('\xa5' * 100))
827             port_ip_pkts.append((Ether(src=self.pg0.remote_mac,
828                                        dst=self.pg0.local_mac) /
829                                  port_ip_hdr))
830             port_mpls_pkts.append((Ether(src=self.pg0.remote_mac,
831                                          dst=self.pg0.local_mac) /
832                                    MPLS(label=66, ttl=2) /
833                                    port_ip_hdr))
834
835             src_ip_hdr = (IP(dst="10.0.0.1", src="20.0.0.%d" % ii) /
836                           UDP(sport=1234, dport=1234) /
837                           Raw('\xa5' * 100))
838             src_ip_pkts.append((Ether(src=self.pg0.remote_mac,
839                                       dst=self.pg0.local_mac) /
840                                 src_ip_hdr))
841             src_mpls_pkts.append((Ether(src=self.pg0.remote_mac,
842                                         dst=self.pg0.local_mac) /
843                                   MPLS(label=66, ttl=2) /
844                                   src_ip_hdr))
845
846         route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
847                                     [VppRoutePath(self.pg1.remote_ip4,
848                                                   self.pg1.sw_if_index),
849                                      VppRoutePath(self.pg2.remote_ip4,
850                                                   self.pg2.sw_if_index)])
851         route_10_0_0_1.add_vpp_config()
852
853         binding = VppMplsIpBind(self, 66, "10.0.0.1", 32)
854         binding.add_vpp_config()
855
856         #
857         # inject the packet on pg0 - expect load-balancing across the 2 paths
858         #  - since the default hash config is to use IP src,dst and port
859         #    src,dst
860         # We are not going to ensure equal amounts of packets across each link,
861         # since the hash algorithm is statistical and therefore this can never
862         # be guaranteed. But wuth 64 different packets we do expect some
863         # balancing. So instead just ensure there is traffic on each link.
864         #
865         self.send_and_expect_load_balancing(self.pg0, port_ip_pkts,
866                                             [self.pg1, self.pg2])
867         self.send_and_expect_load_balancing(self.pg0, src_ip_pkts,
868                                             [self.pg1, self.pg2])
869         self.send_and_expect_load_balancing(self.pg0, port_mpls_pkts,
870                                             [self.pg1, self.pg2])
871         self.send_and_expect_load_balancing(self.pg0, src_mpls_pkts,
872                                             [self.pg1, self.pg2])
873
874         #
875         # change the flow hash config so it's only IP src,dst
876         #  - now only the stream with differing source address will
877         #    load-balance
878         #
879         self.vapi.set_ip_flow_hash(0, src=1, dst=1, sport=0, dport=0)
880
881         self.send_and_expect_load_balancing(self.pg0, src_ip_pkts,
882                                             [self.pg1, self.pg2])
883         self.send_and_expect_load_balancing(self.pg0, src_mpls_pkts,
884                                             [self.pg1, self.pg2])
885
886         self.send_and_expect_one_itf(self.pg0, port_ip_pkts, self.pg2)
887
888         #
889         # change the flow hash config back to defaults
890         #
891         self.vapi.set_ip_flow_hash(0, src=1, dst=1, sport=1, dport=1)
892
893         #
894         # Recursive prefixes
895         #  - testing that 2 stages of load-balancing occurs and there is no
896         #    polarisation (i.e. only 2 of 4 paths are used)
897         #
898         port_pkts = []
899         src_pkts = []
900
901         for ii in range(257):
902             port_pkts.append((Ether(src=self.pg0.remote_mac,
903                                     dst=self.pg0.local_mac) /
904                               IP(dst="1.1.1.1", src="20.0.0.1") /
905                               UDP(sport=1234, dport=1234 + ii) /
906                               Raw('\xa5' * 100)))
907             src_pkts.append((Ether(src=self.pg0.remote_mac,
908                                    dst=self.pg0.local_mac) /
909                              IP(dst="1.1.1.1", src="20.0.0.%d" % ii) /
910                              UDP(sport=1234, dport=1234) /
911                              Raw('\xa5' * 100)))
912
913         route_10_0_0_2 = VppIpRoute(self, "10.0.0.2", 32,
914                                     [VppRoutePath(self.pg3.remote_ip4,
915                                                   self.pg3.sw_if_index),
916                                      VppRoutePath(self.pg4.remote_ip4,
917                                                   self.pg4.sw_if_index)])
918         route_10_0_0_2.add_vpp_config()
919
920         route_1_1_1_1 = VppIpRoute(self, "1.1.1.1", 32,
921                                    [VppRoutePath("10.0.0.2", 0xffffffff),
922                                     VppRoutePath("10.0.0.1", 0xffffffff)])
923         route_1_1_1_1.add_vpp_config()
924
925         #
926         # inject the packet on pg0 - expect load-balancing across all 4 paths
927         #
928         self.vapi.cli("clear trace")
929         self.send_and_expect_load_balancing(self.pg0, port_pkts,
930                                             [self.pg1, self.pg2,
931                                              self.pg3, self.pg4])
932         self.send_and_expect_load_balancing(self.pg0, src_pkts,
933                                             [self.pg1, self.pg2,
934                                              self.pg3, self.pg4])
935
936         #
937         # Recursive prefixes
938         #  - testing that 2 stages of load-balancing, no choices
939         #
940         port_pkts = []
941
942         for ii in range(257):
943             port_pkts.append((Ether(src=self.pg0.remote_mac,
944                                     dst=self.pg0.local_mac) /
945                               IP(dst="1.1.1.2", src="20.0.0.2") /
946                               UDP(sport=1234, dport=1234 + ii) /
947                               Raw('\xa5' * 100)))
948
949         route_10_0_0_3 = VppIpRoute(self, "10.0.0.3", 32,
950                                     [VppRoutePath(self.pg3.remote_ip4,
951                                                   self.pg3.sw_if_index)])
952         route_10_0_0_3.add_vpp_config()
953
954         route_1_1_1_2 = VppIpRoute(self, "1.1.1.2", 32,
955                                    [VppRoutePath("10.0.0.3", 0xffffffff)])
956         route_1_1_1_2.add_vpp_config()
957
958         #
959         # inject the packet on pg0 - expect load-balancing across all 4 paths
960         #
961         self.vapi.cli("clear trace")
962         self.send_and_expect_one_itf(self.pg0, port_pkts, self.pg3)
963
964
965 class TestIPVlan0(VppTestCase):
966     """ IPv4 VLAN-0 """
967
968     def setUp(self):
969         super(TestIPVlan0, self).setUp()
970
971         self.create_pg_interfaces(range(2))
972         mpls_tbl = VppMplsTable(self, 0)
973         mpls_tbl.add_vpp_config()
974
975         for i in self.pg_interfaces:
976             i.admin_up()
977             i.config_ip4()
978             i.resolve_arp()
979             i.enable_mpls()
980
981     def tearDown(self):
982         for i in self.pg_interfaces:
983             i.disable_mpls()
984             i.unconfig_ip4()
985             i.admin_down()
986         super(TestIPVlan0, self).tearDown()
987
988     def send_and_expect(self, input, pkts, output):
989         input.add_stream(pkts)
990         self.pg_enable_capture(self.pg_interfaces)
991         self.pg_start()
992         rx = output.get_capture(len(pkts))
993
994     def test_ip_vlan_0(self):
995         """ IP VLAN-0 """
996
997         pkts = (Ether(src=self.pg0.remote_mac,
998                       dst=self.pg0.local_mac) /
999                 Dot1Q(vlan=0) /
1000                 IP(dst=self.pg1.remote_ip4,
1001                    src=self.pg0.remote_ip4) /
1002                 UDP(sport=1234, dport=1234) /
1003                 Raw('\xa5' * 100)) * 65
1004
1005         #
1006         # Expect that packets sent on VLAN-0 are forwarded on the
1007         # main interface.
1008         #
1009         self.send_and_expect(self.pg0, pkts, self.pg1)
1010
1011
1012 class TestIPPunt(VppTestCase):
1013     """ IPv4 Punt Police/Redirect """
1014
1015     def setUp(self):
1016         super(TestIPPunt, self).setUp()
1017
1018         self.create_pg_interfaces(range(2))
1019
1020         for i in self.pg_interfaces:
1021             i.admin_up()
1022             i.config_ip4()
1023             i.resolve_arp()
1024
1025     def tearDown(self):
1026         super(TestIPPunt, self).tearDown()
1027         for i in self.pg_interfaces:
1028             i.unconfig_ip4()
1029             i.admin_down()
1030
1031     def send_and_expect(self, input, pkts, output):
1032         self.vapi.cli("clear trace")
1033         input.add_stream(pkts)
1034         self.pg_enable_capture(self.pg_interfaces)
1035         self.pg_start()
1036         rx = output.get_capture(len(pkts))
1037         return rx
1038
1039     def send_and_assert_no_replies(self, intf, pkts, remark):
1040         self.vapi.cli("clear trace")
1041         intf.add_stream(pkts)
1042         self.pg_enable_capture(self.pg_interfaces)
1043         self.pg_start()
1044         for i in self.pg_interfaces:
1045             i.get_capture(0)
1046             i.assert_nothing_captured(remark=remark)
1047
1048     def test_ip_punt(self):
1049         """ IP punt police and redirect """
1050
1051         p = (Ether(src=self.pg0.remote_mac,
1052                    dst=self.pg0.local_mac) /
1053              IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
1054              TCP(sport=1234, dport=1234) /
1055              Raw('\xa5' * 100))
1056
1057         pkts = p * 1025
1058
1059         #
1060         # Configure a punt redirect via pg1.
1061         #
1062         nh_addr = socket.inet_pton(socket.AF_INET,
1063                                    self.pg1.remote_ip4)
1064         self.vapi.ip_punt_redirect(self.pg0.sw_if_index,
1065                                    self.pg1.sw_if_index,
1066                                    nh_addr)
1067
1068         self.send_and_expect(self.pg0, pkts, self.pg1)
1069
1070         #
1071         # add a policer
1072         #
1073         policer = self.vapi.policer_add_del("ip4-punt", 400, 0, 10, 0,
1074                                             rate_type=1)
1075         self.vapi.ip_punt_police(policer.policer_index)
1076
1077         self.vapi.cli("clear trace")
1078         self.pg0.add_stream(pkts)
1079         self.pg_enable_capture(self.pg_interfaces)
1080         self.pg_start()
1081
1082         #
1083         # the number of packet recieved should be greater than 0,
1084         # but not equal to the number sent, since some were policed
1085         #
1086         rx = self.pg1._get_capture(1)
1087         self.assertTrue(len(rx) > 0)
1088         self.assertTrue(len(rx) < len(pkts))
1089
1090         #
1091         # remove the poilcer. back to full rx
1092         #
1093         self.vapi.ip_punt_police(policer.policer_index, is_add=0)
1094         self.vapi.policer_add_del("ip4-punt", 400, 0, 10, 0,
1095                                   rate_type=1, is_add=0)
1096         self.send_and_expect(self.pg0, pkts, self.pg1)
1097
1098         #
1099         # remove the redirect. expect full drop.
1100         #
1101         self.vapi.ip_punt_redirect(self.pg0.sw_if_index,
1102                                    self.pg1.sw_if_index,
1103                                    nh_addr,
1104                                    is_add=0)
1105         self.send_and_assert_no_replies(self.pg0, pkts,
1106                                         "IP no punt config")
1107
1108         #
1109         # Add a redirect that is not input port selective
1110         #
1111         self.vapi.ip_punt_redirect(0xffffffff,
1112                                    self.pg1.sw_if_index,
1113                                    nh_addr)
1114         self.send_and_expect(self.pg0, pkts, self.pg1)
1115
1116         self.vapi.ip_punt_redirect(0xffffffff,
1117                                    self.pg1.sw_if_index,
1118                                    nh_addr,
1119                                    is_add=0)
1120
1121
1122 class TestIPDeag(VppTestCase):
1123     """ IPv4 Deaggregate Routes """
1124
1125     def setUp(self):
1126         super(TestIPDeag, self).setUp()
1127
1128         self.create_pg_interfaces(range(3))
1129
1130         for i in self.pg_interfaces:
1131             i.admin_up()
1132             i.config_ip4()
1133             i.resolve_arp()
1134
1135     def tearDown(self):
1136         super(TestIPDeag, self).tearDown()
1137         for i in self.pg_interfaces:
1138             i.unconfig_ip4()
1139             i.admin_down()
1140
1141     def send_and_expect(self, input, pkts, output):
1142         self.vapi.cli("clear trace")
1143         input.add_stream(pkts)
1144         self.pg_enable_capture(self.pg_interfaces)
1145         self.pg_start()
1146         rx = output.get_capture(len(pkts))
1147         return rx
1148
1149     def send_and_assert_no_replies(self, intf, pkts, remark):
1150         self.vapi.cli("clear trace")
1151         intf.add_stream(pkts)
1152         self.pg_enable_capture(self.pg_interfaces)
1153         self.pg_start()
1154         for i in self.pg_interfaces:
1155             i.get_capture(0)
1156             i.assert_nothing_captured(remark=remark)
1157
1158     def test_ip_deag(self):
1159         """ IP Deag Routes """
1160
1161         #
1162         # Create a table to be used for:
1163         #  1 - another destination address lookup
1164         #  2 - a source address lookup
1165         #
1166         table_dst = VppIpTable(self, 1)
1167         table_src = VppIpTable(self, 2)
1168         table_dst.add_vpp_config()
1169         table_src.add_vpp_config()
1170
1171         #
1172         # Add a route in the default table to point to a deag/
1173         # second lookup in each of these tables
1174         #
1175         route_to_dst = VppIpRoute(self, "1.1.1.1", 32,
1176                                   [VppRoutePath("0.0.0.0",
1177                                                 0xffffffff,
1178                                                 nh_table_id=1)])
1179         route_to_src = VppIpRoute(self, "1.1.1.2", 32,
1180                                   [VppRoutePath("0.0.0.0",
1181                                                 0xffffffff,
1182                                                 nh_table_id=2,
1183                                                 is_source_lookup=1)])
1184         route_to_dst.add_vpp_config()
1185         route_to_src.add_vpp_config()
1186
1187         #
1188         # packets to these destination are dropped, since they'll
1189         # hit the respective default routes in the second table
1190         #
1191         p_dst = (Ether(src=self.pg0.remote_mac,
1192                        dst=self.pg0.local_mac) /
1193                  IP(src="5.5.5.5", dst="1.1.1.1") /
1194                  TCP(sport=1234, dport=1234) /
1195                  Raw('\xa5' * 100))
1196         p_src = (Ether(src=self.pg0.remote_mac,
1197                        dst=self.pg0.local_mac) /
1198                  IP(src="2.2.2.2", dst="1.1.1.2") /
1199                  TCP(sport=1234, dport=1234) /
1200                  Raw('\xa5' * 100))
1201         pkts_dst = p_dst * 257
1202         pkts_src = p_src * 257
1203
1204         self.send_and_assert_no_replies(self.pg0, pkts_dst,
1205                                         "IP in dst table")
1206         self.send_and_assert_no_replies(self.pg0, pkts_src,
1207                                         "IP in src table")
1208
1209         #
1210         # add a route in the dst table to forward via pg1
1211         #
1212         route_in_dst = VppIpRoute(self, "1.1.1.1", 32,
1213                                   [VppRoutePath(self.pg1.remote_ip4,
1214                                                 self.pg1.sw_if_index)],
1215                                   table_id=1)
1216         route_in_dst.add_vpp_config()
1217         self.send_and_expect(self.pg0, pkts_dst, self.pg1)
1218
1219         #
1220         # add a route in the src table to forward via pg2
1221         #
1222         route_in_src = VppIpRoute(self, "2.2.2.2", 32,
1223                                   [VppRoutePath(self.pg2.remote_ip4,
1224                                                 self.pg2.sw_if_index)],
1225                                   table_id=2)
1226         route_in_src.add_vpp_config()
1227         self.send_and_expect(self.pg0, pkts_src, self.pg2)
1228
1229
1230 if __name__ == '__main__':
1231     unittest.main(testRunner=VppTestRunner)