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