6 from framework import VppTestCase, VppTestRunner
7 from vpp_ip_route import VppIpRoute, VppRoutePath, VppMplsRoute, \
8 VppMplsTable, VppIpMRoute, VppMRoutePath, VppIpTable, \
9 MRouteEntryFlags, MRouteItfFlags, MPLS_LABEL_INVALID, DpoProto
10 from vpp_bier import *
11 from vpp_udp_encap import *
13 from scapy.packet import Raw
14 from scapy.layers.l2 import Ether
15 from scapy.layers.inet import IP, UDP, ICMP
16 from scapy.layers.inet6 import IPv6
17 from scapy.contrib.mpls import MPLS
18 from scapy.contrib.bier import *
21 class TestBFIB(VppTestCase):
22 """ BIER FIB Test Case """
25 """ BFIB Unit Tests """
26 error = self.vapi.cli("test bier")
29 self.logger.critical(error)
30 self.assertEqual(error.find("Failed"), -1)
33 class TestBier(VppTestCase):
34 """ BIER Test Case """
37 super(TestBier, self).setUp()
39 # create 2 pg interfaces
40 self.create_pg_interfaces(range(3))
42 # create the default MPLS table
44 tbl = VppMplsTable(self, 0)
46 self.tables.append(tbl)
48 tbl = VppIpTable(self, 10)
50 self.tables.append(tbl)
52 # setup both interfaces
53 for i in self.pg_interfaces:
62 for i in self.pg_interfaces:
67 super(TestBier, self).tearDown()
69 def test_bier_midpoint(self):
73 # Add a BIER table for sub-domain 0, set 0, and BSL 256
75 bti = VppBierTableID(0, 0, BIERLength.BIER_LEN_256)
76 bt = VppBierTable(self, bti, 77)
80 # A packet with no bits set gets dropped
82 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
83 MPLS(label=77, ttl=255) /
84 BIER(length=BIERLength.BIER_LEN_256,
85 BitString=chr(0)*64) /
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, 256):
102 nh = "10.0.%d.%d" % (i / 255, i % 255)
103 nh_routes.append(VppIpRoute(self, nh, 32,
104 [VppRoutePath(self.pg1.remote_ip4,
105 self.pg1.sw_if_index,
107 nh_routes[-1].add_vpp_config()
109 bier_routes.append(VppBierRoute(self, bti, i,
110 [VppRoutePath(nh, 0xffffffff,
112 bier_routes[-1].add_vpp_config()
115 # A packet with all bits set gets spat out to BP:1
117 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
118 MPLS(label=77, ttl=255) /
119 BIER(length=BIERLength.BIER_LEN_256) /
120 IPv6(src=self.pg0.remote_ip6, dst=self.pg0.remote_ip6) /
121 UDP(sport=1234, dport=1234) /
125 self.pg0.add_stream(pkts)
126 self.pg_enable_capture(self.pg_interfaces)
129 rx = self.pg1.get_capture(255)
133 # The packets are not required to be sent in bit-position order
134 # when we setup the routes above we used the bit-position to
135 # construct the out-label. so use that here to determine the BP
138 bp = olabel.label - 2000
140 blabel = olabel[MPLS].payload
141 self.assertEqual(blabel.label, 100+bp)
142 self.assertEqual(blabel.ttl, 254)
144 bier_hdr = blabel[MPLS].payload
146 self.assertEqual(bier_hdr.id, 5)
147 self.assertEqual(bier_hdr.version, 0)
148 self.assertEqual(bier_hdr.length, BIERLength.BIER_LEN_256)
149 self.assertEqual(bier_hdr.entropy, 0)
150 self.assertEqual(bier_hdr.OAM, 0)
151 self.assertEqual(bier_hdr.RSV, 0)
152 self.assertEqual(bier_hdr.DSCP, 0)
153 self.assertEqual(bier_hdr.Proto, 5)
155 # The bit-string should consist only of the BP given by i.
160 bitstring = chr(0) + bitstring
162 bitstring = chr(1 << bpi % 8) + bitstring
164 while len(bitstring) < 32:
165 bitstring = chr(0) + bitstring
167 self.assertEqual(len(bitstring), len(bier_hdr.BitString))
168 self.assertEqual(bitstring, bier_hdr.BitString)
170 def test_bier_head(self):
174 # Add a BIER table for sub-domain 0, set 0, and BSL 256
176 bti = VppBierTableID(0, 0, BIERLength.BIER_LEN_256)
177 bt = VppBierTable(self, bti, 77)
181 # 2 bit positions via two next hops
185 ip_route_1 = VppIpRoute(self, nh1, 32,
186 [VppRoutePath(self.pg1.remote_ip4,
187 self.pg1.sw_if_index,
189 ip_route_2 = VppIpRoute(self, nh2, 32,
190 [VppRoutePath(self.pg1.remote_ip4,
191 self.pg1.sw_if_index,
193 ip_route_1.add_vpp_config()
194 ip_route_2.add_vpp_config()
196 bier_route_1 = VppBierRoute(self, bti, 1,
197 [VppRoutePath(nh1, 0xffffffff,
199 bier_route_2 = VppBierRoute(self, bti, 2,
200 [VppRoutePath(nh2, 0xffffffff,
202 bier_route_1.add_vpp_config()
203 bier_route_2.add_vpp_config()
206 # An imposition object with both bit-positions set
208 bi = VppBierImp(self, bti, 333, chr(0x3) * 32)
212 # Add a multicast route that will forward into the BIER doamin
214 route_ing_232_1_1_1 = VppIpMRoute(
218 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
219 paths=[VppMRoutePath(self.pg0.sw_if_index,
220 MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
221 VppMRoutePath(0xffffffff,
222 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
223 proto=DpoProto.DPO_PROTO_BIER,
224 bier_imp=bi.bi_index)])
225 route_ing_232_1_1_1.add_vpp_config()
228 # inject an IP packet. We expect it to be BIER encapped and
231 p = (Ether(dst=self.pg0.local_mac,
232 src=self.pg0.remote_mac) /
233 IP(src="1.1.1.1", dst="232.1.1.1") /
234 UDP(sport=1234, dport=1234))
236 self.pg0.add_stream([p])
237 self.pg_enable_capture(self.pg_interfaces)
240 rx = self.pg1.get_capture(2)
243 # Encap Stack is; eth, MPLS, MPLS, BIER
245 igp_mpls = rx[0][MPLS]
246 self.assertEqual(igp_mpls.label, 2001)
247 self.assertEqual(igp_mpls.ttl, 64)
248 self.assertEqual(igp_mpls.s, 0)
249 bier_mpls = igp_mpls[MPLS].payload
250 self.assertEqual(bier_mpls.label, 101)
251 self.assertEqual(bier_mpls.ttl, 64)
252 self.assertEqual(bier_mpls.s, 1)
253 self.assertEqual(rx[0][BIER].length, 2)
255 igp_mpls = rx[1][MPLS]
256 self.assertEqual(igp_mpls.label, 2002)
257 self.assertEqual(igp_mpls.ttl, 64)
258 self.assertEqual(igp_mpls.s, 0)
259 bier_mpls = igp_mpls[MPLS].payload
260 self.assertEqual(bier_mpls.label, 102)
261 self.assertEqual(bier_mpls.ttl, 64)
262 self.assertEqual(bier_mpls.s, 1)
263 self.assertEqual(rx[0][BIER].length, 2)
265 def test_bier_tail(self):
269 # Add a BIER table for sub-domain 0, set 0, and BSL 256
271 bti = VppBierTableID(0, 0, BIERLength.BIER_LEN_256)
272 bt = VppBierTable(self, bti, 77)
278 bdt = VppBierDispTable(self, 8)
282 # BIER route in table that's for-us
284 bier_route_1 = VppBierRoute(self, bti, 1,
285 [VppRoutePath("0.0.0.0",
288 bier_route_1.add_vpp_config()
291 # An entry in the disposition table
293 bier_de_1 = VppBierDispEntry(self, bdt.id, 99,
294 BIER_HDR_PAYLOAD.BIER_HDR_PROTO_IPV4,
295 "0.0.0.0", 0, rpf_id=8192)
296 bier_de_1.add_vpp_config()
299 # A multicast route to forward post BIER disposition
301 route_eg_232_1_1_1 = VppIpMRoute(
305 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
306 paths=[VppMRoutePath(self.pg1.sw_if_index,
307 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
308 route_eg_232_1_1_1.add_vpp_config()
309 route_eg_232_1_1_1.update_rpf_id(8192)
312 # A packet with all bits set gets spat out to BP:1
314 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
315 MPLS(label=77, ttl=255) /
316 BIER(length=BIERLength.BIER_LEN_256, BFRID=99) /
317 IP(src="1.1.1.1", dst="232.1.1.1") /
318 UDP(sport=1234, dport=1234) /
321 self.send_and_expect(self.pg0, [p], self.pg1)
324 # A packet that does not match the Disposition entry gets dropped
326 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
327 MPLS(label=77, ttl=255) /
328 BIER(length=BIERLength.BIER_LEN_256, BFRID=77) /
329 IP(src="1.1.1.1", dst="232.1.1.1") /
330 UDP(sport=1234, dport=1234) /
332 self.send_and_assert_no_replies(self.pg0, p*2,
333 "no matching disposition entry")
336 # Add the default route to the disposition table
338 bier_de_2 = VppBierDispEntry(self, bdt.id, 0,
339 BIER_HDR_PAYLOAD.BIER_HDR_PROTO_IPV4,
340 "0.0.0.0", 0, rpf_id=8192)
341 bier_de_2.add_vpp_config()
344 # now the previous packet is forwarded
346 self.send_and_expect(self.pg0, [p], self.pg1)
348 def test_bier_e2e(self):
349 """ BIER end-to-end """
352 # Add a BIER table for sub-domain 0, set 0, and BSL 256
354 bti = VppBierTableID(0, 0, BIERLength.BIER_LEN_256)
355 bt = VppBierTable(self, bti, 77)
359 # Impostion Sets bit string 101010101....
362 bi = VppBierImp(self, bti, 333, chr(0x5) * 32)
366 # Add a multicast route that will forward into the BIER doamin
368 route_ing_232_1_1_1 = VppIpMRoute(
372 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
373 paths=[VppMRoutePath(self.pg0.sw_if_index,
374 MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
375 VppMRoutePath(0xffffffff,
376 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
377 proto=DpoProto.DPO_PROTO_BIER,
378 bier_imp=bi.bi_index)])
379 route_ing_232_1_1_1.add_vpp_config()
382 # disposition table 8
384 bdt = VppBierDispTable(self, 8)
388 # BIER route in table that's for-us, resolving through
391 bier_route_1 = VppBierRoute(self, bti, 1,
392 [VppRoutePath("0.0.0.0",
395 bier_route_1.add_vpp_config()
398 # An entry in the disposition table for sender 333
401 bier_de_1 = VppBierDispEntry(self, bdt.id, 333,
402 BIER_HDR_PAYLOAD.BIER_HDR_PROTO_IPV4,
403 "0.0.0.0", 10, rpf_id=8192)
404 bier_de_1.add_vpp_config()
407 # Add a multicast route that will forward the traffic
410 route_eg_232_1_1_1 = VppIpMRoute(
414 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
416 paths=[VppMRoutePath(self.pg1.sw_if_index,
417 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
418 route_eg_232_1_1_1.add_vpp_config()
419 route_eg_232_1_1_1.update_rpf_id(8192)
422 # inject a packet in VRF-0. We expect it to be BIER encapped,
423 # replicated, then hit the disposition and be forwarded
424 # out of VRF 10, i.e. on pg1
426 p = (Ether(dst=self.pg0.local_mac,
427 src=self.pg0.remote_mac) /
428 IP(src="1.1.1.1", dst="232.1.1.1") /
429 UDP(sport=1234, dport=1234))
431 rx = self.send_and_expect(self.pg0, p*65, self.pg1)
436 self.assertEqual(rx[0][IP].src, "1.1.1.1")
437 self.assertEqual(rx[0][IP].dst, "232.1.1.1")
439 def test_bier_head_o_udp(self):
440 """BIER head over UDP"""
443 # Add a BIER table for sub-domain 1, set 0, and BSL 256
445 bti = VppBierTableID(1, 0, BIERLength.BIER_LEN_256)
446 bt = VppBierTable(self, bti, 77)
450 # 1 bit positions via 1 next hops
453 ip_route = VppIpRoute(self, nh1, 32,
454 [VppRoutePath(self.pg1.remote_ip4,
455 self.pg1.sw_if_index,
457 ip_route.add_vpp_config()
459 udp_encap = VppUdpEncap(self, 4,
463 udp_encap.add_vpp_config()
465 bier_route = VppBierRoute(self, bti, 1,
466 [VppRoutePath("0.0.0.0",
470 bier_route.add_vpp_config()
473 # An 2 imposition objects with all bit-positions set
474 # only use the second, but creating 2 tests with a non-zero
475 # value index in the route add
477 bi = VppBierImp(self, bti, 333, chr(0xff) * 32)
479 bi2 = VppBierImp(self, bti, 334, chr(0xff) * 32)
483 # Add a multicast route that will forward into the BIER doamin
485 route_ing_232_1_1_1 = VppIpMRoute(
489 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
490 paths=[VppMRoutePath(self.pg0.sw_if_index,
491 MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
492 VppMRoutePath(0xffffffff,
493 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
494 proto=DpoProto.DPO_PROTO_BIER,
495 bier_imp=bi2.bi_index)])
496 route_ing_232_1_1_1.add_vpp_config()
499 # inject a packet an IP. We expect it to be BIER and UDP encapped,
501 p = (Ether(dst=self.pg0.local_mac,
502 src=self.pg0.remote_mac) /
503 IP(src="1.1.1.1", dst="232.1.1.1") /
504 UDP(sport=1234, dport=1234))
506 self.pg0.add_stream([p])
507 self.pg_enable_capture(self.pg_interfaces)
510 rx = self.pg1.get_capture(1)
513 # Encap Stack is, eth, IP, UDP, BIFT, BIER
515 self.assertEqual(rx[0][IP].src, self.pg0.local_ip4)
516 self.assertEqual(rx[0][IP].dst, nh1)
517 self.assertEqual(rx[0][UDP].sport, 330)
518 self.assertEqual(rx[0][UDP].dport, 8138)
519 self.assertEqual(rx[0][BIFT].bsl, 2)
520 self.assertEqual(rx[0][BIFT].sd, 1)
521 self.assertEqual(rx[0][BIFT].set, 0)
522 self.assertEqual(rx[0][BIFT].ttl, 64)
523 self.assertEqual(rx[0][BIER].length, 2)
525 def test_bier_tail_o_udp(self):
526 """BIER Tail over UDP"""
529 # Add a BIER table for sub-domain 0, set 0, and BSL 256
531 bti = VppBierTableID(1, 0, BIERLength.BIER_LEN_256)
532 bt = VppBierTable(self, bti, MPLS_LABEL_INVALID)
538 bdt = VppBierDispTable(self, 8)
542 # BIER route in table that's for-us
544 bier_route_1 = VppBierRoute(self, bti, 1,
545 [VppRoutePath("0.0.0.0",
548 bier_route_1.add_vpp_config()
551 # An entry in the disposition table
553 bier_de_1 = VppBierDispEntry(self, bdt.id, 99,
554 BIER_HDR_PAYLOAD.BIER_HDR_PROTO_IPV4,
555 "0.0.0.0", 0, rpf_id=8192)
556 bier_de_1.add_vpp_config()
559 # A multicast route to forward post BIER disposition
561 route_eg_232_1_1_1 = VppIpMRoute(
565 MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
566 paths=[VppMRoutePath(self.pg1.sw_if_index,
567 MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
568 route_eg_232_1_1_1.add_vpp_config()
569 route_eg_232_1_1_1.update_rpf_id(8192)
572 # A packet with all bits set gets spat out to BP:1
574 p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
575 IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
576 UDP(sport=333, dport=8138) /
577 BIFT(sd=1, set=0, bsl=2, ttl=255) /
578 BIER(length=BIERLength.BIER_LEN_256, BFRID=99) /
579 IP(src="1.1.1.1", dst="232.1.1.1") /
580 UDP(sport=1234, dport=1234) /
583 rx = self.send_and_expect(self.pg0, [p], self.pg1)
586 if __name__ == '__main__':
587 unittest.main(testRunner=VppTestRunner)