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