MPLS hash function improvements
[vpp.git] / test / test_ip6.py
1 #!/usr/bin/env python
2
3 import unittest
4 from socket import AF_INET6
5
6 from framework import VppTestCase, VppTestRunner
7 from vpp_sub_interface import VppSubInterface, VppDot1QSubint
8 from vpp_pg_interface import is_ipv6_misc
9 from vpp_ip_route import VppIpRoute, VppRoutePath, find_route, VppIpMRoute, \
10     VppMRoutePath, MRouteItfFlags, MRouteEntryFlags, VppMplsIpBind, \
11     VppMplsRoute
12 from vpp_neighbor import find_nbr, VppNeighbor
13
14 from scapy.packet import Raw
15 from scapy.layers.l2 import Ether, Dot1Q
16 from scapy.layers.inet6 import IPv6, UDP, ICMPv6ND_NS, ICMPv6ND_RS, \
17     ICMPv6ND_RA, ICMPv6NDOptSrcLLAddr, getmacbyip6, ICMPv6MRD_Solicitation, \
18     ICMPv6NDOptMTU, ICMPv6NDOptSrcLLAddr, ICMPv6NDOptPrefixInfo, \
19     ICMPv6ND_NA, ICMPv6NDOptDstLLAddr, ICMPv6DestUnreach, icmp6types
20
21 from util import ppp
22 from scapy.utils6 import in6_getnsma, in6_getnsmac, in6_ptop, in6_islladdr, \
23     in6_mactoifaceid, in6_ismaddr
24 from scapy.utils import inet_pton, inet_ntop
25 from scapy.contrib.mpls import MPLS
26
27
28 def mk_ll_addr(mac):
29     euid = in6_mactoifaceid(mac)
30     addr = "fe80::" + euid
31     return addr
32
33
34 class TestIPv6ND(VppTestCase):
35     def validate_ra(self, intf, rx, dst_ip=None):
36         if not dst_ip:
37             dst_ip = intf.remote_ip6
38
39         # unicasted packets must come to the unicast mac
40         self.assertEqual(rx[Ether].dst, intf.remote_mac)
41
42         # and from the router's MAC
43         self.assertEqual(rx[Ether].src, intf.local_mac)
44
45         # the rx'd RA should be addressed to the sender's source
46         self.assertTrue(rx.haslayer(ICMPv6ND_RA))
47         self.assertEqual(in6_ptop(rx[IPv6].dst),
48                          in6_ptop(dst_ip))
49
50         # and come from the router's link local
51         self.assertTrue(in6_islladdr(rx[IPv6].src))
52         self.assertEqual(in6_ptop(rx[IPv6].src),
53                          in6_ptop(mk_ll_addr(intf.local_mac)))
54
55     def validate_na(self, intf, rx, dst_ip=None, tgt_ip=None):
56         if not dst_ip:
57             dst_ip = intf.remote_ip6
58         if not tgt_ip:
59             dst_ip = intf.local_ip6
60
61         # unicasted packets must come to the unicast mac
62         self.assertEqual(rx[Ether].dst, intf.remote_mac)
63
64         # and from the router's MAC
65         self.assertEqual(rx[Ether].src, intf.local_mac)
66
67         # the rx'd NA should be addressed to the sender's source
68         self.assertTrue(rx.haslayer(ICMPv6ND_NA))
69         self.assertEqual(in6_ptop(rx[IPv6].dst),
70                          in6_ptop(dst_ip))
71
72         # and come from the target address
73         self.assertEqual(in6_ptop(rx[IPv6].src), in6_ptop(tgt_ip))
74
75         # Dest link-layer options should have the router's MAC
76         dll = rx[ICMPv6NDOptDstLLAddr]
77         self.assertEqual(dll.lladdr, intf.local_mac)
78
79     def send_and_expect_ra(self, intf, pkts, remark, dst_ip=None,
80                            filter_out_fn=is_ipv6_misc):
81         intf.add_stream(pkts)
82         self.pg_enable_capture(self.pg_interfaces)
83         self.pg_start()
84         rx = intf.get_capture(1, filter_out_fn=filter_out_fn)
85
86         self.assertEqual(len(rx), 1)
87         rx = rx[0]
88         self.validate_ra(intf, rx, dst_ip)
89
90     def send_and_expect_na(self, intf, pkts, remark, dst_ip=None,
91                            tgt_ip=None,
92                            filter_out_fn=is_ipv6_misc):
93         intf.add_stream(pkts)
94         self.pg_enable_capture(self.pg_interfaces)
95         self.pg_start()
96         rx = intf.get_capture(1, filter_out_fn=filter_out_fn)
97
98         self.assertEqual(len(rx), 1)
99         rx = rx[0]
100         self.validate_na(intf, rx, dst_ip, tgt_ip)
101
102     def send_and_assert_no_replies(self, intf, pkts, remark):
103         intf.add_stream(pkts)
104         self.pg_enable_capture(self.pg_interfaces)
105         self.pg_start()
106         for i in self.pg_interfaces:
107             i.get_capture(0)
108             i.assert_nothing_captured(remark=remark)
109
110
111 class TestIPv6(TestIPv6ND):
112     """ IPv6 Test Case """
113
114     @classmethod
115     def setUpClass(cls):
116         super(TestIPv6, cls).setUpClass()
117
118     def setUp(self):
119         """
120         Perform test setup before test case.
121
122         **Config:**
123             - create 3 pg interfaces
124                 - untagged pg0 interface
125                 - Dot1Q subinterface on pg1
126                 - Dot1AD subinterface on pg2
127             - setup interfaces:
128                 - put it into UP state
129                 - set IPv6 addresses
130                 - resolve neighbor address using NDP
131             - configure 200 fib entries
132
133         :ivar list interfaces: pg interfaces and subinterfaces.
134         :ivar dict flows: IPv4 packet flows in test.
135         :ivar list pg_if_packet_sizes: packet sizes in test.
136
137         *TODO:* Create AD sub interface
138         """
139         super(TestIPv6, self).setUp()
140
141         # create 3 pg interfaces
142         self.create_pg_interfaces(range(3))
143
144         # create 2 subinterfaces for p1 and pg2
145         self.sub_interfaces = [
146             VppDot1QSubint(self, self.pg1, 100),
147             VppDot1QSubint(self, self.pg2, 200)
148             # TODO: VppDot1ADSubint(self, self.pg2, 200, 300, 400)
149         ]
150
151         # packet flows mapping pg0 -> pg1.sub, pg2.sub, etc.
152         self.flows = dict()
153         self.flows[self.pg0] = [self.pg1.sub_if, self.pg2.sub_if]
154         self.flows[self.pg1.sub_if] = [self.pg0, self.pg2.sub_if]
155         self.flows[self.pg2.sub_if] = [self.pg0, self.pg1.sub_if]
156
157         # packet sizes
158         self.pg_if_packet_sizes = [64, 512, 1518, 9018]
159         self.sub_if_packet_sizes = [64, 512, 1518 + 4, 9018 + 4]
160
161         self.interfaces = list(self.pg_interfaces)
162         self.interfaces.extend(self.sub_interfaces)
163
164         # setup all interfaces
165         for i in self.interfaces:
166             i.admin_up()
167             i.config_ip6()
168             i.resolve_ndp()
169
170         # config 2M FIB entries
171         self.config_fib_entries(200)
172
173     def tearDown(self):
174         """Run standard test teardown and log ``show ip6 neighbors``."""
175         for i in self.sub_interfaces:
176             i.unconfig_ip6()
177             i.ip6_disable()
178             i.admin_down()
179             i.remove_vpp_config()
180
181         super(TestIPv6, self).tearDown()
182         if not self.vpp_dead:
183             self.logger.info(self.vapi.cli("show ip6 neighbors"))
184             # info(self.vapi.cli("show ip6 fib"))  # many entries
185
186     def config_fib_entries(self, count):
187         """For each interface add to the FIB table *count* routes to
188         "fd02::1/128" destination with interface's local address as next-hop
189         address.
190
191         :param int count: Number of FIB entries.
192
193         - *TODO:* check if the next-hop address shouldn't be remote address
194           instead of local address.
195         """
196         n_int = len(self.interfaces)
197         percent = 0
198         counter = 0.0
199         dest_addr = inet_pton(AF_INET6, "fd02::1")
200         dest_addr_len = 128
201         for i in self.interfaces:
202             next_hop_address = i.local_ip6n
203             for j in range(count / n_int):
204                 self.vapi.ip_add_del_route(
205                     dest_addr, dest_addr_len, next_hop_address, is_ipv6=1)
206                 counter += 1
207                 if counter / count * 100 > percent:
208                     self.logger.info("Configure %d FIB entries .. %d%% done" %
209                                      (count, percent))
210                     percent += 1
211
212     def create_stream(self, src_if, packet_sizes):
213         """Create input packet stream for defined interface.
214
215         :param VppInterface src_if: Interface to create packet stream for.
216         :param list packet_sizes: Required packet sizes.
217         """
218         pkts = []
219         for i in range(0, 257):
220             dst_if = self.flows[src_if][i % 2]
221             info = self.create_packet_info(src_if, dst_if)
222             payload = self.info_to_payload(info)
223             p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
224                  IPv6(src=src_if.remote_ip6, dst=dst_if.remote_ip6) /
225                  UDP(sport=1234, dport=1234) /
226                  Raw(payload))
227             info.data = p.copy()
228             if isinstance(src_if, VppSubInterface):
229                 p = src_if.add_dot1_layer(p)
230             size = packet_sizes[(i // 2) % len(packet_sizes)]
231             self.extend_packet(p, size)
232             pkts.append(p)
233         return pkts
234
235     def verify_capture(self, dst_if, capture):
236         """Verify captured input packet stream for defined interface.
237
238         :param VppInterface dst_if: Interface to verify captured packet stream
239                                     for.
240         :param list capture: Captured packet stream.
241         """
242         self.logger.info("Verifying capture on interface %s" % dst_if.name)
243         last_info = dict()
244         for i in self.interfaces:
245             last_info[i.sw_if_index] = None
246         is_sub_if = False
247         dst_sw_if_index = dst_if.sw_if_index
248         if hasattr(dst_if, 'parent'):
249             is_sub_if = True
250         for packet in capture:
251             if is_sub_if:
252                 # Check VLAN tags and Ethernet header
253                 packet = dst_if.remove_dot1_layer(packet)
254             self.assertTrue(Dot1Q not in packet)
255             try:
256                 ip = packet[IPv6]
257                 udp = packet[UDP]
258                 payload_info = self.payload_to_info(str(packet[Raw]))
259                 packet_index = payload_info.index
260                 self.assertEqual(payload_info.dst, dst_sw_if_index)
261                 self.logger.debug(
262                     "Got packet on port %s: src=%u (id=%u)" %
263                     (dst_if.name, payload_info.src, packet_index))
264                 next_info = self.get_next_packet_info_for_interface2(
265                     payload_info.src, dst_sw_if_index,
266                     last_info[payload_info.src])
267                 last_info[payload_info.src] = next_info
268                 self.assertTrue(next_info is not None)
269                 self.assertEqual(packet_index, next_info.index)
270                 saved_packet = next_info.data
271                 # Check standard fields
272                 self.assertEqual(ip.src, saved_packet[IPv6].src)
273                 self.assertEqual(ip.dst, saved_packet[IPv6].dst)
274                 self.assertEqual(udp.sport, saved_packet[UDP].sport)
275                 self.assertEqual(udp.dport, saved_packet[UDP].dport)
276             except:
277                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
278                 raise
279         for i in self.interfaces:
280             remaining_packet = self.get_next_packet_info_for_interface2(
281                 i.sw_if_index, dst_sw_if_index, last_info[i.sw_if_index])
282             self.assertTrue(remaining_packet is None,
283                             "Interface %s: Packet expected from interface %s "
284                             "didn't arrive" % (dst_if.name, i.name))
285
286     def test_fib(self):
287         """ IPv6 FIB test
288
289         Test scenario:
290             - Create IPv6 stream for pg0 interface
291             - Create IPv6 tagged streams for pg1's and pg2's subinterface.
292             - Send and verify received packets on each interface.
293         """
294
295         pkts = self.create_stream(self.pg0, self.pg_if_packet_sizes)
296         self.pg0.add_stream(pkts)
297
298         for i in self.sub_interfaces:
299             pkts = self.create_stream(i, self.sub_if_packet_sizes)
300             i.parent.add_stream(pkts)
301
302         self.pg_enable_capture(self.pg_interfaces)
303         self.pg_start()
304
305         pkts = self.pg0.get_capture()
306         self.verify_capture(self.pg0, pkts)
307
308         for i in self.sub_interfaces:
309             pkts = i.parent.get_capture()
310             self.verify_capture(i, pkts)
311
312     def test_ns(self):
313         """ IPv6 Neighbour Solicitation Exceptions
314
315         Test scenario:
316            - Send an NS Sourced from an address not covered by the link sub-net
317            - Send an NS to an mcast address the router has not joined
318            - Send NS for a target address the router does not onn.
319         """
320
321         #
322         # An NS from a non link source address
323         #
324         nsma = in6_getnsma(inet_pton(AF_INET6, self.pg0.local_ip6))
325         d = inet_ntop(AF_INET6, nsma)
326
327         p = (Ether(dst=in6_getnsmac(nsma)) /
328              IPv6(dst=d, src="2002::2") /
329              ICMPv6ND_NS(tgt=self.pg0.local_ip6) /
330              ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac))
331         pkts = [p]
332
333         self.send_and_assert_no_replies(
334             self.pg0, pkts,
335             "No response to NS source by address not on sub-net")
336
337         #
338         # An NS for sent to a solicited mcast group the router is
339         # not a member of FAILS
340         #
341         if 0:
342             nsma = in6_getnsma(inet_pton(AF_INET6, "fd::ffff"))
343             d = inet_ntop(AF_INET6, nsma)
344
345             p = (Ether(dst=in6_getnsmac(nsma)) /
346                  IPv6(dst=d, src=self.pg0.remote_ip6) /
347                  ICMPv6ND_NS(tgt=self.pg0.local_ip6) /
348                  ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac))
349             pkts = [p]
350
351             self.send_and_assert_no_replies(
352                 self.pg0, pkts,
353                 "No response to NS sent to unjoined mcast address")
354
355         #
356         # An NS whose target address is one the router does not own
357         #
358         nsma = in6_getnsma(inet_pton(AF_INET6, self.pg0.local_ip6))
359         d = inet_ntop(AF_INET6, nsma)
360
361         p = (Ether(dst=in6_getnsmac(nsma)) /
362              IPv6(dst=d, src=self.pg0.remote_ip6) /
363              ICMPv6ND_NS(tgt="fd::ffff") /
364              ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac))
365         pkts = [p]
366
367         self.send_and_assert_no_replies(self.pg0, pkts,
368                                         "No response to NS for unknown target")
369
370         #
371         # A neighbor entry that has no associated FIB-entry
372         #
373         self.pg0.generate_remote_hosts(4)
374         nd_entry = VppNeighbor(self,
375                                self.pg0.sw_if_index,
376                                self.pg0.remote_hosts[2].mac,
377                                self.pg0.remote_hosts[2].ip6,
378                                af=AF_INET6,
379                                is_no_fib_entry=1)
380         nd_entry.add_vpp_config()
381
382         #
383         # check we have the neighbor, but no route
384         #
385         self.assertTrue(find_nbr(self,
386                                  self.pg0.sw_if_index,
387                                  self.pg0._remote_hosts[2].ip6,
388                                  inet=AF_INET6))
389         self.assertFalse(find_route(self,
390                                     self.pg0._remote_hosts[2].ip6,
391                                     128,
392                                     inet=AF_INET6))
393
394         #
395         # send an NS from a link local address to the interface's global
396         # address
397         #
398         p = (Ether(dst=in6_getnsmac(nsma), src=self.pg0.remote_mac) /
399              IPv6(dst=d, src=self.pg0._remote_hosts[2].ip6_ll) /
400              ICMPv6ND_NS(tgt=self.pg0.local_ip6) /
401              ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac))
402
403         self.send_and_expect_na(self.pg0, p,
404                                 "NS from link-local",
405                                 dst_ip=self.pg0._remote_hosts[2].ip6_ll,
406                                 tgt_ip=self.pg0.local_ip6)
407
408         #
409         # we should have learned an ND entry for the peer's link-local
410         # but not inserted a route to it in the FIB
411         #
412         self.assertTrue(find_nbr(self,
413                                  self.pg0.sw_if_index,
414                                  self.pg0._remote_hosts[2].ip6_ll,
415                                  inet=AF_INET6))
416         self.assertFalse(find_route(self,
417                                     self.pg0._remote_hosts[2].ip6_ll,
418                                     128,
419                                     inet=AF_INET6))
420
421         #
422         # An NS to the router's own Link-local
423         #
424         p = (Ether(dst=in6_getnsmac(nsma), src=self.pg0.remote_mac) /
425              IPv6(dst=d, src=self.pg0._remote_hosts[3].ip6_ll) /
426              ICMPv6ND_NS(tgt=self.pg0.local_ip6_ll) /
427              ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac))
428
429         self.send_and_expect_na(self.pg0, p,
430                                 "NS to/from link-local",
431                                 dst_ip=self.pg0._remote_hosts[3].ip6_ll,
432                                 tgt_ip=self.pg0.local_ip6_ll)
433
434         #
435         # we should have learned an ND entry for the peer's link-local
436         # but not inserted a route to it in the FIB
437         #
438         self.assertTrue(find_nbr(self,
439                                  self.pg0.sw_if_index,
440                                  self.pg0._remote_hosts[3].ip6_ll,
441                                  inet=AF_INET6))
442         self.assertFalse(find_route(self,
443                                     self.pg0._remote_hosts[3].ip6_ll,
444                                     128,
445                                     inet=AF_INET6))
446
447     def validate_ra(self, intf, rx, dst_ip=None, mtu=9000, pi_opt=None):
448         if not dst_ip:
449             dst_ip = intf.remote_ip6
450
451         # unicasted packets must come to the unicast mac
452         self.assertEqual(rx[Ether].dst, intf.remote_mac)
453
454         # and from the router's MAC
455         self.assertEqual(rx[Ether].src, intf.local_mac)
456
457         # the rx'd RA should be addressed to the sender's source
458         self.assertTrue(rx.haslayer(ICMPv6ND_RA))
459         self.assertEqual(in6_ptop(rx[IPv6].dst),
460                          in6_ptop(dst_ip))
461
462         # and come from the router's link local
463         self.assertTrue(in6_islladdr(rx[IPv6].src))
464         self.assertEqual(in6_ptop(rx[IPv6].src),
465                          in6_ptop(mk_ll_addr(intf.local_mac)))
466
467         # it should contain the links MTU
468         ra = rx[ICMPv6ND_RA]
469         self.assertEqual(ra[ICMPv6NDOptMTU].mtu, mtu)
470
471         # it should contain the source's link layer address option
472         sll = ra[ICMPv6NDOptSrcLLAddr]
473         self.assertEqual(sll.lladdr, intf.local_mac)
474
475         if not pi_opt:
476             # the RA should not contain prefix information
477             self.assertFalse(ra.haslayer(ICMPv6NDOptPrefixInfo))
478         else:
479             raos = rx.getlayer(ICMPv6NDOptPrefixInfo, 1)
480
481             # the options are nested in the scapy packet in way that i cannot
482             # decipher how to decode. this 1st layer of option always returns
483             # nested classes, so a direct obj1=obj2 comparison always fails.
484             # however, the getlayer(.., 2) does give one instnace.
485             # so we cheat here and construct a new opt instnace for comparison
486             rd = ICMPv6NDOptPrefixInfo(prefixlen=raos.prefixlen,
487                                        prefix=raos.prefix,
488                                        L=raos.L,
489                                        A=raos.A)
490             if type(pi_opt) is list:
491                 for ii in range(len(pi_opt)):
492                     self.assertEqual(pi_opt[ii], rd)
493                     rd = rx.getlayer(ICMPv6NDOptPrefixInfo, ii+2)
494             else:
495                 self.assertEqual(pi_opt, raos)
496
497     def send_and_expect_ra(self, intf, pkts, remark, dst_ip=None,
498                            filter_out_fn=is_ipv6_misc,
499                            opt=None):
500         intf.add_stream(pkts)
501         self.pg_enable_capture(self.pg_interfaces)
502         self.pg_start()
503         rx = intf.get_capture(1, filter_out_fn=filter_out_fn)
504
505         self.assertEqual(len(rx), 1)
506         rx = rx[0]
507         self.validate_ra(intf, rx, dst_ip, pi_opt=opt)
508
509     def test_rs(self):
510         """ IPv6 Router Solicitation Exceptions
511
512         Test scenario:
513         """
514
515         #
516         # Before we begin change the IPv6 RA responses to use the unicast
517         # address - that way we will not confuse them with the periodic
518         # RAs which go to the mcast address
519         # Sit and wait for the first periodic RA.
520         #
521         # TODO
522         #
523         self.pg0.ip6_ra_config(send_unicast=1)
524
525         #
526         # An RS from a link source address
527         #  - expect an RA in return
528         #
529         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
530              IPv6(dst=self.pg0.local_ip6, src=self.pg0.remote_ip6) /
531              ICMPv6ND_RS())
532         pkts = [p]
533         self.send_and_expect_ra(self.pg0, pkts, "Genuine RS")
534
535         #
536         # For the next RS sent the RA should be rate limited
537         #
538         self.send_and_assert_no_replies(self.pg0, pkts, "RA rate limited")
539
540         #
541         # When we reconfiure the IPv6 RA config, we reset the RA rate limiting,
542         # so we need to do this before each test below so as not to drop
543         # packets for rate limiting reasons. Test this works here.
544         #
545         self.pg0.ip6_ra_config(send_unicast=1)
546         self.send_and_expect_ra(self.pg0, pkts, "Rate limit reset RS")
547
548         #
549         # An RS sent from a non-link local source
550         #
551         self.pg0.ip6_ra_config(send_unicast=1)
552         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
553              IPv6(dst=self.pg0.local_ip6, src="2002::ffff") /
554              ICMPv6ND_RS())
555         pkts = [p]
556         self.send_and_assert_no_replies(self.pg0, pkts,
557                                         "RS from non-link source")
558
559         #
560         # Source an RS from a link local address
561         #
562         self.pg0.ip6_ra_config(send_unicast=1)
563         ll = mk_ll_addr(self.pg0.remote_mac)
564         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
565              IPv6(dst=self.pg0.local_ip6, src=ll) /
566              ICMPv6ND_RS())
567         pkts = [p]
568         self.send_and_expect_ra(self.pg0, pkts,
569                                 "RS sourced from link-local",
570                                 dst_ip=ll)
571
572         #
573         # Send the RS multicast
574         #
575         self.pg0.ip6_ra_config(send_unicast=1)
576         dmac = in6_getnsmac(inet_pton(AF_INET6, "ff02::2"))
577         ll = mk_ll_addr(self.pg0.remote_mac)
578         p = (Ether(dst=dmac, src=self.pg0.remote_mac) /
579              IPv6(dst="ff02::2", src=ll) /
580              ICMPv6ND_RS())
581         pkts = [p]
582         self.send_and_expect_ra(self.pg0, pkts,
583                                 "RS sourced from link-local",
584                                 dst_ip=ll)
585
586         #
587         # Source from the unspecified address ::. This happens when the RS
588         # is sent before the host has a configured address/sub-net,
589         # i.e. auto-config. Since the sender has no IP address, the reply
590         # comes back mcast - so the capture needs to not filter this.
591         # If we happen to pick up the periodic RA at this point then so be it,
592         # it's not an error.
593         #
594         self.pg0.ip6_ra_config(send_unicast=1, suppress=1)
595         p = (Ether(dst=dmac, src=self.pg0.remote_mac) /
596              IPv6(dst="ff02::2", src="::") /
597              ICMPv6ND_RS())
598         pkts = [p]
599         self.send_and_expect_ra(self.pg0, pkts,
600                                 "RS sourced from unspecified",
601                                 dst_ip="ff02::1",
602                                 filter_out_fn=None)
603
604         #
605         # Configure The RA to announce the links prefix
606         #
607         self.pg0.ip6_ra_prefix(self.pg0.local_ip6n,
608                                self.pg0.local_ip6_prefix_len)
609
610         #
611         # RAs should now contain the prefix information option
612         #
613         opt = ICMPv6NDOptPrefixInfo(prefixlen=self.pg0.local_ip6_prefix_len,
614                                     prefix=self.pg0.local_ip6,
615                                     L=1,
616                                     A=1)
617
618         self.pg0.ip6_ra_config(send_unicast=1)
619         ll = mk_ll_addr(self.pg0.remote_mac)
620         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
621              IPv6(dst=self.pg0.local_ip6, src=ll) /
622              ICMPv6ND_RS())
623         self.send_and_expect_ra(self.pg0, p,
624                                 "RA with prefix-info",
625                                 dst_ip=ll,
626                                 opt=opt)
627
628         #
629         # Change the prefix info to not off-link
630         #  L-flag is clear
631         #
632         self.pg0.ip6_ra_prefix(self.pg0.local_ip6n,
633                                self.pg0.local_ip6_prefix_len,
634                                off_link=1)
635
636         opt = ICMPv6NDOptPrefixInfo(prefixlen=self.pg0.local_ip6_prefix_len,
637                                     prefix=self.pg0.local_ip6,
638                                     L=0,
639                                     A=1)
640
641         self.pg0.ip6_ra_config(send_unicast=1)
642         self.send_and_expect_ra(self.pg0, p,
643                                 "RA with Prefix info with L-flag=0",
644                                 dst_ip=ll,
645                                 opt=opt)
646
647         #
648         # Change the prefix info to not off-link, no-autoconfig
649         #  L and A flag are clear in the advert
650         #
651         self.pg0.ip6_ra_prefix(self.pg0.local_ip6n,
652                                self.pg0.local_ip6_prefix_len,
653                                off_link=1,
654                                no_autoconfig=1)
655
656         opt = ICMPv6NDOptPrefixInfo(prefixlen=self.pg0.local_ip6_prefix_len,
657                                     prefix=self.pg0.local_ip6,
658                                     L=0,
659                                     A=0)
660
661         self.pg0.ip6_ra_config(send_unicast=1)
662         self.send_and_expect_ra(self.pg0, p,
663                                 "RA with Prefix info with A & L-flag=0",
664                                 dst_ip=ll,
665                                 opt=opt)
666
667         #
668         # Change the flag settings back to the defaults
669         #  L and A flag are set in the advert
670         #
671         self.pg0.ip6_ra_prefix(self.pg0.local_ip6n,
672                                self.pg0.local_ip6_prefix_len)
673
674         opt = ICMPv6NDOptPrefixInfo(prefixlen=self.pg0.local_ip6_prefix_len,
675                                     prefix=self.pg0.local_ip6,
676                                     L=1,
677                                     A=1)
678
679         self.pg0.ip6_ra_config(send_unicast=1)
680         self.send_and_expect_ra(self.pg0, p,
681                                 "RA with Prefix info",
682                                 dst_ip=ll,
683                                 opt=opt)
684
685         #
686         # Change the prefix info to not off-link, no-autoconfig
687         #  L and A flag are clear in the advert
688         #
689         self.pg0.ip6_ra_prefix(self.pg0.local_ip6n,
690                                self.pg0.local_ip6_prefix_len,
691                                off_link=1,
692                                no_autoconfig=1)
693
694         opt = ICMPv6NDOptPrefixInfo(prefixlen=self.pg0.local_ip6_prefix_len,
695                                     prefix=self.pg0.local_ip6,
696                                     L=0,
697                                     A=0)
698
699         self.pg0.ip6_ra_config(send_unicast=1)
700         self.send_and_expect_ra(self.pg0, p,
701                                 "RA with Prefix info with A & L-flag=0",
702                                 dst_ip=ll,
703                                 opt=opt)
704
705         #
706         # Use the reset to defults option to revert to defaults
707         #  L and A flag are clear in the advert
708         #
709         self.pg0.ip6_ra_prefix(self.pg0.local_ip6n,
710                                self.pg0.local_ip6_prefix_len,
711                                use_default=1)
712
713         opt = ICMPv6NDOptPrefixInfo(prefixlen=self.pg0.local_ip6_prefix_len,
714                                     prefix=self.pg0.local_ip6,
715                                     L=1,
716                                     A=1)
717
718         self.pg0.ip6_ra_config(send_unicast=1)
719         self.send_and_expect_ra(self.pg0, p,
720                                 "RA with Prefix reverted to defaults",
721                                 dst_ip=ll,
722                                 opt=opt)
723
724         #
725         # Advertise Another prefix. With no L-flag/A-flag
726         #
727         self.pg0.ip6_ra_prefix(self.pg1.local_ip6n,
728                                self.pg1.local_ip6_prefix_len,
729                                off_link=1,
730                                no_autoconfig=1)
731
732         opt = [ICMPv6NDOptPrefixInfo(prefixlen=self.pg0.local_ip6_prefix_len,
733                                      prefix=self.pg0.local_ip6,
734                                      L=1,
735                                      A=1),
736                ICMPv6NDOptPrefixInfo(prefixlen=self.pg1.local_ip6_prefix_len,
737                                      prefix=self.pg1.local_ip6,
738                                      L=0,
739                                      A=0)]
740
741         self.pg0.ip6_ra_config(send_unicast=1)
742         ll = mk_ll_addr(self.pg0.remote_mac)
743         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
744              IPv6(dst=self.pg0.local_ip6, src=ll) /
745              ICMPv6ND_RS())
746         self.send_and_expect_ra(self.pg0, p,
747                                 "RA with multiple Prefix infos",
748                                 dst_ip=ll,
749                                 opt=opt)
750
751         #
752         # Remove the first refix-info - expect the second is still in the
753         # advert
754         #
755         self.pg0.ip6_ra_prefix(self.pg0.local_ip6n,
756                                self.pg0.local_ip6_prefix_len,
757                                is_no=1)
758
759         opt = ICMPv6NDOptPrefixInfo(prefixlen=self.pg1.local_ip6_prefix_len,
760                                     prefix=self.pg1.local_ip6,
761                                     L=0,
762                                     A=0)
763
764         self.pg0.ip6_ra_config(send_unicast=1)
765         self.send_and_expect_ra(self.pg0, p,
766                                 "RA with Prefix reverted to defaults",
767                                 dst_ip=ll,
768                                 opt=opt)
769
770         #
771         # Remove the second prefix-info - expect no prefix-info i nthe adverts
772         #
773         self.pg0.ip6_ra_prefix(self.pg1.local_ip6n,
774                                self.pg1.local_ip6_prefix_len,
775                                is_no=1)
776
777         self.pg0.ip6_ra_config(send_unicast=1)
778         self.send_and_expect_ra(self.pg0, p,
779                                 "RA with Prefix reverted to defaults",
780                                 dst_ip=ll)
781
782         #
783         # Reset the periodic advertisements back to default values
784         #
785         self.pg0.ip6_ra_config(no=1, suppress=1, send_unicast=0)
786
787
788 class IPv6NDProxyTest(TestIPv6ND):
789     """ IPv6 ND ProxyTest Case """
790
791     def setUp(self):
792         super(IPv6NDProxyTest, self).setUp()
793
794         # create 3 pg interfaces
795         self.create_pg_interfaces(range(3))
796
797         # pg0 is the master interface, with the configured subnet
798         self.pg0.admin_up()
799         self.pg0.config_ip6()
800         self.pg0.resolve_ndp()
801
802         self.pg1.ip6_enable()
803         self.pg2.ip6_enable()
804
805     def tearDown(self):
806         super(IPv6NDProxyTest, self).tearDown()
807
808     def test_nd_proxy(self):
809         """ IPv6 Proxy ND """
810
811         #
812         # Generate some hosts in the subnet that we are proxying
813         #
814         self.pg0.generate_remote_hosts(8)
815
816         nsma = in6_getnsma(inet_pton(AF_INET6, self.pg0.local_ip6))
817         d = inet_ntop(AF_INET6, nsma)
818
819         #
820         # Send an NS for one of those remote hosts on one of the proxy links
821         # expect no response since it's from an address that is not
822         # on the link that has the prefix configured
823         #
824         ns_pg1 = (Ether(dst=in6_getnsmac(nsma), src=self.pg1.remote_mac) /
825                   IPv6(dst=d, src=self.pg0._remote_hosts[2].ip6) /
826                   ICMPv6ND_NS(tgt=self.pg0.local_ip6) /
827                   ICMPv6NDOptSrcLLAddr(lladdr=self.pg0._remote_hosts[2].mac))
828
829         self.send_and_assert_no_replies(self.pg1, ns_pg1, "Off link NS")
830
831         #
832         # Add proxy support for the host
833         #
834         self.vapi.ip6_nd_proxy(
835             inet_pton(AF_INET6, self.pg0._remote_hosts[2].ip6),
836             self.pg1.sw_if_index)
837
838         #
839         # try that NS again. this time we expect an NA back
840         #
841         self.send_and_expect_na(self.pg1, ns_pg1,
842                                 "NS to proxy entry",
843                                 dst_ip=self.pg0._remote_hosts[2].ip6,
844                                 tgt_ip=self.pg0.local_ip6)
845
846         #
847         # ... and that we have an entry in the ND cache
848         #
849         self.assertTrue(find_nbr(self,
850                                  self.pg1.sw_if_index,
851                                  self.pg0._remote_hosts[2].ip6,
852                                  inet=AF_INET6))
853
854         #
855         # ... and we can route traffic to it
856         #
857         t = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
858              IPv6(dst=self.pg0._remote_hosts[2].ip6,
859                   src=self.pg0.remote_ip6) /
860              UDP(sport=10000, dport=20000) /
861              Raw('\xa5' * 100))
862
863         self.pg0.add_stream(t)
864         self.pg_enable_capture(self.pg_interfaces)
865         self.pg_start()
866         rx = self.pg1.get_capture(1)
867         rx = rx[0]
868
869         self.assertEqual(rx[Ether].dst, self.pg0._remote_hosts[2].mac)
870         self.assertEqual(rx[Ether].src, self.pg1.local_mac)
871
872         self.assertEqual(rx[IPv6].src, t[IPv6].src)
873         self.assertEqual(rx[IPv6].dst, t[IPv6].dst)
874
875         #
876         # Test we proxy for the host on the main interface
877         #
878         ns_pg0 = (Ether(dst=in6_getnsmac(nsma), src=self.pg0.remote_mac) /
879                   IPv6(dst=d, src=self.pg0.remote_ip6) /
880                   ICMPv6ND_NS(tgt=self.pg0._remote_hosts[2].ip6) /
881                   ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac))
882
883         self.send_and_expect_na(self.pg0, ns_pg0,
884                                 "NS to proxy entry on main",
885                                 tgt_ip=self.pg0._remote_hosts[2].ip6,
886                                 dst_ip=self.pg0.remote_ip6)
887
888         #
889         # Setup and resolve proxy for another host on another interface
890         #
891         ns_pg2 = (Ether(dst=in6_getnsmac(nsma), src=self.pg2.remote_mac) /
892                   IPv6(dst=d, src=self.pg0._remote_hosts[3].ip6) /
893                   ICMPv6ND_NS(tgt=self.pg0.local_ip6) /
894                   ICMPv6NDOptSrcLLAddr(lladdr=self.pg0._remote_hosts[2].mac))
895
896         self.vapi.ip6_nd_proxy(
897             inet_pton(AF_INET6, self.pg0._remote_hosts[3].ip6),
898             self.pg2.sw_if_index)
899
900         self.send_and_expect_na(self.pg2, ns_pg2,
901                                 "NS to proxy entry other interface",
902                                 dst_ip=self.pg0._remote_hosts[3].ip6,
903                                 tgt_ip=self.pg0.local_ip6)
904
905         self.assertTrue(find_nbr(self,
906                                  self.pg2.sw_if_index,
907                                  self.pg0._remote_hosts[3].ip6,
908                                  inet=AF_INET6))
909
910         #
911         # hosts can communicate. pg2->pg1
912         #
913         t2 = (Ether(dst=self.pg2.local_mac,
914                     src=self.pg0.remote_hosts[3].mac) /
915               IPv6(dst=self.pg0._remote_hosts[2].ip6,
916                    src=self.pg0._remote_hosts[3].ip6) /
917               UDP(sport=10000, dport=20000) /
918               Raw('\xa5' * 100))
919
920         self.pg2.add_stream(t2)
921         self.pg_enable_capture(self.pg_interfaces)
922         self.pg_start()
923         rx = self.pg1.get_capture(1)
924         rx = rx[0]
925
926         self.assertEqual(rx[Ether].dst, self.pg0._remote_hosts[2].mac)
927         self.assertEqual(rx[Ether].src, self.pg1.local_mac)
928
929         self.assertEqual(rx[IPv6].src, t2[IPv6].src)
930         self.assertEqual(rx[IPv6].dst, t2[IPv6].dst)
931
932         #
933         # remove the proxy configs
934         #
935         self.vapi.ip6_nd_proxy(
936             inet_pton(AF_INET6, self.pg0._remote_hosts[2].ip6),
937             self.pg1.sw_if_index,
938             is_del=1)
939         self.vapi.ip6_nd_proxy(
940             inet_pton(AF_INET6, self.pg0._remote_hosts[3].ip6),
941             self.pg2.sw_if_index,
942             is_del=1)
943
944         self.assertFalse(find_nbr(self,
945                                   self.pg2.sw_if_index,
946                                   self.pg0._remote_hosts[3].ip6,
947                                   inet=AF_INET6))
948         self.assertFalse(find_nbr(self,
949                                   self.pg1.sw_if_index,
950                                   self.pg0._remote_hosts[2].ip6,
951                                   inet=AF_INET6))
952
953         #
954         # no longer proxy-ing...
955         #
956         self.send_and_assert_no_replies(self.pg0, ns_pg0, "Proxy unconfigured")
957         self.send_and_assert_no_replies(self.pg1, ns_pg1, "Proxy unconfigured")
958         self.send_and_assert_no_replies(self.pg2, ns_pg2, "Proxy unconfigured")
959
960         #
961         # no longer forwarding. traffic generates NS out of the glean/main
962         # interface
963         #
964         self.pg2.add_stream(t2)
965         self.pg_enable_capture(self.pg_interfaces)
966         self.pg_start()
967
968         rx = self.pg0.get_capture(1)
969
970         self.assertTrue(rx[0].haslayer(ICMPv6ND_NS))
971
972
973 class TestIPNull(VppTestCase):
974     """ IPv6 routes via NULL """
975
976     def setUp(self):
977         super(TestIPNull, self).setUp()
978
979         # create 2 pg interfaces
980         self.create_pg_interfaces(range(1))
981
982         for i in self.pg_interfaces:
983             i.admin_up()
984             i.config_ip6()
985             i.resolve_ndp()
986
987     def tearDown(self):
988         super(TestIPNull, self).tearDown()
989         for i in self.pg_interfaces:
990             i.unconfig_ip6()
991             i.admin_down()
992
993     def test_ip_null(self):
994         """ IP NULL route """
995
996         p = (Ether(src=self.pg0.remote_mac,
997                    dst=self.pg0.local_mac) /
998              IPv6(src=self.pg0.remote_ip6, dst="2001::1") /
999              UDP(sport=1234, dport=1234) /
1000              Raw('\xa5' * 100))
1001
1002         #
1003         # A route via IP NULL that will reply with ICMP unreachables
1004         #
1005         ip_unreach = VppIpRoute(self, "2001::", 64, [], is_unreach=1, is_ip6=1)
1006         ip_unreach.add_vpp_config()
1007
1008         self.pg0.add_stream(p)
1009         self.pg_enable_capture(self.pg_interfaces)
1010         self.pg_start()
1011
1012         rx = self.pg0.get_capture(1)
1013         rx = rx[0]
1014         icmp = rx[ICMPv6DestUnreach]
1015
1016         # 0 = "No route to destination"
1017         self.assertEqual(icmp.code, 0)
1018
1019         # ICMP is rate limited. pause a bit
1020         self.sleep(1)
1021
1022         #
1023         # A route via IP NULL that will reply with ICMP prohibited
1024         #
1025         ip_prohibit = VppIpRoute(self, "2001::1", 128, [],
1026                                  is_prohibit=1, is_ip6=1)
1027         ip_prohibit.add_vpp_config()
1028
1029         self.pg0.add_stream(p)
1030         self.pg_enable_capture(self.pg_interfaces)
1031         self.pg_start()
1032
1033         rx = self.pg0.get_capture(1)
1034         rx = rx[0]
1035         icmp = rx[ICMPv6DestUnreach]
1036
1037         # 1 = "Communication with destination administratively prohibited"
1038         self.assertEqual(icmp.code, 1)
1039
1040
1041 class TestIPDisabled(VppTestCase):
1042     """ IPv6 disabled """
1043
1044     def setUp(self):
1045         super(TestIPDisabled, self).setUp()
1046
1047         # create 2 pg interfaces
1048         self.create_pg_interfaces(range(2))
1049
1050         # PG0 is IP enalbed
1051         self.pg0.admin_up()
1052         self.pg0.config_ip6()
1053         self.pg0.resolve_ndp()
1054
1055         # PG 1 is not IP enabled
1056         self.pg1.admin_up()
1057
1058     def tearDown(self):
1059         super(TestIPDisabled, self).tearDown()
1060         for i in self.pg_interfaces:
1061             i.unconfig_ip4()
1062             i.admin_down()
1063
1064     def send_and_assert_no_replies(self, intf, pkts, remark):
1065         intf.add_stream(pkts)
1066         self.pg_enable_capture(self.pg_interfaces)
1067         self.pg_start()
1068         for i in self.pg_interfaces:
1069             i.get_capture(0)
1070             i.assert_nothing_captured(remark=remark)
1071
1072     def test_ip_disabled(self):
1073         """ IP Disabled """
1074
1075         #
1076         # An (S,G).
1077         # one accepting interface, pg0, 2 forwarding interfaces
1078         #
1079         route_ff_01 = VppIpMRoute(
1080             self,
1081             "::",
1082             "ffef::1", 128,
1083             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
1084             [VppMRoutePath(self.pg1.sw_if_index,
1085                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
1086              VppMRoutePath(self.pg0.sw_if_index,
1087                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)],
1088             is_ip6=1)
1089         route_ff_01.add_vpp_config()
1090
1091         pu = (Ether(src=self.pg1.remote_mac,
1092                     dst=self.pg1.local_mac) /
1093               IPv6(src="2001::1", dst=self.pg0.remote_ip6) /
1094               UDP(sport=1234, dport=1234) /
1095               Raw('\xa5' * 100))
1096         pm = (Ether(src=self.pg1.remote_mac,
1097                     dst=self.pg1.local_mac) /
1098               IPv6(src="2001::1", dst="ffef::1") /
1099               UDP(sport=1234, dport=1234) /
1100               Raw('\xa5' * 100))
1101
1102         #
1103         # PG1 does not forward IP traffic
1104         #
1105         self.send_and_assert_no_replies(self.pg1, pu, "IPv6 disabled")
1106         self.send_and_assert_no_replies(self.pg1, pm, "IPv6 disabled")
1107
1108         #
1109         # IP enable PG1
1110         #
1111         self.pg1.config_ip6()
1112
1113         #
1114         # Now we get packets through
1115         #
1116         self.pg1.add_stream(pu)
1117         self.pg_enable_capture(self.pg_interfaces)
1118         self.pg_start()
1119         rx = self.pg0.get_capture(1)
1120
1121         self.pg1.add_stream(pm)
1122         self.pg_enable_capture(self.pg_interfaces)
1123         self.pg_start()
1124         rx = self.pg0.get_capture(1)
1125
1126         #
1127         # Disable PG1
1128         #
1129         self.pg1.unconfig_ip6()
1130
1131         #
1132         # PG1 does not forward IP traffic
1133         #
1134         self.send_and_assert_no_replies(self.pg1, pu, "IPv6 disabled")
1135         self.send_and_assert_no_replies(self.pg1, pm, "IPv6 disabled")
1136
1137
1138 class TestIP6LoadBalance(VppTestCase):
1139     """ IPv6 Load-Balancing """
1140
1141     def setUp(self):
1142         super(TestIP6LoadBalance, self).setUp()
1143
1144         self.create_pg_interfaces(range(5))
1145
1146         for i in self.pg_interfaces:
1147             i.admin_up()
1148             i.config_ip6()
1149             i.resolve_ndp()
1150             i.enable_mpls()
1151
1152     def tearDown(self):
1153         super(TestIP6LoadBalance, self).tearDown()
1154         for i in self.pg_interfaces:
1155             i.unconfig_ip6()
1156             i.admin_down()
1157             i.disable_mpls()
1158
1159     def send_and_expect_load_balancing(self, input, pkts, outputs):
1160         input.add_stream(pkts)
1161         self.pg_enable_capture(self.pg_interfaces)
1162         self.pg_start()
1163         for oo in outputs:
1164             rx = oo._get_capture(1)
1165             self.assertNotEqual(0, len(rx))
1166
1167     def send_and_expect_one_itf(self, input, pkts, itf):
1168         input.add_stream(pkts)
1169         self.pg_enable_capture(self.pg_interfaces)
1170         self.pg_start()
1171         rx = itf.get_capture(len(pkts))
1172
1173     def test_ip6_load_balance(self):
1174         """ IPv6 Load-Balancing """
1175
1176         #
1177         # An array of packets that differ only in the destination port
1178         #  - IP only
1179         #  - MPLS EOS
1180         #  - MPLS non-EOS
1181         #  - MPLS non-EOS with an entropy label
1182         #
1183         port_ip_pkts = []
1184         port_mpls_pkts = []
1185         port_mpls_neos_pkts = []
1186         port_ent_pkts = []
1187
1188         #
1189         # An array of packets that differ only in the source address
1190         #
1191         src_ip_pkts = []
1192         src_mpls_pkts = []
1193
1194         for ii in range(65):
1195             port_ip_hdr = (IPv6(dst="3000::1", src="3000:1::1") /
1196                            UDP(sport=1234, dport=1234 + ii) /
1197                            Raw('\xa5' * 100))
1198             port_ip_pkts.append((Ether(src=self.pg0.remote_mac,
1199                                        dst=self.pg0.local_mac) /
1200                                  port_ip_hdr))
1201             port_mpls_pkts.append((Ether(src=self.pg0.remote_mac,
1202                                          dst=self.pg0.local_mac) /
1203                                    MPLS(label=66, ttl=2) /
1204                                    port_ip_hdr))
1205             port_mpls_neos_pkts.append((Ether(src=self.pg0.remote_mac,
1206                                               dst=self.pg0.local_mac) /
1207                                         MPLS(label=67, ttl=2) /
1208                                         MPLS(label=77, ttl=2) /
1209                                         port_ip_hdr))
1210             port_ent_pkts.append((Ether(src=self.pg0.remote_mac,
1211                                         dst=self.pg0.local_mac) /
1212                                   MPLS(label=67, ttl=2) /
1213                                   MPLS(label=14, ttl=2) /
1214                                   MPLS(label=999, ttl=2) /
1215                                   port_ip_hdr))
1216             src_ip_hdr = (IPv6(dst="3000::1", src="3000:1::%d" % ii) /
1217                           UDP(sport=1234, dport=1234) /
1218                           Raw('\xa5' * 100))
1219             src_ip_pkts.append((Ether(src=self.pg0.remote_mac,
1220                                       dst=self.pg0.local_mac) /
1221                                 src_ip_hdr))
1222             src_mpls_pkts.append((Ether(src=self.pg0.remote_mac,
1223                                         dst=self.pg0.local_mac) /
1224                                   MPLS(label=66, ttl=2) /
1225                                   src_ip_hdr))
1226
1227         #
1228         # A route for the IP pacekts
1229         #
1230         route_3000_1 = VppIpRoute(self, "3000::1", 128,
1231                                   [VppRoutePath(self.pg1.remote_ip6,
1232                                                 self.pg1.sw_if_index,
1233                                                 is_ip6=1),
1234                                    VppRoutePath(self.pg2.remote_ip6,
1235                                                 self.pg2.sw_if_index,
1236                                                 is_ip6=1)],
1237                                   is_ip6=1)
1238         route_3000_1.add_vpp_config()
1239
1240         #
1241         # a local-label for the EOS packets
1242         #
1243         binding = VppMplsIpBind(self, 66, "3000::1", 128, is_ip6=1)
1244         binding.add_vpp_config()
1245
1246         #
1247         # An MPLS route for the non-EOS packets
1248         #
1249         route_67 = VppMplsRoute(self, 67, 0,
1250                                 [VppRoutePath(self.pg1.remote_ip6,
1251                                               self.pg1.sw_if_index,
1252                                               labels=[67],
1253                                               is_ip6=1),
1254                                  VppRoutePath(self.pg2.remote_ip6,
1255                                               self.pg2.sw_if_index,
1256                                               labels=[67],
1257                                               is_ip6=1)])
1258         route_67.add_vpp_config()
1259
1260         #
1261         # inject the packet on pg0 - expect load-balancing across the 2 paths
1262         #  - since the default hash config is to use IP src,dst and port
1263         #    src,dst
1264         # We are not going to ensure equal amounts of packets across each link,
1265         # since the hash algorithm is statistical and therefore this can never
1266         # be guaranteed. But wuth 64 different packets we do expect some
1267         # balancing. So instead just ensure there is traffic on each link.
1268         #
1269         self.send_and_expect_load_balancing(self.pg0, port_ip_pkts,
1270                                             [self.pg1, self.pg2])
1271         self.send_and_expect_load_balancing(self.pg0, src_ip_pkts,
1272                                             [self.pg1, self.pg2])
1273         self.send_and_expect_load_balancing(self.pg0, port_mpls_pkts,
1274                                             [self.pg1, self.pg2])
1275         self.send_and_expect_load_balancing(self.pg0, src_mpls_pkts,
1276                                             [self.pg1, self.pg2])
1277         self.send_and_expect_load_balancing(self.pg0, port_mpls_neos_pkts,
1278                                             [self.pg1, self.pg2])
1279
1280         #
1281         # The packets with Entropy label in should not load-balance,
1282         # since the Entorpy value is fixed.
1283         #
1284         self.send_and_expect_one_itf(self.pg0, port_ent_pkts, self.pg1)
1285
1286         #
1287         # change the flow hash config so it's only IP src,dst
1288         #  - now only the stream with differing source address will
1289         #    load-balance
1290         #
1291         self.vapi.set_ip_flow_hash(0, is_ip6=1, src=1, dst=1, sport=0, dport=0)
1292
1293         self.send_and_expect_load_balancing(self.pg0, src_ip_pkts,
1294                                             [self.pg1, self.pg2])
1295         self.send_and_expect_load_balancing(self.pg0, src_mpls_pkts,
1296                                             [self.pg1, self.pg2])
1297         self.send_and_expect_one_itf(self.pg0, port_ip_pkts, self.pg2)
1298
1299         #
1300         # change the flow hash config back to defaults
1301         #
1302         self.vapi.set_ip_flow_hash(0, is_ip6=1, src=1, dst=1, sport=1, dport=1)
1303
1304         #
1305         # Recursive prefixes
1306         #  - testing that 2 stages of load-balancing occurs and there is no
1307         #    polarisation (i.e. only 2 of 4 paths are used)
1308         #
1309         port_pkts = []
1310         src_pkts = []
1311
1312         for ii in range(257):
1313             port_pkts.append((Ether(src=self.pg0.remote_mac,
1314                                     dst=self.pg0.local_mac) /
1315                               IPv6(dst="4000::1", src="4000:1::1") /
1316                               UDP(sport=1234, dport=1234 + ii) /
1317                               Raw('\xa5' * 100)))
1318             src_pkts.append((Ether(src=self.pg0.remote_mac,
1319                                    dst=self.pg0.local_mac) /
1320                              IPv6(dst="4000::1", src="4000:1::%d" % ii) /
1321                              UDP(sport=1234, dport=1234) /
1322                              Raw('\xa5' * 100)))
1323
1324         route_3000_2 = VppIpRoute(self, "3000::2", 128,
1325                                   [VppRoutePath(self.pg3.remote_ip6,
1326                                                 self.pg3.sw_if_index,
1327                                                 is_ip6=1),
1328                                    VppRoutePath(self.pg4.remote_ip6,
1329                                                 self.pg4.sw_if_index,
1330                                                 is_ip6=1)],
1331                                   is_ip6=1)
1332         route_3000_2.add_vpp_config()
1333
1334         route_4000_1 = VppIpRoute(self, "4000::1", 128,
1335                                   [VppRoutePath("3000::1",
1336                                                 0xffffffff,
1337                                                 is_ip6=1),
1338                                    VppRoutePath("3000::2",
1339                                                 0xffffffff,
1340                                                 is_ip6=1)],
1341                                   is_ip6=1)
1342         route_4000_1.add_vpp_config()
1343
1344         #
1345         # inject the packet on pg0 - expect load-balancing across all 4 paths
1346         #
1347         self.vapi.cli("clear trace")
1348         self.send_and_expect_load_balancing(self.pg0, port_pkts,
1349                                             [self.pg1, self.pg2,
1350                                              self.pg3, self.pg4])
1351         self.send_and_expect_load_balancing(self.pg0, src_pkts,
1352                                             [self.pg1, self.pg2,
1353                                              self.pg3, self.pg4])
1354
1355
1356 if __name__ == '__main__':
1357     unittest.main(testRunner=VppTestRunner)