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