5 from framework import VppTestCase, VppTestRunner
6 from vpp_sub_interface import VppSubInterface, VppDot1QSubint, VppDot1ADSubint
7 from vpp_ip_route import VppIpMRoute, VppMRoutePath, VppMFibSignal
9 from scapy.packet import Raw
10 from scapy.layers.l2 import Ether
11 from scapy.layers.inet import IP, UDP, getmacbyip, ICMP
12 from scapy.layers.inet6 import IPv6, getmacbyip6
17 MFIB_ITF_FLAG_NONE = 0
18 MFIB_ITF_FLAG_NEGATE_SIGNAL = 1
19 MFIB_ITF_FLAG_ACCEPT = 2
20 MFIB_ITF_FLAG_FORWARD = 4
21 MFIB_ITF_FLAG_SIGNAL_PRESENT = 8
22 MFIB_ITF_FLAG_INTERNAL_COPY = 16
25 class MRouteEntryFlags:
26 MFIB_ENTRY_FLAG_NONE = 0
27 MFIB_ENTRY_FLAG_SIGNAL = 1
28 MFIB_ENTRY_FLAG_DROP = 2
29 MFIB_ENTRY_FLAG_CONNECTED = 4
30 MFIB_ENTRY_FLAG_INHERIT_ACCEPT = 8
33 # The number of packets sent is set to 90 so that when we replicate more than 3
34 # times, which we do for some entries, we will generate more than 256 packets
35 # to the next node in the VLIB graph. Thus we are testing the code's
36 # correctness handling this over-flow
41 class TestMFIB(VppTestCase):
42 """ MFIB Test Case """
45 super(TestMFIB, self).setUp()
48 """ MFIB Unit Tests """
49 error = self.vapi.cli("test mfib")
52 self.logger.critical(error)
53 self.assertEqual(error.find("Failed"), -1)
56 class TestIPMcast(VppTestCase):
57 """ IP Multicast Test Case """
60 super(TestIPMcast, self).setUp()
62 # create 8 pg interfaces
63 self.create_pg_interfaces(range(8))
66 for i in self.pg_interfaces:
73 def create_stream_ip4(self, src_if, src_ip, dst_ip, payload_size=0):
75 # default to small packet sizes
76 p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
77 IP(src=src_ip, dst=dst_ip) /
78 UDP(sport=1234, dport=1234))
80 payload_size = 64 - len(p)
81 p = p / Raw('\xa5' * payload_size)
83 for i in range(0, N_PKTS_IN_STREAM):
87 def create_stream_ip6(self, src_if, src_ip, dst_ip):
89 for i in range(0, N_PKTS_IN_STREAM):
90 info = self.create_packet_info(src_if, src_if)
91 payload = self.info_to_payload(info)
92 p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
93 IPv6(src=src_ip, dst=dst_ip) /
94 UDP(sport=1234, dport=1234) /
100 def verify_filter(self, capture, sent):
101 if not len(capture) == len(sent):
102 # filter out any IPv6 RAs from the captur
104 if (p.haslayer(IPv6)):
108 def verify_capture_ip4(self, src_if, sent):
109 rxd = self.pg1.get_capture(N_PKTS_IN_STREAM)
112 capture = self.verify_filter(rxd, sent)
114 self.assertEqual(len(capture), len(sent))
116 for i in range(len(capture)):
120 # the rx'd packet has the MPLS label popped
122 self.assertEqual(eth.type, 0x800)
127 # check the MAC address on the RX'd packet is correctly formed
128 self.assertEqual(eth.dst, getmacbyip(rx_ip.dst))
130 self.assertEqual(rx_ip.src, tx_ip.src)
131 self.assertEqual(rx_ip.dst, tx_ip.dst)
132 # IP processing post pop has decremented the TTL
133 self.assertEqual(rx_ip.ttl + 1, tx_ip.ttl)
138 def verify_capture_ip6(self, src_if, sent):
139 capture = self.pg1.get_capture(N_PKTS_IN_STREAM)
141 self.assertEqual(len(capture), len(sent))
143 for i in range(len(capture)):
147 # the rx'd packet has the MPLS label popped
149 self.assertEqual(eth.type, 0x86DD)
154 # check the MAC address on the RX'd packet is correctly formed
155 self.assertEqual(eth.dst, getmacbyip6(rx_ip.dst))
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.hlim + 1, tx_ip.hlim)
162 def test_ip_mcast(self):
163 """ IP Multicast Replication """
166 # a stream that matches the default route. gets dropped.
168 self.vapi.cli("clear trace")
169 tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "232.1.1.1")
170 self.pg0.add_stream(tx)
172 self.pg_enable_capture(self.pg_interfaces)
175 self.pg0.assert_nothing_captured(
176 remark="IP multicast packets forwarded on default route")
180 # one accepting interface, pg0, 7 forwarding interfaces
181 # many forwarding interfaces test the case where the replicare DPO
182 # needs to use extra cache lines for the buckets.
184 route_232_1_1_1 = VppIpMRoute(
188 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
189 [VppMRoutePath(self.pg0.sw_if_index,
190 MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
191 VppMRoutePath(self.pg1.sw_if_index,
192 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
193 VppMRoutePath(self.pg2.sw_if_index,
194 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
195 VppMRoutePath(self.pg3.sw_if_index,
196 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
197 VppMRoutePath(self.pg4.sw_if_index,
198 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
199 VppMRoutePath(self.pg5.sw_if_index,
200 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
201 VppMRoutePath(self.pg6.sw_if_index,
202 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
203 VppMRoutePath(self.pg7.sw_if_index,
204 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
205 route_232_1_1_1.add_vpp_config()
209 # one accepting interface, pg0, 2 forwarding interfaces
211 route_1_1_1_1_232_1_1_1 = VppIpMRoute(
215 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
216 [VppMRoutePath(self.pg0.sw_if_index,
217 MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
218 VppMRoutePath(self.pg1.sw_if_index,
219 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
220 VppMRoutePath(self.pg2.sw_if_index,
221 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
222 route_1_1_1_1_232_1_1_1.add_vpp_config()
226 # one accepting interface, pg0, 1 forwarding interfaces
228 route_232 = VppIpMRoute(
232 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
233 [VppMRoutePath(self.pg0.sw_if_index,
234 MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
235 VppMRoutePath(self.pg1.sw_if_index,
236 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
237 route_232.add_vpp_config()
240 # a stream that matches the route for (1.1.1.1,232.1.1.1)
243 self.vapi.cli("clear trace")
244 tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "232.1.1.1")
245 self.pg0.add_stream(tx)
247 self.pg_enable_capture(self.pg_interfaces)
250 # We expect replications on Pg1->7
251 self.verify_capture_ip4(self.pg1, tx)
252 self.verify_capture_ip4(self.pg2, tx)
253 self.verify_capture_ip4(self.pg3, tx)
254 self.verify_capture_ip4(self.pg4, tx)
255 self.verify_capture_ip4(self.pg5, tx)
256 self.verify_capture_ip4(self.pg6, tx)
257 self.verify_capture_ip4(self.pg7, tx)
259 # no replications on Pg0
260 self.pg0.assert_nothing_captured(
261 remark="IP multicast packets forwarded on PG0")
262 self.pg3.assert_nothing_captured(
263 remark="IP multicast packets forwarded on PG3")
266 # a stream that matches the route for (1.1.1.1,232.1.1.1)
269 self.vapi.cli("clear trace")
270 tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "232.1.1.1",
272 self.pg0.add_stream(tx)
274 self.pg_enable_capture(self.pg_interfaces)
277 # We expect replications on Pg1->7
278 self.verify_capture_ip4(self.pg1, tx)
279 self.verify_capture_ip4(self.pg2, tx)
280 self.verify_capture_ip4(self.pg3, tx)
281 self.verify_capture_ip4(self.pg4, tx)
282 self.verify_capture_ip4(self.pg5, tx)
283 self.verify_capture_ip4(self.pg6, tx)
284 self.verify_capture_ip4(self.pg7, tx)
286 # no replications on Pg0
287 self.pg0.assert_nothing_captured(
288 remark="IP multicast packets forwarded on PG0")
289 self.pg3.assert_nothing_captured(
290 remark="IP multicast packets forwarded on PG3")
293 # a stream that matches the route for (*,232.0.0.0/8)
294 # Send packets with the 9th bit set so we test the correct clearing
295 # of that bit in the mac rewrite
297 self.vapi.cli("clear trace")
298 tx = self.create_stream_ip4(self.pg0, "1.1.1.1", "232.255.255.255")
299 self.pg0.add_stream(tx)
301 self.pg_enable_capture(self.pg_interfaces)
304 # We expect replications on Pg1 only
305 self.verify_capture_ip4(self.pg1, tx)
307 # no replications on Pg0, Pg2 not Pg3
308 self.pg0.assert_nothing_captured(
309 remark="IP multicast packets forwarded on PG0")
310 self.pg2.assert_nothing_captured(
311 remark="IP multicast packets forwarded on PG2")
312 self.pg3.assert_nothing_captured(
313 remark="IP multicast packets forwarded on PG3")
316 # a stream that matches the route for (*,232.1.1.1)
318 self.vapi.cli("clear trace")
319 tx = self.create_stream_ip4(self.pg0, "1.1.1.2", "232.1.1.1")
320 self.pg0.add_stream(tx)
322 self.pg_enable_capture(self.pg_interfaces)
325 # We expect replications on Pg1, 2, 3.
326 self.verify_capture_ip4(self.pg1, tx)
327 self.verify_capture_ip4(self.pg2, tx)
328 self.verify_capture_ip4(self.pg3, tx)
330 # no replications on Pg0
331 self.pg0.assert_nothing_captured(
332 remark="IP multicast packets forwarded on PG0")
334 route_232_1_1_1.remove_vpp_config()
335 route_1_1_1_1_232_1_1_1.remove_vpp_config()
336 route_232.remove_vpp_config()
338 def test_ip6_mcast(self):
339 """ IPv6 Multicast Replication """
342 # a stream that matches the default route. gets dropped.
344 self.vapi.cli("clear trace")
345 tx = self.create_stream_ip6(self.pg0, "2001::1", "ff01::1")
346 self.pg0.add_stream(tx)
348 self.pg_enable_capture(self.pg_interfaces)
351 self.pg0.assert_nothing_captured(
352 remark="IPv6 multicast packets forwarded on default route")
356 # one accepting interface, pg0, 3 forwarding interfaces
358 route_ff01_1 = VppIpMRoute(
362 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
363 [VppMRoutePath(self.pg0.sw_if_index,
364 MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
365 VppMRoutePath(self.pg1.sw_if_index,
366 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
367 VppMRoutePath(self.pg2.sw_if_index,
368 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
369 VppMRoutePath(self.pg3.sw_if_index,
370 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)],
372 route_ff01_1.add_vpp_config()
376 # one accepting interface, pg0, 2 forwarding interfaces
378 route_2001_ff01_1 = VppIpMRoute(
382 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
383 [VppMRoutePath(self.pg0.sw_if_index,
384 MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
385 VppMRoutePath(self.pg1.sw_if_index,
386 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD),
387 VppMRoutePath(self.pg2.sw_if_index,
388 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)],
390 route_2001_ff01_1.add_vpp_config()
394 # one accepting interface, pg0, 1 forwarding interface
396 route_ff01 = VppIpMRoute(
400 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
401 [VppMRoutePath(self.pg0.sw_if_index,
402 MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
403 VppMRoutePath(self.pg1.sw_if_index,
404 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)],
406 route_ff01.add_vpp_config()
409 # a stream that matches the route for (*, ff01::/16)
411 self.vapi.cli("clear trace")
412 tx = self.create_stream_ip6(self.pg0, "2002::1", "ff01:2::255")
413 self.pg0.add_stream(tx)
415 self.pg_enable_capture(self.pg_interfaces)
418 # We expect replications on Pg1
419 self.verify_capture_ip6(self.pg1, tx)
421 # no replications on Pg0, Pg3
422 self.pg0.assert_nothing_captured(
423 remark="IP multicast packets forwarded on PG0")
424 self.pg2.assert_nothing_captured(
425 remark="IP multicast packets forwarded on PG2")
426 self.pg3.assert_nothing_captured(
427 remark="IP multicast packets forwarded on PG3")
430 # a stream that matches the route for (*,ff01::1)
432 self.vapi.cli("clear trace")
433 tx = self.create_stream_ip6(self.pg0, "2002::2", "ff01::1")
434 self.pg0.add_stream(tx)
436 self.pg_enable_capture(self.pg_interfaces)
439 # We expect replications on Pg1, 2, 3.
440 self.verify_capture_ip6(self.pg1, tx)
441 self.verify_capture_ip6(self.pg2, tx)
442 self.verify_capture_ip6(self.pg3, tx)
444 # no replications on Pg0
445 self.pg0.assert_nothing_captured(
446 remark="IPv6 multicast packets forwarded on PG0")
449 # a stream that matches the route for (2001::1, ff00::1)
451 self.vapi.cli("clear trace")
452 tx = self.create_stream_ip6(self.pg0, "2001::1", "ff01::1")
453 self.pg0.add_stream(tx)
455 self.pg_enable_capture(self.pg_interfaces)
458 # We expect replications on Pg1, 2,
459 self.verify_capture_ip6(self.pg1, tx)
460 self.verify_capture_ip6(self.pg2, tx)
462 # no replications on Pg0, Pg3
463 self.pg0.assert_nothing_captured(
464 remark="IP multicast packets forwarded on PG0")
465 self.pg3.assert_nothing_captured(
466 remark="IP multicast packets forwarded on PG3")
468 route_ff01.remove_vpp_config()
469 route_ff01_1.remove_vpp_config()
470 route_2001_ff01_1.remove_vpp_config()
472 def _mcast_connected_send_stream(self, dst_ip):
473 self.vapi.cli("clear trace")
474 tx = self.create_stream_ip4(self.pg0,
477 self.pg0.add_stream(tx)
479 self.pg_enable_capture(self.pg_interfaces)
482 # We expect replications on Pg1.
483 self.verify_capture_ip4(self.pg1, tx)
487 def test_ip_mcast_connected(self):
488 """ IP Multicast Connected Source check """
492 # one accepting interface, pg0, 1 forwarding interfaces
494 route_232_1_1_1 = VppIpMRoute(
498 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
499 [VppMRoutePath(self.pg0.sw_if_index,
500 MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
501 VppMRoutePath(self.pg1.sw_if_index,
502 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
504 route_232_1_1_1.add_vpp_config()
505 route_232_1_1_1.update_entry_flags(
506 MRouteEntryFlags.MFIB_ENTRY_FLAG_CONNECTED)
509 # Now the (*,G) is present, send from connected source
511 tx = self._mcast_connected_send_stream("232.1.1.1")
514 # Constrct a representation of the signal we expect on pg0
516 signal_232_1_1_1_itf_0 = VppMFibSignal(self,
518 self.pg0.sw_if_index,
522 # read the only expected signal
524 signals = self.vapi.mfib_signal_dump()
526 self.assertEqual(1, len(signals))
528 signal_232_1_1_1_itf_0.compare(signals[0])
531 # reading the signal allows for the generation of another
532 # so send more packets and expect the next signal
534 tx = self._mcast_connected_send_stream("232.1.1.1")
536 signals = self.vapi.mfib_signal_dump()
537 self.assertEqual(1, len(signals))
538 signal_232_1_1_1_itf_0.compare(signals[0])
541 # A Second entry with connected check
542 # one accepting interface, pg0, 1 forwarding interfaces
544 route_232_1_1_2 = VppIpMRoute(
548 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
549 [VppMRoutePath(self.pg0.sw_if_index,
550 MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
551 VppMRoutePath(self.pg1.sw_if_index,
552 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
554 route_232_1_1_2.add_vpp_config()
555 route_232_1_1_2.update_entry_flags(
556 MRouteEntryFlags.MFIB_ENTRY_FLAG_CONNECTED)
559 # Send traffic to both entries. One read should net us two signals
561 signal_232_1_1_2_itf_0 = VppMFibSignal(self,
563 self.pg0.sw_if_index,
565 tx = self._mcast_connected_send_stream("232.1.1.1")
566 tx2 = self._mcast_connected_send_stream("232.1.1.2")
569 # read the only expected signal
571 signals = self.vapi.mfib_signal_dump()
573 self.assertEqual(2, len(signals))
575 signal_232_1_1_1_itf_0.compare(signals[1])
576 signal_232_1_1_2_itf_0.compare(signals[0])
578 route_232_1_1_1.remove_vpp_config()
579 route_232_1_1_2.remove_vpp_config()
581 def test_ip_mcast_signal(self):
582 """ IP Multicast Signal """
586 # one accepting interface, pg0, 1 forwarding interfaces
588 route_232_1_1_1 = VppIpMRoute(
592 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
593 [VppMRoutePath(self.pg0.sw_if_index,
594 MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
595 VppMRoutePath(self.pg1.sw_if_index,
596 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
598 route_232_1_1_1.add_vpp_config()
599 route_232_1_1_1.update_entry_flags(
600 MRouteEntryFlags.MFIB_ENTRY_FLAG_SIGNAL)
603 # Now the (*,G) is present, send from connected source
605 tx = self._mcast_connected_send_stream("232.1.1.1")
608 # Constrct a representation of the signal we expect on pg0
610 signal_232_1_1_1_itf_0 = VppMFibSignal(self,
612 self.pg0.sw_if_index,
616 # read the only expected signal
618 signals = self.vapi.mfib_signal_dump()
620 self.assertEqual(1, len(signals))
622 signal_232_1_1_1_itf_0.compare(signals[0])
625 # reading the signal allows for the generation of another
626 # so send more packets and expect the next signal
628 tx = self._mcast_connected_send_stream("232.1.1.1")
630 signals = self.vapi.mfib_signal_dump()
631 self.assertEqual(1, len(signals))
632 signal_232_1_1_1_itf_0.compare(signals[0])
635 # Set the negate-signal on the accepting interval - the signals
638 route_232_1_1_1.update_path_flags(
639 self.pg0.sw_if_index,
640 (MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT |
641 MRouteItfFlags.MFIB_ITF_FLAG_NEGATE_SIGNAL))
643 tx = self._mcast_connected_send_stream("232.1.1.1")
645 signals = self.vapi.mfib_signal_dump()
646 self.assertEqual(0, len(signals))
649 # Clear the SIGNAL flag on the entry and the signals should
650 # come back since the interface is still NEGATE-SIGNAL
652 route_232_1_1_1.update_entry_flags(
653 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE)
655 tx = self._mcast_connected_send_stream("232.1.1.1")
657 signals = self.vapi.mfib_signal_dump()
658 self.assertEqual(1, len(signals))
659 signal_232_1_1_1_itf_0.compare(signals[0])
662 # Lastly remove the NEGATE-SIGNAL from the interface and the
663 # signals should stop
665 route_232_1_1_1.update_path_flags(self.pg0.sw_if_index,
666 MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT)
668 tx = self._mcast_connected_send_stream("232.1.1.1")
669 signals = self.vapi.mfib_signal_dump()
670 self.assertEqual(0, len(signals))
675 route_232_1_1_1.remove_vpp_config()
678 if __name__ == '__main__':
679 unittest.main(testRunner=VppTestRunner)