diff options
author | Neale Ranns <nranns@cisco.com> | 2018-02-27 03:45:38 -0800 |
---|---|---|
committer | Damjan Marion <dmarion.lists@gmail.com> | 2018-03-16 20:28:50 +0000 |
commit | 039cbfe254be998f7311bc4638bb262b44efac19 (patch) | |
tree | cc75e6130f3fdbdd1dbfe48669e6c17534240e11 /test | |
parent | 0d65d11053395bde7bd8c36439e9025e50ea8028 (diff) |
QoS recording and marking
Change-Id: Ie5a50def4ec1e4a3b3404a8b6ab9ec248bc16744
Signed-off-by: Neale Ranns <nranns@cisco.com>
Diffstat (limited to 'test')
-rw-r--r-- | test/test_qos.py | 366 | ||||
-rw-r--r-- | test/vpp_papi_provider.py | 36 |
2 files changed, 402 insertions, 0 deletions
diff --git a/test/test_qos.py b/test/test_qos.py new file mode 100644 index 00000000000..a940bd3a64c --- /dev/null +++ b/test/test_qos.py @@ -0,0 +1,366 @@ +#!/usr/bin/env python + +import unittest +import socket +import struct + +from framework import VppTestCase, VppTestRunner +from vpp_object import VppObject +from vpp_papi_provider import QOS_SOURCE +from vpp_ip_route import VppIpRoute, VppRoutePath, VppMplsRoute +from vpp_sub_interface import VppSubInterface, VppDot1QSubint + +from scapy.packet import Raw +from scapy.layers.l2 import Ether, Dot1Q +from scapy.layers.inet import IP, UDP +from scapy.layers.inet6 import IPv6 +from scapy.contrib.mpls import MPLS + + +class TestQOS(VppTestCase): + """ QOS Test Case """ + + def setUp(self): + super(TestQOS, self).setUp() + + self.create_pg_interfaces(range(5)) + + for i in self.pg_interfaces: + i.admin_up() + i.config_ip4() + i.resolve_arp() + i.config_ip6() + i.resolve_ndp() + + def tearDown(self): + for i in self.pg_interfaces: + i.unconfig_ip4() + i.unconfig_ip6() + + super(TestQOS, self).tearDown() + + def test_qos_ip(self): + """ QoS Mark IP """ + + # + # for table 1 map the n=0xff possible values of input QoS mark, + # n to 1-n + # + output = [chr(0)] * 256 + for i in range(0, 255): + output[i] = chr(255 - i) + os = ''.join(output) + rows = [{'outputs': os}, + {'outputs': os}, + {'outputs': os}, + {'outputs': os}] + + self.vapi.qos_egress_map_update(1, rows) + + # + # For table 2 (and up) use the value n for everything + # + output = [chr(2)] * 256 + os = ''.join(output) + rows = [{'outputs': os}, + {'outputs': os}, + {'outputs': os}, + {'outputs': os}] + + self.vapi.qos_egress_map_update(2, rows) + + output = [chr(3)] * 256 + os = ''.join(output) + rows = [{'outputs': os}, + {'outputs': os}, + {'outputs': os}, + {'outputs': os}] + + self.vapi.qos_egress_map_update(3, rows) + + output = [chr(4)] * 256 + os = ''.join(output) + rows = [{'outputs': os}, + {'outputs': os}, + {'outputs': os}, + {'outputs': os}] + self.vapi.qos_egress_map_update(4, rows) + self.vapi.qos_egress_map_update(5, rows) + self.vapi.qos_egress_map_update(6, rows) + self.vapi.qos_egress_map_update(7, rows) + + self.logger.info(self.vapi.cli("sh qos eg map")) + + # + # Bind interface pgN to table n + # + self.vapi.qos_mark_enable_disable(self.pg1.sw_if_index, + QOS_SOURCE.IP, + 1, + 1) + self.vapi.qos_mark_enable_disable(self.pg2.sw_if_index, + QOS_SOURCE.IP, + 2, + 1) + self.vapi.qos_mark_enable_disable(self.pg3.sw_if_index, + QOS_SOURCE.IP, + 3, + 1) + self.vapi.qos_mark_enable_disable(self.pg4.sw_if_index, + QOS_SOURCE.IP, + 4, + 1) + + # + # packets ingress on Pg0 + # + p_v4 = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, tos=1) / + UDP(sport=1234, dport=1234) / + Raw(chr(100) * 65)) + p_v6 = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IPv6(src=self.pg0.remote_ip6, dst=self.pg1.remote_ip6, + tc=1) / + UDP(sport=1234, dport=1234) / + Raw(chr(100) * 65)) + + # + # Since we have not yet enabled the recording of the input QoS + # from the input iP header, the egress packet's ToS will be unchanged + # + rx = self.send_and_expect(self.pg0, p_v4 * 65, self.pg1) + for p in rx: + self.assertEqual(p[IP].tos, 1) + rx = self.send_and_expect(self.pg0, p_v6 * 65, self.pg1) + for p in rx: + self.assertEqual(p[IPv6].tc, 1) + + # + # Enable QoS recrding on IP input for pg0 + # + self.vapi.qos_record_enable_disable(self.pg0.sw_if_index, + QOS_SOURCE.IP, + 1) + + # + # send the same packets, this time expect the input TOS of 1 + # to be mapped to pg1's egress value of 254 + # + rx = self.send_and_expect(self.pg0, p_v4 * 65, self.pg1) + for p in rx: + self.assertEqual(p[IP].tos, 254) + rx = self.send_and_expect(self.pg0, p_v6 * 65, self.pg1) + for p in rx: + self.assertEqual(p[IPv6].tc, 254) + + # + # different input ToS to test the mapping + # + p_v4[IP].tos = 127 + rx = self.send_and_expect(self.pg0, p_v4 * 65, self.pg1) + for p in rx: + self.assertEqual(p[IP].tos, 128) + p_v6[IPv6].tc = 127 + rx = self.send_and_expect(self.pg0, p_v6 * 65, self.pg1) + for p in rx: + self.assertEqual(p[IPv6].tc, 128) + + p_v4[IP].tos = 254 + rx = self.send_and_expect(self.pg0, p_v4 * 65, self.pg1) + for p in rx: + self.assertEqual(p[IP].tos, 1) + p_v6[IPv6].tc = 254 + rx = self.send_and_expect(self.pg0, p_v6 * 65, self.pg1) + for p in rx: + self.assertEqual(p[IPv6].tc, 1) + + # + # send packets out the other interfaces to test the maps are + # correctly applied + # + p_v4[IP].dst = self.pg2.remote_ip4 + rx = self.send_and_expect(self.pg0, p_v4 * 65, self.pg2) + for p in rx: + self.assertEqual(p[IP].tos, 2) + + p_v4[IP].dst = self.pg3.remote_ip4 + rx = self.send_and_expect(self.pg0, p_v4 * 65, self.pg3) + for p in rx: + self.assertEqual(p[IP].tos, 3) + + p_v6[IPv6].dst = self.pg3.remote_ip6 + rx = self.send_and_expect(self.pg0, p_v6 * 65, self.pg3) + for p in rx: + self.assertEqual(p[IPv6].tc, 3) + + # + # remove the map on pg2 and pg3, now expect an unchanged IP tos + # + self.vapi.qos_mark_enable_disable(self.pg2.sw_if_index, + QOS_SOURCE.IP, + 2, + 0) + self.vapi.qos_mark_enable_disable(self.pg3.sw_if_index, + QOS_SOURCE.IP, + 3, + 0) + self.logger.info(self.vapi.cli("sh int feat pg2")) + + p_v4[IP].dst = self.pg2.remote_ip4 + rx = self.send_and_expect(self.pg0, p_v4 * 65, self.pg2) + for p in rx: + self.assertEqual(p[IP].tos, 254) + + p_v4[IP].dst = self.pg3.remote_ip4 + rx = self.send_and_expect(self.pg0, p_v4 * 65, self.pg3) + for p in rx: + self.assertEqual(p[IP].tos, 254) + + # + # still mapping out of pg1 + # + p_v4[IP].dst = self.pg1.remote_ip4 + rx = self.send_and_expect(self.pg0, p_v4 * 65, self.pg1) + for p in rx: + self.assertEqual(p[IP].tos, 1) + + # + # disable the input recording on pg0 + # + self.vapi.qos_record_enable_disable(self.pg0.sw_if_index, + QOS_SOURCE.IP, + 0) + + # + # back to an unchanged TOS value + # + rx = self.send_and_expect(self.pg0, p_v4 * 65, self.pg1) + for p in rx: + self.assertEqual(p[IP].tos, 254) + + # + # disable the egress map on pg1 and pg4 + # + self.vapi.qos_mark_enable_disable(self.pg1.sw_if_index, + QOS_SOURCE.IP, + 1, + 0) + self.vapi.qos_mark_enable_disable(self.pg4.sw_if_index, + QOS_SOURCE.IP, + 4, + 0) + + # + # unchanged Tos on pg1 + # + rx = self.send_and_expect(self.pg0, p_v4 * 65, self.pg1) + for p in rx: + self.assertEqual(p[IP].tos, 254) + + # + # clean-up the masp + # + self.vapi.qos_egress_map_delete(1) + self.vapi.qos_egress_map_delete(4) + self.vapi.qos_egress_map_delete(2) + self.vapi.qos_egress_map_delete(3) + self.vapi.qos_egress_map_delete(5) + self.vapi.qos_egress_map_delete(6) + self.vapi.qos_egress_map_delete(7) + + def test_qos_mpls(self): + """ QoS Mark MPLS """ + + # + # 255 QoS for all input values + # + output = [chr(255)] * 256 + os = ''.join(output) + rows = [{'outputs': os}, + {'outputs': os}, + {'outputs': os}, + {'outputs': os}] + + self.vapi.qos_egress_map_update(1, rows) + + # + # a route with 1 MPLS label + # + route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32, + [VppRoutePath(self.pg1.remote_ip4, + self.pg1.sw_if_index, + labels=[32])]) + route_10_0_0_1.add_vpp_config() + + # + # a route with 3 MPLS labels + # + route_10_0_0_3 = VppIpRoute(self, "10.0.0.3", 32, + [VppRoutePath(self.pg1.remote_ip4, + self.pg1.sw_if_index, + labels=[63, 33, 34])]) + route_10_0_0_3.add_vpp_config() + + # + # enable IP QoS recording on the input Pg0 and MPLS egress marking + # on Pg1 + # + self.vapi.qos_record_enable_disable(self.pg0.sw_if_index, + QOS_SOURCE.IP, + 1) + self.vapi.qos_mark_enable_disable(self.pg1.sw_if_index, + QOS_SOURCE.MPLS, + 1, + 1) + + # + # packet that will get one label added and 3 labels added resp. + # + p_1 = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst="10.0.0.1", tos=1) / + UDP(sport=1234, dport=1234) / + Raw(chr(100) * 65)) + p_3 = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst="10.0.0.3", tos=1) / + UDP(sport=1234, dport=1234) / + Raw(chr(100) * 65)) + + rx = self.send_and_expect(self.pg0, p_1 * 65, self.pg1) + + # + # only 3 bits of ToS value in MPLS make sure tos is correct + # and the label and EOS bit have not been corrupted + # + for p in rx: + self.assertEqual(p[MPLS].cos, 7) + self.assertEqual(p[MPLS].label, 32) + self.assertEqual(p[MPLS].s, 1) + rx = self.send_and_expect(self.pg0, p_3 * 65, self.pg1) + for p in rx: + self.assertEqual(p[MPLS].cos, 7) + self.assertEqual(p[MPLS].label, 63) + self.assertEqual(p[MPLS].s, 0) + h = p[MPLS].payload + self.assertEqual(h[MPLS].cos, 7) + self.assertEqual(h[MPLS].label, 33) + self.assertEqual(h[MPLS].s, 0) + h = h[MPLS].payload + self.assertEqual(h[MPLS].cos, 7) + self.assertEqual(h[MPLS].label, 34) + self.assertEqual(h[MPLS].s, 1) + + # + # cleanup + # + self.vapi.qos_record_enable_disable(self.pg0.sw_if_index, + QOS_SOURCE.IP, + 0) + self.vapi.qos_mark_enable_disable(self.pg1.sw_if_index, + QOS_SOURCE.MPLS, + 1, + 0) + self.vapi.qos_egress_map_delete(1) + + +if __name__ == '__main__': + unittest.main(testRunner=VppTestRunner) diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index bd770efdd36..da59bc86ca0 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -36,6 +36,13 @@ class L2_VTR_OP: L2_TRANSLATE_2_2 = 8 +class QOS_SOURCE: + EXT = 0 + VLAN = 1 + MPLS = 2 + IP = 3 + + class UnexpectedApiReturnValueError(Exception): """ exception raised when the API return value is unexpected """ pass @@ -3255,3 +3262,32 @@ class VppPapiProvider(object): """ IPIP tunnel Delete """ return self.api(self.papi.ipip_del_tunnel, {'sw_if_index': sw_if_index}) + + def qos_egress_map_update(self, id, outputs): + """ QOS egress map update """ + return self.api(self.papi.qos_egress_map_update, + {'map_id': id, + 'rows': outputs}) + + def qos_egress_map_delete(self, id): + """ QOS egress map delete """ + return self.api(self.papi.qos_egress_map_delete, + {'map_id': id}) + + def qos_mark_enable_disable(self, sw_if_index, + output_source, + map_id, + enable): + """ QOS Mark Enable/Disable """ + return self.api(self.papi.qos_mark_enable_disable, + {'map_id': map_id, + 'sw_if_index': sw_if_index, + 'output_source': output_source, + 'enable': enable}) + + def qos_record_enable_disable(self, sw_if_index, input_source, enable): + """ IP QoS recording Enble/Disable """ + return self.api(self.papi.qos_record_enable_disable, + {'sw_if_index': sw_if_index, + 'input_source': input_source, + 'enable': enable}) |