6 from framework import VppTestCase, VppTestRunner, running_extended_tests
7 from vpp_ip import DpoProto
8 from vpp_ip_route import VppIpRoute, VppRoutePath, VppMplsRoute, \
9 VppMplsTable, VppIpMRoute, VppMRoutePath, VppIpTable, \
10 MRouteEntryFlags, MRouteItfFlags, MPLS_LABEL_INVALID, \
12 from vpp_bier import *
13 from vpp_udp_encap import *
15 from scapy.packet import Raw
16 from scapy.layers.l2 import Ether
17 from scapy.layers.inet import IP, UDP, ICMP
18 from scapy.layers.inet6 import IPv6
19 from scapy.contrib.mpls import MPLS
20 from scapy.contrib.bier import *
23 class TestBFIB(VppTestCase):
24 """ BIER FIB Test Case """
27 """ BFIB Unit Tests """
28 error = self.vapi.cli("test bier")
31 self.logger.critical(error)
32 self.assertEqual(error.find("Failed"), -1)
35 class TestBier(VppTestCase):
36 """ BIER Test Case """
39 super(TestBier, self).setUp()
41 # create 2 pg interfaces
42 self.create_pg_interfaces(range(3))
44 # create the default MPLS table
46 tbl = VppMplsTable(self, 0)
48 self.tables.append(tbl)
50 tbl = VppIpTable(self, 10)
52 self.tables.append(tbl)
54 # setup both interfaces
55 for i in self.pg_interfaces:
64 for i in self.pg_interfaces:
69 super(TestBier, self).tearDown()
71 def bier_midpoint(self, hdr_len_id, n_bytes, max_bp):
75 # Add a BIER table for sub-domain 0, set 0, and BSL 256
77 bti = VppBierTableID(0, 0, hdr_len_id)
78 bt = VppBierTable(self, bti, 77)
82 # A packet with no bits set gets dropped
84 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
85 MPLS(label=77, ttl=255) /
86 BIER(length=hdr_len_id) /
87 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.remote_ip6) /
88 UDP(sport=1234, dport=1234) /
92 self.send_and_assert_no_replies(self.pg0, pkts,
96 # Add a BIER route for each bit-position in the table via a different
97 # next-hop. Testing whether the BIER walk and replicate forwarding
98 # function works for all bit posisitons.
102 for i in range(1, max_bp+1):
103 nh = "10.0.%d.%d" % (i / 255, i % 255)
105 VppIpRoute(self, nh, 32,
106 [VppRoutePath(self.pg1.remote_ip4,
107 self.pg1.sw_if_index,
108 labels=[VppMplsLabel(2000+i)])]))
109 nh_routes[-1].add_vpp_config()
112 VppBierRoute(self, bti, i,
113 [VppRoutePath(nh, 0xffffffff,
114 labels=[VppMplsLabel(100+i)])]))
115 bier_routes[-1].add_vpp_config()
118 # A packet with all bits set gets replicated once for each bit
120 pkt_sizes = [64, 1400]
122 for pkt_size in pkt_sizes:
123 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
124 MPLS(label=77, ttl=255) /
125 BIER(length=hdr_len_id, BitString=chr(255)*n_bytes) /
126 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.remote_ip6) /
127 UDP(sport=1234, dport=1234) /
128 Raw(chr(5) * pkt_size))
131 self.pg0.add_stream(pkts)
132 self.pg_enable_capture(self.pg_interfaces)
135 rx = self.pg1.get_capture(max_bp)
139 # The packets are not required to be sent in bit-position order
140 # when we setup the routes above we used the bit-position to
141 # construct the out-label. so use that here to determine the BP
144 bp = olabel.label - 2000
146 blabel = olabel[MPLS].payload
147 self.assertEqual(blabel.label, 100+bp)
148 self.assertEqual(blabel.ttl, 254)
150 bier_hdr = blabel[MPLS].payload
152 self.assertEqual(bier_hdr.id, 5)
153 self.assertEqual(bier_hdr.version, 0)
154 self.assertEqual(bier_hdr.length, hdr_len_id)
155 self.assertEqual(bier_hdr.entropy, 0)
156 self.assertEqual(bier_hdr.OAM, 0)
157 self.assertEqual(bier_hdr.RSV, 0)
158 self.assertEqual(bier_hdr.DSCP, 0)
159 self.assertEqual(bier_hdr.Proto, 5)
161 # The bit-string should consist only of the BP given by i.
162 byte_array = ['\0'] * (n_bytes)
163 byte_val = chr(1 << (bp - 1) % 8)
164 byte_pos = n_bytes - (((bp - 1) / 8) + 1)
165 byte_array[byte_pos] = byte_val
166 bitstring = ''.join(byte_array)
168 self.assertEqual(len(bitstring), len(bier_hdr.BitString))
169 self.assertEqual(bitstring, bier_hdr.BitString)
172 # cleanup. not strictly necessary, but it's much quicker this way
173 # becuase the bier_fib_dump and ip_fib_dump will be empty when the
174 # auto-cleanup kicks in
176 for br in bier_routes:
177 br.remove_vpp_config()
178 for nhr in nh_routes:
179 nhr.remove_vpp_config()
181 @unittest.skipUnless(running_extended_tests(), "part of extended tests")
182 def test_bier_midpoint_1024(self):
183 """BIER midpoint BSL:1024"""
184 self.bier_midpoint(BIERLength.BIER_LEN_1024, 128, 1024)
186 @unittest.skipUnless(running_extended_tests(), "part of extended tests")
187 def test_bier_midpoint_512(self):
188 """BIER midpoint BSL:512"""
189 self.bier_midpoint(BIERLength.BIER_LEN_512, 64, 512)
191 @unittest.skipUnless(running_extended_tests(), "part of extended tests")
192 def test_bier_midpoint_256(self):
193 """BIER midpoint BSL:256"""
194 self.bier_midpoint(BIERLength.BIER_LEN_256, 32, 256)
196 @unittest.skipUnless(running_extended_tests(), "part of extended tests")
197 def test_bier_midpoint_128(self):
198 """BIER midpoint BSL:128"""
199 self.bier_midpoint(BIERLength.BIER_LEN_128, 16, 128)
201 def test_bier_midpoint_64(self):
202 """BIER midpoint BSL:64"""
203 self.bier_midpoint(BIERLength.BIER_LEN_64, 8, 64)
205 def test_bier_head(self):
209 # Add a BIER table for sub-domain 0, set 0, and BSL 256
211 bti = VppBierTableID(0, 0, BIERLength.BIER_LEN_256)
212 bt = VppBierTable(self, bti, 77)
216 # 2 bit positions via two next hops
220 ip_route_1 = VppIpRoute(self, nh1, 32,
221 [VppRoutePath(self.pg1.remote_ip4,
222 self.pg1.sw_if_index,
223 labels=[VppMplsLabel(2001)])])
224 ip_route_2 = VppIpRoute(self, nh2, 32,
225 [VppRoutePath(self.pg1.remote_ip4,
226 self.pg1.sw_if_index,
227 labels=[VppMplsLabel(2002)])])
228 ip_route_1.add_vpp_config()
229 ip_route_2.add_vpp_config()
231 bier_route_1 = VppBierRoute(self, bti, 1,
232 [VppRoutePath(nh1, 0xffffffff,
233 labels=[VppMplsLabel(101)])])
234 bier_route_2 = VppBierRoute(self, bti, 2,
235 [VppRoutePath(nh2, 0xffffffff,
236 labels=[VppMplsLabel(102)])])
237 bier_route_1.add_vpp_config()
238 bier_route_2.add_vpp_config()
241 # An imposition object with both bit-positions set
243 bi = VppBierImp(self, bti, 333, chr(0x3) * 32)
247 # Add a multicast route that will forward into the BIER doamin
249 route_ing_232_1_1_1 = VppIpMRoute(
253 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
254 paths=[VppMRoutePath(self.pg0.sw_if_index,
255 MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
256 VppMRoutePath(0xffffffff,
257 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
258 proto=DpoProto.DPO_PROTO_BIER,
259 bier_imp=bi.bi_index)])
260 route_ing_232_1_1_1.add_vpp_config()
263 # inject an IP packet. We expect it to be BIER encapped and
266 p = (Ether(dst=self.pg0.local_mac,
267 src=self.pg0.remote_mac) /
268 IP(src="1.1.1.1", dst="232.1.1.1") /
269 UDP(sport=1234, dport=1234))
271 self.pg0.add_stream([p])
272 self.pg_enable_capture(self.pg_interfaces)
275 rx = self.pg1.get_capture(2)
278 # Encap Stack is; eth, MPLS, MPLS, BIER
280 igp_mpls = rx[0][MPLS]
281 self.assertEqual(igp_mpls.label, 2001)
282 self.assertEqual(igp_mpls.ttl, 64)
283 self.assertEqual(igp_mpls.s, 0)
284 bier_mpls = igp_mpls[MPLS].payload
285 self.assertEqual(bier_mpls.label, 101)
286 self.assertEqual(bier_mpls.ttl, 64)
287 self.assertEqual(bier_mpls.s, 1)
288 self.assertEqual(rx[0][BIER].length, 2)
290 igp_mpls = rx[1][MPLS]
291 self.assertEqual(igp_mpls.label, 2002)
292 self.assertEqual(igp_mpls.ttl, 64)
293 self.assertEqual(igp_mpls.s, 0)
294 bier_mpls = igp_mpls[MPLS].payload
295 self.assertEqual(bier_mpls.label, 102)
296 self.assertEqual(bier_mpls.ttl, 64)
297 self.assertEqual(bier_mpls.s, 1)
298 self.assertEqual(rx[0][BIER].length, 2)
300 def test_bier_tail(self):
304 # Add a BIER table for sub-domain 0, set 0, and BSL 256
306 bti = VppBierTableID(0, 0, BIERLength.BIER_LEN_256)
307 bt = VppBierTable(self, bti, 77)
313 bdt = VppBierDispTable(self, 8)
317 # BIER route in table that's for-us
319 bier_route_1 = VppBierRoute(
321 [VppRoutePath("0.0.0.0",
323 proto=DpoProto.DPO_PROTO_BIER,
325 bier_route_1.add_vpp_config()
328 # An entry in the disposition table
330 bier_de_1 = VppBierDispEntry(self, bdt.id, 99,
331 BIER_HDR_PAYLOAD.BIER_HDR_PROTO_IPV4,
332 DpoProto.DPO_PROTO_BIER,
333 "0.0.0.0", 0, rpf_id=8192)
334 bier_de_1.add_vpp_config()
337 # A multicast route to forward post BIER disposition
339 route_eg_232_1_1_1 = VppIpMRoute(
343 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
344 paths=[VppMRoutePath(self.pg1.sw_if_index,
345 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
346 route_eg_232_1_1_1.add_vpp_config()
347 route_eg_232_1_1_1.update_rpf_id(8192)
350 # A packet with all bits set gets spat out to BP:1
352 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
353 MPLS(label=77, ttl=255) /
354 BIER(length=BIERLength.BIER_LEN_256,
355 BitString=chr(255)*32,
357 IP(src="1.1.1.1", dst="232.1.1.1") /
358 UDP(sport=1234, dport=1234) /
361 self.send_and_expect(self.pg0, [p], self.pg1)
364 # A packet that does not match the Disposition entry gets dropped
366 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
367 MPLS(label=77, ttl=255) /
368 BIER(length=BIERLength.BIER_LEN_256,
369 BitString=chr(255)*32,
371 IP(src="1.1.1.1", dst="232.1.1.1") /
372 UDP(sport=1234, dport=1234) /
374 self.send_and_assert_no_replies(self.pg0, p*2,
375 "no matching disposition entry")
378 # Add the default route to the disposition table
380 bier_de_2 = VppBierDispEntry(self, bdt.id, 0,
381 BIER_HDR_PAYLOAD.BIER_HDR_PROTO_IPV4,
382 DpoProto.DPO_PROTO_BIER,
383 "0.0.0.0", 0, rpf_id=8192)
384 bier_de_2.add_vpp_config()
387 # now the previous packet is forwarded
389 self.send_and_expect(self.pg0, [p], self.pg1)
391 def bier_e2e(self, hdr_len_id, n_bytes, max_bp):
392 """ BIER end-to-end"""
395 # Add a BIER table for sub-domain 0, set 0, and BSL 256
397 bti = VppBierTableID(0, 0, hdr_len_id)
398 bt = VppBierTable(self, bti, 77)
401 lowest = ['\0'] * (n_bytes)
403 highest = ['\0'] * (n_bytes)
404 highest[0] = chr(128)
407 # Impostion Sets bit strings
409 bi_low = VppBierImp(self, bti, 333, lowest)
410 bi_low.add_vpp_config()
411 bi_high = VppBierImp(self, bti, 334, highest)
412 bi_high.add_vpp_config()
415 # Add a multicast route that will forward into the BIER doamin
417 route_ing_232_1_1_1 = VppIpMRoute(
421 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
422 paths=[VppMRoutePath(self.pg0.sw_if_index,
423 MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
424 VppMRoutePath(0xffffffff,
425 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
426 proto=DpoProto.DPO_PROTO_BIER,
427 bier_imp=bi_low.bi_index)])
428 route_ing_232_1_1_1.add_vpp_config()
429 route_ing_232_1_1_2 = VppIpMRoute(
433 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
434 paths=[VppMRoutePath(self.pg0.sw_if_index,
435 MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
436 VppMRoutePath(0xffffffff,
437 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
438 proto=DpoProto.DPO_PROTO_BIER,
439 bier_imp=bi_high.bi_index)])
440 route_ing_232_1_1_2.add_vpp_config()
443 # disposition table 8
445 bdt = VppBierDispTable(self, 8)
449 # BIER routes in table that are for-us, resolving through
452 bier_route_1 = VppBierRoute(
454 [VppRoutePath("0.0.0.0",
456 proto=DpoProto.DPO_PROTO_BIER,
458 bier_route_1.add_vpp_config()
459 bier_route_max = VppBierRoute(self, bti, max_bp,
460 [VppRoutePath("0.0.0.0",
463 bier_route_max.add_vpp_config()
466 # An entry in the disposition table for sender 333
469 bier_de_1 = VppBierDispEntry(self, bdt.id, 333,
470 BIER_HDR_PAYLOAD.BIER_HDR_PROTO_IPV4,
471 DpoProto.DPO_PROTO_BIER,
472 "0.0.0.0", 10, rpf_id=8192)
473 bier_de_1.add_vpp_config()
474 bier_de_1 = VppBierDispEntry(self, bdt.id, 334,
475 BIER_HDR_PAYLOAD.BIER_HDR_PROTO_IPV4,
476 DpoProto.DPO_PROTO_BIER,
477 "0.0.0.0", 10, rpf_id=8193)
478 bier_de_1.add_vpp_config()
481 # Add a multicast routes that will forward the traffic
484 route_eg_232_1_1_1 = VppIpMRoute(
488 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
490 paths=[VppMRoutePath(self.pg1.sw_if_index,
491 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
492 route_eg_232_1_1_1.add_vpp_config()
493 route_eg_232_1_1_1.update_rpf_id(8192)
494 route_eg_232_1_1_2 = VppIpMRoute(
498 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
500 paths=[VppMRoutePath(self.pg1.sw_if_index,
501 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
502 route_eg_232_1_1_2.add_vpp_config()
503 route_eg_232_1_1_2.update_rpf_id(8193)
506 # inject a packet in VRF-0. We expect it to be BIER encapped,
507 # replicated, then hit the disposition and be forwarded
508 # out of VRF 10, i.e. on pg1
510 p = (Ether(dst=self.pg0.local_mac,
511 src=self.pg0.remote_mac) /
512 IP(src="1.1.1.1", dst="232.1.1.1") /
513 UDP(sport=1234, dport=1234) /
516 rx = self.send_and_expect(self.pg0, p*65, self.pg1)
518 self.assertEqual(rx[0][IP].src, "1.1.1.1")
519 self.assertEqual(rx[0][IP].dst, "232.1.1.1")
521 p = (Ether(dst=self.pg0.local_mac,
522 src=self.pg0.remote_mac) /
523 IP(src="1.1.1.1", dst="232.1.1.2") /
524 UDP(sport=1234, dport=1234) /
527 rx = self.send_and_expect(self.pg0, p*65, self.pg1)
528 self.assertEqual(rx[0][IP].src, "1.1.1.1")
529 self.assertEqual(rx[0][IP].dst, "232.1.1.2")
531 @unittest.skipUnless(running_extended_tests(), "part of extended tests")
532 def test_bier_e2e_1024(self):
533 """ BIER end-to-end BSL:1024"""
534 self.bier_e2e(BIERLength.BIER_LEN_1024, 128, 1024)
536 @unittest.skipUnless(running_extended_tests(), "part of extended tests")
537 def test_bier_e2e_512(self):
538 """ BIER end-to-end BSL:512"""
539 self.bier_e2e(BIERLength.BIER_LEN_512, 64, 512)
541 @unittest.skipUnless(running_extended_tests(), "part of extended tests")
542 def test_bier_e2e_256(self):
543 """ BIER end-to-end BSL:256"""
544 self.bier_e2e(BIERLength.BIER_LEN_256, 32, 256)
546 @unittest.skipUnless(running_extended_tests(), "part of extended tests")
547 def test_bier_e2e_128(self):
548 """ BIER end-to-end BSL:128"""
549 self.bier_e2e(BIERLength.BIER_LEN_128, 16, 128)
551 def test_bier_e2e_64(self):
552 """ BIER end-to-end BSL:64"""
553 self.bier_e2e(BIERLength.BIER_LEN_64, 8, 64)
555 def test_bier_head_o_udp(self):
556 """BIER head over UDP"""
559 # Add a BIER table for sub-domain 1, set 0, and BSL 256
561 bti = VppBierTableID(1, 0, BIERLength.BIER_LEN_256)
562 bt = VppBierTable(self, bti, 77)
566 # 1 bit positions via 1 next hops
569 ip_route = VppIpRoute(self, nh1, 32,
570 [VppRoutePath(self.pg1.remote_ip4,
571 self.pg1.sw_if_index,
572 labels=[VppMplsLabel(2001)])])
573 ip_route.add_vpp_config()
575 udp_encap = VppUdpEncap(self, 4,
579 udp_encap.add_vpp_config()
581 bier_route = VppBierRoute(
583 [VppRoutePath("0.0.0.0",
587 bier_route.add_vpp_config()
590 # An 2 imposition objects with all bit-positions set
591 # only use the second, but creating 2 tests with a non-zero
592 # value index in the route add
594 bi = VppBierImp(self, bti, 333, chr(0xff) * 32)
596 bi2 = VppBierImp(self, bti, 334, chr(0xff) * 32)
600 # Add a multicast route that will forward into the BIER doamin
602 route_ing_232_1_1_1 = VppIpMRoute(
606 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
607 paths=[VppMRoutePath(self.pg0.sw_if_index,
608 MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
609 VppMRoutePath(0xffffffff,
610 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
611 proto=DpoProto.DPO_PROTO_BIER,
612 bier_imp=bi2.bi_index)])
613 route_ing_232_1_1_1.add_vpp_config()
616 # inject a packet an IP. We expect it to be BIER and UDP encapped,
618 p = (Ether(dst=self.pg0.local_mac,
619 src=self.pg0.remote_mac) /
620 IP(src="1.1.1.1", dst="232.1.1.1") /
621 UDP(sport=1234, dport=1234))
623 self.pg0.add_stream([p])
624 self.pg_enable_capture(self.pg_interfaces)
627 rx = self.pg1.get_capture(1)
630 # Encap Stack is, eth, IP, UDP, BIFT, BIER
632 self.assertEqual(rx[0][IP].src, self.pg0.local_ip4)
633 self.assertEqual(rx[0][IP].dst, nh1)
634 self.assertEqual(rx[0][UDP].sport, 330)
635 self.assertEqual(rx[0][UDP].dport, 8138)
636 self.assertEqual(rx[0][BIFT].bsl, 2)
637 self.assertEqual(rx[0][BIFT].sd, 1)
638 self.assertEqual(rx[0][BIFT].set, 0)
639 self.assertEqual(rx[0][BIFT].ttl, 64)
640 self.assertEqual(rx[0][BIER].length, 2)
642 def test_bier_tail_o_udp(self):
643 """BIER Tail over UDP"""
646 # Add a BIER table for sub-domain 0, set 0, and BSL 256
648 bti = VppBierTableID(1, 0, BIERLength.BIER_LEN_256)
649 bt = VppBierTable(self, bti, MPLS_LABEL_INVALID)
655 bdt = VppBierDispTable(self, 8)
659 # BIER route in table that's for-us
661 bier_route_1 = VppBierRoute(
663 [VppRoutePath("0.0.0.0",
665 proto=DpoProto.DPO_PROTO_BIER,
667 bier_route_1.add_vpp_config()
670 # An entry in the disposition table
672 bier_de_1 = VppBierDispEntry(self, bdt.id, 99,
673 BIER_HDR_PAYLOAD.BIER_HDR_PROTO_IPV4,
674 DpoProto.DPO_PROTO_BIER,
675 "0.0.0.0", 0, rpf_id=8192)
676 bier_de_1.add_vpp_config()
679 # A multicast route to forward post BIER disposition
681 route_eg_232_1_1_1 = VppIpMRoute(
685 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
686 paths=[VppMRoutePath(self.pg1.sw_if_index,
687 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
688 route_eg_232_1_1_1.add_vpp_config()
689 route_eg_232_1_1_1.update_rpf_id(8192)
692 # A packet with all bits set gets spat out to BP:1
694 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
695 IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
696 UDP(sport=333, dport=8138) /
697 BIFT(sd=1, set=0, bsl=2, ttl=255) /
698 BIER(length=BIERLength.BIER_LEN_256,
699 BitString=chr(255)*32,
701 IP(src="1.1.1.1", dst="232.1.1.1") /
702 UDP(sport=1234, dport=1234) /
705 rx = self.send_and_expect(self.pg0, [p], self.pg1)
708 if __name__ == '__main__':
709 unittest.main(testRunner=VppTestRunner)