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