tests: replace pycodestyle with black
[vpp.git] / test / test_bier.py
1 #!/usr/bin/env python3
2
3 import unittest
4
5 from config import config
6 from framework import VppTestCase, VppTestRunner
7 from vpp_ip import DpoProto
8 from vpp_ip_route import (
9     VppIpRoute,
10     VppRoutePath,
11     VppMplsTable,
12     VppIpMRoute,
13     VppMRoutePath,
14     VppIpTable,
15     MPLS_LABEL_INVALID,
16     VppMplsLabel,
17     FibPathProto,
18     FibPathType,
19 )
20 from vpp_bier import (
21     BIER_HDR_PAYLOAD,
22     VppBierImp,
23     VppBierDispEntry,
24     VppBierDispTable,
25     VppBierTable,
26     VppBierTableID,
27     VppBierRoute,
28 )
29 from vpp_udp_encap import VppUdpEncap
30 from vpp_papi import VppEnum
31
32 import scapy.compat
33 from scapy.packet import Raw
34 from scapy.layers.l2 import Ether
35 from scapy.layers.inet import IP, UDP
36 from scapy.layers.inet6 import IPv6
37 from scapy.contrib.mpls import MPLS
38 from scapy.contrib.bier import BIER, BIERLength, BIFT
39
40 NUM_PKTS = 67
41
42
43 class TestBFIB(VppTestCase):
44     """BIER FIB Test Case"""
45
46     def test_bfib(self):
47         """BFIB Unit Tests"""
48         error = self.vapi.cli("test bier")
49
50         if error:
51             self.logger.critical(error)
52         self.assertNotIn("Failed", error)
53
54
55 class TestBier(VppTestCase):
56     """BIER Test Case"""
57
58     def setUp(self):
59         super(TestBier, self).setUp()
60
61         # create 2 pg interfaces
62         self.create_pg_interfaces(range(3))
63
64         # create the default MPLS table
65         self.tables = []
66         tbl = VppMplsTable(self, 0)
67         tbl.add_vpp_config()
68         self.tables.append(tbl)
69
70         tbl = VppIpTable(self, 10)
71         tbl.add_vpp_config()
72         self.tables.append(tbl)
73
74         # setup both interfaces
75         for i in self.pg_interfaces:
76             if i == self.pg2:
77                 i.set_table_ip4(10)
78             i.admin_up()
79             i.config_ip4()
80             i.resolve_arp()
81             i.enable_mpls()
82
83     def tearDown(self):
84         for i in self.pg_interfaces:
85             i.disable_mpls()
86             i.unconfig_ip4()
87             i.set_table_ip4(0)
88             i.admin_down()
89         super(TestBier, self).tearDown()
90
91     def bier_midpoint(self, hdr_len_id, n_bytes, max_bp):
92         """BIER midpoint"""
93
94         #
95         # Add a BIER table for sub-domain 0, set 0, and BSL 256
96         #
97         bti = VppBierTableID(0, 0, hdr_len_id)
98         bt = VppBierTable(self, bti, 77)
99         bt.add_vpp_config()
100
101         #
102         # A packet with no bits set gets dropped
103         #
104         p = (
105             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
106             / MPLS(label=77, ttl=255)
107             / BIER(length=hdr_len_id)
108             / IPv6(src=self.pg0.remote_ip6, dst=self.pg0.remote_ip6)
109             / UDP(sport=1234, dport=1234)
110             / Raw()
111         )
112         pkts = [p]
113
114         self.send_and_assert_no_replies(self.pg0, pkts, "Empty Bit-String")
115
116         #
117         # Add a BIER route for each bit-position in the table via a different
118         # next-hop. Testing whether the BIER walk and replicate forwarding
119         # function works for all bit posisitons.
120         #
121         nh_routes = []
122         bier_routes = []
123         for i in range(1, max_bp + 1):
124             nh = "10.0.%d.%d" % (i / 255, i % 255)
125             nh_routes.append(
126                 VppIpRoute(
127                     self,
128                     nh,
129                     32,
130                     [
131                         VppRoutePath(
132                             self.pg1.remote_ip4,
133                             self.pg1.sw_if_index,
134                             labels=[VppMplsLabel(2000 + i)],
135                         )
136                     ],
137                 )
138             )
139             nh_routes[-1].add_vpp_config()
140
141             bier_routes.append(
142                 VppBierRoute(
143                     self,
144                     bti,
145                     i,
146                     [VppRoutePath(nh, 0xFFFFFFFF, labels=[VppMplsLabel(100 + i)])],
147                 )
148             )
149             bier_routes[-1].add_vpp_config()
150
151         #
152         # A packet with all bits set gets replicated once for each bit
153         #
154         pkt_sizes = [64, 1400]
155
156         for pkt_size in pkt_sizes:
157             p = (
158                 Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
159                 / MPLS(label=77, ttl=255)
160                 / BIER(length=hdr_len_id, BitString=scapy.compat.chb(255) * n_bytes)
161                 / IPv6(src=self.pg0.remote_ip6, dst=self.pg0.remote_ip6)
162                 / UDP(sport=1234, dport=1234)
163                 / Raw(scapy.compat.chb(5) * pkt_size)
164             )
165             pkts = p
166
167             self.pg0.add_stream(pkts)
168             self.pg_enable_capture(self.pg_interfaces)
169             self.pg_start()
170
171             rx = self.pg1.get_capture(max_bp)
172
173             for rxp in rx:
174                 #
175                 # The packets are not required to be sent in bit-position order
176                 # when we setup the routes above we used the bit-position to
177                 # construct the out-label. so use that here to determine the BP
178                 #
179                 olabel = rxp[MPLS]
180                 bp = olabel.label - 2000
181
182                 blabel = olabel[MPLS].payload
183                 self.assertEqual(blabel.label, 100 + bp)
184                 self.assertEqual(blabel.ttl, 254)
185
186                 bier_hdr = blabel[MPLS].payload
187
188                 self.assertEqual(bier_hdr.id, 5)
189                 self.assertEqual(bier_hdr.version, 0)
190                 self.assertEqual(bier_hdr.length, hdr_len_id)
191                 self.assertEqual(bier_hdr.entropy, 0)
192                 self.assertEqual(bier_hdr.OAM, 0)
193                 self.assertEqual(bier_hdr.RSV, 0)
194                 self.assertEqual(bier_hdr.DSCP, 0)
195                 self.assertEqual(bier_hdr.Proto, 5)
196
197                 # The bit-string should consist only of the BP given by i.
198                 byte_array = [b"\0"] * (n_bytes)
199                 byte_val = scapy.compat.chb(1 << (bp - 1) % 8)
200                 byte_pos = n_bytes - (((bp - 1) // 8) + 1)
201                 byte_array[byte_pos] = byte_val
202                 bitstring = b"".join(byte_array)
203
204                 self.assertEqual(len(bitstring), len(bier_hdr.BitString))
205                 self.assertEqual(bitstring, bier_hdr.BitString)
206
207         #
208         # cleanup. not strictly necessary, but it's much quicker this way
209         # because the bier_fib_dump and ip_fib_dump will be empty when the
210         # auto-cleanup kicks in
211         #
212         for br in bier_routes:
213             br.remove_vpp_config()
214         for nhr in nh_routes:
215             nhr.remove_vpp_config()
216
217     @unittest.skipUnless(config.extended, "part of extended tests")
218     def test_bier_midpoint_1024(self):
219         """BIER midpoint BSL:1024"""
220         self.bier_midpoint(BIERLength.BIER_LEN_1024, 128, 1024)
221
222     @unittest.skipUnless(config.extended, "part of extended tests")
223     def test_bier_midpoint_512(self):
224         """BIER midpoint BSL:512"""
225         self.bier_midpoint(BIERLength.BIER_LEN_512, 64, 512)
226
227     @unittest.skipUnless(config.extended, "part of extended tests")
228     def test_bier_midpoint_256(self):
229         """BIER midpoint BSL:256"""
230         self.bier_midpoint(BIERLength.BIER_LEN_256, 32, 256)
231
232     @unittest.skipUnless(config.extended, "part of extended tests")
233     def test_bier_midpoint_128(self):
234         """BIER midpoint BSL:128"""
235         self.bier_midpoint(BIERLength.BIER_LEN_128, 16, 128)
236
237     def test_bier_midpoint_64(self):
238         """BIER midpoint BSL:64"""
239         self.bier_midpoint(BIERLength.BIER_LEN_64, 8, 64)
240
241     def test_bier_load_balance(self):
242         """BIER load-balance"""
243
244         #
245         # Add a BIER table for sub-domain 0, set 0, and BSL 256
246         #
247         bti = VppBierTableID(0, 0, BIERLength.BIER_LEN_64)
248         bt = VppBierTable(self, bti, 77)
249         bt.add_vpp_config()
250
251         #
252         # packets with varying entropy
253         #
254         pkts = []
255         for ii in range(257):
256             pkts.append(
257                 (
258                     Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
259                     / MPLS(label=77, ttl=255)
260                     / BIER(
261                         length=BIERLength.BIER_LEN_64,
262                         entropy=ii,
263                         BitString=scapy.compat.chb(255) * 16,
264                     )
265                     / IPv6(src=self.pg0.remote_ip6, dst=self.pg0.remote_ip6)
266                     / UDP(sport=1234, dport=1234)
267                     / Raw()
268                 )
269             )
270
271         #
272         # 4 next hops
273         #
274         nhs = [
275             {"ip": "10.0.0.1", "label": 201},
276             {"ip": "10.0.0.2", "label": 202},
277             {"ip": "10.0.0.3", "label": 203},
278             {"ip": "10.0.0.4", "label": 204},
279         ]
280
281         for nh in nhs:
282             ipr = VppIpRoute(
283                 self,
284                 nh["ip"],
285                 32,
286                 [
287                     VppRoutePath(
288                         self.pg1.remote_ip4,
289                         self.pg1.sw_if_index,
290                         labels=[VppMplsLabel(nh["label"])],
291                     )
292                 ],
293             )
294             ipr.add_vpp_config()
295
296         bier_route = VppBierRoute(
297             self,
298             bti,
299             1,
300             [
301                 VppRoutePath(nhs[0]["ip"], 0xFFFFFFFF, labels=[VppMplsLabel(101)]),
302                 VppRoutePath(nhs[1]["ip"], 0xFFFFFFFF, labels=[VppMplsLabel(101)]),
303             ],
304         )
305         bier_route.add_vpp_config()
306
307         rx = self.send_and_expect(self.pg0, pkts, self.pg1)
308
309         #
310         # we should have recieved a packet from each neighbor
311         #
312         for nh in nhs[:2]:
313             self.assertTrue(sum(p[MPLS].label == nh["label"] for p in rx))
314
315         #
316         # add the other paths
317         #
318         bier_route.update_paths(
319             [
320                 VppRoutePath(nhs[0]["ip"], 0xFFFFFFFF, labels=[VppMplsLabel(101)]),
321                 VppRoutePath(nhs[1]["ip"], 0xFFFFFFFF, labels=[VppMplsLabel(101)]),
322                 VppRoutePath(nhs[2]["ip"], 0xFFFFFFFF, labels=[VppMplsLabel(101)]),
323                 VppRoutePath(nhs[3]["ip"], 0xFFFFFFFF, labels=[VppMplsLabel(101)]),
324             ]
325         )
326
327         rx = self.send_and_expect(self.pg0, pkts, self.pg1)
328
329         for nh in nhs:
330             self.assertTrue(sum(p[MPLS].label == nh["label"] for p in rx))
331
332         #
333         # remove first two paths
334         #
335         bier_route.remove_path(
336             VppRoutePath(nhs[0]["ip"], 0xFFFFFFFF, labels=[VppMplsLabel(101)])
337         )
338         bier_route.remove_path(
339             VppRoutePath(nhs[1]["ip"], 0xFFFFFFFF, labels=[VppMplsLabel(101)])
340         )
341
342         rx = self.send_and_expect(self.pg0, pkts, self.pg1)
343         for nh in nhs[2:]:
344             self.assertTrue(sum(p[MPLS].label == nh["label"] for p in rx))
345
346         #
347         # remove the last of the paths, deleteing the entry
348         #
349         bier_route.remove_all_paths()
350
351         self.send_and_assert_no_replies(self.pg0, pkts)
352
353     def test_bier_head(self):
354         """BIER head"""
355
356         MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
357         MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
358
359         #
360         # Add a BIER table for sub-domain 0, set 0, and BSL 256
361         #
362         bti = VppBierTableID(0, 0, BIERLength.BIER_LEN_256)
363         bt = VppBierTable(self, bti, 77)
364         bt.add_vpp_config()
365
366         #
367         # 2 bit positions via two next hops
368         #
369         nh1 = "10.0.0.1"
370         nh2 = "10.0.0.2"
371         ip_route_1 = VppIpRoute(
372             self,
373             nh1,
374             32,
375             [
376                 VppRoutePath(
377                     self.pg1.remote_ip4,
378                     self.pg1.sw_if_index,
379                     labels=[VppMplsLabel(2001)],
380                 )
381             ],
382         )
383         ip_route_2 = VppIpRoute(
384             self,
385             nh2,
386             32,
387             [
388                 VppRoutePath(
389                     self.pg1.remote_ip4,
390                     self.pg1.sw_if_index,
391                     labels=[VppMplsLabel(2002)],
392                 )
393             ],
394         )
395         ip_route_1.add_vpp_config()
396         ip_route_2.add_vpp_config()
397
398         bier_route_1 = VppBierRoute(
399             self, bti, 1, [VppRoutePath(nh1, 0xFFFFFFFF, labels=[VppMplsLabel(101)])]
400         )
401         bier_route_2 = VppBierRoute(
402             self, bti, 2, [VppRoutePath(nh2, 0xFFFFFFFF, labels=[VppMplsLabel(102)])]
403         )
404         bier_route_1.add_vpp_config()
405         bier_route_2.add_vpp_config()
406
407         #
408         # An imposition object with both bit-positions set
409         #
410         bi = VppBierImp(self, bti, 333, scapy.compat.chb(0x3) * 32)
411         bi.add_vpp_config()
412
413         #
414         # Add a multicast route that will forward into the BIER doamin
415         #
416         route_ing_232_1_1_1 = VppIpMRoute(
417             self,
418             "0.0.0.0",
419             "232.1.1.1",
420             32,
421             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
422             paths=[
423                 VppMRoutePath(
424                     self.pg0.sw_if_index, MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT
425                 ),
426                 VppMRoutePath(
427                     0xFFFFFFFF,
428                     MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
429                     proto=FibPathProto.FIB_PATH_NH_PROTO_BIER,
430                     type=FibPathType.FIB_PATH_TYPE_BIER_IMP,
431                     bier_imp=bi.bi_index,
432                 ),
433             ],
434         )
435         route_ing_232_1_1_1.add_vpp_config()
436
437         #
438         # inject an IP packet. We expect it to be BIER encapped and
439         # replicated.
440         #
441         p = (
442             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
443             / IP(src="1.1.1.1", dst="232.1.1.1")
444             / UDP(sport=1234, dport=1234)
445         )
446
447         self.pg0.add_stream([p])
448         self.pg_enable_capture(self.pg_interfaces)
449         self.pg_start()
450
451         rx = self.pg1.get_capture(2)
452
453         #
454         # Encap Stack is; eth, MPLS, MPLS, BIER
455         #
456         igp_mpls = rx[0][MPLS]
457         self.assertEqual(igp_mpls.label, 2001)
458         self.assertEqual(igp_mpls.ttl, 64)
459         self.assertEqual(igp_mpls.s, 0)
460         bier_mpls = igp_mpls[MPLS].payload
461         self.assertEqual(bier_mpls.label, 101)
462         self.assertEqual(bier_mpls.ttl, 64)
463         self.assertEqual(bier_mpls.s, 1)
464         self.assertEqual(rx[0][BIER].length, 2)
465
466         igp_mpls = rx[1][MPLS]
467         self.assertEqual(igp_mpls.label, 2002)
468         self.assertEqual(igp_mpls.ttl, 64)
469         self.assertEqual(igp_mpls.s, 0)
470         bier_mpls = igp_mpls[MPLS].payload
471         self.assertEqual(bier_mpls.label, 102)
472         self.assertEqual(bier_mpls.ttl, 64)
473         self.assertEqual(bier_mpls.s, 1)
474         self.assertEqual(rx[0][BIER].length, 2)
475
476     def test_bier_tail(self):
477         """BIER Tail"""
478
479         MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
480         MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
481
482         #
483         # Add a BIER table for sub-domain 0, set 0, and BSL 256
484         #
485         bti = VppBierTableID(0, 0, BIERLength.BIER_LEN_256)
486         bt = VppBierTable(self, bti, 77)
487         bt.add_vpp_config()
488
489         #
490         # disposition table
491         #
492         bdt = VppBierDispTable(self, 8)
493         bdt.add_vpp_config()
494
495         #
496         # BIER route in table that's for-us
497         #
498         bier_route_1 = VppBierRoute(
499             self,
500             bti,
501             1,
502             [
503                 VppRoutePath(
504                     "0.0.0.0",
505                     0xFFFFFFFF,
506                     proto=FibPathProto.FIB_PATH_NH_PROTO_BIER,
507                     nh_table_id=8,
508                 )
509             ],
510         )
511         bier_route_1.add_vpp_config()
512
513         #
514         # An entry in the disposition table
515         #
516         bier_de_1 = VppBierDispEntry(
517             self,
518             bdt.id,
519             99,
520             BIER_HDR_PAYLOAD.BIER_HDR_PROTO_IPV4,
521             FibPathProto.FIB_PATH_NH_PROTO_BIER,
522             "0.0.0.0",
523             0,
524             rpf_id=8192,
525         )
526         bier_de_1.add_vpp_config()
527
528         #
529         # A multicast route to forward post BIER disposition
530         #
531         route_eg_232_1_1_1 = VppIpMRoute(
532             self,
533             "0.0.0.0",
534             "232.1.1.1",
535             32,
536             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
537             paths=[
538                 VppMRoutePath(
539                     self.pg1.sw_if_index, MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD
540                 )
541             ],
542         )
543         route_eg_232_1_1_1.add_vpp_config()
544         route_eg_232_1_1_1.update_rpf_id(8192)
545
546         #
547         # A packet with all bits set gets spat out to BP:1
548         #
549         p = (
550             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
551             / MPLS(label=77, ttl=255)
552             / BIER(
553                 length=BIERLength.BIER_LEN_256,
554                 BitString=scapy.compat.chb(255) * 32,
555                 BFRID=99,
556             )
557             / IP(src="1.1.1.1", dst="232.1.1.1")
558             / UDP(sport=1234, dport=1234)
559             / Raw()
560         )
561
562         self.send_and_expect(self.pg0, [p], self.pg1)
563
564         #
565         # A packet that does not match the Disposition entry gets dropped
566         #
567         p = (
568             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
569             / MPLS(label=77, ttl=255)
570             / BIER(
571                 length=BIERLength.BIER_LEN_256,
572                 BitString=scapy.compat.chb(255) * 32,
573                 BFRID=77,
574             )
575             / IP(src="1.1.1.1", dst="232.1.1.1")
576             / UDP(sport=1234, dport=1234)
577             / Raw()
578         )
579         self.send_and_assert_no_replies(
580             self.pg0, p * 2, "no matching disposition entry"
581         )
582
583         #
584         # Add the default route to the disposition table
585         #
586         bier_de_2 = VppBierDispEntry(
587             self,
588             bdt.id,
589             0,
590             BIER_HDR_PAYLOAD.BIER_HDR_PROTO_IPV4,
591             FibPathProto.FIB_PATH_NH_PROTO_BIER,
592             "0.0.0.0",
593             0,
594             rpf_id=8192,
595         )
596         bier_de_2.add_vpp_config()
597
598         #
599         # now the previous packet is forwarded
600         #
601         self.send_and_expect(self.pg0, [p], self.pg1)
602
603         #
604         # A multicast route to forward post BIER disposition that needs
605         # a check against sending back into the BIER core
606         #
607         bi = VppBierImp(self, bti, 333, scapy.compat.chb(0x3) * 32)
608         bi.add_vpp_config()
609
610         route_eg_232_1_1_2 = VppIpMRoute(
611             self,
612             "0.0.0.0",
613             "232.1.1.2",
614             32,
615             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
616             paths=[
617                 VppMRoutePath(
618                     0xFFFFFFFF,
619                     MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
620                     proto=DpoProto.DPO_PROTO_BIER,
621                     type=FibPathType.FIB_PATH_TYPE_BIER_IMP,
622                     bier_imp=bi.bi_index,
623                 ),
624                 VppMRoutePath(
625                     self.pg1.sw_if_index, MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD
626                 ),
627             ],
628         )
629         route_eg_232_1_1_2.add_vpp_config()
630         route_eg_232_1_1_2.update_rpf_id(8192)
631
632         p = (
633             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
634             / MPLS(label=77, ttl=255)
635             / BIER(
636                 length=BIERLength.BIER_LEN_256,
637                 BitString=scapy.compat.chb(255) * 32,
638                 BFRID=77,
639             )
640             / IP(src="1.1.1.1", dst="232.1.1.2")
641             / UDP(sport=1234, dport=1234)
642             / Raw()
643         )
644         self.send_and_expect(self.pg0, [p], self.pg1)
645
646     def bier_e2e(self, hdr_len_id, n_bytes, max_bp):
647         """BIER end-to-end"""
648
649         MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
650         MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
651
652         #
653         # Add a BIER table for sub-domain 0, set 0, and BSL 256
654         #
655         bti = VppBierTableID(0, 0, hdr_len_id)
656         bt = VppBierTable(self, bti, 77)
657         bt.add_vpp_config()
658
659         lowest = [b"\0"] * (n_bytes)
660         lowest[-1] = scapy.compat.chb(1)
661         highest = [b"\0"] * (n_bytes)
662         highest[0] = scapy.compat.chb(128)
663
664         #
665         # Impostion Sets bit strings
666         #
667         bi_low = VppBierImp(self, bti, 333, lowest)
668         bi_low.add_vpp_config()
669         bi_high = VppBierImp(self, bti, 334, highest)
670         bi_high.add_vpp_config()
671
672         #
673         # Add a multicast route that will forward into the BIER doamin
674         #
675         route_ing_232_1_1_1 = VppIpMRoute(
676             self,
677             "0.0.0.0",
678             "232.1.1.1",
679             32,
680             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
681             paths=[
682                 VppMRoutePath(
683                     self.pg0.sw_if_index, MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT
684                 ),
685                 VppMRoutePath(
686                     0xFFFFFFFF,
687                     MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
688                     proto=FibPathProto.FIB_PATH_NH_PROTO_BIER,
689                     type=FibPathType.FIB_PATH_TYPE_BIER_IMP,
690                     bier_imp=bi_low.bi_index,
691                 ),
692             ],
693         )
694         route_ing_232_1_1_1.add_vpp_config()
695         route_ing_232_1_1_2 = VppIpMRoute(
696             self,
697             "0.0.0.0",
698             "232.1.1.2",
699             32,
700             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
701             paths=[
702                 VppMRoutePath(
703                     self.pg0.sw_if_index, MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT
704                 ),
705                 VppMRoutePath(
706                     0xFFFFFFFF,
707                     MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
708                     proto=FibPathProto.FIB_PATH_NH_PROTO_BIER,
709                     type=FibPathType.FIB_PATH_TYPE_BIER_IMP,
710                     bier_imp=bi_high.bi_index,
711                 ),
712             ],
713         )
714         route_ing_232_1_1_2.add_vpp_config()
715
716         #
717         # disposition table 8
718         #
719         bdt = VppBierDispTable(self, 8)
720         bdt.add_vpp_config()
721
722         #
723         # BIER routes in table that are for-us, resolving through
724         # disp table 8.
725         #
726         bier_route_1 = VppBierRoute(
727             self,
728             bti,
729             1,
730             [
731                 VppRoutePath(
732                     "0.0.0.0",
733                     0xFFFFFFFF,
734                     proto=FibPathProto.FIB_PATH_NH_PROTO_BIER,
735                     nh_table_id=8,
736                 )
737             ],
738         )
739         bier_route_1.add_vpp_config()
740         bier_route_max = VppBierRoute(
741             self,
742             bti,
743             max_bp,
744             [
745                 VppRoutePath(
746                     "0.0.0.0",
747                     0xFFFFFFFF,
748                     proto=FibPathProto.FIB_PATH_NH_PROTO_BIER,
749                     nh_table_id=8,
750                 )
751             ],
752         )
753         bier_route_max.add_vpp_config()
754
755         #
756         # An entry in the disposition table for sender 333
757         #  lookup in VRF 10
758         #
759         bier_de_1 = VppBierDispEntry(
760             self,
761             bdt.id,
762             333,
763             BIER_HDR_PAYLOAD.BIER_HDR_PROTO_IPV4,
764             FibPathProto.FIB_PATH_NH_PROTO_BIER,
765             "0.0.0.0",
766             10,
767             rpf_id=8192,
768         )
769         bier_de_1.add_vpp_config()
770         bier_de_1 = VppBierDispEntry(
771             self,
772             bdt.id,
773             334,
774             BIER_HDR_PAYLOAD.BIER_HDR_PROTO_IPV4,
775             FibPathProto.FIB_PATH_NH_PROTO_BIER,
776             "0.0.0.0",
777             10,
778             rpf_id=8193,
779         )
780         bier_de_1.add_vpp_config()
781
782         #
783         # Add a multicast routes that will forward the traffic
784         # post-disposition
785         #
786         route_eg_232_1_1_1 = VppIpMRoute(
787             self,
788             "0.0.0.0",
789             "232.1.1.1",
790             32,
791             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
792             table_id=10,
793             paths=[
794                 VppMRoutePath(
795                     self.pg1.sw_if_index, MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD
796                 )
797             ],
798         )
799         route_eg_232_1_1_1.add_vpp_config()
800         route_eg_232_1_1_1.update_rpf_id(8192)
801         route_eg_232_1_1_2 = VppIpMRoute(
802             self,
803             "0.0.0.0",
804             "232.1.1.2",
805             32,
806             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
807             table_id=10,
808             paths=[
809                 VppMRoutePath(
810                     self.pg1.sw_if_index, MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD
811                 )
812             ],
813         )
814         route_eg_232_1_1_2.add_vpp_config()
815         route_eg_232_1_1_2.update_rpf_id(8193)
816
817         #
818         # inject a packet in VRF-0. We expect it to be BIER encapped,
819         # replicated, then hit the disposition and be forwarded
820         # out of VRF 10, i.e. on pg1
821         #
822         p = (
823             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
824             / IP(src="1.1.1.1", dst="232.1.1.1")
825             / UDP(sport=1234, dport=1234)
826             / Raw(scapy.compat.chb(5) * 32)
827         )
828
829         rx = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg1)
830
831         self.assertEqual(rx[0][IP].src, "1.1.1.1")
832         self.assertEqual(rx[0][IP].dst, "232.1.1.1")
833
834         p = (
835             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
836             / IP(src="1.1.1.1", dst="232.1.1.2")
837             / UDP(sport=1234, dport=1234)
838             / Raw(scapy.compat.chb(5) * 512)
839         )
840
841         rx = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg1)
842         self.assertEqual(rx[0][IP].src, "1.1.1.1")
843         self.assertEqual(rx[0][IP].dst, "232.1.1.2")
844
845     @unittest.skipUnless(config.extended, "part of extended tests")
846     def test_bier_e2e_1024(self):
847         """BIER end-to-end BSL:1024"""
848         self.bier_e2e(BIERLength.BIER_LEN_1024, 128, 1024)
849
850     @unittest.skipUnless(config.extended, "part of extended tests")
851     def test_bier_e2e_512(self):
852         """BIER end-to-end BSL:512"""
853         self.bier_e2e(BIERLength.BIER_LEN_512, 64, 512)
854
855     @unittest.skipUnless(config.extended, "part of extended tests")
856     def test_bier_e2e_256(self):
857         """BIER end-to-end BSL:256"""
858         self.bier_e2e(BIERLength.BIER_LEN_256, 32, 256)
859
860     @unittest.skipUnless(config.extended, "part of extended tests")
861     def test_bier_e2e_128(self):
862         """BIER end-to-end BSL:128"""
863         self.bier_e2e(BIERLength.BIER_LEN_128, 16, 128)
864
865     def test_bier_e2e_64(self):
866         """BIER end-to-end BSL:64"""
867         self.bier_e2e(BIERLength.BIER_LEN_64, 8, 64)
868
869     def test_bier_head_o_udp(self):
870         """BIER head over UDP"""
871
872         MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
873         MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
874
875         #
876         # Add a BIER table for sub-domain 1, set 0, and BSL 256
877         #
878         bti = VppBierTableID(1, 0, BIERLength.BIER_LEN_256)
879         bt = VppBierTable(self, bti, 77)
880         bt.add_vpp_config()
881
882         #
883         # 1 bit positions via 1 next hops
884         #
885         nh1 = "10.0.0.1"
886         ip_route = VppIpRoute(
887             self,
888             nh1,
889             32,
890             [
891                 VppRoutePath(
892                     self.pg1.remote_ip4,
893                     self.pg1.sw_if_index,
894                     labels=[VppMplsLabel(2001)],
895                 )
896             ],
897         )
898         ip_route.add_vpp_config()
899
900         udp_encap = VppUdpEncap(self, self.pg0.local_ip4, nh1, 330, 8138)
901         udp_encap.add_vpp_config()
902
903         bier_route = VppBierRoute(
904             self,
905             bti,
906             1,
907             [
908                 VppRoutePath(
909                     "0.0.0.0",
910                     0xFFFFFFFF,
911                     type=FibPathType.FIB_PATH_TYPE_UDP_ENCAP,
912                     next_hop_id=udp_encap.id,
913                 )
914             ],
915         )
916         bier_route.add_vpp_config()
917
918         #
919         # An 2 imposition objects with all bit-positions set
920         # only use the second, but creating 2 tests with a non-zero
921         # value index in the route add
922         #
923         bi = VppBierImp(self, bti, 333, scapy.compat.chb(0xFF) * 32)
924         bi.add_vpp_config()
925         bi2 = VppBierImp(self, bti, 334, scapy.compat.chb(0xFF) * 32)
926         bi2.add_vpp_config()
927
928         #
929         # Add a multicast route that will forward into the BIER doamin
930         #
931         route_ing_232_1_1_1 = VppIpMRoute(
932             self,
933             "0.0.0.0",
934             "232.1.1.1",
935             32,
936             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
937             paths=[
938                 VppMRoutePath(
939                     self.pg0.sw_if_index, MRouteItfFlags.MFIB_API_ITF_FLAG_ACCEPT
940                 ),
941                 VppMRoutePath(
942                     0xFFFFFFFF,
943                     MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD,
944                     proto=FibPathProto.FIB_PATH_NH_PROTO_BIER,
945                     type=FibPathType.FIB_PATH_TYPE_BIER_IMP,
946                     bier_imp=bi2.bi_index,
947                 ),
948             ],
949         )
950         route_ing_232_1_1_1.add_vpp_config()
951
952         #
953         # inject a packet an IP. We expect it to be BIER and UDP encapped,
954         #
955         p = (
956             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
957             / IP(src="1.1.1.1", dst="232.1.1.1")
958             / UDP(sport=1234, dport=1234)
959         )
960
961         self.pg0.add_stream([p])
962         self.pg_enable_capture(self.pg_interfaces)
963         self.pg_start()
964
965         rx = self.pg1.get_capture(1)
966
967         #
968         # Encap Stack is, eth, IP, UDP, BIFT, BIER
969         #
970         self.assertEqual(rx[0][IP].src, self.pg0.local_ip4)
971         self.assertEqual(rx[0][IP].dst, nh1)
972         self.assertEqual(rx[0][UDP].sport, 330)
973         self.assertEqual(rx[0][UDP].dport, 8138)
974         self.assertEqual(rx[0][BIFT].bsl, BIERLength.BIER_LEN_256)
975         self.assertEqual(rx[0][BIFT].sd, 1)
976         self.assertEqual(rx[0][BIFT].set, 0)
977         self.assertEqual(rx[0][BIFT].ttl, 64)
978         self.assertEqual(rx[0][BIER].length, 2)
979
980     def test_bier_tail_o_udp(self):
981         """BIER Tail over UDP"""
982
983         MRouteItfFlags = VppEnum.vl_api_mfib_itf_flags_t
984         MRouteEntryFlags = VppEnum.vl_api_mfib_entry_flags_t
985
986         #
987         # Add a BIER table for sub-domain 0, set 0, and BSL 256
988         #
989         bti = VppBierTableID(1, 0, BIERLength.BIER_LEN_256)
990         bt = VppBierTable(self, bti, MPLS_LABEL_INVALID)
991         bt.add_vpp_config()
992
993         #
994         # disposition table
995         #
996         bdt = VppBierDispTable(self, 8)
997         bdt.add_vpp_config()
998
999         #
1000         # BIER route in table that's for-us
1001         #
1002         bier_route_1 = VppBierRoute(
1003             self,
1004             bti,
1005             1,
1006             [
1007                 VppRoutePath(
1008                     "0.0.0.0",
1009                     0xFFFFFFFF,
1010                     proto=FibPathProto.FIB_PATH_NH_PROTO_BIER,
1011                     nh_table_id=8,
1012                 )
1013             ],
1014         )
1015         bier_route_1.add_vpp_config()
1016
1017         #
1018         # An entry in the disposition table
1019         #
1020         bier_de_1 = VppBierDispEntry(
1021             self,
1022             bdt.id,
1023             99,
1024             BIER_HDR_PAYLOAD.BIER_HDR_PROTO_IPV4,
1025             FibPathProto.FIB_PATH_NH_PROTO_BIER,
1026             "0.0.0.0",
1027             0,
1028             rpf_id=8192,
1029         )
1030         bier_de_1.add_vpp_config()
1031
1032         #
1033         # A multicast route to forward post BIER disposition
1034         #
1035         route_eg_232_1_1_1 = VppIpMRoute(
1036             self,
1037             "0.0.0.0",
1038             "232.1.1.1",
1039             32,
1040             MRouteEntryFlags.MFIB_API_ENTRY_FLAG_NONE,
1041             paths=[
1042                 VppMRoutePath(
1043                     self.pg1.sw_if_index, MRouteItfFlags.MFIB_API_ITF_FLAG_FORWARD
1044                 )
1045             ],
1046         )
1047         route_eg_232_1_1_1.add_vpp_config()
1048         route_eg_232_1_1_1.update_rpf_id(8192)
1049
1050         #
1051         # A packet with all bits set gets spat out to BP:1
1052         #
1053         p = (
1054             Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac)
1055             / IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4)
1056             / UDP(sport=333, dport=8138)
1057             / BIFT(sd=1, set=0, bsl=2, ttl=255)
1058             / BIER(
1059                 length=BIERLength.BIER_LEN_256,
1060                 BitString=scapy.compat.chb(255) * 32,
1061                 BFRID=99,
1062             )
1063             / IP(src="1.1.1.1", dst="232.1.1.1")
1064             / UDP(sport=1234, dport=1234)
1065             / Raw()
1066         )
1067
1068         rx = self.send_and_expect(self.pg0, [p], self.pg1)
1069
1070
1071 if __name__ == "__main__":
1072     unittest.main(testRunner=VppTestRunner)