BIER
[vpp.git] / test / test_ip_mcast.py
1 #!/usr/bin/env python
2
3 import unittest
4
5 from framework import VppTestCase, VppTestRunner
6 from vpp_sub_interface import VppSubInterface, VppDot1QSubint, VppDot1ADSubint
7 from vpp_ip_route import VppIpMRoute, VppMRoutePath, VppMFibSignal, \
8     MRouteItfFlags, MRouteEntryFlags, VppIpTable, DpoProto
9
10 from scapy.packet import Raw
11 from scapy.layers.l2 import Ether
12 from scapy.layers.inet import IP, UDP, getmacbyip, ICMP
13 from scapy.layers.inet6 import IPv6, getmacbyip6
14 from util import ppp
15
16 #
17 # The number of packets sent is set to 90 so that when we replicate more than 3
18 # times, which we do for some entries, we will generate more than 256 packets
19 # to the next node in the VLIB graph. Thus we are testing the code's
20 # correctness handling this over-flow
21 #
22 N_PKTS_IN_STREAM = 90
23
24
25 class TestMFIB(VppTestCase):
26     """ MFIB Test Case """
27
28     def setUp(self):
29         super(TestMFIB, self).setUp()
30
31     def test_mfib(self):
32         """ MFIB Unit Tests """
33         error = self.vapi.cli("test mfib")
34
35         if error:
36             self.logger.critical(error)
37         self.assertEqual(error.find("Failed"), -1)
38
39
40 class TestIPMcast(VppTestCase):
41     """ IP Multicast Test Case """
42
43     def setUp(self):
44         super(TestIPMcast, self).setUp()
45
46         # create 8 pg interfaces
47         self.create_pg_interfaces(range(9))
48
49         # setup interfaces
50         for i in self.pg_interfaces[:8]:
51             i.admin_up()
52             i.config_ip4()
53             i.config_ip6()
54             i.resolve_arp()
55             i.resolve_ndp()
56
57         # one more in a vrf
58         tbl4 = VppIpTable(self, 10)
59         tbl4.add_vpp_config()
60         self.pg8.set_table_ip4(10)
61         self.pg8.config_ip4()
62
63         tbl6 = VppIpTable(self, 10, is_ip6=1)
64         tbl6.add_vpp_config()
65         self.pg8.set_table_ip6(10)
66         self.pg8.config_ip6()
67
68     def tearDown(self):
69         for i in self.pg_interfaces:
70             i.unconfig_ip4()
71             i.unconfig_ip6()
72             i.admin_down()
73
74         self.pg8.set_table_ip4(0)
75         self.pg8.set_table_ip6(0)
76         super(TestIPMcast, self).tearDown()
77
78     def create_stream_ip4(self, src_if, src_ip, dst_ip, payload_size=0):
79         pkts = []
80         # default to small packet sizes
81         p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
82              IP(src=src_ip, dst=dst_ip) /
83              UDP(sport=1234, dport=1234))
84         if not payload_size:
85             payload_size = 64 - len(p)
86             p = p / Raw('\xa5' * payload_size)
87
88         for i in range(0, N_PKTS_IN_STREAM):
89             pkts.append(p)
90         return pkts
91
92     def create_stream_ip6(self, src_if, src_ip, dst_ip):
93         pkts = []
94         for i in range(0, N_PKTS_IN_STREAM):
95             info = self.create_packet_info(src_if, src_if)
96             payload = self.info_to_payload(info)
97             p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
98                  IPv6(src=src_ip, dst=dst_ip) /
99                  UDP(sport=1234, dport=1234) /
100                  Raw(payload))
101             info.data = p.copy()
102             pkts.append(p)
103         return pkts
104
105     def verify_filter(self, capture, sent):
106         if not len(capture) == len(sent):
107             # filter out any IPv6 RAs from the captur
108             for p in capture:
109                 if (p.haslayer(IPv6)):
110                     capture.remove(p)
111         return capture
112
113     def verify_capture_ip4(self, rx_if, sent):
114         rxd = rx_if.get_capture(len(sent))
115
116         try:
117             capture = self.verify_filter(rxd, sent)
118
119             self.assertEqual(len(capture), len(sent))
120
121             for i in range(len(capture)):
122                 tx = sent[i]
123                 rx = capture[i]
124
125                 eth = rx[Ether]
126                 self.assertEqual(eth.type, 0x800)
127
128                 tx_ip = tx[IP]
129                 rx_ip = rx[IP]
130
131                 # check the MAC address on the RX'd packet is correctly formed
132                 self.assertEqual(eth.dst, getmacbyip(rx_ip.dst))
133
134                 self.assertEqual(rx_ip.src, tx_ip.src)
135                 self.assertEqual(rx_ip.dst, tx_ip.dst)
136                 # IP processing post pop has decremented the TTL
137                 self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl)
138
139         except:
140             raise
141
142     def verify_capture_ip6(self, rx_if, sent):
143         capture = rx_if.get_capture(len(sent))
144
145         self.assertEqual(len(capture), len(sent))
146
147         for i in range(len(capture)):
148             tx = sent[i]
149             rx = capture[i]
150
151             eth = rx[Ether]
152             self.assertEqual(eth.type, 0x86DD)
153
154             tx_ip = tx[IPv6]
155             rx_ip = rx[IPv6]
156
157             # check the MAC address on the RX'd packet is correctly formed
158             self.assertEqual(eth.dst, getmacbyip6(rx_ip.dst))
159
160             self.assertEqual(rx_ip.src, tx_ip.src)
161             self.assertEqual(rx_ip.dst, tx_ip.dst)
162             # IP processing post pop has decremented the TTL
163             self.assertEqual(rx_ip.hlim + 1, tx_ip.hlim)
164
165     def test_ip_mcast(self):
166         """ IP Multicast Replication """
167
168         #
169         # a stream that matches the default route. gets dropped.
170         #
171         self.vapi.cli("clear trace")
172         tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "232.1.1.1")
173         self.pg0.add_stream(tx)
174
175         self.pg_enable_capture(self.pg_interfaces)
176         self.pg_start()
177
178         self.pg0.assert_nothing_captured(
179             remark="IP multicast packets forwarded on default route")
180
181         #
182         # A (*,G).
183         # one accepting interface, pg0, 7 forwarding interfaces
184         #  many forwarding interfaces test the case where the replicare DPO
185         #  needs to use extra cache lines for the buckets.
186         #
187         route_232_1_1_1 = VppIpMRoute(
188             self,
189             "0.0.0.0",
190             "232.1.1.1", 32,
191             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
192             [VppMRoutePath(self.pg0.sw_if_index,
193                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
194              VppMRoutePath(self.pg1.sw_if_index,
195                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
196              VppMRoutePath(self.pg2.sw_if_index,
197                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
198              VppMRoutePath(self.pg3.sw_if_index,
199                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
200              VppMRoutePath(self.pg4.sw_if_index,
201                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
202              VppMRoutePath(self.pg5.sw_if_index,
203                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
204              VppMRoutePath(self.pg6.sw_if_index,
205                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
206              VppMRoutePath(self.pg7.sw_if_index,
207                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
208         route_232_1_1_1.add_vpp_config()
209
210         #
211         # An (S,G).
212         # one accepting interface, pg0, 2 forwarding interfaces
213         #
214         route_1_1_1_1_232_1_1_1 = VppIpMRoute(
215             self,
216             "1.1.1.1",
217             "232.1.1.1", 64,
218             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
219             [VppMRoutePath(self.pg0.sw_if_index,
220                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
221              VppMRoutePath(self.pg1.sw_if_index,
222                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
223              VppMRoutePath(self.pg2.sw_if_index,
224                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
225         route_1_1_1_1_232_1_1_1.add_vpp_config()
226
227         #
228         # An (*,G/m).
229         # one accepting interface, pg0, 1 forwarding interfaces
230         #
231         route_232 = VppIpMRoute(
232             self,
233             "0.0.0.0",
234             "232.0.0.0", 8,
235             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
236             [VppMRoutePath(self.pg0.sw_if_index,
237                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
238              VppMRoutePath(self.pg1.sw_if_index,
239                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
240         route_232.add_vpp_config()
241
242         #
243         # a stream that matches the route for (1.1.1.1,232.1.1.1)
244         #  small packets
245         #
246         self.vapi.cli("clear trace")
247         tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "232.1.1.1")
248         self.pg0.add_stream(tx)
249
250         self.pg_enable_capture(self.pg_interfaces)
251         self.pg_start()
252
253         # We expect replications on Pg1->7
254         self.verify_capture_ip4(self.pg1, tx)
255         self.verify_capture_ip4(self.pg2, tx)
256
257         # no replications on Pg0
258         self.pg0.assert_nothing_captured(
259             remark="IP multicast packets forwarded on PG0")
260         self.pg3.assert_nothing_captured(
261             remark="IP multicast packets forwarded on PG3")
262
263         #
264         # a stream that matches the route for (1.1.1.1,232.1.1.1)
265         #  large packets
266         #
267         self.vapi.cli("clear trace")
268         tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "232.1.1.1",
269                                     payload_size=1024)
270         self.pg0.add_stream(tx)
271
272         self.pg_enable_capture(self.pg_interfaces)
273         self.pg_start()
274
275         # We expect replications on Pg1->7
276         self.verify_capture_ip4(self.pg1, tx)
277         self.verify_capture_ip4(self.pg2, tx)
278
279         # no replications on Pg0
280         self.pg0.assert_nothing_captured(
281             remark="IP multicast packets forwarded on PG0")
282         self.pg3.assert_nothing_captured(
283             remark="IP multicast packets forwarded on PG3")
284
285         #
286         # a stream that matches the route for (*,232.0.0.0/8)
287         # Send packets with the 9th bit set so we test the correct clearing
288         # of that bit in the mac rewrite
289         #
290         self.vapi.cli("clear trace")
291         tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "232.255.255.255")
292         self.pg0.add_stream(tx)
293
294         self.pg_enable_capture(self.pg_interfaces)
295         self.pg_start()
296
297         # We expect replications on Pg1 only
298         self.verify_capture_ip4(self.pg1, tx)
299
300         # no replications on Pg0, Pg2 not Pg3
301         self.pg0.assert_nothing_captured(
302             remark="IP multicast packets forwarded on PG0")
303         self.pg2.assert_nothing_captured(
304             remark="IP multicast packets forwarded on PG2")
305         self.pg3.assert_nothing_captured(
306             remark="IP multicast packets forwarded on PG3")
307
308         #
309         # a stream that matches the route for (*,232.1.1.1)
310         #
311         self.vapi.cli("clear trace")
312         tx = self.create_stream_ip4(self.pg0, "1.1.1.2", "232.1.1.1")
313         self.pg0.add_stream(tx)
314
315         self.pg_enable_capture(self.pg_interfaces)
316         self.pg_start()
317
318         # We expect replications on Pg1, 2, 3.
319         self.verify_capture_ip4(self.pg1, tx)
320         self.verify_capture_ip4(self.pg2, tx)
321         self.verify_capture_ip4(self.pg3, tx)
322         self.verify_capture_ip4(self.pg4, tx)
323         self.verify_capture_ip4(self.pg5, tx)
324         self.verify_capture_ip4(self.pg6, tx)
325         self.verify_capture_ip4(self.pg7, tx)
326
327     def test_ip6_mcast(self):
328         """ IPv6 Multicast Replication """
329
330         #
331         # a stream that matches the default route. gets dropped.
332         #
333         self.vapi.cli("clear trace")
334         tx = self.create_stream_ip6(self.pg0, "2001::1", "ff01::1")
335         self.pg0.add_stream(tx)
336
337         self.pg_enable_capture(self.pg_interfaces)
338         self.pg_start()
339
340         self.pg0.assert_nothing_captured(
341             remark="IPv6 multicast packets forwarded on default route")
342
343         #
344         # A (*,G).
345         # one accepting interface, pg0, 3 forwarding interfaces
346         #
347         route_ff01_1 = VppIpMRoute(
348             self,
349             "::",
350             "ff01::1", 128,
351             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
352             [VppMRoutePath(self.pg0.sw_if_index,
353                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT,
354                            proto=DpoProto.DPO_PROTO_IP6),
355              VppMRoutePath(self.pg1.sw_if_index,
356                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
357                            proto=DpoProto.DPO_PROTO_IP6),
358              VppMRoutePath(self.pg2.sw_if_index,
359                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
360                            proto=DpoProto.DPO_PROTO_IP6),
361              VppMRoutePath(self.pg3.sw_if_index,
362                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
363                            proto=DpoProto.DPO_PROTO_IP6)],
364             is_ip6=1)
365         route_ff01_1.add_vpp_config()
366
367         #
368         # An (S,G).
369         # one accepting interface, pg0, 2 forwarding interfaces
370         #
371         route_2001_ff01_1 = VppIpMRoute(
372             self,
373             "2001::1",
374             "ff01::1", 256,
375             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
376             [VppMRoutePath(self.pg0.sw_if_index,
377                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT,
378                            proto=DpoProto.DPO_PROTO_IP6),
379              VppMRoutePath(self.pg1.sw_if_index,
380                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
381                            proto=DpoProto.DPO_PROTO_IP6),
382              VppMRoutePath(self.pg2.sw_if_index,
383                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
384                            proto=DpoProto.DPO_PROTO_IP6)],
385             is_ip6=1)
386         route_2001_ff01_1.add_vpp_config()
387
388         #
389         # An (*,G/m).
390         # one accepting interface, pg0, 1 forwarding interface
391         #
392         route_ff01 = VppIpMRoute(
393             self,
394             "::",
395             "ff01::", 16,
396             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
397             [VppMRoutePath(self.pg0.sw_if_index,
398                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT,
399                            proto=DpoProto.DPO_PROTO_IP6),
400              VppMRoutePath(self.pg1.sw_if_index,
401                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
402                            proto=DpoProto.DPO_PROTO_IP6)],
403             is_ip6=1)
404         route_ff01.add_vpp_config()
405
406         #
407         # a stream that matches the route for (*, ff01::/16)
408         #
409         self.vapi.cli("clear trace")
410         tx = self.create_stream_ip6(self.pg0, "2002::1", "ff01:2::255")
411         self.pg0.add_stream(tx)
412
413         self.pg_enable_capture(self.pg_interfaces)
414         self.pg_start()
415
416         # We expect replications on Pg1
417         self.verify_capture_ip6(self.pg1, tx)
418
419         # no replications on Pg0, Pg3
420         self.pg0.assert_nothing_captured(
421             remark="IP multicast packets forwarded on PG0")
422         self.pg2.assert_nothing_captured(
423             remark="IP multicast packets forwarded on PG2")
424         self.pg3.assert_nothing_captured(
425             remark="IP multicast packets forwarded on PG3")
426
427         #
428         # Bounce the interface and it should still work
429         #
430         self.pg1.admin_down()
431         self.pg0.add_stream(tx)
432         self.pg_enable_capture(self.pg_interfaces)
433         self.pg_start()
434         self.pg1.assert_nothing_captured(
435             remark="IP multicast packets forwarded on down PG1")
436
437         self.pg1.admin_up()
438         self.pg0.add_stream(tx)
439         self.pg_enable_capture(self.pg_interfaces)
440         self.pg_start()
441         self.verify_capture_ip6(self.pg1, tx)
442
443         #
444         # a stream that matches the route for (*,ff01::1)
445         #
446         self.vapi.cli("clear trace")
447         tx = self.create_stream_ip6(self.pg0, "2002::2", "ff01::1")
448         self.pg0.add_stream(tx)
449
450         self.pg_enable_capture(self.pg_interfaces)
451         self.pg_start()
452
453         # We expect replications on Pg1, 2, 3.
454         self.verify_capture_ip6(self.pg1, tx)
455         self.verify_capture_ip6(self.pg2, tx)
456         self.verify_capture_ip6(self.pg3, tx)
457
458         # no replications on Pg0
459         self.pg0.assert_nothing_captured(
460             remark="IPv6 multicast packets forwarded on PG0")
461
462         #
463         # a stream that matches the route for (2001::1, ff00::1)
464         #
465         self.vapi.cli("clear trace")
466         tx = self.create_stream_ip6(self.pg0, "2001::1", "ff01::1")
467         self.pg0.add_stream(tx)
468
469         self.pg_enable_capture(self.pg_interfaces)
470         self.pg_start()
471
472         # We expect replications on Pg1, 2,
473         self.verify_capture_ip6(self.pg1, tx)
474         self.verify_capture_ip6(self.pg2, tx)
475
476         # no replications on Pg0, Pg3
477         self.pg0.assert_nothing_captured(
478             remark="IP multicast packets forwarded on PG0")
479         self.pg3.assert_nothing_captured(
480             remark="IP multicast packets forwarded on PG3")
481
482     def _mcast_connected_send_stream(self, dst_ip):
483         self.vapi.cli("clear trace")
484         tx = self.create_stream_ip4(self.pg0,
485                                     self.pg0.remote_ip4,
486                                     dst_ip)
487         self.pg0.add_stream(tx)
488
489         self.pg_enable_capture(self.pg_interfaces)
490         self.pg_start()
491
492         # We expect replications on Pg1.
493         self.verify_capture_ip4(self.pg1, tx)
494
495         return tx
496
497     def test_ip_mcast_connected(self):
498         """ IP Multicast Connected Source check """
499
500         #
501         # A (*,G).
502         # one accepting interface, pg0, 1 forwarding interfaces
503         #
504         route_232_1_1_1 = VppIpMRoute(
505             self,
506             "0.0.0.0",
507             "232.1.1.1", 32,
508             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
509             [VppMRoutePath(self.pg0.sw_if_index,
510                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
511              VppMRoutePath(self.pg1.sw_if_index,
512                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
513
514         route_232_1_1_1.add_vpp_config()
515         route_232_1_1_1.update_entry_flags(
516             MRouteEntryFlags.MFIB_ENTRY_FLAG_CONNECTED)
517
518         #
519         # Now the (*,G) is present, send from connected source
520         #
521         tx = self._mcast_connected_send_stream("232.1.1.1")
522
523         #
524         # Constrct a representation of the signal we expect on pg0
525         #
526         signal_232_1_1_1_itf_0 = VppMFibSignal(self,
527                                                route_232_1_1_1,
528                                                self.pg0.sw_if_index,
529                                                tx[0])
530
531         #
532         # read the only expected signal
533         #
534         signals = self.vapi.mfib_signal_dump()
535
536         self.assertEqual(1, len(signals))
537
538         signal_232_1_1_1_itf_0.compare(signals[0])
539
540         #
541         # reading the signal allows for the generation of another
542         # so send more packets and expect the next signal
543         #
544         tx = self._mcast_connected_send_stream("232.1.1.1")
545
546         signals = self.vapi.mfib_signal_dump()
547         self.assertEqual(1, len(signals))
548         signal_232_1_1_1_itf_0.compare(signals[0])
549
550         #
551         # A Second entry with connected check
552         # one accepting interface, pg0, 1 forwarding interfaces
553         #
554         route_232_1_1_2 = VppIpMRoute(
555             self,
556             "0.0.0.0",
557             "232.1.1.2", 32,
558             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
559             [VppMRoutePath(self.pg0.sw_if_index,
560                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
561              VppMRoutePath(self.pg1.sw_if_index,
562                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
563
564         route_232_1_1_2.add_vpp_config()
565         route_232_1_1_2.update_entry_flags(
566             MRouteEntryFlags.MFIB_ENTRY_FLAG_CONNECTED)
567
568         #
569         # Send traffic to both entries. One read should net us two signals
570         #
571         signal_232_1_1_2_itf_0 = VppMFibSignal(self,
572                                                route_232_1_1_2,
573                                                self.pg0.sw_if_index,
574                                                tx[0])
575         tx = self._mcast_connected_send_stream("232.1.1.1")
576         tx2 = self._mcast_connected_send_stream("232.1.1.2")
577
578         #
579         # read the only expected signal
580         #
581         signals = self.vapi.mfib_signal_dump()
582
583         self.assertEqual(2, len(signals))
584
585         signal_232_1_1_1_itf_0.compare(signals[1])
586         signal_232_1_1_2_itf_0.compare(signals[0])
587
588         route_232_1_1_1.update_entry_flags(
589             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE)
590         route_232_1_1_2.update_entry_flags(
591             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE)
592
593     def test_ip_mcast_signal(self):
594         """ IP Multicast Signal """
595
596         #
597         # A (*,G).
598         # one accepting interface, pg0, 1 forwarding interfaces
599         #
600         route_232_1_1_1 = VppIpMRoute(
601             self,
602             "0.0.0.0",
603             "232.1.1.1", 32,
604             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
605             [VppMRoutePath(self.pg0.sw_if_index,
606                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
607              VppMRoutePath(self.pg1.sw_if_index,
608                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
609
610         route_232_1_1_1.add_vpp_config()
611         route_232_1_1_1.update_entry_flags(
612             MRouteEntryFlags.MFIB_ENTRY_FLAG_SIGNAL)
613
614         #
615         # Now the (*,G) is present, send from connected source
616         #
617         tx = self._mcast_connected_send_stream("232.1.1.1")
618
619         #
620         # Constrct a representation of the signal we expect on pg0
621         #
622         signal_232_1_1_1_itf_0 = VppMFibSignal(self,
623                                                route_232_1_1_1,
624                                                self.pg0.sw_if_index,
625                                                tx[0])
626
627         #
628         # read the only expected signal
629         #
630         signals = self.vapi.mfib_signal_dump()
631
632         self.assertEqual(1, len(signals))
633
634         signal_232_1_1_1_itf_0.compare(signals[0])
635
636         #
637         # reading the signal allows for the generation of another
638         # so send more packets and expect the next signal
639         #
640         tx = self._mcast_connected_send_stream("232.1.1.1")
641
642         signals = self.vapi.mfib_signal_dump()
643         self.assertEqual(1, len(signals))
644         signal_232_1_1_1_itf_0.compare(signals[0])
645
646         #
647         # Set the negate-signal on the accepting interval - the signals
648         # should stop
649         #
650         route_232_1_1_1.update_path_flags(
651             self.pg0.sw_if_index,
652             (MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT |
653              MRouteItfFlags.MFIB_ITF_FLAG_NEGATE_SIGNAL))
654
655         self.vapi.cli("clear trace")
656         tx = self._mcast_connected_send_stream("232.1.1.1")
657
658         signals = self.vapi.mfib_signal_dump()
659         self.assertEqual(0, len(signals))
660
661         #
662         # Clear the SIGNAL flag on the entry and the signals should
663         # come back since the interface is still NEGATE-SIGNAL
664         #
665         route_232_1_1_1.update_entry_flags(
666             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE)
667
668         tx = self._mcast_connected_send_stream("232.1.1.1")
669
670         signals = self.vapi.mfib_signal_dump()
671         self.assertEqual(1, len(signals))
672         signal_232_1_1_1_itf_0.compare(signals[0])
673
674         #
675         # Lastly remove the NEGATE-SIGNAL from the interface and the
676         # signals should stop
677         #
678         route_232_1_1_1.update_path_flags(self.pg0.sw_if_index,
679                                           MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT)
680
681         tx = self._mcast_connected_send_stream("232.1.1.1")
682         signals = self.vapi.mfib_signal_dump()
683         self.assertEqual(0, len(signals))
684
685     def test_ip_mcast_vrf(self):
686         """ IP Multicast Replication in non-default table"""
687
688         #
689         # An (S,G).
690         # one accepting interface, pg0, 2 forwarding interfaces
691         #
692         route_1_1_1_1_232_1_1_1 = VppIpMRoute(
693             self,
694             "1.1.1.1",
695             "232.1.1.1", 64,
696             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
697             [VppMRoutePath(self.pg8.sw_if_index,
698                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
699              VppMRoutePath(self.pg1.sw_if_index,
700                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
701              VppMRoutePath(self.pg2.sw_if_index,
702                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)],
703             table_id=10)
704         route_1_1_1_1_232_1_1_1.add_vpp_config()
705
706         #
707         # a stream that matches the route for (1.1.1.1,232.1.1.1)
708         #  small packets
709         #
710         self.vapi.cli("clear trace")
711         tx = self.create_stream_ip4(self.pg8, "1.1.1.1", "232.1.1.1")
712         self.pg8.add_stream(tx)
713
714         self.pg_enable_capture(self.pg_interfaces)
715         self.pg_start()
716
717         # We expect replications on Pg1 & 2
718         self.verify_capture_ip4(self.pg1, tx)
719         self.verify_capture_ip4(self.pg2, tx)
720
721     def test_ip6_mcast_vrf(self):
722         """ IPv6 Multicast Replication in non-default table"""
723
724         #
725         # An (S,G).
726         # one accepting interface, pg0, 2 forwarding interfaces
727         #
728         route_2001_ff01_1 = VppIpMRoute(
729             self,
730             "2001::1",
731             "ff01::1", 256,
732             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
733             [VppMRoutePath(self.pg8.sw_if_index,
734                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT,
735                            proto=DpoProto.DPO_PROTO_IP6),
736              VppMRoutePath(self.pg1.sw_if_index,
737                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
738                            proto=DpoProto.DPO_PROTO_IP6),
739              VppMRoutePath(self.pg2.sw_if_index,
740                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
741                            proto=DpoProto.DPO_PROTO_IP6)],
742             table_id=10,
743             is_ip6=1)
744         route_2001_ff01_1.add_vpp_config()
745
746         #
747         # a stream that matches the route for (2001::1, ff00::1)
748         #
749         self.vapi.cli("clear trace")
750         tx = self.create_stream_ip6(self.pg8, "2001::1", "ff01::1")
751         self.pg8.add_stream(tx)
752
753         self.pg_enable_capture(self.pg_interfaces)
754         self.pg_start()
755
756         # We expect replications on Pg1, 2,
757         self.verify_capture_ip6(self.pg1, tx)
758         self.verify_capture_ip6(self.pg2, tx)
759
760 if __name__ == '__main__':
761     unittest.main(testRunner=VppTestRunner)