aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeale Ranns <nranns@cisco.com>2018-02-27 03:45:38 -0800
committerDamjan Marion <dmarion.lists@gmail.com>2018-03-16 20:28:50 +0000
commit039cbfe254be998f7311bc4638bb262b44efac19 (patch)
treecc75e6130f3fdbdd1dbfe48669e6c17534240e11
parent0d65d11053395bde7bd8c36439e9025e50ea8028 (diff)
QoS recording and marking
Change-Id: Ie5a50def4ec1e4a3b3404a8b6ab9ec248bc16744 Signed-off-by: Neale Ranns <nranns@cisco.com>
-rw-r--r--src/plugins/nat/nat64.h3
-rw-r--r--src/plugins/nat/nat66.h3
-rw-r--r--src/vnet.am15
-rw-r--r--src/vnet/buffer.c4
-rw-r--r--src/vnet/buffer.h26
-rw-r--r--src/vnet/ethernet/packet.h9
-rw-r--r--src/vnet/ip/ip.h3
-rw-r--r--src/vnet/ip/ip6_packet.h1
-rw-r--r--src/vnet/ip/lookup.c2
-rw-r--r--src/vnet/map/map.h4
-rw-r--r--src/vnet/mpls/mpls_output.c5
-rw-r--r--src/vnet/qos/qos.api100
-rw-r--r--src/vnet/qos/qos_api.c161
-rw-r--r--src/vnet/qos/qos_egress_map.c262
-rw-r--r--src/vnet/qos/qos_egress_map.h82
-rw-r--r--src/vnet/qos/qos_mark.c447
-rw-r--r--src/vnet/qos/qos_mark.h39
-rw-r--r--src/vnet/qos/qos_record.c326
-rw-r--r--src/vnet/qos/qos_record.h32
-rw-r--r--src/vnet/qos/qos_types.c59
-rw-r--r--src/vnet/qos/qos_types.h78
-rw-r--r--src/vnet/vnet_all_api_h.h1
-rw-r--r--test/test_qos.py366
-rw-r--r--test/vpp_papi_provider.py36
24 files changed, 2046 insertions, 18 deletions
diff --git a/src/plugins/nat/nat64.h b/src/plugins/nat/nat64.h
index e2334a5ac6f..addba7a3704 100644
--- a/src/plugins/nat/nat64.h
+++ b/src/plugins/nat/nat64.h
@@ -377,9 +377,6 @@ u32 nat64_get_worker_in2out (ip6_address_t * addr);
*/
u32 nat64_get_worker_out2in (ip4_header_t * ip);
-#define u8_ptr_add(ptr, index) (((u8 *)ptr) + index)
-#define u16_net_add(u, val) clib_host_to_net_u16(clib_net_to_host_u16(u) + (val))
-
#endif /* __included_nat64_h__ */
/*
diff --git a/src/plugins/nat/nat66.h b/src/plugins/nat/nat66.h
index d2ddd0c70e1..ac5557fc491 100644
--- a/src/plugins/nat/nat66.h
+++ b/src/plugins/nat/nat66.h
@@ -75,9 +75,6 @@ int nat66_static_mapping_add_del (ip6_address_t * l_addr,
ip6_address_t * e_addr, u32 vrf_id,
u8 is_add);
-#define u8_ptr_add(ptr, index) (((u8 *)ptr) + index)
-#define u16_net_add(u, val) clib_host_to_net_u16(clib_net_to_host_u16(u) + (val))
-
#endif /* __included_nat66_h__ */
/*
diff --git a/src/vnet.am b/src/vnet.am
index 4f968474131..567262fc150 100644
--- a/src/vnet.am
+++ b/src/vnet.am
@@ -348,7 +348,7 @@ libvnet_la_SOURCES += \
vnet/ip/ip_frag.c \
vnet/ip/ip.c \
vnet/ip/ip_init.c \
- vnet/ip/ip_in_out_acl.c \
+ vnet/ip/ip_in_out_acl.c \
vnet/ip/lookup.c \
vnet/ip/ping.c \
vnet/ip/punt_api.c \
@@ -1198,6 +1198,19 @@ libvnet_la_SOURCES += \
vnet/util/trajectory.c
########################################
+# QoS
+########################################
+
+libvnet_la_SOURCES += \
+ vnet/qos/qos_types.c \
+ vnet/qos/qos_api.c \
+ vnet/qos/qos_egress_map.c \
+ vnet/qos/qos_record.c \
+ vnet/qos/qos_mark.c
+
+API_FILES += vnet/qos/qos.api
+
+########################################
# BIER
########################################
diff --git a/src/vnet/buffer.c b/src/vnet/buffer.c
index f12adf7eb64..f793fb4fc03 100644
--- a/src/vnet/buffer.c
+++ b/src/vnet/buffer.c
@@ -38,6 +38,10 @@ format_vnet_buffer (u8 * s, va_list * args)
if (b->flags & VNET_BUFFER_F_L4_HDR_OFFSET_VALID)
a = format (a, "l4-hdr-offset %d ", vnet_buffer (b)->l4_hdr_offset);
+ if (b->flags & VNET_BUFFER_F_QOS_DATA_VALID)
+ a = format (a, "qos %d.%d ",
+ vnet_buffer2 (b)->qos.bits, vnet_buffer2 (b)->qos.source);
+
s = format (s, "%U", format_vlib_buffer, b);
if (a)
s = format (s, "\n%U%v", format_white_space, indent, a);
diff --git a/src/vnet/buffer.h b/src/vnet/buffer.h
index 7a4bc245a30..e785145c0b5 100644
--- a/src/vnet/buffer.h
+++ b/src/vnet/buffer.h
@@ -63,7 +63,8 @@
_(15, L3_HDR_OFFSET_VALID, 0) \
_(16, L4_HDR_OFFSET_VALID, 0) \
_(17, FLOW_REPORT, "flow-report") \
- _(18, IS_DVR, "dvr")
+ _(18, IS_DVR, "dvr") \
+ _(19, QOS_DATA_VALID, 0)
#define VNET_BUFFER_FLAGS_VLAN_BITS \
(VNET_BUFFER_F_VLAN_1_DEEP | VNET_BUFFER_F_VLAN_2_DEEP)
@@ -186,6 +187,8 @@ typedef struct
u8 ttl;
u8 exp;
u8 first;
+ /* Rewrite length */
+ u32 save_rewrite_length;
/*
* BIER - the nubmer of bytes in the header.
* the len field inthe header is not authoritative. It's the
@@ -362,16 +365,29 @@ STATIC_ASSERT (sizeof (vnet_buffer_opaque_t) <=
/* Full cache line (64 bytes) of additional space */
typedef struct
{
+ /**
+ * QoS marking data that needs to persist from the recording nodes
+ * (nominally in the ingress path) to the marking node (in the
+ * egress path)
+ */
+ struct
+ {
+ u8 bits;
+ u8 source;
+ } qos;
+
+ u8 __unused[2];
+
union
{
-#if VLIB_BUFFER_TRACE_TRAJECTORY > 0
- /* buffer trajectory tracing */
struct
{
+#if VLIB_BUFFER_TRACE_TRAJECTORY > 0
+ /* buffer trajectory tracing */
u16 *trajectory_trace;
- };
#endif
- u32 unused[12];
+ };
+ u32 unused[11];
};
} vnet_buffer_opaque2_t;
diff --git a/src/vnet/ethernet/packet.h b/src/vnet/ethernet/packet.h
index 964cf638101..09f5e7f3d0b 100644
--- a/src/vnet/ethernet/packet.h
+++ b/src/vnet/ethernet/packet.h
@@ -91,6 +91,15 @@ typedef struct
u16 type;
} ethernet_vlan_header_t;
+always_inline void
+ethernet_vlan_header_set_priority_net_order (ethernet_vlan_header_t * h,
+ u8 prio)
+{
+ u8 *bytes = (u8 *) (&h->priority_cfi_and_id);
+
+ bytes[0] &= 0x1f;
+ bytes[0] |= (prio & 0x7) << 5;
+}
/* VLAN with ethertype first and vlan id second */
typedef struct
diff --git a/src/vnet/ip/ip.h b/src/vnet/ip/ip.h
index 747b4385816..50965d411e6 100644
--- a/src/vnet/ip/ip.h
+++ b/src/vnet/ip/ip.h
@@ -65,6 +65,9 @@
#include <vnet/ip/icmp6.h>
#include <vnet/classify/vnet_classify.h>
+#define u8_ptr_add(ptr, index) (((u8 *)ptr) + index)
+#define u16_net_add(u, val) clib_host_to_net_u16(clib_net_to_host_u16(u) + (val))
+
/* Per protocol info. */
typedef struct
{
diff --git a/src/vnet/ip/ip6_packet.h b/src/vnet/ip/ip6_packet.h
index 9916f3000cb..70c3dab473d 100644
--- a/src/vnet/ip/ip6_packet.h
+++ b/src/vnet/ip/ip6_packet.h
@@ -360,6 +360,7 @@ ip6_set_traffic_class_network_order (ip6_header_t * ip6, u8 dscp)
{
u32 tmp =
clib_net_to_host_u32 (ip6->ip_version_traffic_class_and_flow_label);
+ tmp &= 0xf00fffff;
tmp |= (dscp << 20);
ip6->ip_version_traffic_class_and_flow_label = clib_host_to_net_u32 (tmp);
}
diff --git a/src/vnet/ip/lookup.c b/src/vnet/ip/lookup.c
index 256000a2242..3bbf2abd8dc 100644
--- a/src/vnet/ip/lookup.c
+++ b/src/vnet/ip/lookup.c
@@ -322,7 +322,7 @@ const ip46_address_t zero_addr = {
0, 0},
};
-clib_error_t *
+static clib_error_t *
vnet_ip_route_cmd (vlib_main_t * vm,
unformat_input_t * main_input, vlib_cli_command_t * cmd)
{
diff --git a/src/vnet/map/map.h b/src/vnet/map/map.h
index 7243e814f2e..5700cc1c191 100644
--- a/src/vnet/map/map.h
+++ b/src/vnet/map/map.h
@@ -523,10 +523,6 @@ int map_ip6_reass_conf_lifetime(u16 lifetime_ms);
int map_ip6_reass_conf_buffers(u32 buffers);
#define MAP_IP6_REASS_CONF_BUFFERS_MAX (0xffffffff)
-
-#define u8_ptr_add(ptr, index) (((u8 *)ptr) + index)
-#define u16_net_add(u, val) clib_host_to_net_u16(clib_net_to_host_u16(u) + (val))
-
static_always_inline void
ip4_map_t_embedded_address (map_domain_t *d,
ip6_address_t *ip6, const ip4_address_t *ip4)
diff --git a/src/vnet/mpls/mpls_output.c b/src/vnet/mpls/mpls_output.c
index 22e6c1062c3..b7bb7694c71 100644
--- a/src/vnet/mpls/mpls_output.c
+++ b/src/vnet/mpls/mpls_output.c
@@ -125,6 +125,8 @@ mpls_output_inline (vlib_main_t * vm,
/* Update packet buffer attributes/set output interface. */
rw_len0 = adj0[0].rewrite_header.data_bytes;
rw_len1 = adj1[0].rewrite_header.data_bytes;
+ vnet_buffer (p0)->mpls.save_rewrite_length = rw_len0;
+ vnet_buffer (p1)->mpls.save_rewrite_length = rw_len1;
/* Bump the adj counters for packet and bytes */
vlib_increment_combined_counter
@@ -238,7 +240,8 @@ mpls_output_inline (vlib_main_t * vm,
/* Update packet buffer attributes/set output interface. */
rw_len0 = adj0[0].rewrite_header.data_bytes;
-
+ vnet_buffer (p0)->mpls.save_rewrite_length = rw_len0;
+
vlib_increment_combined_counter
(&adjacency_counters,
thread_index,
diff --git a/src/vnet/qos/qos.api b/src/vnet/qos/qos.api
new file mode 100644
index 00000000000..a7bd19acc9e
--- /dev/null
+++ b/src/vnet/qos/qos.api
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** \file
+
+ This file defines QoS record and mark API messages which are generally
+ called through a shared memory interface.
+*/
+
+option version = "1.0.0";
+
+/** \brief Enable/Disable QoS recording
+ The QoS bits from the packet at the specified input layer are copied
+ into the packet. Recording should be used in conjunction with marking
+ @param sw_if_index - The interface on which recording is enabled.
+ @param enable - enable=1 or disable the feautre
+ @param input_source - The input source/layer at which the QoS bits
+ are copied from the packet. See qos_source_t.
+*/
+autoreply define qos_record_enable_disable
+{
+ u32 client_index;
+ u32 context;
+ u32 sw_if_index;
+ u8 input_source;
+ u8 enable;
+};
+
+/** \brief A row within a QoS map
+ Each value translates from an input value to an output.
+*/
+typeonly define qos_egress_map_row
+{
+ u8 outputs[256];
+};
+
+/** \brief Update a QoS Map
+ A QoS map, translates from the QoS value in the packet set by the 'record'
+ feature, to the value used for output in the 'mark' feature.
+ There is one row in the map for each input/record source.
+ The MAP is then applied to the egress interface at for a given output source
+ @param map_id - client provided identifier for the map
+ @param rows - one row (per-input source) of output values
+*/
+autoreply define qos_egress_map_update
+{
+ u32 client_index;
+ u32 context;
+ u32 map_id;
+ vl_api_qos_egress_map_row_t rows[4];
+};
+
+/** \brief Delete a Qos Map
+ @param map_id - ID of the map to delete
+*/
+autoreply define qos_egress_map_delete
+{
+ u32 client_index;
+ u32 context;
+ u32 map_id;
+};
+
+/** \brief Enable/Disable QoS marking
+ The QoS bits from the packet are mapped (using the desired egress map)
+ into the header of the 'output-source'. Marking should be used in
+ conjunction with recording
+ @param sw_if_index - The interface on which recording is enabled.
+ @param enable - enable=1 or disable the feautre
+ @param output_source - The output source/layer at which the QoS bits
+ are written into the packet. See qos_source_t.
+ @param map_id - The ID of the MAP in which the translation from input
+ to output is performed.
+*/
+autoreply define qos_mark_enable_disable
+{
+ u32 client_index;
+ u32 context;
+ u32 map_id;
+ u32 sw_if_index;
+ u8 output_source;
+ u8 enable;
+};
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/qos/qos_api.c b/src/vnet/qos/qos_api.c
new file mode 100644
index 00000000000..cdb9e46ea36
--- /dev/null
+++ b/src/vnet/qos/qos_api.c
@@ -0,0 +1,161 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *------------------------------------------------------------------
+ */
+
+#include <vnet/vnet.h>
+#include <vlibmemory/api.h>
+#include <vnet/api_errno.h>
+
+#include <vnet/qos/qos_record.h>
+#include <vnet/qos/qos_mark.h>
+#include <vnet/qos/qos_egress_map.h>
+
+#include <vnet/vnet_msg_enum.h>
+
+#define vl_typedefs /* define message structures */
+#include <vnet/vnet_all_api_h.h>
+#undef vl_typedefs
+
+#define vl_endianfun /* define message structures */
+#include <vnet/vnet_all_api_h.h>
+#undef vl_endianfun
+
+/* instantiate all the print functions we know about */
+#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
+#define vl_printfun
+#include <vnet/vnet_all_api_h.h>
+#undef vl_printfun
+
+#include <vlibapi/api_helper_macros.h>
+
+
+#define foreach_qos_api_msg \
+ _(QOS_RECORD_ENABLE_DISABLE, qos_record_enable_disable) \
+ _(QOS_EGRESS_MAP_DELETE, qos_egress_map_delete) \
+ _(QOS_EGRESS_MAP_UPDATE, qos_egress_map_update) \
+ _(QOS_MARK_ENABLE_DISABLE, qos_mark_enable_disable)
+
+void
+vl_api_qos_record_enable_disable_t_handler (vl_api_qos_record_enable_disable_t
+ * mp)
+{
+ vl_api_qos_record_enable_disable_reply_t *rmp;
+ int rv = 0;
+
+ if (mp->input_source > QOS_N_SOURCES)
+ rv = VNET_API_ERROR_INVALID_VALUE;
+ else
+ {
+ if (mp->enable)
+ rv = qos_record_enable (ntohl (mp->sw_if_index), mp->input_source);
+ else
+ rv = qos_record_disable (ntohl (mp->sw_if_index), mp->input_source);
+ }
+
+ REPLY_MACRO (VL_API_QOS_RECORD_ENABLE_DISABLE_REPLY);
+}
+
+void
+vl_api_qos_egress_map_update_t_handler (vl_api_qos_egress_map_update_t * mp)
+{
+ vl_api_qos_egress_map_update_reply_t *rmp;
+ qos_source_t qs;
+ int rv = 0;
+
+ FOR_EACH_QOS_SOURCE (qs)
+ {
+ qos_egress_map_update (ntohl (mp->map_id), qs, &mp->rows[qs].outputs[0]);
+ }
+
+ REPLY_MACRO (VL_API_QOS_EGRESS_MAP_UPDATE_REPLY);
+}
+
+void
+vl_api_qos_egress_map_delete_t_handler (vl_api_qos_egress_map_delete_t * mp)
+{
+ vl_api_qos_egress_map_delete_reply_t *rmp;
+ int rv = 0;
+
+ qos_egress_map_delete (ntohl (mp->map_id));
+
+ REPLY_MACRO (VL_API_QOS_EGRESS_MAP_DELETE_REPLY);
+}
+
+void
+ vl_api_qos_mark_enable_disable_t_handler
+ (vl_api_qos_mark_enable_disable_t * mp)
+{
+ vl_api_qos_mark_enable_disable_reply_t *rmp;
+ int rv = 0;
+
+ if (mp->output_source > QOS_N_SOURCES)
+ rv = VNET_API_ERROR_INVALID_VALUE;
+ else
+ {
+ if (mp->enable)
+ rv = qos_mark_enable (ntohl (mp->sw_if_index),
+ mp->output_source, ntohl (mp->map_id));
+ else
+ rv = qos_mark_disable (ntohl (mp->sw_if_index), mp->output_source);
+ }
+
+ REPLY_MACRO (VL_API_QOS_MARK_ENABLE_DISABLE_REPLY);
+}
+
+#define vl_msg_name_crc_list
+#include <vnet/qos/qos.api.h>
+#undef vl_msg_name_crc_list
+
+static void
+setup_message_id_table (api_main_t * am)
+{
+#define _(id,n,crc) vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id);
+ foreach_vl_msg_name_crc_qos;
+#undef _
+}
+
+static clib_error_t *
+qos_api_hookup (vlib_main_t * vm)
+{
+ api_main_t *am = &api_main;
+
+#define _(N,n) \
+ vl_msg_api_set_handlers(VL_API_##N, #n, \
+ vl_api_##n##_t_handler, \
+ vl_noop_handler, \
+ vl_api_##n##_t_endian, \
+ vl_api_##n##_t_print, \
+ sizeof(vl_api_##n##_t), 1);
+ foreach_qos_api_msg;
+#undef _
+
+ /*
+ * Set up the (msg_name, crc, message-id) table
+ */
+ setup_message_id_table (am);
+
+ return 0;
+}
+
+VLIB_API_INIT_FUNCTION (qos_api_hookup);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/qos/qos_egress_map.c b/src/vnet/qos/qos_egress_map.c
new file mode 100644
index 00000000000..365643995ba
--- /dev/null
+++ b/src/vnet/qos/qos_egress_map.c
@@ -0,0 +1,262 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *------------------------------------------------------------------
+ */
+
+#include <vnet/qos/qos_egress_map.h>
+#include <vnet/qos/qos_mark.h>
+
+/**
+ * Pool from which to allocate table
+ */
+qos_egress_map_t *qem_pool;
+
+/**
+ * DB to map user table-IDs to internal table indicies.
+ */
+uword *qem_db;
+
+index_t
+qos_egress_map_find (qos_egress_map_id_t mid)
+{
+ uword *p = NULL;
+
+ p = hash_get (qem_db, mid);
+
+ if (NULL != p)
+ return p[0];
+
+ return (INDEX_INVALID);
+}
+
+qos_egress_map_t *
+qos_egress_map_find_i (qos_egress_map_id_t mid)
+{
+ index_t qemi;
+
+ qemi = qos_egress_map_find (mid);
+
+ if (INDEX_INVALID != qemi)
+ {
+ return (pool_elt_at_index (qem_pool, qemi));
+ }
+
+ return (NULL);
+}
+
+static qos_egress_map_t *
+qos_egress_map_find_or_create (qos_egress_map_id_t mid)
+{
+ qos_egress_map_t *qem;
+
+ /*
+ * Find the existing or create a new table
+ */
+ qem = qos_egress_map_find_i (mid);
+
+ if (NULL == qem)
+ {
+ index_t qemi;
+
+ pool_get_aligned (qem_pool, qem, CLIB_CACHE_LINE_BYTES);
+ qemi = qem - qem_pool;
+
+ memset (qem, 0, sizeof (*qem));
+ hash_set (qem_db, mid, qemi);
+ }
+
+ return (qem);
+}
+
+void
+qos_egress_map_update (qos_egress_map_id_t mid,
+ qos_source_t input_source, qos_bits_t * values)
+{
+ qos_egress_map_t *qem;
+
+ qem = qos_egress_map_find_or_create (mid);
+
+ clib_memcpy (qem->qem_output[input_source],
+ values, sizeof (qem->qem_output[input_source]));
+}
+
+void
+qos_egress_map_delete (qos_egress_map_id_t mid)
+{
+ qos_egress_map_t *qem;
+
+ qem = qos_egress_map_find_i (mid);
+ hash_unset (qem_db, mid);
+
+ if (NULL != qem)
+ {
+ pool_put (qem_pool, qem);
+ }
+}
+
+static clib_error_t *
+qos_egress_map_update_cli (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ qos_egress_map_id_t map_id;
+ qos_egress_map_t *qem;
+ u8 add;
+
+ add = 1;
+ map_id = ~0;
+ qem = NULL;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "delete") || unformat (input, "del"))
+ add = 0;
+ else if (unformat (input, "id %d", &map_id))
+ qem = qos_egress_map_find_or_create (map_id);
+ else
+ {
+ int qs, qi, qo;
+
+ if (NULL == qem)
+ return clib_error_return (0, "map-id must be specified");
+
+ while (unformat
+ (input, "[%U][%d]=%d", unformat_qos_source, &qs, &qi, &qo))
+ qem->qem_output[qs][qi] = qo;
+ break;
+ }
+ }
+
+ if (!add)
+ qos_egress_map_delete (map_id);
+
+ return (NULL);
+}
+
+/*?
+ * Update a Egress Qos Map table
+ *
+ * @cliexpar
+ * @cliexcmd{qos egress map id 0 [ip][4]=4}
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (qos_egress_map_update_command, static) = {
+ .path = "qos egress map",
+ .short_help = "qos egress map id %d [delete] {[SOURCE][INPUT]=OUTPUT}",
+ .function = qos_egress_map_update_cli,
+ .is_mp_safe = 1,
+};
+/* *INDENT-ON* */
+
+u8 *
+format_qos_egress_map (u8 * s, va_list * args)
+{
+ qos_egress_map_t *qem = va_arg (*args, qos_egress_map_t *);
+ u32 indent = va_arg (*args, u32);
+ int qs;
+ u32 ii;
+
+ FOR_EACH_QOS_SOURCE (qs)
+ {
+ s = format (s, "%U%U:[",
+ format_white_space, indent, format_qos_source, qs);
+
+ for (ii = 0; ii < ARRAY_LEN (qem->qem_output[qs]) - 1; ii++)
+ {
+ s = format (s, "%d,", qem->qem_output[qs][ii]);
+ }
+ s = format (s, "%d]\n", qem->qem_output[qs][ii]);
+ }
+
+ return (s);
+}
+
+static clib_error_t *
+qos_egress_map_show (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ qos_egress_map_id_t map_id;
+ qos_egress_map_t *qem;
+ clib_error_t *error;
+
+ map_id = ~0;
+ qem = NULL;
+ error = NULL;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "id %d", &map_id))
+ ;
+ else
+ {
+ error = unformat_parse_error (input);
+ goto done;
+ }
+ }
+
+ if (~0 == map_id)
+ {
+ index_t qemi;
+
+ /* *INDENT-OFF* */
+ hash_foreach(map_id, qemi, qem_db,
+ ({
+ vlib_cli_output (vm, " Map-ID:%d\n%U",
+ map_id,
+ format_qos_egress_map,
+ pool_elt_at_index(qem_pool, qemi), 2);
+ }));
+ /* *INDENT-ON* */
+ }
+ else
+ {
+ qem = qos_egress_map_find_i (map_id);
+
+ if (NULL == qem)
+ {
+ error = clib_error_return (0, "No Map for ID %d", map_id);
+ }
+ else
+ {
+ vlib_cli_output (vm, " Map-ID:%d\n%U",
+ map_id, format_qos_egress_map, qem, 2);
+ }
+ }
+
+done:
+ return (error);
+}
+
+/*?
+ * Show Egress Qos Maps
+ *
+ * @cliexpar
+ * @cliexcmd{show qos egress map}
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (qos_egress_map_show_command, static) = {
+ .path = "show qos egress map",
+ .short_help = "show qos egress map id %d",
+ .function = qos_egress_map_show,
+ .is_mp_safe = 1,
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/qos/qos_egress_map.h b/src/vnet/qos/qos_egress_map.h
new file mode 100644
index 00000000000..f4b282cb7ba
--- /dev/null
+++ b/src/vnet/qos/qos_egress_map.h
@@ -0,0 +1,82 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *------------------------------------------------------------------
+ */
+
+/**
+ * A QOS egress map translates from the COS bits stored in the packet's
+ * meta-data into a per-protocol COS value
+ */
+
+#ifndef __QOS_EGRESS_MAP_H__
+#define __QOS_EGRESS_MAP_H__
+
+#include <vnet/qos/qos_types.h>
+#include <vnet/dpo/dpo.h>
+
+/**
+ * An attempt at type safety
+ */
+typedef u32 qos_egress_map_id_t;
+
+/**
+ * For a given output source a table maps each value of every input sorce.
+ */
+typedef struct qos_egress_map_t_
+{
+ /**
+ * The array of output mapped values;
+ * output = eq_qos[input-source][input-value]
+ */
+ qos_bits_t qem_output[QOS_N_SOURCES][256];
+} qos_egress_map_t;
+
+extern u8 *format_qos_egress_map (u8 * s, va_list * args);
+
+/**
+ * Add a qos-egress map to an interface. If sw_if_index = ~0
+ * then the configuration is for the 'default' table.
+ * If the table is ~0, this is a removal.
+ * the egress mapping is applied. For example, is output is MPLS then
+ * the QoS markings will occur for MPLS pakcets.
+ */
+extern void qos_egress_map_update (qos_egress_map_id_t tid,
+ qos_source_t input_source,
+ qos_bits_t * values);
+extern void qos_egress_map_delete (qos_egress_map_id_t tid);
+
+/**
+ * Get the VPP QoS map index from the user's map-ID
+ */
+extern index_t qos_egress_map_find (qos_egress_map_id_t tid);
+
+/**
+ * Data-plane functions
+ */
+
+/**
+ * Pool from which to allocate map
+ */
+extern qos_egress_map_t *qem_pool;
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/qos/qos_mark.c b/src/vnet/qos/qos_mark.c
new file mode 100644
index 00000000000..cec7192b018
--- /dev/null
+++ b/src/vnet/qos/qos_mark.c
@@ -0,0 +1,447 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/ip/ip.h>
+#include <vnet/feature/feature.h>
+#include <vnet/qos/qos_egress_map.h>
+#include <vnet/qos/qos_mark.h>
+
+/**
+ * per-interface vector of which MAP is used by which interface
+ * for each output source
+ */
+index_t *qos_mark_configs[QOS_N_SOURCES];
+
+void
+qos_mark_ip_enable_disable (u32 sw_if_index, u8 enable)
+{
+ vnet_feature_enable_disable ("ip6-output", "ip6-qos-mark",
+ sw_if_index, enable, NULL, 0);
+ vnet_feature_enable_disable ("ip4-output", "ip4-qos-mark",
+ sw_if_index, enable, NULL, 0);
+}
+
+void
+qos_mark_vlan_enable_disable (u32 sw_if_index, u8 enable)
+{
+ vnet_feature_enable_disable ("interface-output", "vlan-qos-mark",
+ sw_if_index, enable, NULL, 0);
+}
+
+void
+qos_mark_mpls_enable_disable (u32 sw_if_index, u8 enable)
+{
+ vnet_feature_enable_disable ("mpls-output", "mpls-qos-mark",
+ sw_if_index, enable, NULL, 0);
+}
+
+static void
+qos_egress_map_feature_config (u32 sw_if_index, qos_source_t qs, u8 enable)
+{
+ switch (qs)
+ {
+ case QOS_SOURCE_EXT:
+ ASSERT (0);
+ break;
+ case QOS_SOURCE_VLAN:
+ qos_mark_vlan_enable_disable (sw_if_index, enable);
+ break;
+ case QOS_SOURCE_MPLS:
+ qos_mark_mpls_enable_disable (sw_if_index, enable);
+ break;
+ case QOS_SOURCE_IP:
+ qos_mark_ip_enable_disable (sw_if_index, enable);
+ break;
+ }
+}
+
+always_inline qos_egress_map_t *
+qos_egress_map_interface (u32 sw_if_index, qos_source_t output_source)
+{
+ ASSERT (vec_len (qos_mark_configs[output_source]) > sw_if_index);
+
+ return pool_elt_at_index (qem_pool,
+ qos_mark_configs[output_source][sw_if_index]);
+}
+
+/**
+ * per-packet trace data
+ */
+typedef struct qos_mark_trace_t_
+{
+ /* per-pkt trace data */
+ qos_bits_t bits;
+ qos_source_t input;
+ u32 used;
+} qos_mark_trace_t;
+
+static inline uword
+qos_mark_inline (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame, qos_source_t output_source, int is_ip6)
+{
+ u32 n_left_from, *from, *to_next, next_index;
+
+ next_index = 0;
+ n_left_from = frame->n_vectors;
+ from = vlib_frame_vector_args (frame);
+
+ while (n_left_from > 0)
+ {
+ u32 n_left_to_next;
+
+ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ qos_source_t input_source0;
+ ethernet_vlan_header_t *vlan0;
+ u32 sw_if_index0, next0, bi0;
+ qos_egress_map_t *qem0;
+ ip4_header_t *ip4_0;
+ ip6_header_t *ip6_0;
+ vlib_buffer_t *b0;
+ qos_bits_t qos0;
+ u8 *mpls_bytes_0;
+ u8 eos0;
+
+ next0 = 0;
+ bi0 = from[0];
+ to_next[0] = bi0;
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX];
+ input_source0 = vnet_buffer2 (b0)->qos.source;
+
+ qem0 = qos_egress_map_interface (sw_if_index0, output_source);
+ qos0 = qem0->qem_output[input_source0][vnet_buffer2 (b0)->qos.bits];
+
+ if (PREDICT_TRUE (b0->flags & VNET_BUFFER_F_QOS_DATA_VALID))
+ {
+ /* there is a source of QoS recording for this packet */
+ if (QOS_SOURCE_IP == output_source)
+ {
+ if (is_ip6)
+ {
+ ip6_0 = (vlib_buffer_get_current (b0) +
+ vnet_buffer (b0)->ip.save_rewrite_length);
+
+ ip6_set_traffic_class_network_order (ip6_0, qos0);
+ }
+ else
+ {
+ ip4_0 = (vlib_buffer_get_current (b0) +
+ vnet_buffer (b0)->ip.save_rewrite_length);
+ if (PREDICT_FALSE (qos0 != ip4_0->tos))
+ {
+ ip4_0->tos = qos0;
+ ip4_0->checksum = ip4_header_checksum (ip4_0);
+ }
+ }
+ }
+ else if (QOS_SOURCE_MPLS == output_source)
+ {
+ mpls_bytes_0 = (vlib_buffer_get_current (b0) +
+ vnet_buffer (b0)->mpls.save_rewrite_length);
+
+ /* apply to all the labels in the stack */
+ do
+ {
+ /* clear out the old COS bts */
+ mpls_bytes_0[2] &= 0xf1;
+ /* OR in 3 bits of the mapped value */
+ mpls_bytes_0[2] |= (qos0 & 0x7) << 1;
+ eos0 = mpls_bytes_0[2] & 0x1;
+ mpls_bytes_0 += 4;
+ }
+ while (!eos0);
+ }
+ else if (QOS_SOURCE_VLAN == output_source)
+ {
+ vlan0 = (vlib_buffer_get_current (b0) +
+ sizeof (ethernet_header_t));
+
+ ethernet_vlan_header_set_priority_net_order (vlan0, qos0);
+ }
+ }
+ vnet_feature_next (sw_if_index0, &next0, b0);
+
+ if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+ {
+ qos_mark_trace_t *t =
+ vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->bits = qos0;
+ t->input = input_source0;
+ t->used = (b0->flags & VNET_BUFFER_F_QOS_DATA_VALID);
+ }
+
+ /* verify speculative enqueue, maybe switch current next frame */
+ vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+ to_next, n_left_to_next,
+ bi0, next0);
+ }
+
+ vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+ }
+
+ return frame->n_vectors;
+}
+
+/* packet trace format function */
+static u8 *
+format_qos_mark_trace (u8 * s, va_list * args)
+{
+ CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+ CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+ qos_mark_trace_t *t = va_arg (*args, qos_mark_trace_t *);
+
+ s = format (s, "source:%U qos:%d used:%s",
+ format_qos_source, t->input, t->bits, (t->used ? "yes" : "no"));
+
+ return s;
+}
+
+static inline uword
+ip4_qos_mark (vlib_main_t * vm, vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return (qos_mark_inline (vm, node, frame, QOS_SOURCE_IP, 0));
+}
+
+static inline uword
+ip6_qos_mark (vlib_main_t * vm, vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return (qos_mark_inline (vm, node, frame, QOS_SOURCE_IP, 1));
+}
+
+static inline uword
+mpls_qos_mark (vlib_main_t * vm, vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return (qos_mark_inline (vm, node, frame, QOS_SOURCE_MPLS, 0));
+}
+
+static inline uword
+vlan_qos_mark (vlib_main_t * vm, vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return (qos_mark_inline (vm, node, frame, QOS_SOURCE_VLAN, 0));
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (ip4_qos_mark_node) = {
+ .function = ip4_qos_mark,
+ .name = "ip4-qos-mark",
+ .vector_size = sizeof (u32),
+ .format_trace = format_qos_mark_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = 0,
+ .n_next_nodes = 1,
+
+ .next_nodes = {
+ [0] = "ip4-drop",
+ },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (ip4_qos_mark_node, ip4_qos_mark);
+
+VNET_FEATURE_INIT (ip4_qos_mark_node, static) = {
+ .arc_name = "ip4-output",
+ .node_name = "ip4-qos-mark",
+};
+
+VLIB_REGISTER_NODE (ip6_qos_mark_node) = {
+ .function = ip6_qos_mark,
+ .name = "ip6-qos-mark",
+ .vector_size = sizeof (u32),
+ .format_trace = format_qos_mark_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = 0,
+ .n_next_nodes = 1,
+
+ .next_nodes = {
+ [0] = "ip6-drop",
+ },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (ip6_qos_mark_node, ip6_qos_mark);
+
+VNET_FEATURE_INIT (ip6_qos_mark_node, static) = {
+ .arc_name = "ip6-output",
+ .node_name = "ip6-qos-mark",
+};
+
+VLIB_REGISTER_NODE (mpls_qos_mark_node) = {
+ .function = mpls_qos_mark,
+ .name = "mpls-qos-mark",
+ .vector_size = sizeof (u32),
+ .format_trace = format_qos_mark_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = 0,
+ .n_next_nodes = 1,
+
+ .next_nodes = {
+ [0] = "mpls-drop",
+ },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (mpls_qos_mark_node, mpls_qos_mark);
+
+VNET_FEATURE_INIT (mpls_qos_mark_node, static) = {
+ .arc_name = "mpls-output",
+ .node_name = "mpls-qos-mark",
+};
+VLIB_REGISTER_NODE (vlan_qos_mark_node) = {
+ .function = vlan_qos_mark,
+ .name = "vlan-qos-mark",
+ .vector_size = sizeof (u32),
+ .format_trace = format_qos_mark_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = 0,
+ .n_next_nodes = 1,
+
+ .next_nodes = {
+ [0] = "error-drop",
+ },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (vlan_qos_mark_node, vlan_qos_mark);
+
+VNET_FEATURE_INIT (vlan_qos_mark_node, static) = {
+ .arc_name = "interface-output",
+ .node_name = "vlan-qos-mark",
+};
+/* *INDENT-ON* */
+
+int
+qos_mark_enable (u32 sw_if_index,
+ qos_source_t output_source, qos_egress_map_id_t mid)
+{
+ index_t qemi;
+
+ vec_validate_init_empty (qos_mark_configs[output_source],
+ sw_if_index, INDEX_INVALID);
+
+ qemi = qos_egress_map_find (mid);
+
+ if (INDEX_INVALID == qemi)
+ return VNET_API_ERROR_NO_SUCH_TABLE;
+
+ if (INDEX_INVALID == qos_mark_configs[output_source][sw_if_index])
+ {
+ qos_egress_map_feature_config (sw_if_index, output_source, 1);
+ }
+
+ qos_mark_configs[output_source][sw_if_index] = qemi;
+
+ return (0);
+}
+
+int
+qos_mark_disable (u32 sw_if_index, qos_source_t output_source)
+{
+ if (vec_len (qos_mark_configs[output_source]) < sw_if_index)
+ return VNET_API_ERROR_NO_MATCHING_INTERFACE;
+ if (INDEX_INVALID == qos_mark_configs[output_source][sw_if_index])
+ return VNET_API_ERROR_VALUE_EXIST;
+
+ if (INDEX_INVALID != qos_mark_configs[output_source][sw_if_index])
+ {
+ qos_egress_map_feature_config (sw_if_index, output_source, 0);
+ }
+
+ qos_mark_configs[output_source][sw_if_index] = INDEX_INVALID;
+
+ return (0);
+}
+
+static clib_error_t *
+qos_mark_cli (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ qos_egress_map_id_t map_id;
+ u32 sw_if_index, qs;
+ vnet_main_t *vnm;
+ int rv, enable;
+
+ vnm = vnet_get_main ();
+ map_id = ~0;
+ qs = 0xff;
+ enable = 1;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "id %d", &map_id))
+ ;
+ else if (unformat (input, "disable"))
+ enable = 0;
+ else if (unformat (input, "%U", unformat_qos_source, &qs))
+ ;
+ else if (unformat (input, "%U",
+ unformat_vnet_sw_interface, vnm, &sw_if_index))
+ ;
+ else
+ break;
+ }
+
+ if (~0 == sw_if_index)
+ return clib_error_return (0, "interface must be specified");
+ if (0xff == qs)
+ return clib_error_return (0, "output location must be specified");
+
+ if (enable)
+ rv = qos_mark_enable (sw_if_index, qs, map_id);
+ else
+ rv = qos_mark_disable (sw_if_index, qs);
+
+ if (0 == rv)
+ return (NULL);
+
+ return clib_error_return (0, "Failed to map interface");
+}
+
+/*?
+ * Apply a QoS egress mapping table to an interface for QoS marking packets
+ * at the given output protocol.
+ *
+ * @cliexpar
+ * @cliexcmd{qos egress interface GigEthernet0/9/0 id 0 output ip}
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (qos_egress_map_interface_command, static) = {
+ .path = "qos mark",
+ .short_help = "qos mark <SOURCE> <INTERFACE> id <MAP>",
+ .function = qos_mark_cli,
+ .is_mp_safe = 1,
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+* Local Variables:
+* eval: (c-set-style "gnu")
+* End:
+*
+*/
diff --git a/src/vnet/qos/qos_mark.h b/src/vnet/qos/qos_mark.h
new file mode 100644
index 00000000000..f1705d1bb33
--- /dev/null
+++ b/src/vnet/qos/qos_mark.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __QOS_MARK_H__
+#define __QOS_MARK_H__
+
+#include <vnet/qos/qos_egress_map.h>
+
+/**
+ * enable QoS marking by associating a MAP with an interface.
+ * The output_source specifies which protocol/header the QoS value
+ * will be written into
+ */
+extern int qos_mark_enable (u32 sw_if_index,
+ qos_source_t output_source,
+ qos_egress_map_id_t tid);
+extern int qos_mark_disable (u32 sw_if_index, qos_source_t output_source);
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/qos/qos_record.c b/src/vnet/qos/qos_record.c
new file mode 100644
index 00000000000..047bd25cda4
--- /dev/null
+++ b/src/vnet/qos/qos_record.c
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/qos/qos_record.h>
+#include <vnet/ip/ip.h>
+#include <vnet/ip/ip6_to_ip4.h>
+#include <vnet/feature/feature.h>
+#include <vnet/qos/qos_types.h>
+
+/**
+ * Per-interface, per-protocol vector of feature on/off configurations
+ */
+static u8 *qos_record_configs[QOS_N_SOURCES];
+
+static void
+qos_record_feature_config (u32 sw_if_index,
+ qos_source_t input_source, u8 enable)
+{
+ switch (input_source)
+ {
+ case QOS_SOURCE_IP:
+ vnet_feature_enable_disable ("ip6-unicast", "ip6-qos-record",
+ sw_if_index, enable, NULL, 0);
+ vnet_feature_enable_disable ("ip6-multicast", "ip6-qos-record",
+ sw_if_index, enable, NULL, 0);
+ vnet_feature_enable_disable ("ip4-unicast", "ip4-qos-record",
+ sw_if_index, enable, NULL, 0);
+ vnet_feature_enable_disable ("ip4-multicast", "ip4-qos-record",
+ sw_if_index, enable, NULL, 0);
+ break;
+ case QOS_SOURCE_MPLS:
+ case QOS_SOURCE_VLAN:
+ case QOS_SOURCE_EXT:
+ // not implemented yet
+ break;
+ }
+}
+
+int
+qos_record_enable (u32 sw_if_index, qos_source_t input_source)
+{
+ vec_validate (qos_record_configs[input_source], sw_if_index);
+
+ if (0 == qos_record_configs[input_source][sw_if_index])
+ {
+ qos_record_feature_config (sw_if_index, input_source, 1);
+ }
+
+ qos_record_configs[input_source][sw_if_index]++;
+ return (0);
+}
+
+int
+qos_record_disable (u32 sw_if_index, qos_source_t input_source)
+{
+ if (vec_len (qos_record_configs[input_source]) < sw_if_index)
+ return VNET_API_ERROR_NO_MATCHING_INTERFACE;
+
+ if (0 == qos_record_configs[input_source][sw_if_index])
+ return VNET_API_ERROR_VALUE_EXIST;
+
+ qos_record_configs[input_source][sw_if_index]--;
+
+ if (0 == qos_record_configs[input_source][sw_if_index])
+ {
+ qos_record_feature_config (sw_if_index, input_source, 0);
+ }
+
+ return (0);
+}
+
+/*
+ * Disable recording feautre for all protocols when the interface
+ * is deleted
+ */
+static clib_error_t *
+qos_record_ip_interface_add_del (vnet_main_t * vnm,
+ u32 sw_if_index, u32 is_add)
+{
+ if (!is_add)
+ {
+ qos_source_t qs;
+
+ FOR_EACH_QOS_SOURCE (qs)
+ {
+ qos_record_disable (sw_if_index, qs);
+ }
+ }
+
+ return (NULL);
+}
+
+VNET_SW_INTERFACE_ADD_DEL_FUNCTION (qos_record_ip_interface_add_del);
+
+/**
+ * per-packet trace data
+ */
+typedef struct qos_record_trace_t_
+{
+ /* per-pkt trace data */
+ qos_bits_t bits;
+} qos_record_trace_t;
+
+static inline uword
+qos_record_inline (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame, int is_ip6)
+{
+ u32 n_left_from, *from, *to_next, next_index;
+
+ next_index = 0;
+ n_left_from = frame->n_vectors;
+ from = vlib_frame_vector_args (frame);
+
+ while (n_left_from > 0)
+ {
+ u32 n_left_to_next;
+
+ vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+ while (n_left_from > 0 && n_left_to_next > 0)
+ {
+ ip4_header_t *ip4_0;
+ ip6_header_t *ip6_0;
+ vlib_buffer_t *b0;
+ u32 sw_if_index0, next0, bi0;
+ qos_bits_t qos0;
+
+ next0 = 0;
+ bi0 = from[0];
+ to_next[0] = bi0;
+ from += 1;
+ to_next += 1;
+ n_left_from -= 1;
+ n_left_to_next -= 1;
+
+ b0 = vlib_get_buffer (vm, bi0);
+ if (is_ip6)
+ {
+ ip6_0 = vlib_buffer_get_current (b0);
+ qos0 = ip6_traffic_class_network_order (ip6_0);
+ }
+ else
+ {
+ ip4_0 = vlib_buffer_get_current (b0);
+ qos0 = ip4_0->tos;
+ }
+ vnet_buffer2 (b0)->qos.bits = qos0;
+ vnet_buffer2 (b0)->qos.source = QOS_SOURCE_IP;
+ b0->flags |= VNET_BUFFER_F_QOS_DATA_VALID;
+ sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+
+ vnet_feature_next (sw_if_index0, &next0, b0);
+
+ if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
+ (b0->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ qos_record_trace_t *t =
+ vlib_add_trace (vm, node, b0, sizeof (*t));
+ t->bits = qos0;
+ }
+
+ /* verify speculative enqueue, maybe switch current next frame */
+ vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+ to_next, n_left_to_next,
+ bi0, next0);
+ }
+
+ vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+ }
+
+ return frame->n_vectors;
+}
+
+/* packet trace format function */
+static u8 *
+format_qos_record_trace (u8 * s, va_list * args)
+{
+ CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+ CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+ qos_record_trace_t *t = va_arg (*args, qos_record_trace_t *);
+
+ s = format (s, "qos:%d", t->bits);
+
+ return s;
+}
+
+static inline uword
+ip4_qos_record (vlib_main_t * vm, vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return (qos_record_inline (vm, node, frame, 0));
+}
+
+static inline uword
+ip6_qos_record (vlib_main_t * vm, vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ return (qos_record_inline (vm, node, frame, 1));
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (ip4_qos_record_node) = {
+ .function = ip4_qos_record,
+ .name = "ip4-qos-record",
+ .vector_size = sizeof (u32),
+ .format_trace = format_qos_record_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = 0,
+ .n_next_nodes = 1,
+
+ .next_nodes = {
+ [0] = "ip4-drop",
+ },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (ip4_qos_record_node, ip4_qos_record);
+
+VNET_FEATURE_INIT (ip4_qos_record_node, static) = {
+ .arc_name = "ip4-unicast",
+ .node_name = "ip4-qos-record",
+};
+
+VLIB_REGISTER_NODE (ip6_qos_record_node) = {
+ .function = ip6_qos_record,
+ .name = "ip6-qos-record",
+ .vector_size = sizeof (u32),
+ .format_trace = format_qos_record_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+
+ .n_errors = 0,
+ .n_next_nodes = 1,
+
+ .next_nodes = {
+ [0] = "ip6-drop",
+ },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (ip6_qos_record_node, ip6_qos_record);
+
+VNET_FEATURE_INIT (ip6_qos_record_node, static) = {
+ .arc_name = "ip6-unicast",
+ .node_name = "ip6-qos-record",
+};
+/* *INDENT-ON* */
+
+
+static clib_error_t *
+qos_record_cli (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ u32 sw_if_index, qs;
+ u8 enable;
+
+ qs = 0xff;
+ enable = 1;
+ sw_if_index = ~0;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (input, "%U", unformat_vnet_sw_interface,
+ vnm, &sw_if_index))
+ ;
+ else if (unformat (input, "%U", unformat_qos_source, &qs))
+ ;
+ else if (unformat (input, "enable"))
+ enable = 1;
+ else if (unformat (input, "disable"))
+ enable = 0;
+ else
+ break;
+ }
+
+ if (~0 == sw_if_index)
+ return clib_error_return (0, "interface must be specified");
+ if (0xff == qs)
+ return clib_error_return (0, "input location must be specified");
+
+ if (enable)
+ qos_record_enable (sw_if_index, qs);
+ else
+ qos_record_disable (sw_if_index, qs);
+
+ return (NULL);
+}
+
+/*?
+ * Enable QoS bit recording on an interface using the packet's input DSCP bits
+ * Which input QoS bits to use are either; IP, MPLS or VLAN. If more than
+ * one protocol is chosen (which is foolish) the higer layers override the
+ * lower.
+ *
+ * @cliexpar
+ * @cliexcmd{qos record ip GigEthernet0/1/0}
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (qos_record_command, static) = {
+ .path = "qos record",
+ .short_help = "qos record <record-source> <INTERFACE> [disable]",
+ .function = qos_record_cli,
+ .is_mp_safe = 1,
+};
+/* *INDENT-ON* */
+
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/qos/qos_record.h b/src/vnet/qos/qos_record.h
new file mode 100644
index 00000000000..e5726d650a7
--- /dev/null
+++ b/src/vnet/qos/qos_record.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __QOS_RECORD_H__
+#define __QOS_RECORD_H__
+
+#include <vnet/qos/qos_types.h>
+
+extern int qos_record_disable (u32 sw_if_index, qos_source_t input_source);
+extern int qos_record_enable (u32 sw_if_index, qos_source_t input_source);
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/qos/qos_types.c b/src/vnet/qos/qos_types.c
new file mode 100644
index 00000000000..9fe10cb483d
--- /dev/null
+++ b/src/vnet/qos/qos_types.c
@@ -0,0 +1,59 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *------------------------------------------------------------------
+ */
+
+/**
+ * QoS tyeps
+ */
+
+#include <vnet/qos/qos_types.h>
+
+static const char *qos_source_names[] = QOS_SOURCE_NAMES;
+
+u8 *
+format_qos_source (u8 * s, va_list * args)
+{
+ int qs = va_arg (*args, int);
+
+ return (format (s, "%s", qos_source_names[qs]));
+}
+
+uword
+unformat_qos_source (unformat_input_t * input, va_list * args)
+{
+ int *qs = va_arg (*args, int *);
+
+ if (unformat (input, "ip"))
+ *qs = QOS_SOURCE_IP;
+ else if (unformat (input, "mpls"))
+ *qs = QOS_SOURCE_MPLS;
+ else if (unformat (input, "ext"))
+ *qs = QOS_SOURCE_EXT;
+ else if (unformat (input, "vlan"))
+ *qs = QOS_SOURCE_VLAN;
+ else
+ return 0;
+
+ return 1;
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/qos/qos_types.h b/src/vnet/qos/qos_types.h
new file mode 100644
index 00000000000..6934d041cd8
--- /dev/null
+++ b/src/vnet/qos/qos_types.h
@@ -0,0 +1,78 @@
+/*
+ *------------------------------------------------------------------
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *------------------------------------------------------------------
+ */
+
+/**
+ * QoS tyeps
+ */
+
+#ifndef __QOS_TYPES_H__
+#define __QOS_TYPES_H__
+
+#include <vnet/vnet.h>
+
+/**
+ * Sources for the QoS bits in the packet
+ */
+typedef enum qos_source_t_
+{
+ /**
+ * Some external source, e.g. a plugin.
+ */
+ QOS_SOURCE_EXT,
+ QOS_SOURCE_VLAN,
+ QOS_SOURCE_MPLS,
+ QOS_SOURCE_IP,
+} __attribute__ ((packed)) qos_source_t;
+
+/**
+ * The maximum number of sources. defined outside the enum so switch
+ * statements don't need to handle a non-value nor use a default label
+ */
+#define QOS_N_SOURCES (QOS_SOURCE_IP + 1)
+
+#define QOS_SOURCE_NAMES { \
+ [QOS_SOURCE_EXT] = "ext", \
+ [QOS_SOURCE_IP] = "IP", \
+ [QOS_SOURCE_MPLS] = "MPLS", \
+ [QOS_SOURCE_VLAN] = "VLAN", \
+}
+
+#define FOR_EACH_QOS_SOURCE(_src) \
+ for (_src = QOS_SOURCE_EXT; \
+ _src <= QOS_SOURCE_IP; \
+ _src++)
+
+/**
+ * format/unformat QoS source types
+ */
+extern u8 *format_qos_source (u8 * s, va_list * args);
+extern uword unformat_qos_source (unformat_input_t * input, va_list * args);
+
+/**
+ * Type, er, safety for us water based entities
+ */
+typedef u8 qos_bits_t;
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vnet/vnet_all_api_h.h b/src/vnet/vnet_all_api_h.h
index 44fb7585b91..fea92e7fd27 100644
--- a/src/vnet/vnet_all_api_h.h
+++ b/src/vnet/vnet_all_api_h.h
@@ -71,6 +71,7 @@
#include <vnet/pg/pg.api.h>
#include <vnet/feature/feature.api.h>
#include <vnet/sctp/sctp.api.h>
+#include <vnet/qos/qos.api.h>
/*
* fd.io coding-style-patch-verification: ON
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})