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