QoS recording and marking
[vpp.git] / src / vnet / qos / qos_record.c
1 /*
2  * Copyright (c) 2018 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <vnet/qos/qos_record.h>
17 #include <vnet/ip/ip.h>
18 #include <vnet/ip/ip6_to_ip4.h>
19 #include <vnet/feature/feature.h>
20 #include <vnet/qos/qos_types.h>
21
22 /**
23  * Per-interface, per-protocol vector of feature on/off configurations
24  */
25 static u8 *qos_record_configs[QOS_N_SOURCES];
26
27 static void
28 qos_record_feature_config (u32 sw_if_index,
29                            qos_source_t input_source, u8 enable)
30 {
31   switch (input_source)
32     {
33     case QOS_SOURCE_IP:
34       vnet_feature_enable_disable ("ip6-unicast", "ip6-qos-record",
35                                    sw_if_index, enable, NULL, 0);
36       vnet_feature_enable_disable ("ip6-multicast", "ip6-qos-record",
37                                    sw_if_index, enable, NULL, 0);
38       vnet_feature_enable_disable ("ip4-unicast", "ip4-qos-record",
39                                    sw_if_index, enable, NULL, 0);
40       vnet_feature_enable_disable ("ip4-multicast", "ip4-qos-record",
41                                    sw_if_index, enable, NULL, 0);
42       break;
43     case QOS_SOURCE_MPLS:
44     case QOS_SOURCE_VLAN:
45     case QOS_SOURCE_EXT:
46       // not implemented yet
47       break;
48     }
49 }
50
51 int
52 qos_record_enable (u32 sw_if_index, qos_source_t input_source)
53 {
54   vec_validate (qos_record_configs[input_source], sw_if_index);
55
56   if (0 == qos_record_configs[input_source][sw_if_index])
57     {
58       qos_record_feature_config (sw_if_index, input_source, 1);
59     }
60
61   qos_record_configs[input_source][sw_if_index]++;
62   return (0);
63 }
64
65 int
66 qos_record_disable (u32 sw_if_index, qos_source_t input_source)
67 {
68   if (vec_len (qos_record_configs[input_source]) < sw_if_index)
69     return VNET_API_ERROR_NO_MATCHING_INTERFACE;
70
71   if (0 == qos_record_configs[input_source][sw_if_index])
72     return VNET_API_ERROR_VALUE_EXIST;
73
74   qos_record_configs[input_source][sw_if_index]--;
75
76   if (0 == qos_record_configs[input_source][sw_if_index])
77     {
78       qos_record_feature_config (sw_if_index, input_source, 0);
79     }
80
81   return (0);
82 }
83
84 /*
85  * Disable recording feautre for all protocols when the interface
86  * is deleted
87  */
88 static clib_error_t *
89 qos_record_ip_interface_add_del (vnet_main_t * vnm,
90                                  u32 sw_if_index, u32 is_add)
91 {
92   if (!is_add)
93     {
94       qos_source_t qs;
95
96       FOR_EACH_QOS_SOURCE (qs)
97       {
98         qos_record_disable (sw_if_index, qs);
99       }
100     }
101
102   return (NULL);
103 }
104
105 VNET_SW_INTERFACE_ADD_DEL_FUNCTION (qos_record_ip_interface_add_del);
106
107 /**
108  * per-packet trace data
109  */
110 typedef struct qos_record_trace_t_
111 {
112   /* per-pkt trace data */
113   qos_bits_t bits;
114 } qos_record_trace_t;
115
116 static inline uword
117 qos_record_inline (vlib_main_t * vm,
118                    vlib_node_runtime_t * node,
119                    vlib_frame_t * frame, int is_ip6)
120 {
121   u32 n_left_from, *from, *to_next, next_index;
122
123   next_index = 0;
124   n_left_from = frame->n_vectors;
125   from = vlib_frame_vector_args (frame);
126
127   while (n_left_from > 0)
128     {
129       u32 n_left_to_next;
130
131       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
132
133       while (n_left_from > 0 && n_left_to_next > 0)
134         {
135           ip4_header_t *ip4_0;
136           ip6_header_t *ip6_0;
137           vlib_buffer_t *b0;
138           u32 sw_if_index0, next0, bi0;
139           qos_bits_t qos0;
140
141           next0 = 0;
142           bi0 = from[0];
143           to_next[0] = bi0;
144           from += 1;
145           to_next += 1;
146           n_left_from -= 1;
147           n_left_to_next -= 1;
148
149           b0 = vlib_get_buffer (vm, bi0);
150           if (is_ip6)
151             {
152               ip6_0 = vlib_buffer_get_current (b0);
153               qos0 = ip6_traffic_class_network_order (ip6_0);
154             }
155           else
156             {
157               ip4_0 = vlib_buffer_get_current (b0);
158               qos0 = ip4_0->tos;
159             }
160           vnet_buffer2 (b0)->qos.bits = qos0;
161           vnet_buffer2 (b0)->qos.source = QOS_SOURCE_IP;
162           b0->flags |= VNET_BUFFER_F_QOS_DATA_VALID;
163           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
164
165           vnet_feature_next (sw_if_index0, &next0, b0);
166
167           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
168                              (b0->flags & VLIB_BUFFER_IS_TRACED)))
169             {
170               qos_record_trace_t *t =
171                 vlib_add_trace (vm, node, b0, sizeof (*t));
172               t->bits = qos0;
173             }
174
175           /* verify speculative enqueue, maybe switch current next frame */
176           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
177                                            to_next, n_left_to_next,
178                                            bi0, next0);
179         }
180
181       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
182     }
183
184   return frame->n_vectors;
185 }
186
187 /* packet trace format function */
188 static u8 *
189 format_qos_record_trace (u8 * s, va_list * args)
190 {
191   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
192   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
193   qos_record_trace_t *t = va_arg (*args, qos_record_trace_t *);
194
195   s = format (s, "qos:%d", t->bits);
196
197   return s;
198 }
199
200 static inline uword
201 ip4_qos_record (vlib_main_t * vm, vlib_node_runtime_t * node,
202                 vlib_frame_t * frame)
203 {
204   return (qos_record_inline (vm, node, frame, 0));
205 }
206
207 static inline uword
208 ip6_qos_record (vlib_main_t * vm, vlib_node_runtime_t * node,
209                 vlib_frame_t * frame)
210 {
211   return (qos_record_inline (vm, node, frame, 1));
212 }
213
214 /* *INDENT-OFF* */
215 VLIB_REGISTER_NODE (ip4_qos_record_node) = {
216   .function = ip4_qos_record,
217   .name = "ip4-qos-record",
218   .vector_size = sizeof (u32),
219   .format_trace = format_qos_record_trace,
220   .type = VLIB_NODE_TYPE_INTERNAL,
221
222   .n_errors = 0,
223   .n_next_nodes = 1,
224
225   .next_nodes = {
226     [0] = "ip4-drop",
227   },
228 };
229
230 VLIB_NODE_FUNCTION_MULTIARCH (ip4_qos_record_node, ip4_qos_record);
231
232 VNET_FEATURE_INIT (ip4_qos_record_node, static) = {
233     .arc_name = "ip4-unicast",
234     .node_name = "ip4-qos-record",
235 };
236
237 VLIB_REGISTER_NODE (ip6_qos_record_node) = {
238   .function = ip6_qos_record,
239   .name = "ip6-qos-record",
240   .vector_size = sizeof (u32),
241   .format_trace = format_qos_record_trace,
242   .type = VLIB_NODE_TYPE_INTERNAL,
243
244   .n_errors = 0,
245   .n_next_nodes = 1,
246
247   .next_nodes = {
248     [0] = "ip6-drop",
249   },
250 };
251
252 VLIB_NODE_FUNCTION_MULTIARCH (ip6_qos_record_node, ip6_qos_record);
253
254 VNET_FEATURE_INIT (ip6_qos_record_node, static) = {
255     .arc_name = "ip6-unicast",
256     .node_name = "ip6-qos-record",
257 };
258 /* *INDENT-ON* */
259
260
261 static clib_error_t *
262 qos_record_cli (vlib_main_t * vm,
263                 unformat_input_t * input, vlib_cli_command_t * cmd)
264 {
265   vnet_main_t *vnm = vnet_get_main ();
266   u32 sw_if_index, qs;
267   u8 enable;
268
269   qs = 0xff;
270   enable = 1;
271   sw_if_index = ~0;
272
273   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
274     {
275       if (unformat (input, "%U", unformat_vnet_sw_interface,
276                     vnm, &sw_if_index))
277         ;
278       else if (unformat (input, "%U", unformat_qos_source, &qs))
279         ;
280       else if (unformat (input, "enable"))
281         enable = 1;
282       else if (unformat (input, "disable"))
283         enable = 0;
284       else
285         break;
286     }
287
288   if (~0 == sw_if_index)
289     return clib_error_return (0, "interface must be specified");
290   if (0xff == qs)
291     return clib_error_return (0, "input location must be specified");
292
293   if (enable)
294     qos_record_enable (sw_if_index, qs);
295   else
296     qos_record_disable (sw_if_index, qs);
297
298   return (NULL);
299 }
300
301 /*?
302  * Enable QoS bit recording on an interface using the packet's input DSCP bits
303  * Which input QoS bits to use are either; IP, MPLS or VLAN. If more than
304  * one protocol is chosen (which is foolish) the higer layers override the
305  * lower.
306  *
307  * @cliexpar
308  * @cliexcmd{qos record ip GigEthernet0/1/0}
309  ?*/
310 /* *INDENT-OFF* */
311 VLIB_CLI_COMMAND (qos_record_command, static) = {
312   .path = "qos record",
313   .short_help = "qos record <record-source> <INTERFACE> [disable]",
314   .function = qos_record_cli,
315   .is_mp_safe = 1,
316 };
317 /* *INDENT-ON* */
318
319
320 /*
321  * fd.io coding-style-patch-verification: ON
322  *
323  * Local Variables:
324  * eval: (c-set-style "gnu")
325  * End:
326  */