IP bi-direction multicast - same cable check on egress
[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         #
410         self.vapi.cli("clear trace")
411         tx = self.create_stream_ip6(self.pg0, "2002::1", "ff01:2::255")
412         self.pg0.add_stream(tx)
413
414         self.pg_enable_capture(self.pg_interfaces)
415         self.pg_start()
416
417         # We expect replications on Pg1
418         self.verify_capture_ip6(self.pg1, tx)
419
420         # no replications on Pg0, Pg3
421         self.pg0.assert_nothing_captured(
422             remark="IP multicast packets forwarded on PG0")
423         self.pg2.assert_nothing_captured(
424             remark="IP multicast packets forwarded on PG2")
425         self.pg3.assert_nothing_captured(
426             remark="IP multicast packets forwarded on PG3")
427
428         #
429         # Bounce the interface and it should still work
430         #
431         self.pg1.admin_down()
432         self.pg0.add_stream(tx)
433         self.pg_enable_capture(self.pg_interfaces)
434         self.pg_start()
435         self.pg1.assert_nothing_captured(
436             remark="IP multicast packets forwarded on down PG1")
437
438         self.pg1.admin_up()
439         self.pg0.add_stream(tx)
440         self.pg_enable_capture(self.pg_interfaces)
441         self.pg_start()
442         self.verify_capture_ip6(self.pg1, tx)
443
444         #
445         # a stream that matches the route for (*,ff01::1)
446         #
447         self.vapi.cli("clear trace")
448         tx = self.create_stream_ip6(self.pg0, "2002::2", "ff01::1")
449         self.pg0.add_stream(tx)
450
451         self.pg_enable_capture(self.pg_interfaces)
452         self.pg_start()
453
454         # We expect replications on Pg1, 2, 3.
455         self.verify_capture_ip6(self.pg1, tx)
456         self.verify_capture_ip6(self.pg2, tx)
457         self.verify_capture_ip6(self.pg3, tx)
458
459         # no replications on Pg0
460         self.pg0.assert_nothing_captured(
461             remark="IPv6 multicast packets forwarded on PG0")
462
463         #
464         # a stream that matches the route for (2001::1, ff00::1)
465         #
466         self.vapi.cli("clear trace")
467         tx = self.create_stream_ip6(self.pg0, "2001::1", "ff01::1")
468         self.pg0.add_stream(tx)
469
470         self.pg_enable_capture(self.pg_interfaces)
471         self.pg_start()
472
473         # We expect replications on Pg1, 2,
474         self.verify_capture_ip6(self.pg1, tx)
475         self.verify_capture_ip6(self.pg2, tx)
476
477         # no replications on Pg0, Pg3
478         self.pg0.assert_nothing_captured(
479             remark="IP multicast packets forwarded on PG0")
480         self.pg3.assert_nothing_captured(
481             remark="IP multicast packets forwarded on PG3")
482
483     def _mcast_connected_send_stream(self, dst_ip):
484         self.vapi.cli("clear trace")
485         tx = self.create_stream_ip4(self.pg0,
486                                     self.pg0.remote_ip4,
487                                     dst_ip)
488         self.pg0.add_stream(tx)
489
490         self.pg_enable_capture(self.pg_interfaces)
491         self.pg_start()
492
493         # We expect replications on Pg1.
494         self.verify_capture_ip4(self.pg1, tx)
495
496         return tx
497
498     def test_ip_mcast_connected(self):
499         """ IP Multicast Connected Source check """
500
501         #
502         # A (*,G).
503         # one accepting interface, pg0, 1 forwarding interfaces
504         #
505         route_232_1_1_1 = VppIpMRoute(
506             self,
507             "0.0.0.0",
508             "232.1.1.1", 32,
509             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
510             [VppMRoutePath(self.pg0.sw_if_index,
511                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
512              VppMRoutePath(self.pg1.sw_if_index,
513                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
514
515         route_232_1_1_1.add_vpp_config()
516         route_232_1_1_1.update_entry_flags(
517             MRouteEntryFlags.MFIB_ENTRY_FLAG_CONNECTED)
518
519         #
520         # Now the (*,G) is present, send from connected source
521         #
522         tx = self._mcast_connected_send_stream("232.1.1.1")
523
524         #
525         # Constrct a representation of the signal we expect on pg0
526         #
527         signal_232_1_1_1_itf_0 = VppMFibSignal(self,
528                                                route_232_1_1_1,
529                                                self.pg0.sw_if_index,
530                                                tx[0])
531
532         #
533         # read the only expected signal
534         #
535         signals = self.vapi.mfib_signal_dump()
536
537         self.assertEqual(1, len(signals))
538
539         signal_232_1_1_1_itf_0.compare(signals[0])
540
541         #
542         # reading the signal allows for the generation of another
543         # so send more packets and expect the next signal
544         #
545         tx = self._mcast_connected_send_stream("232.1.1.1")
546
547         signals = self.vapi.mfib_signal_dump()
548         self.assertEqual(1, len(signals))
549         signal_232_1_1_1_itf_0.compare(signals[0])
550
551         #
552         # A Second entry with connected check
553         # one accepting interface, pg0, 1 forwarding interfaces
554         #
555         route_232_1_1_2 = VppIpMRoute(
556             self,
557             "0.0.0.0",
558             "232.1.1.2", 32,
559             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
560             [VppMRoutePath(self.pg0.sw_if_index,
561                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
562              VppMRoutePath(self.pg1.sw_if_index,
563                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
564
565         route_232_1_1_2.add_vpp_config()
566         route_232_1_1_2.update_entry_flags(
567             MRouteEntryFlags.MFIB_ENTRY_FLAG_CONNECTED)
568
569         #
570         # Send traffic to both entries. One read should net us two signals
571         #
572         signal_232_1_1_2_itf_0 = VppMFibSignal(self,
573                                                route_232_1_1_2,
574                                                self.pg0.sw_if_index,
575                                                tx[0])
576         tx = self._mcast_connected_send_stream("232.1.1.1")
577         tx2 = self._mcast_connected_send_stream("232.1.1.2")
578
579         #
580         # read the only expected signal
581         #
582         signals = self.vapi.mfib_signal_dump()
583
584         self.assertEqual(2, len(signals))
585
586         signal_232_1_1_1_itf_0.compare(signals[1])
587         signal_232_1_1_2_itf_0.compare(signals[0])
588
589         route_232_1_1_1.update_entry_flags(
590             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE)
591         route_232_1_1_2.update_entry_flags(
592             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE)
593
594     def test_ip_mcast_signal(self):
595         """ IP Multicast Signal """
596
597         #
598         # A (*,G).
599         # one accepting interface, pg0, 1 forwarding interfaces
600         #
601         route_232_1_1_1 = VppIpMRoute(
602             self,
603             "0.0.0.0",
604             "232.1.1.1", 32,
605             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
606             [VppMRoutePath(self.pg0.sw_if_index,
607                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
608              VppMRoutePath(self.pg1.sw_if_index,
609                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
610
611         route_232_1_1_1.add_vpp_config()
612         route_232_1_1_1.update_entry_flags(
613             MRouteEntryFlags.MFIB_ENTRY_FLAG_SIGNAL)
614
615         #
616         # Now the (*,G) is present, send from connected source
617         #
618         tx = self._mcast_connected_send_stream("232.1.1.1")
619
620         #
621         # Constrct a representation of the signal we expect on pg0
622         #
623         signal_232_1_1_1_itf_0 = VppMFibSignal(self,
624                                                route_232_1_1_1,
625                                                self.pg0.sw_if_index,
626                                                tx[0])
627
628         #
629         # read the only expected signal
630         #
631         signals = self.vapi.mfib_signal_dump()
632
633         self.assertEqual(1, len(signals))
634
635         signal_232_1_1_1_itf_0.compare(signals[0])
636
637         #
638         # reading the signal allows for the generation of another
639         # so send more packets and expect the next signal
640         #
641         tx = self._mcast_connected_send_stream("232.1.1.1")
642
643         signals = self.vapi.mfib_signal_dump()
644         self.assertEqual(1, len(signals))
645         signal_232_1_1_1_itf_0.compare(signals[0])
646
647         #
648         # Set the negate-signal on the accepting interval - the signals
649         # should stop
650         #
651         route_232_1_1_1.update_path_flags(
652             self.pg0.sw_if_index,
653             (MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT |
654              MRouteItfFlags.MFIB_ITF_FLAG_NEGATE_SIGNAL))
655
656         self.vapi.cli("clear trace")
657         tx = self._mcast_connected_send_stream("232.1.1.1")
658
659         signals = self.vapi.mfib_signal_dump()
660         self.assertEqual(0, len(signals))
661
662         #
663         # Clear the SIGNAL flag on the entry and the signals should
664         # come back since the interface is still NEGATE-SIGNAL
665         #
666         route_232_1_1_1.update_entry_flags(
667             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE)
668
669         tx = self._mcast_connected_send_stream("232.1.1.1")
670
671         signals = self.vapi.mfib_signal_dump()
672         self.assertEqual(1, len(signals))
673         signal_232_1_1_1_itf_0.compare(signals[0])
674
675         #
676         # Lastly remove the NEGATE-SIGNAL from the interface and the
677         # signals should stop
678         #
679         route_232_1_1_1.update_path_flags(self.pg0.sw_if_index,
680                                           MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT)
681
682         tx = self._mcast_connected_send_stream("232.1.1.1")
683         signals = self.vapi.mfib_signal_dump()
684         self.assertEqual(0, len(signals))
685
686     def test_ip_mcast_vrf(self):
687         """ IP Multicast Replication in non-default table"""
688
689         #
690         # An (S,G).
691         # one accepting interface, pg0, 2 forwarding interfaces
692         #
693         route_1_1_1_1_232_1_1_1 = VppIpMRoute(
694             self,
695             "1.1.1.1",
696             "232.1.1.1", 64,
697             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
698             [VppMRoutePath(self.pg8.sw_if_index,
699                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
700              VppMRoutePath(self.pg1.sw_if_index,
701                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
702              VppMRoutePath(self.pg2.sw_if_index,
703                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)],
704             table_id=10)
705         route_1_1_1_1_232_1_1_1.add_vpp_config()
706
707         #
708         # a stream that matches the route for (1.1.1.1,232.1.1.1)
709         #  small packets
710         #
711         self.vapi.cli("clear trace")
712         tx = self.create_stream_ip4(self.pg8, "1.1.1.1", "232.1.1.1")
713         self.pg8.add_stream(tx)
714
715         self.pg_enable_capture(self.pg_interfaces)
716         self.pg_start()
717
718         # We expect replications on Pg1 & 2
719         self.verify_capture_ip4(self.pg1, tx)
720         self.verify_capture_ip4(self.pg2, tx)
721
722     def test_ip6_mcast_vrf(self):
723         """ IPv6 Multicast Replication in non-default table"""
724
725         #
726         # An (S,G).
727         # one accepting interface, pg0, 2 forwarding interfaces
728         #
729         route_2001_ff01_1 = VppIpMRoute(
730             self,
731             "2001::1",
732             "ff01::1", 256,
733             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
734             [VppMRoutePath(self.pg8.sw_if_index,
735                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT,
736                            proto=DpoProto.DPO_PROTO_IP6),
737              VppMRoutePath(self.pg1.sw_if_index,
738                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
739                            proto=DpoProto.DPO_PROTO_IP6),
740              VppMRoutePath(self.pg2.sw_if_index,
741                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
742                            proto=DpoProto.DPO_PROTO_IP6)],
743             table_id=10,
744             is_ip6=1)
745         route_2001_ff01_1.add_vpp_config()
746
747         #
748         # a stream that matches the route for (2001::1, ff00::1)
749         #
750         self.vapi.cli("clear trace")
751         tx = self.create_stream_ip6(self.pg8, "2001::1", "ff01::1")
752         self.pg8.add_stream(tx)
753
754         self.pg_enable_capture(self.pg_interfaces)
755         self.pg_start()
756
757         # We expect replications on Pg1, 2,
758         self.verify_capture_ip6(self.pg1, tx)
759         self.verify_capture_ip6(self.pg2, tx)
760
761     def test_bidir(self):
762         """ IP Multicast Bi-directional """
763
764         #
765         # A (*,G). The set of accepting interfaces matching the forwarding
766         #
767         route_232_1_1_1 = VppIpMRoute(
768             self,
769             "0.0.0.0",
770             "232.1.1.1", 32,
771             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
772             [VppMRoutePath(self.pg0.sw_if_index,
773                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT |
774                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
775              VppMRoutePath(self.pg1.sw_if_index,
776                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT |
777                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
778              VppMRoutePath(self.pg2.sw_if_index,
779                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT |
780                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
781              VppMRoutePath(self.pg3.sw_if_index,
782                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT |
783                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
784         route_232_1_1_1.add_vpp_config()
785
786         tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "232.1.1.1")
787         self.pg0.add_stream(tx)
788
789         self.pg_enable_capture(self.pg_interfaces)
790         self.pg_start()
791
792         # We expect replications on Pg1, 2, 3, but not on pg0
793         self.verify_capture_ip4(self.pg1, tx)
794         self.verify_capture_ip4(self.pg2, tx)
795         self.verify_capture_ip4(self.pg3, tx)
796         self.pg0.assert_nothing_captured(
797             remark="IP multicast packets forwarded on PG0")
798
799
800 if __name__ == '__main__':
801     unittest.main(testRunner=VppTestRunner)