blob: 1a4567bd656631e971b0bfb1af1626de97d1f572 [file] [log] [blame]
Neale Rannsd792d9c2017-10-21 10:53:20 -07001#!/usr/bin/env python
2
3import unittest
4import socket
5
6from framework import VppTestCase, VppTestRunner
7from vpp_ip_route import VppIpRoute, VppRoutePath, VppMplsRoute, \
8 VppMplsTable, VppIpMRoute, VppMRoutePath, VppIpTable, \
9 MRouteEntryFlags, MRouteItfFlags, MPLS_LABEL_INVALID, DpoProto
10from vpp_bier import *
11
12from scapy.packet import Raw
13from scapy.layers.l2 import Ether
14from scapy.layers.inet import IP, UDP, ICMP
15from scapy.layers.inet6 import IPv6
16from scapy.contrib.mpls import MPLS
17from scapy.contrib.bier import *
18
19
20class 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
32class 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
389if __name__ == '__main__':
390 unittest.main(testRunner=VppTestRunner)