fib: fib api updates
[vpp.git] / test / test_qos.py
1 #!/usr/bin/env python
2
3 import unittest
4
5 from framework import VppTestCase, VppTestRunner
6 from vpp_sub_interface import VppDot1QSubint
7 from vpp_ip import DpoProto
8 from vpp_ip_route import VppIpRoute, VppRoutePath, VppMplsRoute, \
9     VppMplsLabel, VppMplsTable, FibPathProto
10
11 import scapy.compat
12 from scapy.packet import Raw
13 from scapy.layers.l2 import Ether, Dot1Q
14 from scapy.layers.inet import IP, UDP
15 from scapy.layers.inet6 import IPv6
16 from scapy.contrib.mpls import MPLS
17 from vpp_papi import VppEnum
18
19 NUM_PKTS = 67
20
21
22 class TestQOS(VppTestCase):
23     """ QOS Test Case """
24
25     # Note: Since the enums aren't created dynamically until after
26     #       the papi client attaches to VPP, we put it in a property to
27     #       ensure it is the value at runtime, not at module load time.
28     @property
29     def QOS_SOURCE(self):
30         return VppEnum.vl_api_qos_source_t
31
32     @classmethod
33     def setUpClass(cls):
34         super(TestQOS, cls).setUpClass()
35
36     @classmethod
37     def tearDownClass(cls):
38         super(TestQOS, cls).tearDownClass()
39
40     def setUp(self):
41         super(TestQOS, self).setUp()
42
43         self.create_pg_interfaces(range(5))
44
45         tbl = VppMplsTable(self, 0)
46         tbl.add_vpp_config()
47
48         for i in self.pg_interfaces:
49             i.admin_up()
50             i.config_ip4()
51             i.resolve_arp()
52             i.config_ip6()
53             i.resolve_ndp()
54             i.enable_mpls()
55
56     def tearDown(self):
57         for i in self.pg_interfaces:
58             i.unconfig_ip4()
59             i.unconfig_ip6()
60             i.disable_mpls()
61
62         super(TestQOS, self).tearDown()
63
64     def test_qos_ip(self):
65         """ QoS Mark/Record IP """
66
67         #
68         # for table 1 map the n=0xff possible values of input QoS mark,
69         # n to 1-n
70         #
71         output = [scapy.compat.chb(0)] * 256
72         for i in range(0, 255):
73             output[i] = scapy.compat.chb(255 - i)
74         os = b''.join(output)
75         rows = [{'outputs': os},
76                 {'outputs': os},
77                 {'outputs': os},
78                 {'outputs': os}]
79
80         self.vapi.qos_egress_map_update(1, rows)
81
82         #
83         # For table 2 (and up) use the value n for everything
84         #
85         output = [scapy.compat.chb(2)] * 256
86         os = b''.join(output)
87         rows = [{'outputs': os},
88                 {'outputs': os},
89                 {'outputs': os},
90                 {'outputs': os}]
91
92         self.vapi.qos_egress_map_update(2, rows)
93
94         output = [scapy.compat.chb(3)] * 256
95         os = b''.join(output)
96         rows = [{'outputs': os},
97                 {'outputs': os},
98                 {'outputs': os},
99                 {'outputs': os}]
100
101         self.vapi.qos_egress_map_update(3, rows)
102
103         output = [scapy.compat.chb(4)] * 256
104         os = b''.join(output)
105         rows = [{'outputs': os},
106                 {'outputs': os},
107                 {'outputs': os},
108                 {'outputs': os}]
109         self.vapi.qos_egress_map_update(4, rows)
110         self.vapi.qos_egress_map_update(5, rows)
111         self.vapi.qos_egress_map_update(6, rows)
112         self.vapi.qos_egress_map_update(7, rows)
113
114         self.logger.info(self.vapi.cli("sh qos eg map"))
115
116         #
117         # Bind interface pgN to table n
118         #
119         self.vapi.qos_mark_enable_disable(self.pg1.sw_if_index,
120                                           self.QOS_SOURCE.QOS_API_SOURCE_IP,
121                                           1,
122                                           1)
123         self.vapi.qos_mark_enable_disable(self.pg2.sw_if_index,
124                                           self.QOS_SOURCE.QOS_API_SOURCE_IP,
125                                           2,
126                                           1)
127         self.vapi.qos_mark_enable_disable(self.pg3.sw_if_index,
128                                           self.QOS_SOURCE.QOS_API_SOURCE_IP,
129                                           3,
130                                           1)
131         self.vapi.qos_mark_enable_disable(self.pg4.sw_if_index,
132                                           self.QOS_SOURCE.QOS_API_SOURCE_IP,
133                                           4,
134                                           1)
135
136         #
137         # packets ingress on Pg0
138         #
139         p_v4 = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
140                 IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, tos=1) /
141                 UDP(sport=1234, dport=1234) /
142                 Raw(scapy.compat.chb(100) * NUM_PKTS))
143         p_v6 = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
144                 IPv6(src=self.pg0.remote_ip6, dst=self.pg1.remote_ip6,
145                      tc=1) /
146                 UDP(sport=1234, dport=1234) /
147                 Raw(scapy.compat.chb(100) * NUM_PKTS))
148
149         #
150         # Since we have not yet enabled the recording of the input QoS
151         # from the input iP header, the egress packet's ToS will be unchanged
152         #
153         rx = self.send_and_expect(self.pg0, p_v4 * NUM_PKTS, self.pg1)
154         for p in rx:
155             self.assertEqual(p[IP].tos, 1)
156         rx = self.send_and_expect(self.pg0, p_v6 * NUM_PKTS, self.pg1)
157         for p in rx:
158             self.assertEqual(p[IPv6].tc, 1)
159
160         #
161         # Enable QoS recording on IP input for pg0
162         #
163         self.vapi.qos_record_enable_disable(self.pg0.sw_if_index,
164                                             self.QOS_SOURCE.QOS_API_SOURCE_IP,
165                                             1)
166
167         #
168         # send the same packets, this time expect the input TOS of 1
169         # to be mapped to pg1's egress value of 254
170         #
171         rx = self.send_and_expect(self.pg0, p_v4 * NUM_PKTS, self.pg1)
172         for p in rx:
173             self.assertEqual(p[IP].tos, 254)
174         rx = self.send_and_expect(self.pg0, p_v6 * NUM_PKTS, self.pg1)
175         for p in rx:
176             self.assertEqual(p[IPv6].tc, 254)
177
178         #
179         # different input ToS to test the mapping
180         #
181         p_v4[IP].tos = 127
182         rx = self.send_and_expect(self.pg0, p_v4 * NUM_PKTS, self.pg1)
183         for p in rx:
184             self.assertEqual(p[IP].tos, 128)
185         p_v6[IPv6].tc = 127
186         rx = self.send_and_expect(self.pg0, p_v6 * NUM_PKTS, self.pg1)
187         for p in rx:
188             self.assertEqual(p[IPv6].tc, 128)
189
190         p_v4[IP].tos = 254
191         rx = self.send_and_expect(self.pg0, p_v4 * NUM_PKTS, self.pg1)
192         for p in rx:
193             self.assertEqual(p[IP].tos, 1)
194         p_v6[IPv6].tc = 254
195         rx = self.send_and_expect(self.pg0, p_v6 * NUM_PKTS, self.pg1)
196         for p in rx:
197             self.assertEqual(p[IPv6].tc, 1)
198
199         #
200         # send packets out the other interfaces to test the maps are
201         # correctly applied
202         #
203         p_v4[IP].dst = self.pg2.remote_ip4
204         rx = self.send_and_expect(self.pg0, p_v4 * NUM_PKTS, self.pg2)
205         for p in rx:
206             self.assertEqual(p[IP].tos, 2)
207
208         p_v4[IP].dst = self.pg3.remote_ip4
209         rx = self.send_and_expect(self.pg0, p_v4 * NUM_PKTS, self.pg3)
210         for p in rx:
211             self.assertEqual(p[IP].tos, 3)
212
213         p_v6[IPv6].dst = self.pg3.remote_ip6
214         rx = self.send_and_expect(self.pg0, p_v6 * NUM_PKTS, self.pg3)
215         for p in rx:
216             self.assertEqual(p[IPv6].tc, 3)
217
218         #
219         # remove the map on pg2 and pg3, now expect an unchanged IP tos
220         #
221         self.vapi.qos_mark_enable_disable(self.pg2.sw_if_index,
222                                           self.QOS_SOURCE.QOS_API_SOURCE_IP,
223                                           2,
224                                           0)
225         self.vapi.qos_mark_enable_disable(self.pg3.sw_if_index,
226                                           self.QOS_SOURCE.QOS_API_SOURCE_IP,
227                                           3,
228                                           0)
229         self.logger.info(self.vapi.cli("sh int feat pg2"))
230
231         p_v4[IP].dst = self.pg2.remote_ip4
232         rx = self.send_and_expect(self.pg0, p_v4 * NUM_PKTS, self.pg2)
233         for p in rx:
234             self.assertEqual(p[IP].tos, 254)
235
236         p_v4[IP].dst = self.pg3.remote_ip4
237         rx = self.send_and_expect(self.pg0, p_v4 * NUM_PKTS, self.pg3)
238         for p in rx:
239             self.assertEqual(p[IP].tos, 254)
240
241         #
242         # still mapping out of pg1
243         #
244         p_v4[IP].dst = self.pg1.remote_ip4
245         rx = self.send_and_expect(self.pg0, p_v4 * NUM_PKTS, self.pg1)
246         for p in rx:
247             self.assertEqual(p[IP].tos, 1)
248
249         #
250         # disable the input recording on pg0
251         #
252         self.vapi.qos_record_enable_disable(self.pg0.sw_if_index,
253                                             self.QOS_SOURCE.QOS_API_SOURCE_IP,
254                                             0)
255
256         #
257         # back to an unchanged TOS value
258         #
259         rx = self.send_and_expect(self.pg0, p_v4 * NUM_PKTS, self.pg1)
260         for p in rx:
261             self.assertEqual(p[IP].tos, 254)
262
263         #
264         # disable the egress map on pg1 and pg4
265         #
266         self.vapi.qos_mark_enable_disable(self.pg1.sw_if_index,
267                                           self.QOS_SOURCE.QOS_API_SOURCE_IP,
268                                           1,
269                                           0)
270         self.vapi.qos_mark_enable_disable(self.pg4.sw_if_index,
271                                           self.QOS_SOURCE.QOS_API_SOURCE_IP,
272                                           4,
273                                           0)
274
275         #
276         # unchanged Tos on pg1
277         #
278         rx = self.send_and_expect(self.pg0, p_v4 * NUM_PKTS, self.pg1)
279         for p in rx:
280             self.assertEqual(p[IP].tos, 254)
281
282         #
283         # clean-up the map
284         #
285         self.vapi.qos_egress_map_delete(1)
286         self.vapi.qos_egress_map_delete(4)
287         self.vapi.qos_egress_map_delete(2)
288         self.vapi.qos_egress_map_delete(3)
289         self.vapi.qos_egress_map_delete(5)
290         self.vapi.qos_egress_map_delete(6)
291         self.vapi.qos_egress_map_delete(7)
292
293     def test_qos_mpls(self):
294         """ QoS Mark/Record MPLS """
295
296         #
297         # 255 QoS for all input values
298         #
299         from_ext = 7
300         from_ip = 6
301         from_mpls = 5
302         from_vlan = 4
303         output = [scapy.compat.chb(from_ext)] * 256
304         os1 = b''.join(output)
305         output = [scapy.compat.chb(from_vlan)] * 256
306         os2 = b''.join(output)
307         output = [scapy.compat.chb(from_mpls)] * 256
308         os3 = b''.join(output)
309         output = [scapy.compat.chb(from_ip)] * 256
310         os4 = b''.join(output)
311         rows = [{'outputs': os1},
312                 {'outputs': os2},
313                 {'outputs': os3},
314                 {'outputs': os4}]
315
316         self.vapi.qos_egress_map_update(1, rows)
317
318         #
319         # a route with 1 MPLS label
320         #
321         route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
322                                     [VppRoutePath(self.pg1.remote_ip4,
323                                                   self.pg1.sw_if_index,
324                                                   labels=[32])])
325         route_10_0_0_1.add_vpp_config()
326
327         #
328         # a route with 3 MPLS labels
329         #
330         route_10_0_0_3 = VppIpRoute(self, "10.0.0.3", 32,
331                                     [VppRoutePath(self.pg1.remote_ip4,
332                                                   self.pg1.sw_if_index,
333                                                   labels=[63, 33, 34])])
334         route_10_0_0_3.add_vpp_config()
335
336         #
337         # enable IP QoS recording on the input Pg0 and MPLS egress marking
338         # on Pg1
339         #
340         self.vapi.qos_record_enable_disable(self.pg0.sw_if_index,
341                                             self.QOS_SOURCE.QOS_API_SOURCE_IP,
342                                             1)
343         self.vapi.qos_mark_enable_disable(self.pg1.sw_if_index,
344                                           self.QOS_SOURCE.QOS_API_SOURCE_MPLS,
345                                           1,
346                                           1)
347
348         #
349         # packet that will get one label added and 3 labels added resp.
350         #
351         p_1 = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
352                IP(src=self.pg0.remote_ip4, dst="10.0.0.1", tos=1) /
353                UDP(sport=1234, dport=1234) /
354                Raw(scapy.compat.chb(100) * NUM_PKTS))
355         p_3 = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
356                IP(src=self.pg0.remote_ip4, dst="10.0.0.3", tos=1) /
357                UDP(sport=1234, dport=1234) /
358                Raw(scapy.compat.chb(100) * NUM_PKTS))
359
360         rx = self.send_and_expect(self.pg0, p_1 * NUM_PKTS, self.pg1)
361
362         #
363         # only 3 bits of ToS value in MPLS make sure tos is correct
364         # and the label and EOS bit have not been corrupted
365         #
366         for p in rx:
367             self.assertEqual(p[MPLS].cos, from_ip)
368             self.assertEqual(p[MPLS].label, 32)
369             self.assertEqual(p[MPLS].s, 1)
370         rx = self.send_and_expect(self.pg0, p_3 * NUM_PKTS, self.pg1)
371         for p in rx:
372             self.assertEqual(p[MPLS].cos, from_ip)
373             self.assertEqual(p[MPLS].label, 63)
374             self.assertEqual(p[MPLS].s, 0)
375             h = p[MPLS].payload
376             self.assertEqual(h[MPLS].cos, from_ip)
377             self.assertEqual(h[MPLS].label, 33)
378             self.assertEqual(h[MPLS].s, 0)
379             h = h[MPLS].payload
380             self.assertEqual(h[MPLS].cos, from_ip)
381             self.assertEqual(h[MPLS].label, 34)
382             self.assertEqual(h[MPLS].s, 1)
383
384         #
385         # enable MPLS QoS recording on the input Pg0 and IP egress marking
386         # on Pg1
387         #
388         self.vapi.qos_record_enable_disable(
389             self.pg0.sw_if_index,
390             self.QOS_SOURCE.QOS_API_SOURCE_MPLS,
391             1)
392         self.vapi.qos_mark_enable_disable(self.pg1.sw_if_index,
393                                           self.QOS_SOURCE.QOS_API_SOURCE_IP,
394                                           1,
395                                           1)
396
397         #
398         # MPLS x-connect - COS according to pg1 map
399         #
400         route_32_eos = VppMplsRoute(self, 32, 1,
401                                     [VppRoutePath(self.pg1.remote_ip4,
402                                                   self.pg1.sw_if_index,
403                                                   labels=[VppMplsLabel(33)])])
404         route_32_eos.add_vpp_config()
405
406         p_m1 = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
407                 MPLS(label=32, cos=3, ttl=2) /
408                 IP(src=self.pg0.remote_ip4, dst="10.0.0.1", tos=1) /
409                 UDP(sport=1234, dport=1234) /
410                 Raw(scapy.compat.chb(100) * NUM_PKTS))
411
412         rx = self.send_and_expect(self.pg0, p_m1 * NUM_PKTS, self.pg1)
413         for p in rx:
414             self.assertEqual(p[MPLS].cos, from_mpls)
415             self.assertEqual(p[MPLS].label, 33)
416             self.assertEqual(p[MPLS].s, 1)
417
418         #
419         # MPLS deag - COS is copied from MPLS to IP
420         #
421         route_33_eos = VppMplsRoute(self, 33, 1,
422                                     [VppRoutePath("0.0.0.0",
423                                                   0xffffffff,
424                                                   nh_table_id=0)])
425         route_33_eos.add_vpp_config()
426
427         route_10_0_0_4 = VppIpRoute(self, "10.0.0.4", 32,
428                                     [VppRoutePath(self.pg1.remote_ip4,
429                                                   self.pg1.sw_if_index)])
430         route_10_0_0_4.add_vpp_config()
431
432         p_m2 = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
433                 MPLS(label=33, ttl=2, cos=3) /
434                 IP(src=self.pg0.remote_ip4, dst="10.0.0.4", tos=1) /
435                 UDP(sport=1234, dport=1234) /
436                 Raw(scapy.compat.chb(100) * NUM_PKTS))
437
438         rx = self.send_and_expect(self.pg0, p_m2 * NUM_PKTS, self.pg1)
439
440         for p in rx:
441             self.assertEqual(p[IP].tos, from_mpls)
442
443         #
444         # cleanup
445         #
446         self.vapi.qos_record_enable_disable(self.pg0.sw_if_index,
447                                             self.QOS_SOURCE.QOS_API_SOURCE_IP,
448                                             0)
449         self.vapi.qos_mark_enable_disable(self.pg1.sw_if_index,
450                                           self.QOS_SOURCE.QOS_API_SOURCE_MPLS,
451                                           1,
452                                           0)
453         self.vapi.qos_record_enable_disable(
454             self.pg0.sw_if_index,
455             self.QOS_SOURCE.QOS_API_SOURCE_MPLS,
456             0)
457         self.vapi.qos_mark_enable_disable(self.pg1.sw_if_index,
458                                           self.QOS_SOURCE.QOS_API_SOURCE_IP,
459                                           1,
460                                           0)
461         self.vapi.qos_egress_map_delete(1)
462
463     def test_qos_vlan(self):
464         """QoS mark/record VLAN """
465
466         #
467         # QoS for all input values
468         #
469         output = [scapy.compat.chb(0)] * 256
470         for i in range(0, 255):
471             output[i] = scapy.compat.chb(255 - i)
472         os = b''.join(output)
473         rows = [{'outputs': os},
474                 {'outputs': os},
475                 {'outputs': os},
476                 {'outputs': os}]
477
478         self.vapi.qos_egress_map_update(1, rows)
479
480         sub_if = VppDot1QSubint(self, self.pg0, 11)
481
482         sub_if.admin_up()
483         sub_if.config_ip4()
484         sub_if.resolve_arp()
485         sub_if.config_ip6()
486         sub_if.resolve_ndp()
487
488         #
489         # enable VLAN QoS recording/marking on the input Pg0 subinterface and
490         #
491         self.vapi.qos_record_enable_disable(
492             sub_if.sw_if_index,
493             self.QOS_SOURCE.QOS_API_SOURCE_VLAN,
494             1)
495         self.vapi.qos_mark_enable_disable(sub_if.sw_if_index,
496                                           self.QOS_SOURCE.QOS_API_SOURCE_VLAN,
497                                           1,
498                                           1)
499
500         #
501         # IP marking/recording on pg1
502         #
503         self.vapi.qos_record_enable_disable(self.pg1.sw_if_index,
504                                             self.QOS_SOURCE.QOS_API_SOURCE_IP,
505                                             1)
506         self.vapi.qos_mark_enable_disable(self.pg1.sw_if_index,
507                                           self.QOS_SOURCE.QOS_API_SOURCE_IP,
508                                           1,
509                                           1)
510
511         #
512         # a routes to/from sub-interface
513         #
514         route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32,
515                                     [VppRoutePath(sub_if.remote_ip4,
516                                                   sub_if.sw_if_index)])
517         route_10_0_0_1.add_vpp_config()
518         route_10_0_0_2 = VppIpRoute(self, "10.0.0.2", 32,
519                                     [VppRoutePath(self.pg1.remote_ip4,
520                                                   self.pg1.sw_if_index)])
521         route_10_0_0_2.add_vpp_config()
522         route_2001_1 = VppIpRoute(self, "2001::1", 128,
523                                   [VppRoutePath(sub_if.remote_ip6,
524                                                 sub_if.sw_if_index)])
525         route_2001_1.add_vpp_config()
526         route_2001_2 = VppIpRoute(self, "2001::2", 128,
527                                   [VppRoutePath(self.pg1.remote_ip6,
528                                                 self.pg1.sw_if_index)])
529         route_2001_2.add_vpp_config()
530
531         p_v1 = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
532                 Dot1Q(vlan=11, prio=1) /
533                 IP(src="1.1.1.1", dst="10.0.0.2", tos=1) /
534                 UDP(sport=1234, dport=1234) /
535                 Raw(scapy.compat.chb(100) * NUM_PKTS))
536
537         p_v2 = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
538                 IP(src="1.1.1.1", dst="10.0.0.1", tos=1) /
539                 UDP(sport=1234, dport=1234) /
540                 Raw(scapy.compat.chb(100) * NUM_PKTS))
541
542         rx = self.send_and_expect(self.pg1, p_v2 * NUM_PKTS, self.pg0)
543
544         for p in rx:
545             self.assertEqual(p[Dot1Q].prio, 6)
546
547         rx = self.send_and_expect(self.pg0, p_v1 * NUM_PKTS, self.pg1)
548
549         for p in rx:
550             self.assertEqual(p[IP].tos, 254)
551
552         p_v1 = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
553                 Dot1Q(vlan=11, prio=2) /
554                 IPv6(src="2001::1", dst="2001::2", tc=1) /
555                 UDP(sport=1234, dport=1234) /
556                 Raw(scapy.compat.chb(100) * NUM_PKTS))
557
558         p_v2 = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) /
559                 IPv6(src="3001::1", dst="2001::1", tc=1) /
560                 UDP(sport=1234, dport=1234) /
561                 Raw(scapy.compat.chb(100) * NUM_PKTS))
562
563         rx = self.send_and_expect(self.pg1, p_v2 * NUM_PKTS, self.pg0)
564
565         for p in rx:
566             self.assertEqual(p[Dot1Q].prio, 6)
567
568         rx = self.send_and_expect(self.pg0, p_v1 * NUM_PKTS, self.pg1)
569
570         for p in rx:
571             self.assertEqual(p[IPv6].tc, 253)
572
573         #
574         # cleanup
575         #
576         sub_if.unconfig_ip4()
577         sub_if.unconfig_ip6()
578
579         self.vapi.qos_record_enable_disable(
580             sub_if.sw_if_index,
581             self.QOS_SOURCE.QOS_API_SOURCE_VLAN,
582             0)
583         self.vapi.qos_mark_enable_disable(sub_if.sw_if_index,
584                                           self.QOS_SOURCE.QOS_API_SOURCE_VLAN,
585                                           1,
586                                           0)
587         self.vapi.qos_record_enable_disable(self.pg1.sw_if_index,
588                                             self.QOS_SOURCE.QOS_API_SOURCE_IP,
589                                             0)
590         self.vapi.qos_mark_enable_disable(self.pg1.sw_if_index,
591                                           self.QOS_SOURCE.QOS_API_SOURCE_IP,
592                                           1,
593                                           0)
594
595
596 if __name__ == '__main__':
597     unittest.main(testRunner=VppTestRunner)