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