GBP Endpoint Updates
[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_ip import DpoProto
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
13 from scapy.layers.inet6 import IPv6, getmacbyip6
14
15 #
16 # The number of packets sent is set to 91 so that when we replicate more than 3
17 # times, which we do for some entries, we will generate more than 256 packets
18 # to the next node in the VLIB graph. Thus we are testing the code's
19 # correctness handling this over-flow.
20 # It's also an odd number so we hit any single loops.
21 #
22 N_PKTS_IN_STREAM = 91
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, dst_mac=None):
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                 if dst_mac is None:
132                     dst_mac = getmacbyip(rx_ip.dst)
133
134                 # check the MAC address on the RX'd packet is correctly formed
135                 self.assertEqual(eth.dst, dst_mac)
136
137                 self.assertEqual(rx_ip.src, tx_ip.src)
138                 self.assertEqual(rx_ip.dst, tx_ip.dst)
139                 # IP processing post pop has decremented the TTL
140                 self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl)
141
142         except:
143             raise
144
145     def verify_capture_ip6(self, rx_if, sent):
146         capture = rx_if.get_capture(len(sent))
147
148         self.assertEqual(len(capture), len(sent))
149
150         for i in range(len(capture)):
151             tx = sent[i]
152             rx = capture[i]
153
154             eth = rx[Ether]
155             self.assertEqual(eth.type, 0x86DD)
156
157             tx_ip = tx[IPv6]
158             rx_ip = rx[IPv6]
159
160             # check the MAC address on the RX'd packet is correctly formed
161             self.assertEqual(eth.dst, getmacbyip6(rx_ip.dst))
162
163             self.assertEqual(rx_ip.src, tx_ip.src)
164             self.assertEqual(rx_ip.dst, tx_ip.dst)
165             # IP processing post pop has decremented the TTL
166             self.assertEqual(rx_ip.hlim + 1, tx_ip.hlim)
167
168     def test_ip_mcast(self):
169         """ IP Multicast Replication """
170
171         #
172         # a stream that matches the default route. gets dropped.
173         #
174         self.vapi.cli("clear trace")
175         tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "232.1.1.1")
176         self.pg0.add_stream(tx)
177
178         self.pg_enable_capture(self.pg_interfaces)
179         self.pg_start()
180
181         self.pg0.assert_nothing_captured(
182             remark="IP multicast packets forwarded on default route")
183
184         #
185         # A (*,G).
186         # one accepting interface, pg0, 7 forwarding interfaces
187         #  many forwarding interfaces test the case where the replicare DPO
188         #  needs to use extra cache lines for the buckets.
189         #
190         route_232_1_1_1 = VppIpMRoute(
191             self,
192             "0.0.0.0",
193             "232.1.1.1", 32,
194             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
195             [VppMRoutePath(self.pg0.sw_if_index,
196                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
197              VppMRoutePath(self.pg1.sw_if_index,
198                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
199              VppMRoutePath(self.pg2.sw_if_index,
200                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
201              VppMRoutePath(self.pg3.sw_if_index,
202                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
203              VppMRoutePath(self.pg4.sw_if_index,
204                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
205              VppMRoutePath(self.pg5.sw_if_index,
206                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
207              VppMRoutePath(self.pg6.sw_if_index,
208                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
209              VppMRoutePath(self.pg7.sw_if_index,
210                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
211         route_232_1_1_1.add_vpp_config()
212
213         #
214         # An (S,G).
215         # one accepting interface, pg0, 2 forwarding interfaces
216         #
217         route_1_1_1_1_232_1_1_1 = VppIpMRoute(
218             self,
219             "1.1.1.1",
220             "232.1.1.1", 64,
221             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
222             [VppMRoutePath(self.pg0.sw_if_index,
223                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
224              VppMRoutePath(self.pg1.sw_if_index,
225                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
226              VppMRoutePath(self.pg2.sw_if_index,
227                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
228         route_1_1_1_1_232_1_1_1.add_vpp_config()
229
230         #
231         # An (S,G).
232         # one accepting interface, pg0, 2 forwarding interfaces
233         # that use unicast next-hops
234         #
235         route_1_1_1_1_232_1_1_2 = VppIpMRoute(
236             self,
237             "1.1.1.1",
238             "232.1.1.2", 64,
239             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
240             [VppMRoutePath(self.pg0.sw_if_index,
241                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
242              VppMRoutePath(self.pg1.sw_if_index,
243                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
244                            nh=self.pg1.remote_ip4),
245              VppMRoutePath(self.pg2.sw_if_index,
246                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
247                            nh=self.pg2.remote_ip4)])
248         route_1_1_1_1_232_1_1_2.add_vpp_config()
249
250         #
251         # An (*,G/m).
252         # one accepting interface, pg0, 1 forwarding interfaces
253         #
254         route_232 = VppIpMRoute(
255             self,
256             "0.0.0.0",
257             "232.0.0.0", 8,
258             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
259             [VppMRoutePath(self.pg0.sw_if_index,
260                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
261              VppMRoutePath(self.pg1.sw_if_index,
262                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
263         route_232.add_vpp_config()
264
265         #
266         # a stream that matches the route for (1.1.1.1,232.1.1.1)
267         #  small packets
268         #
269         self.vapi.cli("clear trace")
270         tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "232.1.1.1")
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 (1.1.1.1,232.1.1.1)
288         #  large packets
289         #
290         self.vapi.cli("clear trace")
291         tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "232.1.1.1",
292                                     payload_size=1024)
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->7
299         self.verify_capture_ip4(self.pg1, tx)
300         self.verify_capture_ip4(self.pg2, tx)
301
302         # no replications on Pg0
303         self.pg0.assert_nothing_captured(
304             remark="IP multicast packets forwarded on PG0")
305         self.pg3.assert_nothing_captured(
306             remark="IP multicast packets forwarded on PG3")
307
308         #
309         # a stream to the unicast next-hops
310         #
311         self.vapi.cli("clear trace")
312         tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "232.1.1.2")
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->7
319         self.verify_capture_ip4(self.pg1, tx, dst_mac=self.pg1.remote_mac)
320         self.verify_capture_ip4(self.pg2, tx, dst_mac=self.pg2.remote_mac)
321
322         # no replications on Pg0 nor pg3
323         self.pg0.assert_nothing_captured(
324             remark="IP multicast packets forwarded on PG0")
325         self.pg3.assert_nothing_captured(
326             remark="IP multicast packets forwarded on PG3")
327
328         #
329         # a stream that matches the route for (*,232.0.0.0/8)
330         # Send packets with the 9th bit set so we test the correct clearing
331         # of that bit in the mac rewrite
332         #
333         self.vapi.cli("clear trace")
334         tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "232.255.255.255")
335         self.pg0.add_stream(tx)
336
337         self.pg_enable_capture(self.pg_interfaces)
338         self.pg_start()
339
340         # We expect replications on Pg1 only
341         self.verify_capture_ip4(self.pg1, tx)
342
343         # no replications on Pg0, Pg2 not Pg3
344         self.pg0.assert_nothing_captured(
345             remark="IP multicast packets forwarded on PG0")
346         self.pg2.assert_nothing_captured(
347             remark="IP multicast packets forwarded on PG2")
348         self.pg3.assert_nothing_captured(
349             remark="IP multicast packets forwarded on PG3")
350
351         #
352         # a stream that matches the route for (*,232.1.1.1)
353         #
354         self.vapi.cli("clear trace")
355         tx = self.create_stream_ip4(self.pg0, "1.1.1.2", "232.1.1.1")
356         self.pg0.add_stream(tx)
357
358         self.pg_enable_capture(self.pg_interfaces)
359         self.pg_start()
360
361         # We expect replications on Pg1->7
362         self.verify_capture_ip4(self.pg1, tx)
363         self.verify_capture_ip4(self.pg2, tx)
364         self.verify_capture_ip4(self.pg3, tx)
365         self.verify_capture_ip4(self.pg4, tx)
366         self.verify_capture_ip4(self.pg5, tx)
367         self.verify_capture_ip4(self.pg6, tx)
368         self.verify_capture_ip4(self.pg7, tx)
369
370         # no replications on Pg0
371         self.pg0.assert_nothing_captured(
372             remark="IP multicast packets forwarded on PG0")
373
374     def test_ip6_mcast(self):
375         """ IPv6 Multicast Replication """
376
377         #
378         # a stream that matches the default route. gets dropped.
379         #
380         self.vapi.cli("clear trace")
381         tx = self.create_stream_ip6(self.pg0, "2001::1", "ff01::1")
382         self.pg0.add_stream(tx)
383
384         self.pg_enable_capture(self.pg_interfaces)
385         self.pg_start()
386
387         self.pg0.assert_nothing_captured(
388             remark="IPv6 multicast packets forwarded on default route")
389
390         #
391         # A (*,G).
392         # one accepting interface, pg0, 3 forwarding interfaces
393         #
394         route_ff01_1 = VppIpMRoute(
395             self,
396             "::",
397             "ff01::1", 128,
398             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
399             [VppMRoutePath(self.pg0.sw_if_index,
400                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT,
401                            proto=DpoProto.DPO_PROTO_IP6),
402              VppMRoutePath(self.pg1.sw_if_index,
403                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
404                            proto=DpoProto.DPO_PROTO_IP6),
405              VppMRoutePath(self.pg2.sw_if_index,
406                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
407                            proto=DpoProto.DPO_PROTO_IP6),
408              VppMRoutePath(self.pg3.sw_if_index,
409                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
410                            proto=DpoProto.DPO_PROTO_IP6)],
411             is_ip6=1)
412         route_ff01_1.add_vpp_config()
413
414         #
415         # An (S,G).
416         # one accepting interface, pg0, 2 forwarding interfaces
417         #
418         route_2001_ff01_1 = VppIpMRoute(
419             self,
420             "2001::1",
421             "ff01::1", 256,
422             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
423             [VppMRoutePath(self.pg0.sw_if_index,
424                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT,
425                            proto=DpoProto.DPO_PROTO_IP6),
426              VppMRoutePath(self.pg1.sw_if_index,
427                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
428                            proto=DpoProto.DPO_PROTO_IP6),
429              VppMRoutePath(self.pg2.sw_if_index,
430                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
431                            proto=DpoProto.DPO_PROTO_IP6)],
432             is_ip6=1)
433         route_2001_ff01_1.add_vpp_config()
434
435         #
436         # An (*,G/m).
437         # one accepting interface, pg0, 1 forwarding interface
438         #
439         route_ff01 = VppIpMRoute(
440             self,
441             "::",
442             "ff01::", 16,
443             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
444             [VppMRoutePath(self.pg0.sw_if_index,
445                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT,
446                            proto=DpoProto.DPO_PROTO_IP6),
447              VppMRoutePath(self.pg1.sw_if_index,
448                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
449                            proto=DpoProto.DPO_PROTO_IP6)],
450             is_ip6=1)
451         route_ff01.add_vpp_config()
452
453         #
454         # a stream that matches the route for (*, ff01::/16)
455         # sent on the non-accepting interface
456         #
457         self.vapi.cli("clear trace")
458         tx = self.create_stream_ip6(self.pg1, "2002::1", "ff01:2::255")
459         self.send_and_assert_no_replies(self.pg1, tx, "RPF miss")
460
461         #
462         # a stream that matches the route for (*, ff01::/16)
463         # sent on the accepting interface
464         #
465         self.vapi.cli("clear trace")
466         tx = self.create_stream_ip6(self.pg0, "2002::1", "ff01:2::255")
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
473         self.verify_capture_ip6(self.pg1, tx)
474
475         # no replications on Pg0, Pg3
476         self.pg0.assert_nothing_captured(
477             remark="IP multicast packets forwarded on PG0")
478         self.pg2.assert_nothing_captured(
479             remark="IP multicast packets forwarded on PG2")
480         self.pg3.assert_nothing_captured(
481             remark="IP multicast packets forwarded on PG3")
482
483         #
484         # Bounce the interface and it should still work
485         #
486         self.pg1.admin_down()
487         self.pg0.add_stream(tx)
488         self.pg_enable_capture(self.pg_interfaces)
489         self.pg_start()
490         self.pg1.assert_nothing_captured(
491             remark="IP multicast packets forwarded on down PG1")
492
493         self.pg1.admin_up()
494         self.pg0.add_stream(tx)
495         self.pg_enable_capture(self.pg_interfaces)
496         self.pg_start()
497         self.verify_capture_ip6(self.pg1, tx)
498
499         #
500         # a stream that matches the route for (*,ff01::1)
501         #
502         self.vapi.cli("clear trace")
503         tx = self.create_stream_ip6(self.pg0, "2002::2", "ff01::1")
504         self.pg0.add_stream(tx)
505
506         self.pg_enable_capture(self.pg_interfaces)
507         self.pg_start()
508
509         # We expect replications on Pg1, 2, 3.
510         self.verify_capture_ip6(self.pg1, tx)
511         self.verify_capture_ip6(self.pg2, tx)
512         self.verify_capture_ip6(self.pg3, tx)
513
514         # no replications on Pg0
515         self.pg0.assert_nothing_captured(
516             remark="IPv6 multicast packets forwarded on PG0")
517
518         #
519         # a stream that matches the route for (2001::1, ff00::1)
520         #
521         self.vapi.cli("clear trace")
522         tx = self.create_stream_ip6(self.pg0, "2001::1", "ff01::1")
523         self.pg0.add_stream(tx)
524
525         self.pg_enable_capture(self.pg_interfaces)
526         self.pg_start()
527
528         # We expect replications on Pg1, 2,
529         self.verify_capture_ip6(self.pg1, tx)
530         self.verify_capture_ip6(self.pg2, tx)
531
532         # no replications on Pg0, Pg3
533         self.pg0.assert_nothing_captured(
534             remark="IP multicast packets forwarded on PG0")
535         self.pg3.assert_nothing_captured(
536             remark="IP multicast packets forwarded on PG3")
537
538     def _mcast_connected_send_stream(self, dst_ip):
539         self.vapi.cli("clear trace")
540         tx = self.create_stream_ip4(self.pg0,
541                                     self.pg0.remote_ip4,
542                                     dst_ip)
543         self.pg0.add_stream(tx)
544
545         self.pg_enable_capture(self.pg_interfaces)
546         self.pg_start()
547
548         # We expect replications on Pg1.
549         self.verify_capture_ip4(self.pg1, tx)
550
551         return tx
552
553     def test_ip_mcast_connected(self):
554         """ IP Multicast Connected Source check """
555
556         #
557         # A (*,G).
558         # one accepting interface, pg0, 1 forwarding interfaces
559         #
560         route_232_1_1_1 = VppIpMRoute(
561             self,
562             "0.0.0.0",
563             "232.1.1.1", 32,
564             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
565             [VppMRoutePath(self.pg0.sw_if_index,
566                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
567              VppMRoutePath(self.pg1.sw_if_index,
568                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
569
570         route_232_1_1_1.add_vpp_config()
571         route_232_1_1_1.update_entry_flags(
572             MRouteEntryFlags.MFIB_ENTRY_FLAG_CONNECTED)
573
574         #
575         # Now the (*,G) is present, send from connected source
576         #
577         tx = self._mcast_connected_send_stream("232.1.1.1")
578
579         #
580         # Constrct a representation of the signal we expect on pg0
581         #
582         signal_232_1_1_1_itf_0 = VppMFibSignal(self,
583                                                route_232_1_1_1,
584                                                self.pg0.sw_if_index,
585                                                tx[0])
586
587         #
588         # read the only expected signal
589         #
590         signals = self.vapi.mfib_signal_dump()
591
592         self.assertEqual(1, len(signals))
593
594         signal_232_1_1_1_itf_0.compare(signals[0])
595
596         #
597         # reading the signal allows for the generation of another
598         # so send more packets and expect the next signal
599         #
600         tx = self._mcast_connected_send_stream("232.1.1.1")
601
602         signals = self.vapi.mfib_signal_dump()
603         self.assertEqual(1, len(signals))
604         signal_232_1_1_1_itf_0.compare(signals[0])
605
606         #
607         # A Second entry with connected check
608         # one accepting interface, pg0, 1 forwarding interfaces
609         #
610         route_232_1_1_2 = VppIpMRoute(
611             self,
612             "0.0.0.0",
613             "232.1.1.2", 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_2.add_vpp_config()
621         route_232_1_1_2.update_entry_flags(
622             MRouteEntryFlags.MFIB_ENTRY_FLAG_CONNECTED)
623
624         #
625         # Send traffic to both entries. One read should net us two signals
626         #
627         signal_232_1_1_2_itf_0 = VppMFibSignal(self,
628                                                route_232_1_1_2,
629                                                self.pg0.sw_if_index,
630                                                tx[0])
631         tx = self._mcast_connected_send_stream("232.1.1.1")
632         tx2 = self._mcast_connected_send_stream("232.1.1.2")
633
634         #
635         # read the only expected signal
636         #
637         signals = self.vapi.mfib_signal_dump()
638
639         self.assertEqual(2, len(signals))
640
641         signal_232_1_1_1_itf_0.compare(signals[1])
642         signal_232_1_1_2_itf_0.compare(signals[0])
643
644         route_232_1_1_1.update_entry_flags(
645             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE)
646         route_232_1_1_2.update_entry_flags(
647             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE)
648
649     def test_ip_mcast_signal(self):
650         """ IP Multicast Signal """
651
652         #
653         # A (*,G).
654         # one accepting interface, pg0, 1 forwarding interfaces
655         #
656         route_232_1_1_1 = VppIpMRoute(
657             self,
658             "0.0.0.0",
659             "232.1.1.1", 32,
660             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
661             [VppMRoutePath(self.pg0.sw_if_index,
662                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
663              VppMRoutePath(self.pg1.sw_if_index,
664                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
665
666         route_232_1_1_1.add_vpp_config()
667         route_232_1_1_1.update_entry_flags(
668             MRouteEntryFlags.MFIB_ENTRY_FLAG_SIGNAL)
669
670         #
671         # Now the (*,G) is present, send from connected source
672         #
673         tx = self._mcast_connected_send_stream("232.1.1.1")
674
675         #
676         # Constrct a representation of the signal we expect on pg0
677         #
678         signal_232_1_1_1_itf_0 = VppMFibSignal(self,
679                                                route_232_1_1_1,
680                                                self.pg0.sw_if_index,
681                                                tx[0])
682
683         #
684         # read the only expected signal
685         #
686         signals = self.vapi.mfib_signal_dump()
687
688         self.assertEqual(1, len(signals))
689
690         signal_232_1_1_1_itf_0.compare(signals[0])
691
692         #
693         # reading the signal allows for the generation of another
694         # so send more packets and expect the next signal
695         #
696         tx = self._mcast_connected_send_stream("232.1.1.1")
697
698         signals = self.vapi.mfib_signal_dump()
699         self.assertEqual(1, len(signals))
700         signal_232_1_1_1_itf_0.compare(signals[0])
701
702         #
703         # Set the negate-signal on the accepting interval - the signals
704         # should stop
705         #
706         route_232_1_1_1.update_path_flags(
707             self.pg0.sw_if_index,
708             (MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT |
709              MRouteItfFlags.MFIB_ITF_FLAG_NEGATE_SIGNAL))
710
711         self.vapi.cli("clear trace")
712         tx = self._mcast_connected_send_stream("232.1.1.1")
713
714         signals = self.vapi.mfib_signal_dump()
715         self.assertEqual(0, len(signals))
716
717         #
718         # Clear the SIGNAL flag on the entry and the signals should
719         # come back since the interface is still NEGATE-SIGNAL
720         #
721         route_232_1_1_1.update_entry_flags(
722             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE)
723
724         tx = self._mcast_connected_send_stream("232.1.1.1")
725
726         signals = self.vapi.mfib_signal_dump()
727         self.assertEqual(1, len(signals))
728         signal_232_1_1_1_itf_0.compare(signals[0])
729
730         #
731         # Lastly remove the NEGATE-SIGNAL from the interface and the
732         # signals should stop
733         #
734         route_232_1_1_1.update_path_flags(self.pg0.sw_if_index,
735                                           MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT)
736
737         tx = self._mcast_connected_send_stream("232.1.1.1")
738         signals = self.vapi.mfib_signal_dump()
739         self.assertEqual(0, len(signals))
740
741     def test_ip_mcast_vrf(self):
742         """ IP Multicast Replication in non-default table"""
743
744         #
745         # An (S,G).
746         # one accepting interface, pg0, 2 forwarding interfaces
747         #
748         route_1_1_1_1_232_1_1_1 = VppIpMRoute(
749             self,
750             "1.1.1.1",
751             "232.1.1.1", 64,
752             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
753             [VppMRoutePath(self.pg8.sw_if_index,
754                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
755              VppMRoutePath(self.pg1.sw_if_index,
756                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
757              VppMRoutePath(self.pg2.sw_if_index,
758                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)],
759             table_id=10)
760         route_1_1_1_1_232_1_1_1.add_vpp_config()
761
762         #
763         # a stream that matches the route for (1.1.1.1,232.1.1.1)
764         #  small packets
765         #
766         self.vapi.cli("clear trace")
767         tx = self.create_stream_ip4(self.pg8, "1.1.1.1", "232.1.1.1")
768         self.pg8.add_stream(tx)
769
770         self.pg_enable_capture(self.pg_interfaces)
771         self.pg_start()
772
773         # We expect replications on Pg1 & 2
774         self.verify_capture_ip4(self.pg1, tx)
775         self.verify_capture_ip4(self.pg2, tx)
776
777     def test_ip6_mcast_vrf(self):
778         """ IPv6 Multicast Replication in non-default table"""
779
780         #
781         # An (S,G).
782         # one accepting interface, pg0, 2 forwarding interfaces
783         #
784         route_2001_ff01_1 = VppIpMRoute(
785             self,
786             "2001::1",
787             "ff01::1", 256,
788             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
789             [VppMRoutePath(self.pg8.sw_if_index,
790                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT,
791                            proto=DpoProto.DPO_PROTO_IP6),
792              VppMRoutePath(self.pg1.sw_if_index,
793                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
794                            proto=DpoProto.DPO_PROTO_IP6),
795              VppMRoutePath(self.pg2.sw_if_index,
796                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
797                            proto=DpoProto.DPO_PROTO_IP6)],
798             table_id=10,
799             is_ip6=1)
800         route_2001_ff01_1.add_vpp_config()
801
802         #
803         # a stream that matches the route for (2001::1, ff00::1)
804         #
805         self.vapi.cli("clear trace")
806         tx = self.create_stream_ip6(self.pg8, "2001::1", "ff01::1")
807         self.pg8.add_stream(tx)
808
809         self.pg_enable_capture(self.pg_interfaces)
810         self.pg_start()
811
812         # We expect replications on Pg1, 2,
813         self.verify_capture_ip6(self.pg1, tx)
814         self.verify_capture_ip6(self.pg2, tx)
815
816     def test_bidir(self):
817         """ IP Multicast Bi-directional """
818
819         #
820         # A (*,G). The set of accepting interfaces matching the forwarding
821         #
822         route_232_1_1_1 = VppIpMRoute(
823             self,
824             "0.0.0.0",
825             "232.1.1.1", 32,
826             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
827             [VppMRoutePath(self.pg0.sw_if_index,
828                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT |
829                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
830              VppMRoutePath(self.pg1.sw_if_index,
831                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT |
832                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
833              VppMRoutePath(self.pg2.sw_if_index,
834                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT |
835                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
836              VppMRoutePath(self.pg3.sw_if_index,
837                            MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT |
838                            MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
839         route_232_1_1_1.add_vpp_config()
840
841         tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "232.1.1.1")
842         self.pg0.add_stream(tx)
843
844         self.pg_enable_capture(self.pg_interfaces)
845         self.pg_start()
846
847         # We expect replications on Pg1, 2, 3, but not on pg0
848         self.verify_capture_ip4(self.pg1, tx)
849         self.verify_capture_ip4(self.pg2, tx)
850         self.verify_capture_ip4(self.pg3, tx)
851         self.pg0.assert_nothing_captured(
852             remark="IP multicast packets forwarded on PG0")
853
854
855 if __name__ == '__main__':
856     unittest.main(testRunner=VppTestRunner)