03ae52abad4d72922062082d65d82e1f49d228cb
[vpp.git] / test / test_bier.py
1 #!/usr/bin/env python
2
3 import unittest
4 import socket
5
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 *
12
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 *
19
20
21 class TestBFIB(VppTestCase):
22     """ BIER FIB Test Case """
23
24     def test_bfib(self):
25         """ BFIB Unit Tests """
26         error = self.vapi.cli("test bier")
27
28         if error:
29             self.logger.critical(error)
30         self.assertEqual(error.find("Failed"), -1)
31
32
33 class TestBier(VppTestCase):
34     """ BIER Test Case """
35
36     def setUp(self):
37         super(TestBier, self).setUp()
38
39         # create 2 pg interfaces
40         self.create_pg_interfaces(range(3))
41
42         # create the default MPLS table
43         self.tables = []
44         tbl = VppMplsTable(self, 0)
45         tbl.add_vpp_config()
46         self.tables.append(tbl)
47
48         tbl = VppIpTable(self, 10)
49         tbl.add_vpp_config()
50         self.tables.append(tbl)
51
52         # setup both interfaces
53         for i in self.pg_interfaces:
54             if i == self.pg2:
55                 i.set_table_ip4(10)
56             i.admin_up()
57             i.config_ip4()
58             i.resolve_arp()
59             i.enable_mpls()
60
61     def tearDown(self):
62         for i in self.pg_interfaces:
63             i.disable_mpls()
64             i.unconfig_ip4()
65             i.set_table_ip4(0)
66             i.admin_down()
67         super(TestBier, self).tearDown()
68
69     def test_bier_midpoint(self):
70         """BIER midpoint"""
71
72         #
73         # Add a BIER table for sub-domain 0, set 0, and BSL 256
74         #
75         bti = VppBierTableID(0, 0, BIERLength.BIER_LEN_256)
76         bt = VppBierTable(self, bti, 77)
77         bt.add_vpp_config()
78
79         #
80         # A packet with no bits set gets dropped
81         #
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) /
88              Raw())
89         pkts = [p]
90
91         self.send_and_assert_no_replies(self.pg0, pkts,
92                                         "Empty Bit-String")
93
94         #
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.
98         #
99         nh_routes = []
100         bier_routes = []
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,
106                                                       labels=[2000+i])]))
107             nh_routes[-1].add_vpp_config()
108
109             bier_routes.append(VppBierRoute(self, bti, i,
110                                             [VppRoutePath(nh, 0xffffffff,
111                                                           labels=[100+i])]))
112             bier_routes[-1].add_vpp_config()
113
114         #
115         # A packet with all bits set gets spat out to BP:1
116         #
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) /
122              Raw())
123         pkts = [p]
124
125         self.pg0.add_stream(pkts)
126         self.pg_enable_capture(self.pg_interfaces)
127         self.pg_start()
128
129         rx = self.pg1.get_capture(255)
130
131         for rxp in rx:
132             #
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
136             #
137             olabel = rxp[MPLS]
138             bp = olabel.label - 2000
139
140             blabel = olabel[MPLS].payload
141             self.assertEqual(blabel.label, 100+bp)
142             self.assertEqual(blabel.ttl, 254)
143
144             bier_hdr = blabel[MPLS].payload
145
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)
154
155             # The bit-string should consist only of the BP given by i.
156             i = 0
157             bitstring = ""
158             bpi = bp - 1
159             while (i < bpi/8):
160                 bitstring = chr(0) + bitstring
161                 i += 1
162             bitstring = chr(1 << bpi % 8) + bitstring
163
164             while len(bitstring) < 32:
165                 bitstring = chr(0) + bitstring
166
167             self.assertEqual(len(bitstring), len(bier_hdr.BitString))
168             self.assertEqual(bitstring, bier_hdr.BitString)
169
170     def test_bier_head(self):
171         """BIER head"""
172
173         #
174         # Add a BIER table for sub-domain 0, set 0, and BSL 256
175         #
176         bti = VppBierTableID(0, 0, BIERLength.BIER_LEN_256)
177         bt = VppBierTable(self, bti, 77)
178         bt.add_vpp_config()
179
180         #
181         # 2 bit positions via two next hops
182         #
183         nh1 = "10.0.0.1"
184         nh2 = "10.0.0.2"
185         ip_route_1 = VppIpRoute(self, nh1, 32,
186                                 [VppRoutePath(self.pg1.remote_ip4,
187                                               self.pg1.sw_if_index,
188                                               labels=[2001])])
189         ip_route_2 = VppIpRoute(self, nh2, 32,
190                                 [VppRoutePath(self.pg1.remote_ip4,
191                                               self.pg1.sw_if_index,
192                                               labels=[2002])])
193         ip_route_1.add_vpp_config()
194         ip_route_2.add_vpp_config()
195
196         bier_route_1 = VppBierRoute(self, bti, 1,
197                                     [VppRoutePath(nh1, 0xffffffff,
198                                                   labels=[101])])
199         bier_route_2 = VppBierRoute(self, bti, 2,
200                                     [VppRoutePath(nh2, 0xffffffff,
201                                                   labels=[102])])
202         bier_route_1.add_vpp_config()
203         bier_route_2.add_vpp_config()
204
205         #
206         # An imposition object with both bit-positions set
207         #
208         bi = VppBierImp(self, bti, 333, chr(0x3) * 32)
209         bi.add_vpp_config()
210
211         #
212         # Add a multicast route that will forward into the BIER doamin
213         #
214         route_ing_232_1_1_1 = VppIpMRoute(
215             self,
216             "0.0.0.0",
217             "232.1.1.1", 32,
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()
226
227         #
228         # inject an IP packet. We expect it to be BIER encapped and
229         # replicated.
230         #
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))
235
236         self.pg0.add_stream([p])
237         self.pg_enable_capture(self.pg_interfaces)
238         self.pg_start()
239
240         rx = self.pg1.get_capture(2)
241
242         #
243         # Encap Stack is; eth, MPLS, MPLS, BIER
244         #
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)
254
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)
264
265     def test_bier_tail(self):
266         """BIER Tail"""
267
268         #
269         # Add a BIER table for sub-domain 0, set 0, and BSL 256
270         #
271         bti = VppBierTableID(0, 0, BIERLength.BIER_LEN_256)
272         bt = VppBierTable(self, bti, 77)
273         bt.add_vpp_config()
274
275         #
276         # disposition table
277         #
278         bdt = VppBierDispTable(self, 8)
279         bdt.add_vpp_config()
280
281         #
282         # BIER route in table that's for-us
283         #
284         bier_route_1 = VppBierRoute(self, bti, 1,
285                                     [VppRoutePath("0.0.0.0",
286                                                   0xffffffff,
287                                                   nh_table_id=8)])
288         bier_route_1.add_vpp_config()
289
290         #
291         # An entry in the disposition table
292         #
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()
297
298         #
299         # A multicast route to forward post BIER disposition
300         #
301         route_eg_232_1_1_1 = VppIpMRoute(
302             self,
303             "0.0.0.0",
304             "232.1.1.1", 32,
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)
310
311         #
312         # A packet with all bits set gets spat out to BP:1
313         #
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) /
319              Raw())
320
321         self.send_and_expect(self.pg0, [p], self.pg1)
322
323         #
324         # A packet that does not match the Disposition entry gets dropped
325         #
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) /
331              Raw())
332         self.send_and_assert_no_replies(self.pg0, p*2,
333                                         "no matching disposition entry")
334
335         #
336         # Add the default route to the disposition table
337         #
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()
342
343         #
344         # now the previous packet is forwarded
345         #
346         self.send_and_expect(self.pg0, [p], self.pg1)
347
348     def test_bier_e2e(self):
349         """ BIER end-to-end """
350
351         #
352         # Add a BIER table for sub-domain 0, set 0, and BSL 256
353         #
354         bti = VppBierTableID(0, 0, BIERLength.BIER_LEN_256)
355         bt = VppBierTable(self, bti, 77)
356         bt.add_vpp_config()
357
358         #
359         # Impostion Sets bit string 101010101....
360         #  sender 333
361         #
362         bi = VppBierImp(self, bti, 333, chr(0x5) * 32)
363         bi.add_vpp_config()
364
365         #
366         # Add a multicast route that will forward into the BIER doamin
367         #
368         route_ing_232_1_1_1 = VppIpMRoute(
369             self,
370             "0.0.0.0",
371             "232.1.1.1", 32,
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()
380
381         #
382         # disposition table 8
383         #
384         bdt = VppBierDispTable(self, 8)
385         bdt.add_vpp_config()
386
387         #
388         # BIER route in table that's for-us, resolving through
389         # disp table 8.
390         #
391         bier_route_1 = VppBierRoute(self, bti, 1,
392                                     [VppRoutePath("0.0.0.0",
393                                                   0xffffffff,
394                                                   nh_table_id=8)])
395         bier_route_1.add_vpp_config()
396
397         #
398         # An entry in the disposition table for sender 333
399         #  lookup in VRF 10
400         #
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()
405
406         #
407         # Add a multicast route that will forward the traffic
408         # post-disposition
409         #
410         route_eg_232_1_1_1 = VppIpMRoute(
411             self,
412             "0.0.0.0",
413             "232.1.1.1", 32,
414             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
415             table_id=10,
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)
420
421         #
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
425         #
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))
430
431         rx = self.send_and_expect(self.pg0, p*65, self.pg1)
432
433         #
434         # should be IP
435         #
436         self.assertEqual(rx[0][IP].src, "1.1.1.1")
437         self.assertEqual(rx[0][IP].dst, "232.1.1.1")
438
439     def test_bier_head_o_udp(self):
440         """BIER head over UDP"""
441
442         #
443         # Add a BIER table for sub-domain 1, set 0, and BSL 256
444         #
445         bti = VppBierTableID(1, 0, BIERLength.BIER_LEN_256)
446         bt = VppBierTable(self, bti, 77)
447         bt.add_vpp_config()
448
449         #
450         # 1 bit positions via 1 next hops
451         #
452         nh1 = "10.0.0.1"
453         ip_route = VppIpRoute(self, nh1, 32,
454                               [VppRoutePath(self.pg1.remote_ip4,
455                                             self.pg1.sw_if_index,
456                                             labels=[2001])])
457         ip_route.add_vpp_config()
458
459         udp_encap = VppUdpEncap(self, 4,
460                                 self.pg0.local_ip4,
461                                 nh1,
462                                 330, 8138)
463         udp_encap.add_vpp_config()
464
465         bier_route = VppBierRoute(self, bti, 1,
466                                   [VppRoutePath("0.0.0.0",
467                                                 0xFFFFFFFF,
468                                                 is_udp_encap=1,
469                                                 next_hop_id=4)])
470         bier_route.add_vpp_config()
471
472         #
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
476         #
477         bi = VppBierImp(self, bti, 333, chr(0xff) * 32)
478         bi.add_vpp_config()
479         bi2 = VppBierImp(self, bti, 334, chr(0xff) * 32)
480         bi2.add_vpp_config()
481
482         #
483         # Add a multicast route that will forward into the BIER doamin
484         #
485         route_ing_232_1_1_1 = VppIpMRoute(
486             self,
487             "0.0.0.0",
488             "232.1.1.1", 32,
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()
497
498         #
499         # inject a packet an IP. We expect it to be BIER and UDP encapped,
500         #
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))
505
506         self.pg0.add_stream([p])
507         self.pg_enable_capture(self.pg_interfaces)
508         self.pg_start()
509
510         rx = self.pg1.get_capture(1)
511
512         #
513         # Encap Stack is, eth, IP, UDP, BIFT, BIER
514         #
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)
524
525     def test_bier_tail_o_udp(self):
526         """BIER Tail over UDP"""
527
528         #
529         # Add a BIER table for sub-domain 0, set 0, and BSL 256
530         #
531         bti = VppBierTableID(1, 0, BIERLength.BIER_LEN_256)
532         bt = VppBierTable(self, bti, MPLS_LABEL_INVALID)
533         bt.add_vpp_config()
534
535         #
536         # disposition table
537         #
538         bdt = VppBierDispTable(self, 8)
539         bdt.add_vpp_config()
540
541         #
542         # BIER route in table that's for-us
543         #
544         bier_route_1 = VppBierRoute(self, bti, 1,
545                                     [VppRoutePath("0.0.0.0",
546                                                   0xffffffff,
547                                                   nh_table_id=8)])
548         bier_route_1.add_vpp_config()
549
550         #
551         # An entry in the disposition table
552         #
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()
557
558         #
559         # A multicast route to forward post BIER disposition
560         #
561         route_eg_232_1_1_1 = VppIpMRoute(
562             self,
563             "0.0.0.0",
564             "232.1.1.1", 32,
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)
570
571         #
572         # A packet with all bits set gets spat out to BP:1
573         #
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) /
581              Raw())
582
583         rx = self.send_and_expect(self.pg0, [p], self.pg1)
584
585
586 if __name__ == '__main__':
587     unittest.main(testRunner=VppTestRunner)