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