BIER
[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
12 from scapy.packet import Raw
13 from scapy.layers.l2 import Ether
14 from scapy.layers.inet import IP, UDP, ICMP
15 from scapy.layers.inet6 import IPv6
16 from scapy.contrib.mpls import MPLS
17 from scapy.contrib.bier import *
18
19
20 class TestBFIB(VppTestCase):
21     """ BIER FIB Test Case """
22
23     def test_bfib(self):
24         """ BFIB Unit Tests """
25         error = self.vapi.cli("test bier")
26
27         if error:
28             self.logger.critical(error)
29         self.assertEqual(error.find("Failed"), -1)
30
31
32 class TestBier(VppTestCase):
33     """ BIER Test Case """
34
35     def setUp(self):
36         super(TestBier, self).setUp()
37
38         # create 2 pg interfaces
39         self.create_pg_interfaces(range(3))
40
41         # create the default MPLS table
42         self.tables = []
43         tbl = VppMplsTable(self, 0)
44         tbl.add_vpp_config()
45         self.tables.append(tbl)
46
47         tbl = VppIpTable(self, 10)
48         tbl.add_vpp_config()
49         self.tables.append(tbl)
50
51         # setup both interfaces
52         for i in self.pg_interfaces:
53             if i == self.pg2:
54                 i.set_table_ip4(10)
55             i.admin_up()
56             i.config_ip4()
57             i.resolve_arp()
58             i.enable_mpls()
59
60     def tearDown(self):
61         for i in self.pg_interfaces:
62             i.disable_mpls()
63             i.unconfig_ip4()
64             i.set_table_ip4(0)
65             i.admin_down()
66         super(TestBier, self).tearDown()
67
68     def send_and_assert_no_replies(self, intf, pkts, remark):
69         intf.add_stream(pkts)
70         self.pg_enable_capture(self.pg_interfaces)
71         self.pg_start()
72         for i in self.pg_interfaces:
73             i.assert_nothing_captured(remark=remark)
74
75     def send_and_expect(self, input, pkts, output):
76         self.vapi.cli("trace add bier-mpls-lookup 10")
77         input.add_stream(pkts)
78         self.pg_enable_capture(self.pg_interfaces)
79         self.pg_start()
80         rx = output.get_capture(len(pkts))
81
82     def test_bier_midpoint(self):
83         """BIER midpoint"""
84
85         #
86         # Add a BIER table for sub-domain 0, set 0, and BSL 256
87         #
88         bti = VppBierTableID(0, 0, BIERLength.BIER_LEN_256)
89         bt = VppBierTable(self, bti, 77)
90         bt.add_vpp_config()
91
92         #
93         # A packet with no bits set gets dropped
94         #
95         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
96              MPLS(label=77, ttl=255) /
97              BIER(length=BIERLength.BIER_LEN_256,
98                   BitString=chr(0)*64) /
99              IPv6(src=self.pg0.remote_ip6, dst=self.pg0.remote_ip6) /
100              UDP(sport=1234, dport=1234) /
101              Raw())
102         pkts = [p]
103
104         self.send_and_assert_no_replies(self.pg0, pkts,
105                                         "Empty Bit-String")
106
107         #
108         # Add a BIER route for each bit-position in the table via a different
109         # next-hop. Testing whether the BIER walk and replicate forwarding
110         # function works for all bit posisitons.
111         #
112         nh_routes = []
113         bier_routes = []
114         for i in range(1, 256):
115             nh = "10.0.%d.%d" % (i / 255, i % 255)
116             nh_routes.append(VppIpRoute(self, nh, 32,
117                                         [VppRoutePath(self.pg1.remote_ip4,
118                                                       self.pg1.sw_if_index,
119                                                       labels=[2000+i])]))
120             nh_routes[-1].add_vpp_config()
121
122             bier_routes.append(VppBierRoute(self, bti, i, nh, 100+i))
123             bier_routes[-1].add_vpp_config()
124
125         #
126         # A packet with all bits set gets spat out to BP:1
127         #
128         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
129              MPLS(label=77, ttl=255) /
130              BIER(length=BIERLength.BIER_LEN_256) /
131              IPv6(src=self.pg0.remote_ip6, dst=self.pg0.remote_ip6) /
132              UDP(sport=1234, dport=1234) /
133              Raw())
134         pkts = [p]
135
136         self.pg0.add_stream(pkts)
137         self.pg_enable_capture(self.pg_interfaces)
138         self.pg_start()
139
140         rx = self.pg1.get_capture(255)
141
142         for rxp in rx:
143             #
144             # The packets are not required to be sent in bit-position order
145             # when we setup the routes above we used the bit-position to
146             # construct the out-label. so use that here to determine the BP
147             #
148             olabel = rxp[MPLS]
149             bp = olabel.label - 2000
150
151             blabel = olabel[MPLS].payload
152             self.assertEqual(blabel.label, 100+bp)
153
154             bier_hdr = blabel[MPLS].payload
155
156             self.assertEqual(bier_hdr.id, 5)
157             self.assertEqual(bier_hdr.version, 0)
158             self.assertEqual(bier_hdr.length, BIERLength.BIER_LEN_256)
159             self.assertEqual(bier_hdr.entropy, 0)
160             self.assertEqual(bier_hdr.OAM, 0)
161             self.assertEqual(bier_hdr.RSV, 0)
162             self.assertEqual(bier_hdr.DSCP, 0)
163             self.assertEqual(bier_hdr.Proto, 5)
164
165             # The bit-string should consist only of the BP given by i.
166             i = 0
167             bitstring = ""
168             bpi = bp - 1
169             while (i < bpi/8):
170                 bitstring = chr(0) + bitstring
171                 i += 1
172             bitstring = chr(1 << bpi % 8) + bitstring
173
174             while len(bitstring) < 32:
175                 bitstring = chr(0) + bitstring
176
177             self.assertEqual(len(bitstring), len(bier_hdr.BitString))
178             self.assertEqual(bitstring, bier_hdr.BitString)
179
180     def test_bier_head(self):
181         """BIER head"""
182
183         #
184         # Add a BIER table for sub-domain 0, set 0, and BSL 256
185         #
186         bti = VppBierTableID(0, 0, BIERLength.BIER_LEN_256)
187         bt = VppBierTable(self, bti, 77)
188         bt.add_vpp_config()
189
190         #
191         # 2 bit positions via two next hops
192         #
193         nh1 = "10.0.0.1"
194         nh2 = "10.0.0.2"
195         ip_route_1 = VppIpRoute(self, nh1, 32,
196                                 [VppRoutePath(self.pg1.remote_ip4,
197                                               self.pg1.sw_if_index,
198                                               labels=[2001])])
199         ip_route_2 = VppIpRoute(self, nh2, 32,
200                                 [VppRoutePath(self.pg1.remote_ip4,
201                                               self.pg1.sw_if_index,
202                                               labels=[2002])])
203         ip_route_1.add_vpp_config()
204         ip_route_2.add_vpp_config()
205
206         bier_route_1 = VppBierRoute(self, bti, 1, nh1, 101)
207         bier_route_2 = VppBierRoute(self, bti, 2, nh2, 102)
208         bier_route_1.add_vpp_config()
209         bier_route_2.add_vpp_config()
210
211         #
212         # An imposition object with both bit-positions set
213         #
214         bi = VppBierImp(self, bti, 333, chr(0x3) * 32)
215         bi.add_vpp_config()
216
217         #
218         # Add a multicast route that will forward into the BIER doamin
219         #
220         route_ing_232_1_1_1 = VppIpMRoute(
221             self,
222             "0.0.0.0",
223             "232.1.1.1", 32,
224             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
225             paths=[VppMRoutePath(self.pg0.sw_if_index,
226                                  MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
227                    VppMRoutePath(0xffffffff,
228                                  MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
229                                  proto=DpoProto.DPO_PROTO_BIER,
230                                  bier_imp=bi.bi_index)])
231         route_ing_232_1_1_1.add_vpp_config()
232
233         #
234         # inject a packet an IP. We expect it to be BIER encapped,
235         # replicated.
236         #
237         p = (Ether(dst=self.pg0.local_mac,
238                    src=self.pg0.remote_mac) /
239              IP(src="1.1.1.1", dst="232.1.1.1") /
240              UDP(sport=1234, dport=1234))
241
242         self.pg0.add_stream([p])
243         self.pg_enable_capture(self.pg_interfaces)
244         self.pg_start()
245
246         rx = self.pg1.get_capture(2)
247
248     def test_bier_tail(self):
249         """BIER Tail"""
250
251         #
252         # Add a BIER table for sub-domain 0, set 0, and BSL 256
253         #
254         bti = VppBierTableID(0, 0, BIERLength.BIER_LEN_256)
255         bt = VppBierTable(self, bti, 77)
256         bt.add_vpp_config()
257
258         #
259         # disposition table
260         #
261         bdt = VppBierDispTable(self, 8)
262         bdt.add_vpp_config()
263
264         #
265         # BIER route in table that's for-us
266         #
267         bier_route_1 = VppBierRoute(self, bti, 1, "0.0.0.0", 0,
268                                     disp_table=8)
269         bier_route_1.add_vpp_config()
270
271         #
272         # An entry in the disposition table
273         #
274         bier_de_1 = VppBierDispEntry(self, bdt.id, 99,
275                                      BIER_HDR_PAYLOAD.BIER_HDR_PROTO_IPV4,
276                                      "0.0.0.0", 0, rpf_id=8192)
277         bier_de_1.add_vpp_config()
278
279         #
280         # A multicast route to forward post BIER disposition
281         #
282         route_eg_232_1_1_1 = VppIpMRoute(
283             self,
284             "0.0.0.0",
285             "232.1.1.1", 32,
286             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
287             paths=[VppMRoutePath(self.pg1.sw_if_index,
288                                  MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
289         route_eg_232_1_1_1.add_vpp_config()
290         route_eg_232_1_1_1.update_rpf_id(8192)
291
292         #
293         # A packet with all bits set gets spat out to BP:1
294         #
295         p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
296              MPLS(label=77, ttl=255) /
297              BIER(length=BIERLength.BIER_LEN_256, BFRID=99) /
298              IP(src="1.1.1.1", dst="232.1.1.1") /
299              UDP(sport=1234, dport=1234) /
300              Raw())
301
302         self.send_and_expect(self.pg0, [p], self.pg1)
303
304     def test_bier_e2e(self):
305         """ BIER end-to-end """
306
307         #
308         # Add a BIER table for sub-domain 0, set 0, and BSL 256
309         #
310         bti = VppBierTableID(0, 0, BIERLength.BIER_LEN_256)
311         bt = VppBierTable(self, bti, 77)
312         bt.add_vpp_config()
313
314         #
315         # Impostion Sets bit string 101010101....
316         #  sender 333
317         #
318         bi = VppBierImp(self, bti, 333, chr(0x5) * 32)
319         bi.add_vpp_config()
320
321         #
322         # Add a multicast route that will forward into the BIER doamin
323         #
324         route_ing_232_1_1_1 = VppIpMRoute(
325             self,
326             "0.0.0.0",
327             "232.1.1.1", 32,
328             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
329             paths=[VppMRoutePath(self.pg0.sw_if_index,
330                                  MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT),
331                    VppMRoutePath(0xffffffff,
332                                  MRouteItfFlags.MFIB_ITF_FLAG_FORWARD,
333                                  proto=DpoProto.DPO_PROTO_BIER,
334                                  bier_imp=bi.bi_index)])
335         route_ing_232_1_1_1.add_vpp_config()
336
337         #
338         # disposition table 8
339         #
340         bdt = VppBierDispTable(self, 8)
341         bdt.add_vpp_config()
342
343         #
344         # BIER route in table that's for-us, resolving through
345         # disp table 8.
346         #
347         bier_route_1 = VppBierRoute(self, bti, 1, "0.0.0.0",
348                                     MPLS_LABEL_INVALID,
349                                     disp_table=8)
350         bier_route_1.add_vpp_config()
351
352         #
353         # An entry in the disposition table for sender 333
354         #  lookup in VRF 10
355         #
356         bier_de_1 = VppBierDispEntry(self, bdt.id, 333,
357                                      BIER_HDR_PAYLOAD.BIER_HDR_PROTO_IPV4,
358                                      "0.0.0.0", 10, rpf_id=8192)
359         bier_de_1.add_vpp_config()
360
361         #
362         # Add a multicast route that will forward the traffic
363         # post-disposition
364         #
365         route_eg_232_1_1_1 = VppIpMRoute(
366             self,
367             "0.0.0.0",
368             "232.1.1.1", 32,
369             MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE,
370             table_id=10,
371             paths=[VppMRoutePath(self.pg1.sw_if_index,
372                                  MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)])
373         route_eg_232_1_1_1.add_vpp_config()
374         route_eg_232_1_1_1.update_rpf_id(8192)
375
376         #
377         # inject a packet in VRF-0. We expect it to be BIER encapped,
378         # replicated, then hit the disposition and be forwarded
379         # out of VRF 10, i.e. on pg1
380         #
381         p = (Ether(dst=self.pg0.local_mac,
382                    src=self.pg0.remote_mac) /
383              IP(src="1.1.1.1", dst="232.1.1.1") /
384              UDP(sport=1234, dport=1234))
385
386         self.send_and_expect(self.pg0, p*65, self.pg1)
387
388
389 if __name__ == '__main__':
390     unittest.main(testRunner=VppTestRunner)