FIB table add/delete API
[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
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         route_232_1_1_1.remove_vpp_config()
328         route_1_1_1_1_232_1_1_1.remove_vpp_config()
329         route_232.remove_vpp_config()
330
331     def test_ip6_mcast(self):
332         """ IPv6 Multicast Replication """
333
334         #
335         # a stream that matches the default route. gets dropped.
336         #
337         self.vapi.cli("clear trace")
338         tx = self.create_stream_ip6(self.pg0, "2001::1", "ff01::1")
339         self.pg0.add_stream(tx)
340
341         self.pg_enable_capture(self.pg_interfaces)
342         self.pg_start()
343
344         self.pg0.assert_nothing_captured(
345             remark="IPv6 multicast packets forwarded on default route")
346
347         #
348         # A (*,G).
349         # one accepting interface, pg0, 3 forwarding interfaces
350         #
351         route_ff01_1 = VppIpMRoute(
352             self,
353             "::",
354             "ff01::1", 128,
355             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
356             [VppMRoutePath(self.pg0.sw_if_index,
357                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
358              VppMRoutePath(self.pg1.sw_if_index,
359                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
360              VppMRoutePath(self.pg2.sw_if_index,
361                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
362              VppMRoutePath(self.pg3.sw_if_index,
363                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)],
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              VppMRoutePath(self.pg1.sw_if_index,
379                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
380              VppMRoutePath(self.pg2.sw_if_index,
381                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)],
382             is_ip6=1)
383         route_2001_ff01_1.add_vpp_config()
384
385         #
386         # An (*,G/m).
387         # one accepting interface, pg0, 1 forwarding interface
388         #
389         route_ff01 = VppIpMRoute(
390             self,
391             "::",
392             "ff01::", 16,
393             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
394             [VppMRoutePath(self.pg0.sw_if_index,
395                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
396              VppMRoutePath(self.pg1.sw_if_index,
397                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)],
398             is_ip6=1)
399         route_ff01.add_vpp_config()
400
401         #
402         # a stream that matches the route for (*, ff01::/16)
403         #
404         self.vapi.cli("clear trace")
405         tx = self.create_stream_ip6(self.pg0, "2002::1", "ff01:2::255")
406         self.pg0.add_stream(tx)
407
408         self.pg_enable_capture(self.pg_interfaces)
409         self.pg_start()
410
411         # We expect replications on Pg1
412         self.verify_capture_ip6(self.pg1, tx)
413
414         # no replications on Pg0, Pg3
415         self.pg0.assert_nothing_captured(
416             remark="IP multicast packets forwarded on PG0")
417         self.pg2.assert_nothing_captured(
418             remark="IP multicast packets forwarded on PG2")
419         self.pg3.assert_nothing_captured(
420             remark="IP multicast packets forwarded on PG3")
421
422         #
423         # Bounce the interface and it should still work
424         #
425         self.pg1.admin_down()
426         self.pg0.add_stream(tx)
427         self.pg_enable_capture(self.pg_interfaces)
428         self.pg_start()
429         self.pg1.assert_nothing_captured(
430             remark="IP multicast packets forwarded on down PG1")
431
432         self.pg1.admin_up()
433         self.pg0.add_stream(tx)
434         self.pg_enable_capture(self.pg_interfaces)
435         self.pg_start()
436         self.verify_capture_ip6(self.pg1, tx)
437
438         #
439         # a stream that matches the route for (*,ff01::1)
440         #
441         self.vapi.cli("clear trace")
442         tx = self.create_stream_ip6(self.pg0, "2002::2", "ff01::1")
443         self.pg0.add_stream(tx)
444
445         self.pg_enable_capture(self.pg_interfaces)
446         self.pg_start()
447
448         # We expect replications on Pg1, 2, 3.
449         self.verify_capture_ip6(self.pg1, tx)
450         self.verify_capture_ip6(self.pg2, tx)
451         self.verify_capture_ip6(self.pg3, tx)
452
453         # no replications on Pg0
454         self.pg0.assert_nothing_captured(
455             remark="IPv6 multicast packets forwarded on PG0")
456
457         #
458         # a stream that matches the route for (2001::1, ff00::1)
459         #
460         self.vapi.cli("clear trace")
461         tx = self.create_stream_ip6(self.pg0, "2001::1", "ff01::1")
462         self.pg0.add_stream(tx)
463
464         self.pg_enable_capture(self.pg_interfaces)
465         self.pg_start()
466
467         # We expect replications on Pg1, 2,
468         self.verify_capture_ip6(self.pg1, tx)
469         self.verify_capture_ip6(self.pg2, tx)
470
471         # no replications on Pg0, Pg3
472         self.pg0.assert_nothing_captured(
473             remark="IP multicast packets forwarded on PG0")
474         self.pg3.assert_nothing_captured(
475             remark="IP multicast packets forwarded on PG3")
476
477         route_ff01.remove_vpp_config()
478         route_ff01_1.remove_vpp_config()
479         route_2001_ff01_1.remove_vpp_config()
480
481     def _mcast_connected_send_stream(self, dst_ip):
482         self.vapi.cli("clear trace")
483         tx = self.create_stream_ip4(self.pg0,
484                                     self.pg0.remote_ip4,
485                                     dst_ip)
486         self.pg0.add_stream(tx)
487
488         self.pg_enable_capture(self.pg_interfaces)
489         self.pg_start()
490
491         # We expect replications on Pg1.
492         self.verify_capture_ip4(self.pg1, tx)
493
494         return tx
495
496     def test_ip_mcast_connected(self):
497         """ IP Multicast Connected Source check """
498
499         #
500         # A (*,G).
501         # one accepting interface, pg0, 1 forwarding interfaces
502         #
503         route_232_1_1_1 = VppIpMRoute(
504             self,
505             "0.0.0.0",
506             "232.1.1.1", 32,
507             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
508             [VppMRoutePath(self.pg0.sw_if_index,
509                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
510              VppMRoutePath(self.pg1.sw_if_index,
511                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
512
513         route_232_1_1_1.add_vpp_config()
514         route_232_1_1_1.update_entry_flags(
515             MRouteEntryFlags.MFIB_ENTRY_FLAG_CONNECTED)
516
517         #
518         # Now the (*,G) is present, send from connected source
519         #
520         tx = self._mcast_connected_send_stream("232.1.1.1")
521
522         #
523         # Constrct a representation of the signal we expect on pg0
524         #
525         signal_232_1_1_1_itf_0 = VppMFibSignal(self,
526                                                route_232_1_1_1,
527                                                self.pg0.sw_if_index,
528                                                tx[0])
529
530         #
531         # read the only expected signal
532         #
533         signals = self.vapi.mfib_signal_dump()
534
535         self.assertEqual(1, len(signals))
536
537         signal_232_1_1_1_itf_0.compare(signals[0])
538
539         #
540         # reading the signal allows for the generation of another
541         # so send more packets and expect the next signal
542         #
543         tx = self._mcast_connected_send_stream("232.1.1.1")
544
545         signals = self.vapi.mfib_signal_dump()
546         self.assertEqual(1, len(signals))
547         signal_232_1_1_1_itf_0.compare(signals[0])
548
549         #
550         # A Second entry with connected check
551         # one accepting interface, pg0, 1 forwarding interfaces
552         #
553         route_232_1_1_2 = VppIpMRoute(
554             self,
555             "0.0.0.0",
556             "232.1.1.2", 32,
557             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
558             [VppMRoutePath(self.pg0.sw_if_index,
559                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
560              VppMRoutePath(self.pg1.sw_if_index,
561                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
562
563         route_232_1_1_2.add_vpp_config()
564         route_232_1_1_2.update_entry_flags(
565             MRouteEntryFlags.MFIB_ENTRY_FLAG_CONNECTED)
566
567         #
568         # Send traffic to both entries. One read should net us two signals
569         #
570         signal_232_1_1_2_itf_0 = VppMFibSignal(self,
571                                                route_232_1_1_2,
572                                                self.pg0.sw_if_index,
573                                                tx[0])
574         tx = self._mcast_connected_send_stream("232.1.1.1")
575         tx2 = self._mcast_connected_send_stream("232.1.1.2")
576
577         #
578         # read the only expected signal
579         #
580         signals = self.vapi.mfib_signal_dump()
581
582         self.assertEqual(2, len(signals))
583
584         signal_232_1_1_1_itf_0.compare(signals[1])
585         signal_232_1_1_2_itf_0.compare(signals[0])
586
587         route_232_1_1_1.remove_vpp_config()
588         route_232_1_1_2.remove_vpp_config()
589
590     def test_ip_mcast_signal(self):
591         """ IP Multicast Signal """
592
593         #
594         # A (*,G).
595         # one accepting interface, pg0, 1 forwarding interfaces
596         #
597         route_232_1_1_1 = VppIpMRoute(
598             self,
599             "0.0.0.0",
600             "232.1.1.1", 32,
601             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
602             [VppMRoutePath(self.pg0.sw_if_index,
603                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
604              VppMRoutePath(self.pg1.sw_if_index,
605                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
606
607         route_232_1_1_1.add_vpp_config()
608         route_232_1_1_1.update_entry_flags(
609             MRouteEntryFlags.MFIB_ENTRY_FLAG_SIGNAL)
610
611         #
612         # Now the (*,G) is present, send from connected source
613         #
614         tx = self._mcast_connected_send_stream("232.1.1.1")
615
616         #
617         # Constrct a representation of the signal we expect on pg0
618         #
619         signal_232_1_1_1_itf_0 = VppMFibSignal(self,
620                                                route_232_1_1_1,
621                                                self.pg0.sw_if_index,
622                                                tx[0])
623
624         #
625         # read the only expected signal
626         #
627         signals = self.vapi.mfib_signal_dump()
628
629         self.assertEqual(1, len(signals))
630
631         signal_232_1_1_1_itf_0.compare(signals[0])
632
633         #
634         # reading the signal allows for the generation of another
635         # so send more packets and expect the next signal
636         #
637         tx = self._mcast_connected_send_stream("232.1.1.1")
638
639         signals = self.vapi.mfib_signal_dump()
640         self.assertEqual(1, len(signals))
641         signal_232_1_1_1_itf_0.compare(signals[0])
642
643         #
644         # Set the negate-signal on the accepting interval - the signals
645         # should stop
646         #
647         route_232_1_1_1.update_path_flags(
648             self.pg0.sw_if_index,
649             (MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT |
650              MRouteItfFlags.MFIB_ITF_FLAG_NEGATE_SIGNAL))
651
652         self.vapi.cli("clear trace")
653         tx = self._mcast_connected_send_stream("232.1.1.1")
654
655         signals = self.vapi.mfib_signal_dump()
656         self.assertEqual(0, len(signals))
657
658         #
659         # Clear the SIGNAL flag on the entry and the signals should
660         # come back since the interface is still NEGATE-SIGNAL
661         #
662         route_232_1_1_1.update_entry_flags(
663             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE)
664
665         tx = self._mcast_connected_send_stream("232.1.1.1")
666
667         signals = self.vapi.mfib_signal_dump()
668         self.assertEqual(1, len(signals))
669         signal_232_1_1_1_itf_0.compare(signals[0])
670
671         #
672         # Lastly remove the NEGATE-SIGNAL from the interface and the
673         # signals should stop
674         #
675         route_232_1_1_1.update_path_flags(self.pg0.sw_if_index,
676                                           MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT)
677
678         tx = self._mcast_connected_send_stream("232.1.1.1")
679         signals = self.vapi.mfib_signal_dump()
680         self.assertEqual(0, len(signals))
681
682         #
683         # Cleanup
684         #
685         route_232_1_1_1.remove_vpp_config()
686
687     def test_ip_mcast_vrf(self):
688         """ IP Multicast Replication in non-default table"""
689
690         #
691         # An (S,G).
692         # one accepting interface, pg0, 2 forwarding interfaces
693         #
694         route_1_1_1_1_232_1_1_1 = VppIpMRoute(
695             self,
696             "1.1.1.1",
697             "232.1.1.1", 64,
698             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
699             [VppMRoutePath(self.pg8.sw_if_index,
700                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
701              VppMRoutePath(self.pg1.sw_if_index,
702                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
703              VppMRoutePath(self.pg2.sw_if_index,
704                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)],
705             table_id=10)
706         route_1_1_1_1_232_1_1_1.add_vpp_config()
707
708         #
709         # a stream that matches the route for (1.1.1.1,232.1.1.1)
710         #  small packets
711         #
712         self.vapi.cli("clear trace")
713         tx = self.create_stream_ip4(self.pg8, "1.1.1.1", "232.1.1.1")
714         self.pg8.add_stream(tx)
715
716         self.pg_enable_capture(self.pg_interfaces)
717         self.pg_start()
718
719         # We expect replications on Pg1 & 2
720         self.verify_capture_ip4(self.pg1, tx)
721         self.verify_capture_ip4(self.pg2, tx)
722
723     def test_ip6_mcast_vrf(self):
724         """ IPv6 Multicast Replication in non-default table"""
725
726         #
727         # An (S,G).
728         # one accepting interface, pg0, 2 forwarding interfaces
729         #
730         route_2001_ff01_1 = VppIpMRoute(
731             self,
732             "2001::1",
733             "ff01::1", 256,
734             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
735             [VppMRoutePath(self.pg8.sw_if_index,
736                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
737              VppMRoutePath(self.pg1.sw_if_index,
738                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
739              VppMRoutePath(self.pg2.sw_if_index,
740                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)],
741             table_id=10,
742             is_ip6=1)
743         route_2001_ff01_1.add_vpp_config()
744
745         #
746         # a stream that matches the route for (2001::1, ff00::1)
747         #
748         self.vapi.cli("clear trace")
749         tx = self.create_stream_ip6(self.pg8, "2001::1", "ff01::1")
750         self.pg8.add_stream(tx)
751
752         self.pg_enable_capture(self.pg_interfaces)
753         self.pg_start()
754
755         # We expect replications on Pg1, 2,
756         self.verify_capture_ip6(self.pg1, tx)
757         self.verify_capture_ip6(self.pg2, tx)
758
759 if __name__ == '__main__':
760     unittest.main(testRunner=VppTestRunner)