Tests for recursive load-balancing with no choices.
[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 send_and_expect_one_itf(self, input, pkts, itf):
800         input.add_stream(pkts)
801         self.pg_enable_capture(self.pg_interfaces)
802         self.pg_start()
803         rx = itf.get_capture(len(pkts))
804
805     def test_ip_load_balance(self):
806         """ IP Load-Balancing """
807
808         #
809         # An array of packets that differ only in the destination port
810         #
811         port_ip_pkts = []
812         port_mpls_pkts = []
813
814         #
815         # An array of packets that differ only in the source address
816         #
817         src_ip_pkts = []
818         src_mpls_pkts = []
819
820         for ii in range(65):
821             port_ip_hdr = (IP(dst="10.0.0.1", src="20.0.0.1") /
822                            UDP(sport=1234, dport=1234 + ii) /
823                            Raw('\xa5' * 100))
824             port_ip_pkts.append((Ether(src=self.pg0.remote_mac,
825                                        dst=self.pg0.local_mac) /
826                                  port_ip_hdr))
827             port_mpls_pkts.append((Ether(src=self.pg0.remote_mac,
828                                          dst=self.pg0.local_mac) /
829                                    MPLS(label=66, ttl=2) /
830                                    port_ip_hdr))
831
832             src_ip_hdr = (IP(dst="10.0.0.1", src="20.0.0.%d" % ii) /
833                           UDP(sport=1234, dport=1234) /
834                           Raw('\xa5' * 100))
835             src_ip_pkts.append((Ether(src=self.pg0.remote_mac,
836                                       dst=self.pg0.local_mac) /
837                                 src_ip_hdr))
838             src_mpls_pkts.append((Ether(src=self.pg0.remote_mac,
839                                         dst=self.pg0.local_mac) /
840                                   MPLS(label=66, ttl=2) /
841                                   src_ip_hdr))
842
843         route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
844                                     [VppRoutePath(self.pg1.remote_ip4,
845                                                   self.pg1.sw_if_index),
846                                      VppRoutePath(self.pg2.remote_ip4,
847                                                   self.pg2.sw_if_index)])
848         route_10_0_0_1.add_vpp_config()
849
850         binding = VppMplsIpBind(self, 66, "10.0.0.1", 32)
851         binding.add_vpp_config()
852
853         #
854         # inject the packet on pg0 - expect load-balancing across the 2 paths
855         #  - since the default hash config is to use IP src,dst and port
856         #    src,dst
857         # We are not going to ensure equal amounts of packets across each link,
858         # since the hash algorithm is statistical and therefore this can never
859         # be guaranteed. But wuth 64 different packets we do expect some
860         # balancing. So instead just ensure there is traffic on each link.
861         #
862         self.send_and_expect_load_balancing(self.pg0, port_ip_pkts,
863                                             [self.pg1, self.pg2])
864         self.send_and_expect_load_balancing(self.pg0, src_ip_pkts,
865                                             [self.pg1, self.pg2])
866         self.send_and_expect_load_balancing(self.pg0, port_mpls_pkts,
867                                             [self.pg1, self.pg2])
868         self.send_and_expect_load_balancing(self.pg0, src_mpls_pkts,
869                                             [self.pg1, self.pg2])
870
871         #
872         # change the flow hash config so it's only IP src,dst
873         #  - now only the stream with differing source address will
874         #    load-balance
875         #
876         self.vapi.set_ip_flow_hash(0, src=1, dst=1, sport=0, dport=0)
877
878         self.send_and_expect_load_balancing(self.pg0, src_ip_pkts,
879                                             [self.pg1, self.pg2])
880         self.send_and_expect_load_balancing(self.pg0, src_mpls_pkts,
881                                             [self.pg1, self.pg2])
882
883         self.send_and_expect_one_itf(self.pg0, port_ip_pkts, self.pg2)
884
885         #
886         # change the flow hash config back to defaults
887         #
888         self.vapi.set_ip_flow_hash(0, src=1, dst=1, sport=1, dport=1)
889
890         #
891         # Recursive prefixes
892         #  - testing that 2 stages of load-balancing occurs and there is no
893         #    polarisation (i.e. only 2 of 4 paths are used)
894         #
895         port_pkts = []
896         src_pkts = []
897
898         for ii in range(257):
899             port_pkts.append((Ether(src=self.pg0.remote_mac,
900                                     dst=self.pg0.local_mac) /
901                               IP(dst="1.1.1.1", src="20.0.0.1") /
902                               UDP(sport=1234, dport=1234 + ii) /
903                               Raw('\xa5' * 100)))
904             src_pkts.append((Ether(src=self.pg0.remote_mac,
905                                    dst=self.pg0.local_mac) /
906                              IP(dst="1.1.1.1", src="20.0.0.%d" % ii) /
907                              UDP(sport=1234, dport=1234) /
908                              Raw('\xa5' * 100)))
909
910         route_10_0_0_2 = VppIpRoute(self, "10.0.0.2", 32,
911                                     [VppRoutePath(self.pg3.remote_ip4,
912                                                   self.pg3.sw_if_index),
913                                      VppRoutePath(self.pg4.remote_ip4,
914                                                   self.pg4.sw_if_index)])
915         route_10_0_0_2.add_vpp_config()
916
917         route_1_1_1_1 = VppIpRoute(self, "1.1.1.1", 32,
918                                    [VppRoutePath("10.0.0.2", 0xffffffff),
919                                     VppRoutePath("10.0.0.1", 0xffffffff)])
920         route_1_1_1_1.add_vpp_config()
921
922         #
923         # inject the packet on pg0 - expect load-balancing across all 4 paths
924         #
925         self.vapi.cli("clear trace")
926         self.send_and_expect_load_balancing(self.pg0, port_pkts,
927                                             [self.pg1, self.pg2,
928                                              self.pg3, self.pg4])
929         self.send_and_expect_load_balancing(self.pg0, src_pkts,
930                                             [self.pg1, self.pg2,
931                                              self.pg3, self.pg4])
932
933         #
934         # Recursive prefixes
935         #  - testing that 2 stages of load-balancing, no choices
936         #
937         port_pkts = []
938
939         for ii in range(257):
940             port_pkts.append((Ether(src=self.pg0.remote_mac,
941                                     dst=self.pg0.local_mac) /
942                               IP(dst="1.1.1.2", src="20.0.0.2") /
943                               UDP(sport=1234, dport=1234 + ii) /
944                               Raw('\xa5' * 100)))
945
946         route_10_0_0_3 = VppIpRoute(self, "10.0.0.3", 32,
947                                     [VppRoutePath(self.pg3.remote_ip4,
948                                                   self.pg3.sw_if_index)])
949         route_10_0_0_3.add_vpp_config()
950
951         route_1_1_1_2 = VppIpRoute(self, "1.1.1.2", 32,
952                                    [VppRoutePath("10.0.0.3", 0xffffffff)])
953         route_1_1_1_2.add_vpp_config()
954
955         #
956         # inject the packet on pg0 - expect load-balancing across all 4 paths
957         #
958         self.vapi.cli("clear trace")
959         self.send_and_expect_one_itf(self.pg0, port_pkts, self.pg3)
960
961
962 class TestIPVlan0(VppTestCase):
963     """ IPv4 VLAN-0 """
964
965     def setUp(self):
966         super(TestIPVlan0, self).setUp()
967
968         self.create_pg_interfaces(range(2))
969
970         for i in self.pg_interfaces:
971             i.admin_up()
972             i.config_ip4()
973             i.resolve_arp()
974             i.enable_mpls()
975
976     def tearDown(self):
977         super(TestIPVlan0, self).tearDown()
978         for i in self.pg_interfaces:
979             i.disable_mpls()
980             i.unconfig_ip4()
981             i.admin_down()
982
983     def send_and_expect(self, input, pkts, output):
984         input.add_stream(pkts)
985         self.pg_enable_capture(self.pg_interfaces)
986         self.pg_start()
987         rx = output.get_capture(len(pkts))
988
989     def test_ip_vlan_0(self):
990         """ IP VLAN-0 """
991
992         pkts = (Ether(src=self.pg0.remote_mac,
993                       dst=self.pg0.local_mac) /
994                 Dot1Q(vlan=0) /
995                 IP(dst=self.pg1.remote_ip4,
996                    src=self.pg0.remote_ip4) /
997                 UDP(sport=1234, dport=1234) /
998                 Raw('\xa5' * 100)) * 65
999
1000         #
1001         # Expect that packets sent on VLAN-0 are forwarded on the
1002         # main interface.
1003         #
1004         self.send_and_expect(self.pg0, pkts, self.pg1)
1005
1006
1007 if __name__ == '__main__':
1008     unittest.main(testRunner=VppTestRunner)