6 from framework import VppTestCase, VppTestRunner, running_extended_tests
7 from vpp_ip_route import VppIpRoute, VppRoutePath, VppMplsRoute, \
8 VppMplsTable, VppIpMRoute, VppMRoutePath, VppIpTable, \
9 MRouteEntryFlags, MRouteItfFlags, MPLS_LABEL_INVALID, DpoProto, \
11 from vpp_bier import *
12 from vpp_udp_encap import *
14 from scapy.packet import Raw
15 from scapy.layers.l2 import Ether
16 from scapy.layers.inet import IP, UDP, ICMP
17 from scapy.layers.inet6 import IPv6
18 from scapy.contrib.mpls import MPLS
19 from scapy.contrib.bier import *
22 class TestBFIB(VppTestCase):
23 """ BIER FIB Test Case """
26 """ BFIB Unit Tests """
27 error = self.vapi.cli("test bier")
30 self.logger.critical(error)
31 self.assertEqual(error.find("Failed"), -1)
34 class TestBier(VppTestCase):
35 """ BIER Test Case """
38 super(TestBier, self).setUp()
40 # create 2 pg interfaces
41 self.create_pg_interfaces(range(3))
43 # create the default MPLS table
45 tbl = VppMplsTable(self, 0)
47 self.tables.append(tbl)
49 tbl = VppIpTable(self, 10)
51 self.tables.append(tbl)
53 # setup both interfaces
54 for i in self.pg_interfaces:
63 for i in self.pg_interfaces:
68 super(TestBier, self).tearDown()
70 def bier_midpoint(self, hdr_len_id, n_bytes, max_bp):
74 # Add a BIER table for sub-domain 0, set 0, and BSL 256
76 bti = VppBierTableID(0, 0, hdr_len_id)
77 bt = VppBierTable(self, bti, 77)
81 # A packet with no bits set gets dropped
83 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
84 MPLS(label=77, ttl=255) /
85 BIER(length=hdr_len_id) /
86 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.remote_ip6) /
87 UDP(sport=1234, dport=1234) /
91 self.send_and_assert_no_replies(self.pg0, pkts,
95 # Add a BIER route for each bit-position in the table via a different
96 # next-hop. Testing whether the BIER walk and replicate forwarding
97 # function works for all bit posisitons.
101 for i in range(1, max_bp+1):
102 nh = "10.0.%d.%d" % (i / 255, i % 255)
104 VppIpRoute(self, nh, 32,
105 [VppRoutePath(self.pg1.remote_ip4,
106 self.pg1.sw_if_index,
107 labels=[VppMplsLabel(2000+i)])]))
108 nh_routes[-1].add_vpp_config()
111 VppBierRoute(self, bti, i,
112 [VppRoutePath(nh, 0xffffffff,
113 labels=[VppMplsLabel(100+i)])]))
114 bier_routes[-1].add_vpp_config()
117 # A packet with all bits set gets replicated once for each bit
119 pkt_sizes = [64, 1400]
121 for pkt_size in pkt_sizes:
122 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
123 MPLS(label=77, ttl=255) /
124 BIER(length=hdr_len_id, BitString=chr(255)*n_bytes) /
125 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.remote_ip6) /
126 UDP(sport=1234, dport=1234) /
127 Raw(chr(5) * pkt_size))
130 self.pg0.add_stream(pkts)
131 self.pg_enable_capture(self.pg_interfaces)
134 rx = self.pg1.get_capture(max_bp)
138 # The packets are not required to be sent in bit-position order
139 # when we setup the routes above we used the bit-position to
140 # construct the out-label. so use that here to determine the BP
143 bp = olabel.label - 2000
145 blabel = olabel[MPLS].payload
146 self.assertEqual(blabel.label, 100+bp)
147 self.assertEqual(blabel.ttl, 254)
149 bier_hdr = blabel[MPLS].payload
151 self.assertEqual(bier_hdr.id, 5)
152 self.assertEqual(bier_hdr.version, 0)
153 self.assertEqual(bier_hdr.length, hdr_len_id)
154 self.assertEqual(bier_hdr.entropy, 0)
155 self.assertEqual(bier_hdr.OAM, 0)
156 self.assertEqual(bier_hdr.RSV, 0)
157 self.assertEqual(bier_hdr.DSCP, 0)
158 self.assertEqual(bier_hdr.Proto, 5)
160 # The bit-string should consist only of the BP given by i.
161 byte_array = ['\0'] * (n_bytes)
162 byte_val = chr(1 << (bp - 1) % 8)
163 byte_pos = n_bytes - (((bp - 1) / 8) + 1)
164 byte_array[byte_pos] = byte_val
165 bitstring = ''.join(byte_array)
167 self.assertEqual(len(bitstring), len(bier_hdr.BitString))
168 self.assertEqual(bitstring, bier_hdr.BitString)
171 # cleanup. not strictly necessary, but it's much quicker this way
172 # becuase the bier_fib_dump and ip_fib_dump will be empty when the
173 # auto-cleanup kicks in
175 for br in bier_routes:
176 br.remove_vpp_config()
177 for nhr in nh_routes:
178 nhr.remove_vpp_config()
180 @unittest.skipUnless(running_extended_tests(), "part of extended tests")
181 def test_bier_midpoint_1024(self):
182 """BIER midpoint BSL:1024"""
183 self.bier_midpoint(BIERLength.BIER_LEN_1024, 128, 1024)
185 @unittest.skipUnless(running_extended_tests(), "part of extended tests")
186 def test_bier_midpoint_512(self):
187 """BIER midpoint BSL:512"""
188 self.bier_midpoint(BIERLength.BIER_LEN_512, 64, 512)
190 @unittest.skipUnless(running_extended_tests(), "part of extended tests")
191 def test_bier_midpoint_256(self):
192 """BIER midpoint BSL:256"""
193 self.bier_midpoint(BIERLength.BIER_LEN_256, 32, 256)
195 @unittest.skipUnless(running_extended_tests(), "part of extended tests")
196 def test_bier_midpoint_128(self):
197 """BIER midpoint BSL:128"""
198 self.bier_midpoint(BIERLength.BIER_LEN_128, 16, 128)
200 def test_bier_midpoint_64(self):
201 """BIER midpoint BSL:64"""
202 self.bier_midpoint(BIERLength.BIER_LEN_64, 8, 64)
204 def test_bier_head(self):
208 # Add a BIER table for sub-domain 0, set 0, and BSL 256
210 bti = VppBierTableID(0, 0, BIERLength.BIER_LEN_256)
211 bt = VppBierTable(self, bti, 77)
215 # 2 bit positions via two next hops
219 ip_route_1 = VppIpRoute(self, nh1, 32,
220 [VppRoutePath(self.pg1.remote_ip4,
221 self.pg1.sw_if_index,
222 labels=[VppMplsLabel(2001)])])
223 ip_route_2 = VppIpRoute(self, nh2, 32,
224 [VppRoutePath(self.pg1.remote_ip4,
225 self.pg1.sw_if_index,
226 labels=[VppMplsLabel(2002)])])
227 ip_route_1.add_vpp_config()
228 ip_route_2.add_vpp_config()
230 bier_route_1 = VppBierRoute(self, bti, 1,
231 [VppRoutePath(nh1, 0xffffffff,
232 labels=[VppMplsLabel(101)])])
233 bier_route_2 = VppBierRoute(self, bti, 2,
234 [VppRoutePath(nh2, 0xffffffff,
235 labels=[VppMplsLabel(102)])])
236 bier_route_1.add_vpp_config()
237 bier_route_2.add_vpp_config()
240 # An imposition object with both bit-positions set
242 bi = VppBierImp(self, bti, 333, chr(0x3) * 32)
246 # Add a multicast route that will forward into the BIER doamin
248 route_ing_232_1_1_1 = VppIpMRoute(
252 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
253 paths=[VppMRoutePath(self.pg0.sw_if_index,
254 MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
255 VppMRoutePath(0xffffffff,
256 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
257 proto=DpoProto.DPO_PROTO_BIER,
258 bier_imp=bi.bi_index)])
259 route_ing_232_1_1_1.add_vpp_config()
262 # inject an IP packet. We expect it to be BIER encapped and
265 p = (Ether(dst=self.pg0.local_mac,
266 src=self.pg0.remote_mac) /
267 IP(src="1.1.1.1", dst="232.1.1.1") /
268 UDP(sport=1234, dport=1234))
270 self.pg0.add_stream([p])
271 self.pg_enable_capture(self.pg_interfaces)
274 rx = self.pg1.get_capture(2)
277 # Encap Stack is; eth, MPLS, MPLS, BIER
279 igp_mpls = rx[0][MPLS]
280 self.assertEqual(igp_mpls.label, 2001)
281 self.assertEqual(igp_mpls.ttl, 64)
282 self.assertEqual(igp_mpls.s, 0)
283 bier_mpls = igp_mpls[MPLS].payload
284 self.assertEqual(bier_mpls.label, 101)
285 self.assertEqual(bier_mpls.ttl, 64)
286 self.assertEqual(bier_mpls.s, 1)
287 self.assertEqual(rx[0][BIER].length, 2)
289 igp_mpls = rx[1][MPLS]
290 self.assertEqual(igp_mpls.label, 2002)
291 self.assertEqual(igp_mpls.ttl, 64)
292 self.assertEqual(igp_mpls.s, 0)
293 bier_mpls = igp_mpls[MPLS].payload
294 self.assertEqual(bier_mpls.label, 102)
295 self.assertEqual(bier_mpls.ttl, 64)
296 self.assertEqual(bier_mpls.s, 1)
297 self.assertEqual(rx[0][BIER].length, 2)
299 def test_bier_tail(self):
303 # Add a BIER table for sub-domain 0, set 0, and BSL 256
305 bti = VppBierTableID(0, 0, BIERLength.BIER_LEN_256)
306 bt = VppBierTable(self, bti, 77)
312 bdt = VppBierDispTable(self, 8)
316 # BIER route in table that's for-us
318 bier_route_1 = VppBierRoute(self, bti, 1,
319 [VppRoutePath("0.0.0.0",
322 bier_route_1.add_vpp_config()
325 # An entry in the disposition table
327 bier_de_1 = VppBierDispEntry(self, bdt.id, 99,
328 BIER_HDR_PAYLOAD.BIER_HDR_PROTO_IPV4,
329 DpoProto.DPO_PROTO_BIER,
330 "0.0.0.0", 0, rpf_id=8192)
331 bier_de_1.add_vpp_config()
334 # A multicast route to forward post BIER disposition
336 route_eg_232_1_1_1 = VppIpMRoute(
340 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
341 paths=[VppMRoutePath(self.pg1.sw_if_index,
342 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
343 route_eg_232_1_1_1.add_vpp_config()
344 route_eg_232_1_1_1.update_rpf_id(8192)
347 # A packet with all bits set gets spat out to BP:1
349 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
350 MPLS(label=77, ttl=255) /
351 BIER(length=BIERLength.BIER_LEN_256,
352 BitString=chr(255)*32,
354 IP(src="1.1.1.1", dst="232.1.1.1") /
355 UDP(sport=1234, dport=1234) /
358 self.send_and_expect(self.pg0, [p], self.pg1)
361 # A packet that does not match the Disposition entry gets dropped
363 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
364 MPLS(label=77, ttl=255) /
365 BIER(length=BIERLength.BIER_LEN_256,
366 BitString=chr(255)*32,
368 IP(src="1.1.1.1", dst="232.1.1.1") /
369 UDP(sport=1234, dport=1234) /
371 self.send_and_assert_no_replies(self.pg0, p*2,
372 "no matching disposition entry")
375 # Add the default route to the disposition table
377 bier_de_2 = VppBierDispEntry(self, bdt.id, 0,
378 BIER_HDR_PAYLOAD.BIER_HDR_PROTO_IPV4,
379 DpoProto.DPO_PROTO_BIER,
380 "0.0.0.0", 0, rpf_id=8192)
381 bier_de_2.add_vpp_config()
384 # now the previous packet is forwarded
386 self.send_and_expect(self.pg0, [p], self.pg1)
388 def bier_e2e(self, hdr_len_id, n_bytes, max_bp):
389 """ BIER end-to-end"""
392 # Add a BIER table for sub-domain 0, set 0, and BSL 256
394 bti = VppBierTableID(0, 0, hdr_len_id)
395 bt = VppBierTable(self, bti, 77)
398 lowest = ['\0'] * (n_bytes)
400 highest = ['\0'] * (n_bytes)
401 highest[0] = chr(128)
404 # Impostion Sets bit strings
406 bi_low = VppBierImp(self, bti, 333, lowest)
407 bi_low.add_vpp_config()
408 bi_high = VppBierImp(self, bti, 334, highest)
409 bi_high.add_vpp_config()
412 # Add a multicast route that will forward into the BIER doamin
414 route_ing_232_1_1_1 = VppIpMRoute(
418 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
419 paths=[VppMRoutePath(self.pg0.sw_if_index,
420 MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
421 VppMRoutePath(0xffffffff,
422 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
423 proto=DpoProto.DPO_PROTO_BIER,
424 bier_imp=bi_low.bi_index)])
425 route_ing_232_1_1_1.add_vpp_config()
426 route_ing_232_1_1_2 = VppIpMRoute(
430 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
431 paths=[VppMRoutePath(self.pg0.sw_if_index,
432 MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
433 VppMRoutePath(0xffffffff,
434 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
435 proto=DpoProto.DPO_PROTO_BIER,
436 bier_imp=bi_high.bi_index)])
437 route_ing_232_1_1_2.add_vpp_config()
440 # disposition table 8
442 bdt = VppBierDispTable(self, 8)
446 # BIER routes in table that are for-us, resolving through
449 bier_route_1 = VppBierRoute(self, bti, 1,
450 [VppRoutePath("0.0.0.0",
453 bier_route_1.add_vpp_config()
454 bier_route_max = VppBierRoute(self, bti, max_bp,
455 [VppRoutePath("0.0.0.0",
458 bier_route_max.add_vpp_config()
461 # An entry in the disposition table for sender 333
464 bier_de_1 = VppBierDispEntry(self, bdt.id, 333,
465 BIER_HDR_PAYLOAD.BIER_HDR_PROTO_IPV4,
466 DpoProto.DPO_PROTO_BIER,
467 "0.0.0.0", 10, rpf_id=8192)
468 bier_de_1.add_vpp_config()
469 bier_de_1 = VppBierDispEntry(self, bdt.id, 334,
470 BIER_HDR_PAYLOAD.BIER_HDR_PROTO_IPV4,
471 DpoProto.DPO_PROTO_BIER,
472 "0.0.0.0", 10, rpf_id=8193)
473 bier_de_1.add_vpp_config()
476 # Add a multicast routes that will forward the traffic
479 route_eg_232_1_1_1 = VppIpMRoute(
483 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
485 paths=[VppMRoutePath(self.pg1.sw_if_index,
486 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
487 route_eg_232_1_1_1.add_vpp_config()
488 route_eg_232_1_1_1.update_rpf_id(8192)
489 route_eg_232_1_1_2 = VppIpMRoute(
493 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
495 paths=[VppMRoutePath(self.pg1.sw_if_index,
496 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
497 route_eg_232_1_1_2.add_vpp_config()
498 route_eg_232_1_1_2.update_rpf_id(8193)
501 # inject a packet in VRF-0. We expect it to be BIER encapped,
502 # replicated, then hit the disposition and be forwarded
503 # out of VRF 10, i.e. on pg1
505 p = (Ether(dst=self.pg0.local_mac,
506 src=self.pg0.remote_mac) /
507 IP(src="1.1.1.1", dst="232.1.1.1") /
508 UDP(sport=1234, dport=1234) /
511 rx = self.send_and_expect(self.pg0, p*65, self.pg1)
513 self.assertEqual(rx[0][IP].src, "1.1.1.1")
514 self.assertEqual(rx[0][IP].dst, "232.1.1.1")
516 p = (Ether(dst=self.pg0.local_mac,
517 src=self.pg0.remote_mac) /
518 IP(src="1.1.1.1", dst="232.1.1.2") /
519 UDP(sport=1234, dport=1234) /
522 rx = self.send_and_expect(self.pg0, p*65, self.pg1)
523 self.assertEqual(rx[0][IP].src, "1.1.1.1")
524 self.assertEqual(rx[0][IP].dst, "232.1.1.2")
526 @unittest.skipUnless(running_extended_tests(), "part of extended tests")
527 def test_bier_e2e_1024(self):
528 """ BIER end-to-end BSL:1024"""
529 self.bier_e2e(BIERLength.BIER_LEN_1024, 128, 1024)
531 @unittest.skipUnless(running_extended_tests(), "part of extended tests")
532 def test_bier_e2e_512(self):
533 """ BIER end-to-end BSL:512"""
534 self.bier_e2e(BIERLength.BIER_LEN_512, 64, 512)
536 @unittest.skipUnless(running_extended_tests(), "part of extended tests")
537 def test_bier_e2e_256(self):
538 """ BIER end-to-end BSL:256"""
539 self.bier_e2e(BIERLength.BIER_LEN_256, 32, 256)
541 @unittest.skipUnless(running_extended_tests(), "part of extended tests")
542 def test_bier_e2e_128(self):
543 """ BIER end-to-end BSL:128"""
544 self.bier_e2e(BIERLength.BIER_LEN_128, 16, 128)
546 def test_bier_e2e_64(self):
547 """ BIER end-to-end BSL:64"""
548 self.bier_e2e(BIERLength.BIER_LEN_64, 8, 64)
550 def test_bier_head_o_udp(self):
551 """BIER head over UDP"""
554 # Add a BIER table for sub-domain 1, set 0, and BSL 256
556 bti = VppBierTableID(1, 0, BIERLength.BIER_LEN_256)
557 bt = VppBierTable(self, bti, 77)
561 # 1 bit positions via 1 next hops
564 ip_route = VppIpRoute(self, nh1, 32,
565 [VppRoutePath(self.pg1.remote_ip4,
566 self.pg1.sw_if_index,
567 labels=[VppMplsLabel(2001)])])
568 ip_route.add_vpp_config()
570 udp_encap = VppUdpEncap(self, 4,
574 udp_encap.add_vpp_config()
576 bier_route = VppBierRoute(self, bti, 1,
577 [VppRoutePath("0.0.0.0",
581 bier_route.add_vpp_config()
584 # An 2 imposition objects with all bit-positions set
585 # only use the second, but creating 2 tests with a non-zero
586 # value index in the route add
588 bi = VppBierImp(self, bti, 333, chr(0xff) * 32)
590 bi2 = VppBierImp(self, bti, 334, chr(0xff) * 32)
594 # Add a multicast route that will forward into the BIER doamin
596 route_ing_232_1_1_1 = VppIpMRoute(
600 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
601 paths=[VppMRoutePath(self.pg0.sw_if_index,
602 MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
603 VppMRoutePath(0xffffffff,
604 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
605 proto=DpoProto.DPO_PROTO_BIER,
606 bier_imp=bi2.bi_index)])
607 route_ing_232_1_1_1.add_vpp_config()
610 # inject a packet an IP. We expect it to be BIER and UDP encapped,
612 p = (Ether(dst=self.pg0.local_mac,
613 src=self.pg0.remote_mac) /
614 IP(src="1.1.1.1", dst="232.1.1.1") /
615 UDP(sport=1234, dport=1234))
617 self.pg0.add_stream([p])
618 self.pg_enable_capture(self.pg_interfaces)
621 rx = self.pg1.get_capture(1)
624 # Encap Stack is, eth, IP, UDP, BIFT, BIER
626 self.assertEqual(rx[0][IP].src, self.pg0.local_ip4)
627 self.assertEqual(rx[0][IP].dst, nh1)
628 self.assertEqual(rx[0][UDP].sport, 330)
629 self.assertEqual(rx[0][UDP].dport, 8138)
630 self.assertEqual(rx[0][BIFT].bsl, 2)
631 self.assertEqual(rx[0][BIFT].sd, 1)
632 self.assertEqual(rx[0][BIFT].set, 0)
633 self.assertEqual(rx[0][BIFT].ttl, 64)
634 self.assertEqual(rx[0][BIER].length, 2)
636 def test_bier_tail_o_udp(self):
637 """BIER Tail over UDP"""
640 # Add a BIER table for sub-domain 0, set 0, and BSL 256
642 bti = VppBierTableID(1, 0, BIERLength.BIER_LEN_256)
643 bt = VppBierTable(self, bti, MPLS_LABEL_INVALID)
649 bdt = VppBierDispTable(self, 8)
653 # BIER route in table that's for-us
655 bier_route_1 = VppBierRoute(self, bti, 1,
656 [VppRoutePath("0.0.0.0",
659 bier_route_1.add_vpp_config()
662 # An entry in the disposition table
664 bier_de_1 = VppBierDispEntry(self, bdt.id, 99,
665 BIER_HDR_PAYLOAD.BIER_HDR_PROTO_IPV4,
666 DpoProto.DPO_PROTO_BIER,
667 "0.0.0.0", 0, rpf_id=8192)
668 bier_de_1.add_vpp_config()
671 # A multicast route to forward post BIER disposition
673 route_eg_232_1_1_1 = VppIpMRoute(
677 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
678 paths=[VppMRoutePath(self.pg1.sw_if_index,
679 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
680 route_eg_232_1_1_1.add_vpp_config()
681 route_eg_232_1_1_1.update_rpf_id(8192)
684 # A packet with all bits set gets spat out to BP:1
686 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
687 IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
688 UDP(sport=333, dport=8138) /
689 BIFT(sd=1, set=0, bsl=2, ttl=255) /
690 BIER(length=BIERLength.BIER_LEN_256,
691 BitString=chr(255)*32,
693 IP(src="1.1.1.1", dst="232.1.1.1") /
694 UDP(sport=1234, dport=1234) /
697 rx = self.send_and_expect(self.pg0, [p], self.pg1)
700 if __name__ == '__main__':
701 unittest.main(testRunner=VppTestRunner)