fib: fix the drop counter for ipv6 RPF failures
[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         count = self.statistics.get_err_counter(
215             "/err/ip4-input/Multicast RPF check failed")
216         self.assertEqual(count, len(tx))
217
218         #
219         # A (*,G).
220         # one accepting interface, pg0, 7 forwarding interfaces
221         #  many forwarding interfaces test the case where the replicate DPO
222         #  needs to use extra cache lines for the buckets.
223         #
224         route_232_1_1_1 = VppIpMRoute(
225             self,
226             "0.0.0.0",
227             "232.1.1.1", 32,
228             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
229             [VppMRoutePath(self.pg0.sw_if_index,
230                            MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT),
231              VppMRoutePath(self.pg1.sw_if_index,
232                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
233              VppMRoutePath(self.pg2.sw_if_index,
234                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
235              VppMRoutePath(self.pg3.sw_if_index,
236                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
237              VppMRoutePath(self.pg4.sw_if_index,
238                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
239              VppMRoutePath(self.pg5.sw_if_index,
240                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
241              VppMRoutePath(self.pg6.sw_if_index,
242                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
243              VppMRoutePath(self.pg7.sw_if_index,
244                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD)])
245         route_232_1_1_1.add_vpp_config()
246
247         #
248         # An (S,G).
249         # one accepting interface, pg0, 2 forwarding interfaces
250         #
251         route_1_1_1_1_232_1_1_1 = VppIpMRoute(
252             self,
253             "1.1.1.1",
254             "232.1.1.1", 27,  # any grp-len is ok when src is set
255             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
256             [VppMRoutePath(self.pg0.sw_if_index,
257                            MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT),
258              VppMRoutePath(self.pg1.sw_if_index,
259                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
260              VppMRoutePath(self.pg2.sw_if_index,
261                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD)])
262         route_1_1_1_1_232_1_1_1.add_vpp_config()
263
264         #
265         # An (S,G).
266         # one accepting interface, pg0, 2 forwarding interfaces
267         # that use unicast next-hops
268         #
269         route_1_1_1_1_232_1_1_2 = VppIpMRoute(
270             self,
271             "1.1.1.1",
272             "232.1.1.2", 64,
273             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
274             [VppMRoutePath(self.pg0.sw_if_index,
275                            MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT),
276              VppMRoutePath(self.pg1.sw_if_index,
277                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
278                            nh=self.pg1.remote_ip4),
279              VppMRoutePath(self.pg2.sw_if_index,
280                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
281                            nh=self.pg2.remote_ip4)])
282         route_1_1_1_1_232_1_1_2.add_vpp_config()
283
284         #
285         # An (*,G/m).
286         # one accepting interface, pg0, 1 forwarding interfaces
287         #
288         route_232 = VppIpMRoute(
289             self,
290             "0.0.0.0",
291             "232.0.0.0", 8,
292             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
293             [VppMRoutePath(self.pg0.sw_if_index,
294                            MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT),
295              VppMRoutePath(self.pg1.sw_if_index,
296                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD)])
297         route_232.add_vpp_config()
298
299         #
300         # a stream that matches the route for (1.1.1.1,232.1.1.1)
301         #  small packets
302         #
303         self.vapi.cli("clear trace")
304         tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "232.1.1.1")
305         self.pg0.add_stream(tx)
306
307         self.pg_enable_capture(self.pg_interfaces)
308         self.pg_start()
309
310         self.assertEqual(route_1_1_1_1_232_1_1_1.get_stats()['packets'],
311                          len(tx))
312
313         # We expect replications on Pg1->7
314         self.verify_capture_ip4(self.pg1, tx)
315         self.verify_capture_ip4(self.pg2, tx)
316
317         # no replications on Pg0
318         self.pg0.assert_nothing_captured(
319             remark="IP multicast packets forwarded on PG0")
320         self.pg3.assert_nothing_captured(
321             remark="IP multicast packets forwarded on PG3")
322
323         #
324         # a stream that matches the route for (1.1.1.1,232.1.1.1)
325         #  large packets
326         #
327         self.vapi.cli("clear trace")
328         tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "232.1.1.1",
329                                     payload_size=1024)
330         self.pg0.add_stream(tx)
331
332         self.pg_enable_capture(self.pg_interfaces)
333         self.pg_start()
334
335         # We expect replications on Pg1->7
336         self.verify_capture_ip4(self.pg1, tx)
337         self.verify_capture_ip4(self.pg2, tx)
338
339         self.assertEqual(route_1_1_1_1_232_1_1_1.get_stats()['packets'],
340                          2*len(tx))
341
342         # no replications on Pg0
343         self.pg0.assert_nothing_captured(
344             remark="IP multicast packets forwarded on PG0")
345         self.pg3.assert_nothing_captured(
346             remark="IP multicast packets forwarded on PG3")
347
348         #
349         # a stream to the unicast next-hops
350         #
351         self.vapi.cli("clear trace")
352         tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "232.1.1.2")
353         self.pg0.add_stream(tx)
354
355         self.pg_enable_capture(self.pg_interfaces)
356         self.pg_start()
357
358         # We expect replications on Pg1->7
359         self.verify_capture_ip4(self.pg1, tx, dst_mac=self.pg1.remote_mac)
360         self.verify_capture_ip4(self.pg2, tx, dst_mac=self.pg2.remote_mac)
361
362         # no replications on Pg0 nor pg3
363         self.pg0.assert_nothing_captured(
364             remark="IP multicast packets forwarded on PG0")
365         self.pg3.assert_nothing_captured(
366             remark="IP multicast packets forwarded on PG3")
367
368         #
369         # a stream that matches the route for (*,232.0.0.0/8)
370         # Send packets with the 9th bit set so we test the correct clearing
371         # of that bit in the mac rewrite
372         #
373         self.vapi.cli("clear trace")
374         tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "232.255.255.255")
375         self.pg0.add_stream(tx)
376
377         self.pg_enable_capture(self.pg_interfaces)
378         self.pg_start()
379
380         # We expect replications on Pg1 only
381         self.verify_capture_ip4(self.pg1, tx)
382         self.assertEqual(route_232.get_stats()['packets'], len(tx))
383
384         # no replications on Pg0, Pg2 not Pg3
385         self.pg0.assert_nothing_captured(
386             remark="IP multicast packets forwarded on PG0")
387         self.pg2.assert_nothing_captured(
388             remark="IP multicast packets forwarded on PG2")
389         self.pg3.assert_nothing_captured(
390             remark="IP multicast packets forwarded on PG3")
391
392         #
393         # a stream that matches the route for (*,232.1.1.1)
394         #
395         self.vapi.cli("clear trace")
396         tx = self.create_stream_ip4(self.pg0, "1.1.1.2", "232.1.1.1")
397         self.pg0.add_stream(tx)
398
399         self.pg_enable_capture(self.pg_interfaces)
400         self.pg_start()
401
402         # We expect replications on Pg1->7
403         self.verify_capture_ip4(self.pg1, tx)
404         self.verify_capture_ip4(self.pg2, tx)
405         self.verify_capture_ip4(self.pg3, tx)
406         self.verify_capture_ip4(self.pg4, tx)
407         self.verify_capture_ip4(self.pg5, tx)
408         self.verify_capture_ip4(self.pg6, tx)
409         self.verify_capture_ip4(self.pg7, tx)
410
411         # no replications on Pg0
412         self.pg0.assert_nothing_captured(
413             remark="IP multicast packets forwarded on PG0")
414
415         self.vapi.cli("packet mac-filter pg0 off")
416         self.vapi.cli("packet mac-filter pg1 off")
417         self.vapi.cli("packet mac-filter pg2 off")
418         self.vapi.cli("packet mac-filter pg4 off")
419         self.vapi.cli("packet mac-filter pg5 off")
420         self.vapi.cli("packet mac-filter pg6 off")
421         self.vapi.cli("packet mac-filter pg7 off")
422
423     def test_ip6_mcast(self):
424         """ IPv6 Multicast Replication """
425
426         MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
427         MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
428
429         self.vapi.cli("packet mac-filter pg0 on")
430         self.vapi.cli("packet mac-filter pg1 on")
431         self.vapi.cli("packet mac-filter pg2 on")
432         self.vapi.cli("packet mac-filter pg4 on")
433         self.vapi.cli("packet mac-filter pg5 on")
434         self.vapi.cli("packet mac-filter pg6 on")
435         self.vapi.cli("packet mac-filter pg7 on")
436         #
437         # a stream that matches the default route. gets dropped.
438         #
439         self.vapi.cli("clear trace")
440         tx = self.create_stream_ip6(self.pg0, "2001::1", "ff01::1")
441         self.pg0.add_stream(tx)
442
443         self.pg_enable_capture(self.pg_interfaces)
444         self.pg_start()
445
446         self.pg0.assert_nothing_captured(
447             remark="IPv6 multicast packets forwarded on default route")
448
449         #
450         # A (*,G).
451         # one accepting interface, pg0, 3 forwarding interfaces
452         #
453         route_ff01_1 = VppIpMRoute(
454             self,
455             "::",
456             "ff01::1", 128,
457             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
458             [VppMRoutePath(self.pg0.sw_if_index,
459                            MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT,
460                            proto=FibPathProto.FIB_PATH_NH_PROTO_IP6),
461              VppMRoutePath(self.pg1.sw_if_index,
462                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
463                            proto=FibPathProto.FIB_PATH_NH_PROTO_IP6),
464              VppMRoutePath(self.pg2.sw_if_index,
465                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
466                            proto=FibPathProto.FIB_PATH_NH_PROTO_IP6),
467              VppMRoutePath(self.pg3.sw_if_index,
468                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
469                            proto=FibPathProto.FIB_PATH_NH_PROTO_IP6)])
470         route_ff01_1.add_vpp_config()
471
472         #
473         # An (S,G).
474         # one accepting interface, pg0, 2 forwarding interfaces
475         #
476         route_2001_ff01_1 = VppIpMRoute(
477             self,
478             "2001::1",
479             "ff01::1", 0,  # any grp-len is ok when src is set
480             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
481             [VppMRoutePath(self.pg0.sw_if_index,
482                            MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT,
483                            proto=FibPathProto.FIB_PATH_NH_PROTO_IP6),
484              VppMRoutePath(self.pg1.sw_if_index,
485                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
486                            proto=FibPathProto.FIB_PATH_NH_PROTO_IP6),
487              VppMRoutePath(self.pg2.sw_if_index,
488                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
489                            proto=FibPathProto.FIB_PATH_NH_PROTO_IP6)])
490         route_2001_ff01_1.add_vpp_config()
491
492         #
493         # An (*,G/m).
494         # one accepting interface, pg0, 1 forwarding interface
495         #
496         route_ff01 = VppIpMRoute(
497             self,
498             "::",
499             "ff01::", 16,
500             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
501             [VppMRoutePath(self.pg0.sw_if_index,
502                            MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT,
503                            proto=FibPathProto.FIB_PATH_NH_PROTO_IP6),
504              VppMRoutePath(self.pg1.sw_if_index,
505                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
506                            proto=FibPathProto.FIB_PATH_NH_PROTO_IP6)])
507         route_ff01.add_vpp_config()
508
509         #
510         # a stream that matches the route for (*, ff01::/16)
511         # sent on the non-accepting interface
512         #
513         self.vapi.cli("clear trace")
514         tx = self.create_stream_ip6(self.pg1, "2002::1", "ff01:2::255")
515         self.send_and_assert_no_replies(self.pg1, tx, "RPF miss")
516         count = self.statistics.get_err_counter(
517             "/err/ip6-input/Multicast RPF check failed")
518         self.assertEqual(count, 2 * len(tx))
519
520         #
521         # a stream that matches the route for (*, ff01::/16)
522         # sent on the accepting interface
523         #
524         self.vapi.cli("clear trace")
525         tx = self.create_stream_ip6(self.pg0, "2002::1", "ff01:2::255")
526         self.pg0.add_stream(tx)
527
528         self.pg_enable_capture(self.pg_interfaces)
529         self.pg_start()
530
531         # We expect replications on Pg1
532         self.verify_capture_ip6(self.pg1, tx)
533
534         # no replications on Pg0, Pg3
535         self.pg0.assert_nothing_captured(
536             remark="IP multicast packets forwarded on PG0")
537         self.pg2.assert_nothing_captured(
538             remark="IP multicast packets forwarded on PG2")
539         self.pg3.assert_nothing_captured(
540             remark="IP multicast packets forwarded on PG3")
541
542         #
543         # Bounce the interface and it should still work
544         #
545         self.pg1.admin_down()
546         self.pg0.add_stream(tx)
547         self.pg_enable_capture(self.pg_interfaces)
548         self.pg_start()
549         self.pg1.assert_nothing_captured(
550             remark="IP multicast packets forwarded on down PG1")
551
552         self.pg1.admin_up()
553         self.pg0.add_stream(tx)
554         self.pg_enable_capture(self.pg_interfaces)
555         self.pg_start()
556         self.verify_capture_ip6(self.pg1, tx)
557
558         #
559         # a stream that matches the route for (*,ff01::1)
560         #
561         self.vapi.cli("clear trace")
562         tx = self.create_stream_ip6(self.pg0, "2002::2", "ff01::1")
563         self.pg0.add_stream(tx)
564
565         self.pg_enable_capture(self.pg_interfaces)
566         self.pg_start()
567
568         # We expect replications on Pg1, 2, 3.
569         self.verify_capture_ip6(self.pg1, tx)
570         self.verify_capture_ip6(self.pg2, tx)
571         self.verify_capture_ip6(self.pg3, tx)
572
573         # no replications on Pg0
574         self.pg0.assert_nothing_captured(
575             remark="IPv6 multicast packets forwarded on PG0")
576
577         #
578         # a stream that matches the route for (2001::1, ff00::1)
579         #
580         self.vapi.cli("clear trace")
581         tx = self.create_stream_ip6(self.pg0, "2001::1", "ff01::1")
582         self.pg0.add_stream(tx)
583
584         self.pg_enable_capture(self.pg_interfaces)
585         self.pg_start()
586
587         # We expect replications on Pg1, 2,
588         self.verify_capture_ip6(self.pg1, tx)
589         self.verify_capture_ip6(self.pg2, tx)
590
591         # no replications on Pg0, Pg3
592         self.pg0.assert_nothing_captured(
593             remark="IP multicast packets forwarded on PG0")
594         self.pg3.assert_nothing_captured(
595             remark="IP multicast packets forwarded on PG3")
596
597         self.vapi.cli("packet mac-filter pg0 off")
598         self.vapi.cli("packet mac-filter pg1 off")
599         self.vapi.cli("packet mac-filter pg2 off")
600         self.vapi.cli("packet mac-filter pg4 off")
601         self.vapi.cli("packet mac-filter pg5 off")
602         self.vapi.cli("packet mac-filter pg6 off")
603         self.vapi.cli("packet mac-filter pg7 off")
604
605     def _mcast_connected_send_stream(self, dst_ip):
606         self.vapi.cli("clear trace")
607         tx = self.create_stream_ip4(self.pg0,
608                                     self.pg0.remote_ip4,
609                                     dst_ip)
610         self.pg0.add_stream(tx)
611
612         self.pg_enable_capture(self.pg_interfaces)
613         self.pg_start()
614
615         # We expect replications on Pg1.
616         self.verify_capture_ip4(self.pg1, tx)
617
618         return tx
619
620     def test_ip_mcast_connected(self):
621         """ IP Multicast Connected Source check """
622
623         MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
624         MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
625
626         #
627         # A (*,G).
628         # one accepting interface, pg0, 1 forwarding interfaces
629         #
630         route_232_1_1_1 = VppIpMRoute(
631             self,
632             "0.0.0.0",
633             "232.1.1.1", 32,
634             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
635             [VppMRoutePath(self.pg0.sw_if_index,
636                            MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT),
637              VppMRoutePath(self.pg1.sw_if_index,
638                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD)])
639
640         route_232_1_1_1.add_vpp_config()
641         route_232_1_1_1.update_entry_flags(
642             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_CONNECTED)
643
644         #
645         # Now the (*,G) is present, send from connected source
646         #
647         tx = self._mcast_connected_send_stream("232.1.1.1")
648
649         #
650         # Constrct a representation of the signal we expect on pg0
651         #
652         signal_232_1_1_1_itf_0 = VppMFibSignal(self,
653                                                route_232_1_1_1,
654                                                self.pg0.sw_if_index,
655                                                tx[0])
656
657         #
658         # read the only expected signal
659         #
660         signals = self.vapi.mfib_signal_dump()
661
662         self.assertEqual(1, len(signals))
663
664         signal_232_1_1_1_itf_0.compare(signals[0])
665
666         #
667         # reading the signal allows for the generation of another
668         # so send more packets and expect the next signal
669         #
670         tx = self._mcast_connected_send_stream("232.1.1.1")
671
672         signals = self.vapi.mfib_signal_dump()
673         self.assertEqual(1, len(signals))
674         signal_232_1_1_1_itf_0.compare(signals[0])
675
676         #
677         # A Second entry with connected check
678         # one accepting interface, pg0, 1 forwarding interfaces
679         #
680         route_232_1_1_2 = VppIpMRoute(
681             self,
682             "0.0.0.0",
683             "232.1.1.2", 32,
684             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
685             [VppMRoutePath(self.pg0.sw_if_index,
686                            MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT),
687              VppMRoutePath(self.pg1.sw_if_index,
688                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD)])
689
690         route_232_1_1_2.add_vpp_config()
691         route_232_1_1_2.update_entry_flags(
692             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_CONNECTED)
693
694         #
695         # Send traffic to both entries. One read should net us two signals
696         #
697         signal_232_1_1_2_itf_0 = VppMFibSignal(self,
698                                                route_232_1_1_2,
699                                                self.pg0.sw_if_index,
700                                                tx[0])
701         tx = self._mcast_connected_send_stream("232.1.1.1")
702         tx2 = self._mcast_connected_send_stream("232.1.1.2")
703
704         #
705         # read the only expected signal
706         #
707         signals = self.vapi.mfib_signal_dump()
708
709         self.assertEqual(2, len(signals))
710
711         signal_232_1_1_1_itf_0.compare(signals[1])
712         signal_232_1_1_2_itf_0.compare(signals[0])
713
714         route_232_1_1_1.update_entry_flags(
715             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE)
716         route_232_1_1_2.update_entry_flags(
717             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE)
718
719     def test_ip_mcast_signal(self):
720         """ IP Multicast Signal """
721
722         MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
723         MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
724
725         #
726         # A (*,G).
727         # one accepting interface, pg0, 1 forwarding interfaces
728         #
729         route_232_1_1_1 = VppIpMRoute(
730             self,
731             "0.0.0.0",
732             "232.1.1.1", 32,
733             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
734             [VppMRoutePath(self.pg0.sw_if_index,
735                            MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT),
736              VppMRoutePath(self.pg1.sw_if_index,
737                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD)])
738
739         route_232_1_1_1.add_vpp_config()
740
741         route_232_1_1_1.update_entry_flags(
742             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_SIGNAL)
743
744         #
745         # Now the (*,G) is present, send from connected source
746         #
747         tx = self._mcast_connected_send_stream("232.1.1.1")
748
749         #
750         # Constrct a representation of the signal we expect on pg0
751         #
752         signal_232_1_1_1_itf_0 = VppMFibSignal(self,
753                                                route_232_1_1_1,
754                                                self.pg0.sw_if_index,
755                                                tx[0])
756
757         #
758         # read the only expected signal
759         #
760         signals = self.vapi.mfib_signal_dump()
761
762         self.assertEqual(1, len(signals))
763
764         signal_232_1_1_1_itf_0.compare(signals[0])
765
766         #
767         # reading the signal allows for the generation of another
768         # so send more packets and expect the next signal
769         #
770         tx = self._mcast_connected_send_stream("232.1.1.1")
771
772         signals = self.vapi.mfib_signal_dump()
773         self.assertEqual(1, len(signals))
774         signal_232_1_1_1_itf_0.compare(signals[0])
775
776         #
777         # Set the negate-signal on the accepting interval - the signals
778         # should stop
779         #
780         route_232_1_1_1.update_path_flags(
781             self.pg0.sw_if_index,
782             (MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT |
783              MRouteItfFlags.MFIB_API_ITF_FLAG_NEGATE_SIGNAL))
784
785         self.vapi.cli("clear trace")
786         tx = self._mcast_connected_send_stream("232.1.1.1")
787
788         signals = self.vapi.mfib_signal_dump()
789         self.assertEqual(0, len(signals))
790
791         #
792         # Clear the SIGNAL flag on the entry and the signals should
793         # come back since the interface is still NEGATE-SIGNAL
794         #
795         route_232_1_1_1.update_entry_flags(
796             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE)
797
798         tx = self._mcast_connected_send_stream("232.1.1.1")
799
800         signals = self.vapi.mfib_signal_dump()
801         self.assertEqual(1, len(signals))
802         signal_232_1_1_1_itf_0.compare(signals[0])
803
804         #
805         # Lastly remove the NEGATE-SIGNAL from the interface and the
806         # signals should stop
807         #
808         route_232_1_1_1.update_path_flags(
809             self.pg0.sw_if_index,
810             MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT)
811
812         tx = self._mcast_connected_send_stream("232.1.1.1")
813         signals = self.vapi.mfib_signal_dump()
814         self.assertEqual(0, len(signals))
815
816     def test_ip_mcast_vrf(self):
817         """ IP Multicast Replication in non-default table"""
818
819         MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
820         MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
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.1.1.1", 64,
830             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
831             [VppMRoutePath(self.pg8.sw_if_index,
832                            MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT),
833              VppMRoutePath(self.pg1.sw_if_index,
834                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
835              VppMRoutePath(self.pg2.sw_if_index,
836                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD)],
837             table_id=10)
838         route_1_1_1_1_232_1_1_1.add_vpp_config()
839
840         #
841         # a stream that matches the route for (1.1.1.1,232.1.1.1)
842         #  small packets
843         #
844         self.vapi.cli("clear trace")
845         tx = self.create_stream_ip4(self.pg8, "1.1.1.1", "232.1.1.1")
846         self.pg8.add_stream(tx)
847
848         self.pg_enable_capture(self.pg_interfaces)
849         self.pg_start()
850
851         # We expect replications on Pg1 & 2
852         self.verify_capture_ip4(self.pg1, tx)
853         self.verify_capture_ip4(self.pg2, tx)
854
855         #
856         # An (S,G). for for-us
857         #
858         route_0_0_0_0_224_0_0_5 = VppIpMRoute(
859             self,
860             "0.0.0.0",
861             "224.0.0.5", 32,
862             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
863             [VppMRoutePath(self.pg8.sw_if_index,
864                            MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT),
865              VppMRoutePath(0xffffffff,
866                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
867                            type=FibPathType.FIB_PATH_TYPE_LOCAL)],
868             table_id=10)
869         route_0_0_0_0_224_0_0_5.add_vpp_config()
870
871         #
872         # a stream that matches the route for (0.0.0.0, 224.0.0.5)
873         #  small packets
874         #
875         self.vapi.cli("clear trace")
876         self.pg8.resolve_arp()
877
878         #
879         # send a ping to mcast address from peer on pg8
880         #  expect a response
881         #
882         icmp_id = 0xb
883         icmp_seq = 5
884         icmp_load = b'\x0a' * 18
885         tx = (Ether(dst=getmacbyip("224.0.0.5"), src=self.pg8.remote_mac) /
886               IP(src=self.pg8.remote_ip4, dst="224.0.0.5") /
887               ICMP(id=icmp_id, seq=icmp_seq) /
888               Raw(load=icmp_load)) * 2
889
890         self.send_and_expect(self.pg8, tx, self.pg8)
891
892     def test_ip_mcast_gre(self):
893         """ IP Multicast Replication over GRE"""
894
895         MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
896         MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
897
898         gre_if_1 = VppGreInterface(
899             self,
900             self.pg1.local_ip4,
901             self.pg1.remote_ip4).add_vpp_config()
902         gre_if_2 = VppGreInterface(
903             self,
904             self.pg2.local_ip4,
905             self.pg2.remote_ip4).add_vpp_config()
906         gre_if_3 = VppGreInterface(
907             self,
908             self.pg3.local_ip4,
909             self.pg3.remote_ip4).add_vpp_config()
910
911         gre_if_1.admin_up()
912         gre_if_1.config_ip4()
913         gre_if_2.admin_up()
914         gre_if_2.config_ip4()
915         gre_if_3.admin_up()
916         gre_if_3.config_ip4()
917
918         #
919         # An (S,G).
920         # one accepting interface, pg0, 2 forwarding interfaces
921         #
922         route_1_1_1_1_232_1_1_1 = VppIpMRoute(
923             self,
924             "1.1.1.1",
925             "232.2.2.2", 64,
926             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
927             [VppMRoutePath(gre_if_1.sw_if_index,
928                            MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT),
929              VppMRoutePath(gre_if_2.sw_if_index,
930                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
931              VppMRoutePath(gre_if_3.sw_if_index,
932                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD)])
933         route_1_1_1_1_232_1_1_1.add_vpp_config()
934
935         #
936         # a stream that matches the route for (1.1.1.1,232.2.2.2)
937         #  small packets
938         #
939         tx = (Ether(dst=self.pg1.local_mac,
940                     src=self.pg1.remote_mac) /
941               IP(src=self.pg1.remote_ip4,
942                  dst=self.pg1.local_ip4) /
943               GRE() /
944               IP(src="1.1.1.1", dst="232.2.2.2") /
945               UDP(sport=1234, dport=1234) /
946               Raw(b'\a5' * 64)) * 63
947
948         self.vapi.cli("clear trace")
949         self.pg1.add_stream(tx)
950
951         self.pg_enable_capture(self.pg_interfaces)
952         self.pg_start()
953
954         # We expect replications on Pg2 & 3
955         # check the encap headers are as expected based on the egress tunnel
956         rxs = self.pg2.get_capture(len(tx))
957         for rx in rxs:
958             self.assertEqual(rx[IP].src, gre_if_2.t_src)
959             self.assertEqual(rx[IP].dst, gre_if_2.t_dst)
960             self.assert_packet_checksums_valid(rx)
961
962         rxs = self.pg3.get_capture(len(tx))
963         for rx in rxs:
964             self.assertEqual(rx[IP].src, gre_if_3.t_src)
965             self.assertEqual(rx[IP].dst, gre_if_3.t_dst)
966             self.assert_packet_checksums_valid(rx)
967
968     def test_ip6_mcast_gre(self):
969         """ IP6 Multicast Replication over GRE"""
970
971         MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
972         MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
973
974         gre_if_1 = VppGreInterface(
975             self,
976             self.pg1.local_ip4,
977             self.pg1.remote_ip4).add_vpp_config()
978         gre_if_2 = VppGreInterface(
979             self,
980             self.pg2.local_ip4,
981             self.pg2.remote_ip4).add_vpp_config()
982         gre_if_3 = VppGreInterface(
983             self,
984             self.pg3.local_ip4,
985             self.pg3.remote_ip4).add_vpp_config()
986
987         gre_if_1.admin_up()
988         gre_if_1.config_ip6()
989         gre_if_2.admin_up()
990         gre_if_2.config_ip6()
991         gre_if_3.admin_up()
992         gre_if_3.config_ip6()
993
994         #
995         # An (S,G).
996         # one accepting interface, pg0, 2 forwarding interfaces
997         #
998         route_1_1_FF_1 = VppIpMRoute(
999             self,
1000             "1::1",
1001             "FF00::1", 256,
1002             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
1003             [VppMRoutePath(gre_if_1.sw_if_index,
1004                            MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT),
1005              VppMRoutePath(gre_if_2.sw_if_index,
1006                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
1007              VppMRoutePath(gre_if_3.sw_if_index,
1008                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD)])
1009         route_1_1_FF_1.add_vpp_config()
1010
1011         #
1012         # a stream that matches the route for (1::1, FF::1)
1013         #  small packets
1014         #
1015         tx = (Ether(dst=self.pg1.local_mac,
1016                     src=self.pg1.remote_mac) /
1017               IP(src=self.pg1.remote_ip4,
1018                  dst=self.pg1.local_ip4) /
1019               GRE() /
1020               IPv6(src="1::1", dst="FF00::1") /
1021               UDP(sport=1234, dport=1234) /
1022               Raw(b'\a5' * 64)) * 63
1023
1024         self.vapi.cli("clear trace")
1025         self.pg1.add_stream(tx)
1026
1027         self.pg_enable_capture(self.pg_interfaces)
1028         self.pg_start()
1029
1030         # We expect replications on Pg2 & 3
1031         # check the encap headers are as expected based on the egress tunnel
1032         rxs = self.pg2.get_capture(len(tx))
1033         for rx in rxs:
1034             self.assertEqual(rx[IP].src, gre_if_2.t_src)
1035             self.assertEqual(rx[IP].dst, gre_if_2.t_dst)
1036             self.assert_packet_checksums_valid(rx)
1037
1038         rxs = self.pg3.get_capture(len(tx))
1039         for rx in rxs:
1040             self.assertEqual(rx[IP].src, gre_if_3.t_src)
1041             self.assertEqual(rx[IP].dst, gre_if_3.t_dst)
1042             self.assert_packet_checksums_valid(rx)
1043
1044     def test_ip6_mcast_vrf(self):
1045         """ IPv6 Multicast Replication in non-default table"""
1046
1047         MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
1048         MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
1049
1050         #
1051         # An (S,G).
1052         # one accepting interface, pg0, 2 forwarding interfaces
1053         #
1054         route_2001_ff01_1 = VppIpMRoute(
1055             self,
1056             "2001::1",
1057             "ff01::1", 256,
1058             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
1059             [VppMRoutePath(self.pg8.sw_if_index,
1060                            MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT,
1061                            proto=FibPathProto.FIB_PATH_NH_PROTO_IP6),
1062              VppMRoutePath(self.pg1.sw_if_index,
1063                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
1064                            proto=FibPathProto.FIB_PATH_NH_PROTO_IP6),
1065              VppMRoutePath(self.pg2.sw_if_index,
1066                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
1067                            proto=FibPathProto.FIB_PATH_NH_PROTO_IP6)],
1068             table_id=10)
1069         route_2001_ff01_1.add_vpp_config()
1070
1071         #
1072         # a stream that matches the route for (2001::1, ff00::1)
1073         #
1074         self.vapi.cli("clear trace")
1075         tx = self.create_stream_ip6(self.pg8, "2001::1", "ff01::1")
1076         self.pg8.add_stream(tx)
1077
1078         self.pg_enable_capture(self.pg_interfaces)
1079         self.pg_start()
1080
1081         # We expect replications on Pg1, 2,
1082         self.verify_capture_ip6(self.pg1, tx)
1083         self.verify_capture_ip6(self.pg2, tx)
1084
1085     def test_bidir(self):
1086         """ IP Multicast Bi-directional """
1087
1088         MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
1089         MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
1090
1091         #
1092         # A (*,G). The set of accepting interfaces matching the forwarding
1093         #
1094         route_232_1_1_1 = VppIpMRoute(
1095             self,
1096             "0.0.0.0",
1097             "232.1.1.1", 32,
1098             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
1099             [VppMRoutePath(self.pg0.sw_if_index,
1100                            MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT |
1101                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
1102              VppMRoutePath(self.pg1.sw_if_index,
1103                            MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT |
1104                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
1105              VppMRoutePath(self.pg2.sw_if_index,
1106                            MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT |
1107                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD),
1108              VppMRoutePath(self.pg3.sw_if_index,
1109                            MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT |
1110                            MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD)])
1111         route_232_1_1_1.add_vpp_config()
1112
1113         tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "232.1.1.1")
1114         self.pg0.add_stream(tx)
1115
1116         self.pg_enable_capture(self.pg_interfaces)
1117         self.pg_start()
1118
1119         # We expect replications on Pg1, 2, 3, but not on pg0
1120         self.verify_capture_ip4(self.pg1, tx)
1121         self.verify_capture_ip4(self.pg2, tx)
1122         self.verify_capture_ip4(self.pg3, tx)
1123         self.pg0.assert_nothing_captured(
1124             remark="IP multicast packets forwarded on PG0")
1125
1126
1127 if __name__ == '__main__':
1128     unittest.main(testRunner=VppTestRunner)