IP Flow Hash Config fixes
[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
10
11 from scapy.packet import Raw
12 from scapy.layers.l2 import Ether, Dot1Q, ARP
13 from scapy.layers.inet import IP, UDP, ICMP, icmptypes, icmpcodes
14 from util import ppp
15
16
17 class TestIPv4(VppTestCase):
18     """ IPv4 Test Case """
19
20     def setUp(self):
21         """
22         Perform test setup before test case.
23
24         **Config:**
25             - create 3 pg interfaces
26                 - untagged pg0 interface
27                 - Dot1Q subinterface on pg1
28                 - Dot1AD subinterface on pg2
29             - setup interfaces:
30                 - put it into UP state
31                 - set IPv4 addresses
32                 - resolve neighbor address using ARP
33             - configure 200 fib entries
34
35         :ivar list interfaces: pg interfaces and subinterfaces.
36         :ivar dict flows: IPv4 packet flows in test.
37         :ivar list pg_if_packet_sizes: packet sizes in test.
38         """
39         super(TestIPv4, self).setUp()
40
41         # create 3 pg interfaces
42         self.create_pg_interfaces(range(3))
43
44         # create 2 subinterfaces for pg1 and pg2
45         self.sub_interfaces = [
46             VppDot1QSubint(self, self.pg1, 100),
47             VppDot1ADSubint(self, self.pg2, 200, 300, 400)]
48
49         # packet flows mapping pg0 -> pg1.sub, pg2.sub, etc.
50         self.flows = dict()
51         self.flows[self.pg0] = [self.pg1.sub_if, self.pg2.sub_if]
52         self.flows[self.pg1.sub_if] = [self.pg0, self.pg2.sub_if]
53         self.flows[self.pg2.sub_if] = [self.pg0, self.pg1.sub_if]
54
55         # packet sizes
56         self.pg_if_packet_sizes = [64, 512, 1518, 9018]
57         self.sub_if_packet_sizes = [64, 512, 1518 + 4, 9018 + 4]
58
59         self.interfaces = list(self.pg_interfaces)
60         self.interfaces.extend(self.sub_interfaces)
61
62         # setup all interfaces
63         for i in self.interfaces:
64             i.admin_up()
65             i.config_ip4()
66             i.resolve_arp()
67
68         # config 2M FIB entries
69         self.config_fib_entries(200)
70
71     def tearDown(self):
72         """Run standard test teardown and log ``show ip arp``."""
73         super(TestIPv4, self).tearDown()
74         if not self.vpp_dead:
75             self.logger.info(self.vapi.cli("show ip arp"))
76             # info(self.vapi.cli("show ip fib"))  # many entries
77
78     def config_fib_entries(self, count):
79         """For each interface add to the FIB table *count* routes to
80         "10.0.0.1/32" destination with interface's local address as next-hop
81         address.
82
83         :param int count: Number of FIB entries.
84
85         - *TODO:* check if the next-hop address shouldn't be remote address
86           instead of local address.
87         """
88         n_int = len(self.interfaces)
89         percent = 0
90         counter = 0.0
91         dest_addr = socket.inet_pton(socket.AF_INET, "10.0.0.1")
92         dest_addr_len = 32
93         for i in self.interfaces:
94             next_hop_address = i.local_ip4n
95             for j in range(count / n_int):
96                 self.vapi.ip_add_del_route(
97                     dest_addr, dest_addr_len, next_hop_address)
98                 counter += 1
99                 if counter / count * 100 > percent:
100                     self.logger.info("Configure %d FIB entries .. %d%% done" %
101                                      (count, percent))
102                     percent += 1
103
104     def create_stream(self, src_if, packet_sizes):
105         """Create input packet stream for defined interface.
106
107         :param VppInterface src_if: Interface to create packet stream for.
108         :param list packet_sizes: Required packet sizes.
109         """
110         pkts = []
111         for i in range(0, 257):
112             dst_if = self.flows[src_if][i % 2]
113             info = self.create_packet_info(src_if, dst_if)
114             payload = self.info_to_payload(info)
115             p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
116                  IP(src=src_if.remote_ip4, dst=dst_if.remote_ip4) /
117                  UDP(sport=1234, dport=1234) /
118                  Raw(payload))
119             info.data = p.copy()
120             if isinstance(src_if, VppSubInterface):
121                 p = src_if.add_dot1_layer(p)
122             size = packet_sizes[(i // 2) % len(packet_sizes)]
123             self.extend_packet(p, size)
124             pkts.append(p)
125         return pkts
126
127     def verify_capture(self, dst_if, capture):
128         """Verify captured input packet stream for defined interface.
129
130         :param VppInterface dst_if: Interface to verify captured packet stream
131                                     for.
132         :param list capture: Captured packet stream.
133         """
134         self.logger.info("Verifying capture on interface %s" % dst_if.name)
135         last_info = dict()
136         for i in self.interfaces:
137             last_info[i.sw_if_index] = None
138         is_sub_if = False
139         dst_sw_if_index = dst_if.sw_if_index
140         if hasattr(dst_if, 'parent'):
141             is_sub_if = True
142         for packet in capture:
143             if is_sub_if:
144                 # Check VLAN tags and Ethernet header
145                 packet = dst_if.remove_dot1_layer(packet)
146             self.assertTrue(Dot1Q not in packet)
147             try:
148                 ip = packet[IP]
149                 udp = packet[UDP]
150                 payload_info = self.payload_to_info(str(packet[Raw]))
151                 packet_index = payload_info.index
152                 self.assertEqual(payload_info.dst, dst_sw_if_index)
153                 self.logger.debug(
154                     "Got packet on port %s: src=%u (id=%u)" %
155                     (dst_if.name, payload_info.src, packet_index))
156                 next_info = self.get_next_packet_info_for_interface2(
157                     payload_info.src, dst_sw_if_index,
158                     last_info[payload_info.src])
159                 last_info[payload_info.src] = next_info
160                 self.assertTrue(next_info is not None)
161                 self.assertEqual(packet_index, next_info.index)
162                 saved_packet = next_info.data
163                 # Check standard fields
164                 self.assertEqual(ip.src, saved_packet[IP].src)
165                 self.assertEqual(ip.dst, saved_packet[IP].dst)
166                 self.assertEqual(udp.sport, saved_packet[UDP].sport)
167                 self.assertEqual(udp.dport, saved_packet[UDP].dport)
168             except:
169                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
170                 raise
171         for i in self.interfaces:
172             remaining_packet = self.get_next_packet_info_for_interface2(
173                 i.sw_if_index, dst_sw_if_index, last_info[i.sw_if_index])
174             self.assertTrue(remaining_packet is None,
175                             "Interface %s: Packet expected from interface %s "
176                             "didn't arrive" % (dst_if.name, i.name))
177
178     def test_fib(self):
179         """ IPv4 FIB test
180
181         Test scenario:
182
183             - Create IPv4 stream for pg0 interface
184             - Create IPv4 tagged streams for pg1's and pg2's subinterface.
185             - Send and verify received packets on each interface.
186         """
187
188         pkts = self.create_stream(self.pg0, self.pg_if_packet_sizes)
189         self.pg0.add_stream(pkts)
190
191         for i in self.sub_interfaces:
192             pkts = self.create_stream(i, self.sub_if_packet_sizes)
193             i.parent.add_stream(pkts)
194
195         self.pg_enable_capture(self.pg_interfaces)
196         self.pg_start()
197
198         pkts = self.pg0.get_capture()
199         self.verify_capture(self.pg0, pkts)
200
201         for i in self.sub_interfaces:
202             pkts = i.parent.get_capture()
203             self.verify_capture(i, pkts)
204
205
206 class TestIPv4FibCrud(VppTestCase):
207     """ FIB - add/update/delete - ip4 routes
208
209     Test scenario:
210         - add 1k,
211         - del 100,
212         - add new 1k,
213         - del 1.5k
214
215     ..note:: Python API is too slow to add many routes, needs replacement.
216     """
217
218     def config_fib_many_to_one(self, start_dest_addr, next_hop_addr, count):
219         """
220
221         :param start_dest_addr:
222         :param next_hop_addr:
223         :param count:
224         :return list: added ips with 32 prefix
225         """
226         added_ips = []
227         dest_addr = int(socket.inet_pton(socket.AF_INET,
228                                          start_dest_addr).encode('hex'),
229                         16)
230         dest_addr_len = 32
231         n_next_hop_addr = socket.inet_pton(socket.AF_INET, next_hop_addr)
232         for _ in range(count):
233             n_dest_addr = '{:08x}'.format(dest_addr).decode('hex')
234             self.vapi.ip_add_del_route(n_dest_addr, dest_addr_len,
235                                        n_next_hop_addr)
236             added_ips.append(socket.inet_ntoa(n_dest_addr))
237             dest_addr += 1
238         return added_ips
239
240     def unconfig_fib_many_to_one(self, start_dest_addr, next_hop_addr, count):
241
242         removed_ips = []
243         dest_addr = int(socket.inet_pton(socket.AF_INET,
244                                          start_dest_addr).encode('hex'),
245                         16)
246         dest_addr_len = 32
247         n_next_hop_addr = socket.inet_pton(socket.AF_INET, next_hop_addr)
248         for _ in range(count):
249             n_dest_addr = '{:08x}'.format(dest_addr).decode('hex')
250             self.vapi.ip_add_del_route(n_dest_addr, dest_addr_len,
251                                        n_next_hop_addr, is_add=0)
252             removed_ips.append(socket.inet_ntoa(n_dest_addr))
253             dest_addr += 1
254         return removed_ips
255
256     def create_stream(self, src_if, dst_if, dst_ips, count):
257         pkts = []
258
259         for _ in range(count):
260             dst_addr = random.choice(dst_ips)
261             info = self.create_packet_info(src_if, dst_if)
262             payload = self.info_to_payload(info)
263             p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
264                  IP(src=src_if.remote_ip4, dst=dst_addr) /
265                  UDP(sport=1234, dport=1234) /
266                  Raw(payload))
267             info.data = p.copy()
268             self.extend_packet(p, random.choice(self.pg_if_packet_sizes))
269             pkts.append(p)
270
271         return pkts
272
273     def _find_ip_match(self, find_in, pkt):
274         for p in find_in:
275             if self.payload_to_info(str(p[Raw])) == \
276                     self.payload_to_info(str(pkt[Raw])):
277                 if p[IP].src != pkt[IP].src:
278                     break
279                 if p[IP].dst != pkt[IP].dst:
280                     break
281                 if p[UDP].sport != pkt[UDP].sport:
282                     break
283                 if p[UDP].dport != pkt[UDP].dport:
284                     break
285                 return p
286         return None
287
288     @staticmethod
289     def _match_route_detail(route_detail, ip, address_length=32, table_id=0):
290         if route_detail.address == socket.inet_pton(socket.AF_INET, ip):
291             if route_detail.table_id != table_id:
292                 return False
293             elif route_detail.address_length != address_length:
294                 return False
295             else:
296                 return True
297         else:
298             return False
299
300     def verify_capture(self, dst_interface, received_pkts, expected_pkts):
301         self.assertEqual(len(received_pkts), len(expected_pkts))
302         to_verify = list(expected_pkts)
303         for p in received_pkts:
304             self.assertEqual(p.src, dst_interface.local_mac)
305             self.assertEqual(p.dst, dst_interface.remote_mac)
306             x = self._find_ip_match(to_verify, p)
307             to_verify.remove(x)
308         self.assertListEqual(to_verify, [])
309
310     def verify_route_dump(self, fib_dump, ips):
311
312         def _ip_in_route_dump(ip, fib_dump):
313             return next((route for route in fib_dump
314                          if self._match_route_detail(route, ip)),
315                         False)
316
317         for ip in ips:
318             self.assertTrue(_ip_in_route_dump(ip, fib_dump),
319                             'IP {} is not in fib dump.'.format(ip))
320
321     def verify_not_in_route_dump(self, fib_dump, ips):
322
323         def _ip_in_route_dump(ip, fib_dump):
324             return next((route for route in fib_dump
325                          if self._match_route_detail(route, ip)),
326                         False)
327
328         for ip in ips:
329             self.assertFalse(_ip_in_route_dump(ip, fib_dump),
330                              'IP {} is in fib dump.'.format(ip))
331
332     @classmethod
333     def setUpClass(cls):
334         """
335         #. Create and initialize 3 pg interfaces.
336         #. initialize class attributes configured_routes and deleted_routes
337            to store information between tests.
338         """
339         super(TestIPv4FibCrud, cls).setUpClass()
340
341         try:
342             # create 3 pg interfaces
343             cls.create_pg_interfaces(range(3))
344
345             cls.interfaces = list(cls.pg_interfaces)
346
347             # setup all interfaces
348             for i in cls.interfaces:
349                 i.admin_up()
350                 i.config_ip4()
351                 i.resolve_arp()
352
353             cls.configured_routes = []
354             cls.deleted_routes = []
355             cls.pg_if_packet_sizes = [64, 512, 1518, 9018]
356
357         except Exception:
358             super(TestIPv4FibCrud, cls).tearDownClass()
359             raise
360
361     def setUp(self):
362         super(TestIPv4FibCrud, self).setUp()
363         self.reset_packet_infos()
364
365     def test_1_add_routes(self):
366         """ Add 1k routes
367
368         - add 100 routes check with traffic script.
369         """
370         # config 1M FIB entries
371         self.configured_routes.extend(self.config_fib_many_to_one(
372             "10.0.0.0", self.pg0.remote_ip4, 100))
373
374         fib_dump = self.vapi.ip_fib_dump()
375         self.verify_route_dump(fib_dump, self.configured_routes)
376
377         self.stream_1 = self.create_stream(
378             self.pg1, self.pg0, self.configured_routes, 100)
379         self.stream_2 = self.create_stream(
380             self.pg2, self.pg0, self.configured_routes, 100)
381         self.pg1.add_stream(self.stream_1)
382         self.pg2.add_stream(self.stream_2)
383
384         self.pg_enable_capture(self.pg_interfaces)
385         self.pg_start()
386
387         pkts = self.pg0.get_capture(len(self.stream_1) + len(self.stream_2))
388         self.verify_capture(self.pg0, pkts, self.stream_1 + self.stream_2)
389
390     def test_2_del_routes(self):
391         """ Delete 100 routes
392
393         - delete 10 routes check with traffic script.
394         """
395         self.deleted_routes.extend(self.unconfig_fib_many_to_one(
396             "10.0.0.10", self.pg0.remote_ip4, 10))
397         for x in self.deleted_routes:
398             self.configured_routes.remove(x)
399
400         fib_dump = self.vapi.ip_fib_dump()
401         self.verify_route_dump(fib_dump, self.configured_routes)
402
403         self.stream_1 = self.create_stream(
404             self.pg1, self.pg0, self.configured_routes, 100)
405         self.stream_2 = self.create_stream(
406             self.pg2, self.pg0, self.configured_routes, 100)
407         self.stream_3 = self.create_stream(
408             self.pg1, self.pg0, self.deleted_routes, 100)
409         self.stream_4 = self.create_stream(
410             self.pg2, self.pg0, self.deleted_routes, 100)
411         self.pg1.add_stream(self.stream_1 + self.stream_3)
412         self.pg2.add_stream(self.stream_2 + self.stream_4)
413         self.pg_enable_capture(self.pg_interfaces)
414         self.pg_start()
415
416         pkts = self.pg0.get_capture(len(self.stream_1) + len(self.stream_2))
417         self.verify_capture(self.pg0, pkts, self.stream_1 + self.stream_2)
418
419     def test_3_add_new_routes(self):
420         """ Add 1k routes
421
422         - re-add 5 routes check with traffic script.
423         - add 100 routes check with traffic script.
424         """
425         tmp = self.config_fib_many_to_one(
426             "10.0.0.10", self.pg0.remote_ip4, 5)
427         self.configured_routes.extend(tmp)
428         for x in tmp:
429             self.deleted_routes.remove(x)
430
431         self.configured_routes.extend(self.config_fib_many_to_one(
432             "10.0.1.0", self.pg0.remote_ip4, 100))
433
434         fib_dump = self.vapi.ip_fib_dump()
435         self.verify_route_dump(fib_dump, self.configured_routes)
436
437         self.stream_1 = self.create_stream(
438             self.pg1, self.pg0, self.configured_routes, 300)
439         self.stream_2 = self.create_stream(
440             self.pg2, self.pg0, self.configured_routes, 300)
441         self.stream_3 = self.create_stream(
442             self.pg1, self.pg0, self.deleted_routes, 100)
443         self.stream_4 = self.create_stream(
444             self.pg2, self.pg0, self.deleted_routes, 100)
445
446         self.pg1.add_stream(self.stream_1 + self.stream_3)
447         self.pg2.add_stream(self.stream_2 + self.stream_4)
448         self.pg_enable_capture(self.pg_interfaces)
449         self.pg_start()
450
451         pkts = self.pg0.get_capture(len(self.stream_1) + len(self.stream_2))
452         self.verify_capture(self.pg0, pkts, self.stream_1 + self.stream_2)
453
454     def test_4_del_routes(self):
455         """ Delete 1.5k routes
456
457         - delete 5 routes check with traffic script.
458         - add 100 routes check with traffic script.
459         """
460         self.deleted_routes.extend(self.unconfig_fib_many_to_one(
461             "10.0.0.0", self.pg0.remote_ip4, 15))
462         self.deleted_routes.extend(self.unconfig_fib_many_to_one(
463             "10.0.0.20", self.pg0.remote_ip4, 85))
464         self.deleted_routes.extend(self.unconfig_fib_many_to_one(
465             "10.0.1.0", self.pg0.remote_ip4, 100))
466         fib_dump = self.vapi.ip_fib_dump()
467         self.verify_not_in_route_dump(fib_dump, self.deleted_routes)
468
469
470 class TestIPNull(VppTestCase):
471     """ IPv4 routes via NULL """
472
473     def setUp(self):
474         super(TestIPNull, self).setUp()
475
476         # create 2 pg interfaces
477         self.create_pg_interfaces(range(1))
478
479         for i in self.pg_interfaces:
480             i.admin_up()
481             i.config_ip4()
482             i.resolve_arp()
483
484     def tearDown(self):
485         super(TestIPNull, self).tearDown()
486         for i in self.pg_interfaces:
487             i.unconfig_ip4()
488             i.admin_down()
489
490     def test_ip_null(self):
491         """ IP NULL route """
492
493         #
494         # A route via IP NULL that will reply with ICMP unreachables
495         #
496         ip_unreach = VppIpRoute(self, "10.0.0.1", 32, [], is_unreach=1)
497         ip_unreach.add_vpp_config()
498
499         p_unreach = (Ether(src=self.pg0.remote_mac,
500                            dst=self.pg0.local_mac) /
501                      IP(src=self.pg0.remote_ip4, dst="10.0.0.1") /
502                      UDP(sport=1234, dport=1234) /
503                      Raw('\xa5' * 100))
504
505         self.pg0.add_stream(p_unreach)
506         self.pg_enable_capture(self.pg_interfaces)
507         self.pg_start()
508
509         rx = self.pg0.get_capture(1)
510         rx = rx[0]
511         icmp = rx[ICMP]
512
513         self.assertEqual(icmptypes[icmp.type], "dest-unreach")
514         self.assertEqual(icmpcodes[icmp.type][icmp.code], "host-unreachable")
515         self.assertEqual(icmp.src, self.pg0.remote_ip4)
516         self.assertEqual(icmp.dst, "10.0.0.1")
517
518         #
519         # ICMP replies are rate limited. so sit and spin.
520         #
521         self.sleep(1)
522
523         #
524         # A route via IP NULL that will reply with ICMP prohibited
525         #
526         ip_prohibit = VppIpRoute(self, "10.0.0.2", 32, [], is_prohibit=1)
527         ip_prohibit.add_vpp_config()
528
529         p_prohibit = (Ether(src=self.pg0.remote_mac,
530                             dst=self.pg0.local_mac) /
531                       IP(src=self.pg0.remote_ip4, dst="10.0.0.2") /
532                       UDP(sport=1234, dport=1234) /
533                       Raw('\xa5' * 100))
534
535         self.pg0.add_stream(p_prohibit)
536         self.pg_enable_capture(self.pg_interfaces)
537         self.pg_start()
538
539         rx = self.pg0.get_capture(1)
540
541         rx = rx[0]
542         icmp = rx[ICMP]
543
544         self.assertEqual(icmptypes[icmp.type], "dest-unreach")
545         self.assertEqual(icmpcodes[icmp.type][icmp.code], "host-prohibited")
546         self.assertEqual(icmp.src, self.pg0.remote_ip4)
547         self.assertEqual(icmp.dst, "10.0.0.2")
548
549
550 class TestIPDisabled(VppTestCase):
551     """ IPv4 disabled """
552
553     def setUp(self):
554         super(TestIPDisabled, self).setUp()
555
556         # create 2 pg interfaces
557         self.create_pg_interfaces(range(2))
558
559         # PG0 is IP enalbed
560         self.pg0.admin_up()
561         self.pg0.config_ip4()
562         self.pg0.resolve_arp()
563
564         # PG 1 is not IP enabled
565         self.pg1.admin_up()
566
567     def tearDown(self):
568         super(TestIPDisabled, self).tearDown()
569         for i in self.pg_interfaces:
570             i.unconfig_ip4()
571             i.admin_down()
572
573     def send_and_assert_no_replies(self, intf, pkts, remark):
574         intf.add_stream(pkts)
575         self.pg_enable_capture(self.pg_interfaces)
576         self.pg_start()
577         for i in self.pg_interfaces:
578             i.get_capture(0)
579             i.assert_nothing_captured(remark=remark)
580
581     def test_ip_disabled(self):
582         """ IP Disabled """
583
584         #
585         # An (S,G).
586         # one accepting interface, pg0, 2 forwarding interfaces
587         #
588         route_232_1_1_1 = VppIpMRoute(
589             self,
590             "0.0.0.0",
591             "232.1.1.1", 32,
592             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
593             [VppMRoutePath(self.pg1.sw_if_index,
594                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
595              VppMRoutePath(self.pg0.sw_if_index,
596                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
597         route_232_1_1_1.add_vpp_config()
598
599         pu = (Ether(src=self.pg1.remote_mac,
600                     dst=self.pg1.local_mac) /
601               IP(src="10.10.10.10", dst=self.pg0.remote_ip4) /
602               UDP(sport=1234, dport=1234) /
603               Raw('\xa5' * 100))
604         pm = (Ether(src=self.pg1.remote_mac,
605                     dst=self.pg1.local_mac) /
606               IP(src="10.10.10.10", dst="232.1.1.1") /
607               UDP(sport=1234, dport=1234) /
608               Raw('\xa5' * 100))
609
610         #
611         # PG1 does not forward IP traffic
612         #
613         self.send_and_assert_no_replies(self.pg1, pu, "IP disabled")
614         self.send_and_assert_no_replies(self.pg1, pm, "IP disabled")
615
616         #
617         # IP enable PG1
618         #
619         self.pg1.config_ip4()
620
621         #
622         # Now we get packets through
623         #
624         self.pg1.add_stream(pu)
625         self.pg_enable_capture(self.pg_interfaces)
626         self.pg_start()
627         rx = self.pg0.get_capture(1)
628
629         self.pg1.add_stream(pm)
630         self.pg_enable_capture(self.pg_interfaces)
631         self.pg_start()
632         rx = self.pg0.get_capture(1)
633
634         #
635         # Disable PG1
636         #
637         self.pg1.unconfig_ip4()
638
639         #
640         # PG1 does not forward IP traffic
641         #
642         self.send_and_assert_no_replies(self.pg1, pu, "IP disabled")
643         self.send_and_assert_no_replies(self.pg1, pm, "IP disabled")
644
645
646 class TestIPSubNets(VppTestCase):
647     """ IPv4 Subnets """
648
649     def setUp(self):
650         super(TestIPSubNets, self).setUp()
651
652         # create a 2 pg interfaces
653         self.create_pg_interfaces(range(2))
654
655         # pg0 we will use to experiemnt
656         self.pg0.admin_up()
657
658         # pg1 is setup normally
659         self.pg1.admin_up()
660         self.pg1.config_ip4()
661         self.pg1.resolve_arp()
662
663     def tearDown(self):
664         super(TestIPSubNets, self).tearDown()
665         for i in self.pg_interfaces:
666             i.admin_down()
667
668     def send_and_assert_no_replies(self, intf, pkts, remark):
669         intf.add_stream(pkts)
670         self.pg_enable_capture(self.pg_interfaces)
671         self.pg_start()
672         for i in self.pg_interfaces:
673             i.get_capture(0)
674             i.assert_nothing_captured(remark=remark)
675
676     def test_ip_sub_nets(self):
677         """ IP Sub Nets """
678
679         #
680         # Configure a covering route to forward so we know
681         # when we are dropping
682         #
683         cover_route = VppIpRoute(self, "10.0.0.0", 8,
684                                  [VppRoutePath(self.pg1.remote_ip4,
685                                                self.pg1.sw_if_index)])
686         cover_route.add_vpp_config()
687
688         p = (Ether(src=self.pg1.remote_mac,
689                    dst=self.pg1.local_mac) /
690              IP(dst="10.10.10.10", src=self.pg0.local_ip4) /
691              UDP(sport=1234, dport=1234) /
692              Raw('\xa5' * 100))
693
694         self.pg1.add_stream(p)
695         self.pg_enable_capture(self.pg_interfaces)
696         self.pg_start()
697         rx = self.pg1.get_capture(1)
698
699         #
700         # Configure some non-/24 subnets on an IP interface
701         #
702         ip_addr_n = socket.inet_pton(socket.AF_INET, "10.10.10.10")
703
704         self.vapi.sw_interface_add_del_address(self.pg0.sw_if_index,
705                                                ip_addr_n,
706                                                16)
707
708         pn = (Ether(src=self.pg1.remote_mac,
709                     dst=self.pg1.local_mac) /
710               IP(dst="10.10.0.0", src=self.pg0.local_ip4) /
711               UDP(sport=1234, dport=1234) /
712               Raw('\xa5' * 100))
713         pb = (Ether(src=self.pg1.remote_mac,
714                     dst=self.pg1.local_mac) /
715               IP(dst="10.10.255.255", src=self.pg0.local_ip4) /
716               UDP(sport=1234, dport=1234) /
717               Raw('\xa5' * 100))
718
719         self.send_and_assert_no_replies(self.pg1, pn, "IP Network address")
720         self.send_and_assert_no_replies(self.pg1, pb, "IP Broadcast address")
721
722         # remove the sub-net and we are forwarding via the cover again
723         self.vapi.sw_interface_add_del_address(self.pg0.sw_if_index,
724                                                ip_addr_n,
725                                                16,
726                                                is_add=0)
727         self.pg1.add_stream(pn)
728         self.pg_enable_capture(self.pg_interfaces)
729         self.pg_start()
730         rx = self.pg1.get_capture(1)
731         self.pg1.add_stream(pb)
732         self.pg_enable_capture(self.pg_interfaces)
733         self.pg_start()
734         rx = self.pg1.get_capture(1)
735
736         #
737         # A /31 is a special case where the 'other-side' is an attached host
738         # packets to that peer generate ARP requests
739         #
740         ip_addr_n = socket.inet_pton(socket.AF_INET, "10.10.10.10")
741
742         self.vapi.sw_interface_add_del_address(self.pg0.sw_if_index,
743                                                ip_addr_n,
744                                                31)
745
746         pn = (Ether(src=self.pg1.remote_mac,
747                     dst=self.pg1.local_mac) /
748               IP(dst="10.10.10.11", src=self.pg0.local_ip4) /
749               UDP(sport=1234, dport=1234) /
750               Raw('\xa5' * 100))
751
752         self.pg1.add_stream(pn)
753         self.pg_enable_capture(self.pg_interfaces)
754         self.pg_start()
755         rx = self.pg0.get_capture(1)
756         rx[ARP]
757
758         # remove the sub-net and we are forwarding via the cover again
759         self.vapi.sw_interface_add_del_address(self.pg0.sw_if_index,
760                                                ip_addr_n,
761                                                31,
762                                                is_add=0)
763         self.pg1.add_stream(pn)
764         self.pg_enable_capture(self.pg_interfaces)
765         self.pg_start()
766         rx = self.pg1.get_capture(1)
767
768
769 class TestIPLoadBalance(VppTestCase):
770     """ IPv4 Load-Balancing """
771
772     def setUp(self):
773         super(TestIPLoadBalance, self).setUp()
774
775         self.create_pg_interfaces(range(5))
776
777         for i in self.pg_interfaces:
778             i.admin_up()
779             i.config_ip4()
780             i.resolve_arp()
781
782     def tearDown(self):
783         super(TestIPLoadBalance, self).tearDown()
784         for i in self.pg_interfaces:
785             i.unconfig_ip4()
786             i.admin_down()
787
788     def send_and_expect_load_balancing(self, input, pkts, outputs):
789         input.add_stream(pkts)
790         self.pg_enable_capture(self.pg_interfaces)
791         self.pg_start()
792         for oo in outputs:
793             rx = oo._get_capture(1)
794             self.assertNotEqual(0, len(rx))
795
796     def test_ip_load_balance(self):
797         """ IP Load-Balancing """
798
799         #
800         # An array of packets that differ only in the destination port
801         #
802         port_pkts = []
803
804         #
805         # An array of packets that differ only in the source address
806         #
807         src_pkts = []
808
809         for ii in range(65):
810             port_pkts.append((Ether(src=self.pg0.remote_mac,
811                                     dst=self.pg0.local_mac) /
812                               IP(dst="10.0.0.1", src="20.0.0.1") /
813                               UDP(sport=1234, dport=1234 + ii) /
814                               Raw('\xa5' * 100)))
815             src_pkts.append((Ether(src=self.pg0.remote_mac,
816                                    dst=self.pg0.local_mac) /
817                              IP(dst="10.0.0.1", src="20.0.0.%d" % ii) /
818                              UDP(sport=1234, dport=1234) /
819                              Raw('\xa5' * 100)))
820
821         route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
822                                     [VppRoutePath(self.pg1.remote_ip4,
823                                                   self.pg1.sw_if_index),
824                                      VppRoutePath(self.pg2.remote_ip4,
825                                                   self.pg2.sw_if_index)])
826         route_10_0_0_1.add_vpp_config()
827
828         #
829         # inject the packet on pg0 - expect load-balancing across the 2 paths
830         #  - since the default hash config is to use IP src,dst and port
831         #    src,dst
832         # We are not going to ensure equal amounts of packets across each link,
833         # since the hash algorithm is statistical and therefore this can never
834         # be guaranteed. But wuth 64 different packets we do expect some
835         # balancing. So instead just ensure there is traffic on each link.
836         #
837         self.send_and_expect_load_balancing(self.pg0, port_pkts,
838                                             [self.pg1, self.pg2])
839         self.send_and_expect_load_balancing(self.pg0, src_pkts,
840                                             [self.pg1, self.pg2])
841
842         #
843         # change the flow hash config so it's only IP src,dst
844         #  - now only the stream with differing source address will
845         #    load-balance
846         #
847         self.vapi.set_ip_flow_hash(0, src=1, dst=1, sport=0, dport=0)
848
849         self.send_and_expect_load_balancing(self.pg0, src_pkts,
850                                             [self.pg1, self.pg2])
851
852         self.pg0.add_stream(port_pkts)
853         self.pg_enable_capture(self.pg_interfaces)
854         self.pg_start()
855
856         rx = self.pg2.get_capture(len(port_pkts))
857
858         #
859         # change the flow hash config back to defaults
860         #
861         self.vapi.set_ip_flow_hash(0, src=1, dst=1, sport=1, dport=1)
862
863         #
864         # Recursive prefixes
865         #  - testing that 2 stages of load-balancing occurs and there is no
866         #    polarisation (i.e. only 2 of 4 paths are used)
867         #
868         port_pkts = []
869         src_pkts = []
870
871         for ii in range(257):
872             port_pkts.append((Ether(src=self.pg0.remote_mac,
873                                     dst=self.pg0.local_mac) /
874                               IP(dst="1.1.1.1", src="20.0.0.1") /
875                               UDP(sport=1234, dport=1234 + ii) /
876                               Raw('\xa5' * 100)))
877             src_pkts.append((Ether(src=self.pg0.remote_mac,
878                                    dst=self.pg0.local_mac) /
879                              IP(dst="1.1.1.1", src="20.0.0.%d" % ii) /
880                              UDP(sport=1234, dport=1234) /
881                              Raw('\xa5' * 100)))
882
883         route_10_0_0_2 = VppIpRoute(self, "10.0.0.2", 32,
884                                     [VppRoutePath(self.pg3.remote_ip4,
885                                                   self.pg3.sw_if_index),
886                                      VppRoutePath(self.pg4.remote_ip4,
887                                                   self.pg4.sw_if_index)])
888         route_10_0_0_2.add_vpp_config()
889
890         route_1_1_1_1 = VppIpRoute(self, "1.1.1.1", 32,
891                                    [VppRoutePath("10.0.0.2", 0xffffffff),
892                                     VppRoutePath("10.0.0.1", 0xffffffff)])
893         route_1_1_1_1.add_vpp_config()
894
895         #
896         # inject the packet on pg0 - expect load-balancing across all 4 paths
897         #
898         self.vapi.cli("clear trace")
899         self.send_and_expect_load_balancing(self.pg0, port_pkts,
900                                             [self.pg1, self.pg2,
901                                              self.pg3, self.pg4])
902         self.send_and_expect_load_balancing(self.pg0, src_pkts,
903                                             [self.pg1, self.pg2,
904                                              self.pg3, self.pg4])
905
906 if __name__ == '__main__':
907     unittest.main(testRunner=VppTestRunner)