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