blob: 03ae52abad4d72922062082d65d82e1f49d228cb [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 *
Neale Ranns91286372017-12-05 13:24:04 -080011from vpp_udp_encap import *
Neale Rannsd792d9c2017-10-21 10:53:20 -070012
13from scapy.packet import Raw
14from scapy.layers.l2 import Ether
15from scapy.layers.inet import IP, UDP, ICMP
16from scapy.layers.inet6 import IPv6
17from scapy.contrib.mpls import MPLS
18from scapy.contrib.bier import *
19
20
21class 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
33class 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
Neale Rannsd792d9c2017-10-21 10:53:20 -070069 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
Neale Ranns91286372017-12-05 13:24:04 -0800109 bier_routes.append(VppBierRoute(self, bti, i,
110 [VppRoutePath(nh, 0xffffffff,
111 labels=[100+i])]))
Neale Rannsd792d9c2017-10-21 10:53:20 -0700112 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)
Neale Ranns91286372017-12-05 13:24:04 -0800142 self.assertEqual(blabel.ttl, 254)
Neale Rannsd792d9c2017-10-21 10:53:20 -0700143
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
Neale Ranns91286372017-12-05 13:24:04 -0800196 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])])
Neale Rannsd792d9c2017-10-21 10:53:20 -0700202 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 #
Neale Ranns91286372017-12-05 13:24:04 -0800228 # inject an IP packet. We expect it to be BIER encapped and
Neale Rannsd792d9c2017-10-21 10:53:20 -0700229 # 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
Neale Ranns91286372017-12-05 13:24:04 -0800242 #
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
Neale Rannsd792d9c2017-10-21 10:53:20 -0700265 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 #
Neale Ranns91286372017-12-05 13:24:04 -0800284 bier_route_1 = VppBierRoute(self, bti, 1,
285 [VppRoutePath("0.0.0.0",
286 0xffffffff,
287 nh_table_id=8)])
Neale Rannsd792d9c2017-10-21 10:53:20 -0700288 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
Neale Rannsceb4d052017-12-13 09:13:41 -0800323 #
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
Neale Rannsd792d9c2017-10-21 10:53:20 -0700348 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 #
Neale Ranns91286372017-12-05 13:24:04 -0800391 bier_route_1 = VppBierRoute(self, bti, 1,
392 [VppRoutePath("0.0.0.0",
393 0xffffffff,
394 nh_table_id=8)])
Neale Rannsd792d9c2017-10-21 10:53:20 -0700395 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
Neale Ranns91286372017-12-05 13:24:04 -0800431 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 #
Neale Rannseea537a2018-01-09 04:11:28 -0800473 # 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
Neale Ranns91286372017-12-05 13:24:04 -0800476 #
477 bi = VppBierImp(self, bti, 333, chr(0xff) * 32)
478 bi.add_vpp_config()
Neale Rannseea537a2018-01-09 04:11:28 -0800479 bi2 = VppBierImp(self, bti, 334, chr(0xff) * 32)
480 bi2.add_vpp_config()
Neale Ranns91286372017-12-05 13:24:04 -0800481
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,
Neale Rannseea537a2018-01-09 04:11:28 -0800495 bier_imp=bi2.bi_index)])
Neale Ranns91286372017-12-05 13:24:04 -0800496 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)
Neale Rannsd792d9c2017-10-21 10:53:20 -0700584
585
586if __name__ == '__main__':
587 unittest.main(testRunner=VppTestRunner)