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