Trivial: Clean up some typos.
[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 #include <vnet/l2/l2_input.h>
22 #include <vnet/l2/feat_bitmap.h>
23
24 /**
25  * Per-interface, per-protocol vector of feature on/off configurations
26  */
27 static u8 *qos_record_configs[QOS_N_SOURCES];
28 static u32 l2_qos_input_next[QOS_N_SOURCES][32];
29
30 static void
31 qos_record_feature_config (u32 sw_if_index,
32                            qos_source_t input_source, u8 enable)
33 {
34   switch (input_source)
35     {
36     case QOS_SOURCE_IP:
37       vnet_feature_enable_disable ("ip6-unicast", "ip6-qos-record",
38                                    sw_if_index, enable, NULL, 0);
39       vnet_feature_enable_disable ("ip6-multicast", "ip6-qos-record",
40                                    sw_if_index, enable, NULL, 0);
41       vnet_feature_enable_disable ("ip4-unicast", "ip4-qos-record",
42                                    sw_if_index, enable, NULL, 0);
43       vnet_feature_enable_disable ("ip4-multicast", "ip4-qos-record",
44                                    sw_if_index, enable, NULL, 0);
45       l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_L2_IP_QOS_RECORD,
46                                   enable);
47       break;
48     case QOS_SOURCE_MPLS:
49       vnet_feature_enable_disable ("mpls-input", "mpls-qos-record",
50                                    sw_if_index, enable, NULL, 0);
51       break;
52     case QOS_SOURCE_VLAN:
53       vnet_feature_enable_disable ("ip6-unicast", "vlan-ip6-qos-record",
54                                    sw_if_index, enable, NULL, 0);
55       vnet_feature_enable_disable ("ip6-multicast", "vlan-ip6-qos-record",
56                                    sw_if_index, enable, NULL, 0);
57       vnet_feature_enable_disable ("ip4-unicast", "vlan-ip4-qos-record",
58                                    sw_if_index, enable, NULL, 0);
59       vnet_feature_enable_disable ("ip4-multicast", "vlan-ip4-qos-record",
60                                    sw_if_index, enable, NULL, 0);
61       vnet_feature_enable_disable ("mpls-input", "vlan-mpls-qos-record",
62                                    sw_if_index, enable, NULL, 0);
63       break;
64     case QOS_SOURCE_EXT:
65       /* not a valid option for recording */
66       break;
67     }
68 }
69
70 int
71 qos_record_enable (u32 sw_if_index, qos_source_t input_source)
72 {
73   vec_validate (qos_record_configs[input_source], sw_if_index);
74
75   if (0 == qos_record_configs[input_source][sw_if_index])
76     {
77       qos_record_feature_config (sw_if_index, input_source, 1);
78     }
79
80   qos_record_configs[input_source][sw_if_index]++;
81   return (0);
82 }
83
84 int
85 qos_record_disable (u32 sw_if_index, qos_source_t input_source)
86 {
87   if (vec_len (qos_record_configs[input_source]) <= sw_if_index)
88     return VNET_API_ERROR_NO_MATCHING_INTERFACE;
89
90   if (0 == qos_record_configs[input_source][sw_if_index])
91     return VNET_API_ERROR_VALUE_EXIST;
92
93   qos_record_configs[input_source][sw_if_index]--;
94
95   if (0 == qos_record_configs[input_source][sw_if_index])
96     {
97       qos_record_feature_config (sw_if_index, input_source, 0);
98     }
99
100   return (0);
101 }
102
103 /*
104  * Disable recording feature for all protocols when the interface
105  * is deleted
106  */
107 static clib_error_t *
108 qos_record_ip_interface_add_del (vnet_main_t * vnm,
109                                  u32 sw_if_index, u32 is_add)
110 {
111   if (!is_add)
112     {
113       qos_source_t qs;
114
115       FOR_EACH_QOS_SOURCE (qs)
116       {
117         while (qos_record_disable (sw_if_index, qs) == 0);
118       }
119     }
120
121   return (NULL);
122 }
123
124 VNET_SW_INTERFACE_ADD_DEL_FUNCTION (qos_record_ip_interface_add_del);
125
126 /**
127  * per-packet trace data
128  */
129 typedef struct qos_record_trace_t_
130 {
131   /* per-pkt trace data */
132   qos_bits_t bits;
133 } qos_record_trace_t;
134
135 static inline uword
136 qos_record_inline (vlib_main_t * vm,
137                    vlib_node_runtime_t * node,
138                    vlib_frame_t * frame, dpo_proto_t dproto, int is_l2)
139 {
140   u32 n_left_from, *from, *to_next, next_index;
141
142   next_index = 0;
143   n_left_from = frame->n_vectors;
144   from = vlib_frame_vector_args (frame);
145
146   while (n_left_from > 0)
147     {
148       u32 n_left_to_next;
149
150       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
151
152       while (n_left_from > 0 && n_left_to_next > 0)
153         {
154           ip4_header_t *ip4_0;
155           ip6_header_t *ip6_0;
156           vlib_buffer_t *b0;
157           u32 next0, bi0;
158           qos_bits_t qos0;
159           u8 l2_len;
160
161           next0 = 0;
162           bi0 = from[0];
163           to_next[0] = bi0;
164           from += 1;
165           to_next += 1;
166           n_left_from -= 1;
167           n_left_to_next -= 1;
168
169           b0 = vlib_get_buffer (vm, bi0);
170
171           if (is_l2)
172             {
173               l2_len = vnet_buffer (b0)->l2.l2_len;
174               u8 *l3h;
175               u16 ethertype;
176
177               vlib_buffer_advance (b0, l2_len);
178
179               l3h = vlib_buffer_get_current (b0);
180               ethertype = clib_net_to_host_u16 (*(u16 *) (l3h - 2));
181
182               if (ethertype == ETHERNET_TYPE_IP4)
183                 dproto = DPO_PROTO_IP4;
184               else if (ethertype == ETHERNET_TYPE_IP6)
185                 dproto = DPO_PROTO_IP6;
186               else if (ethertype == ETHERNET_TYPE_MPLS)
187                 dproto = DPO_PROTO_MPLS;
188               else
189                 goto non_ip;
190             }
191
192           if (DPO_PROTO_IP6 == dproto)
193             {
194               ip6_0 = vlib_buffer_get_current (b0);
195               qos0 = ip6_traffic_class_network_order (ip6_0);
196             }
197           else if (DPO_PROTO_IP4 == dproto)
198             {
199               ip4_0 = vlib_buffer_get_current (b0);
200               qos0 = ip4_0->tos;
201             }
202           else if (DPO_PROTO_ETHERNET == dproto)
203             {
204               ethernet_vlan_header_t *vlan0;
205
206               vlan0 = (vlib_buffer_get_current (b0) -
207                        sizeof (ethernet_vlan_header_t));
208
209               qos0 = ethernet_vlan_header_get_priority_net_order (vlan0);
210             }
211           else if (DPO_PROTO_MPLS)
212             {
213               mpls_unicast_header_t *mh;
214
215               mh = vlib_buffer_get_current (b0);
216               qos0 = vnet_mpls_uc_get_exp (mh->label_exp_s_ttl);
217             }
218
219           vnet_buffer2 (b0)->qos.bits = qos0;
220           vnet_buffer2 (b0)->qos.source = QOS_SOURCE_IP;
221           b0->flags |= VNET_BUFFER_F_QOS_DATA_VALID;
222
223           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
224                              (b0->flags & VLIB_BUFFER_IS_TRACED)))
225             {
226               qos_record_trace_t *t =
227                 vlib_add_trace (vm, node, b0, sizeof (*t));
228               t->bits = qos0;
229             }
230
231         non_ip:
232           if (is_l2)
233             {
234               vlib_buffer_advance (b0, -l2_len);
235               next0 = vnet_l2_feature_next (b0,
236                                             l2_qos_input_next[QOS_SOURCE_IP],
237                                             L2INPUT_FEAT_L2_IP_QOS_RECORD);
238             }
239           else
240             vnet_feature_next (&next0, b0);
241
242           /* verify speculative enqueue, maybe switch current next frame */
243           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
244                                            to_next, n_left_to_next,
245                                            bi0, next0);
246         }
247
248       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
249     }
250
251   return frame->n_vectors;
252 }
253
254 /* packet trace format function */
255 static u8 *
256 format_qos_record_trace (u8 * s, va_list * args)
257 {
258   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
259   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
260   qos_record_trace_t *t = va_arg (*args, qos_record_trace_t *);
261
262   s = format (s, "qos:%d", t->bits);
263
264   return s;
265 }
266
267 static inline uword
268 ip4_qos_record (vlib_main_t * vm, vlib_node_runtime_t * node,
269                 vlib_frame_t * frame)
270 {
271   return (qos_record_inline (vm, node, frame, DPO_PROTO_IP4, 0));
272 }
273
274 static inline uword
275 ip6_qos_record (vlib_main_t * vm, vlib_node_runtime_t * node,
276                 vlib_frame_t * frame)
277 {
278   return (qos_record_inline (vm, node, frame, DPO_PROTO_IP6, 0));
279 }
280
281 static inline uword
282 mpls_qos_record (vlib_main_t * vm, vlib_node_runtime_t * node,
283                  vlib_frame_t * frame)
284 {
285   return (qos_record_inline (vm, node, frame, DPO_PROTO_MPLS, 0));
286 }
287
288 static inline uword
289 vlan_ip4_qos_record (vlib_main_t * vm, vlib_node_runtime_t * node,
290                      vlib_frame_t * frame)
291 {
292   return (qos_record_inline (vm, node, frame, DPO_PROTO_ETHERNET, 0));
293 }
294
295 static inline uword
296 vlan_ip6_qos_record (vlib_main_t * vm, vlib_node_runtime_t * node,
297                      vlib_frame_t * frame)
298 {
299   return (qos_record_inline (vm, node, frame, DPO_PROTO_ETHERNET, 0));
300 }
301
302 static inline uword
303 vlan_mpls_qos_record (vlib_main_t * vm, vlib_node_runtime_t * node,
304                       vlib_frame_t * frame)
305 {
306   return (qos_record_inline (vm, node, frame, DPO_PROTO_ETHERNET, 0));
307 }
308
309 static inline uword
310 l2_ip_qos_record (vlib_main_t * vm, vlib_node_runtime_t * node,
311                   vlib_frame_t * frame)
312 {
313   return (qos_record_inline (vm, node, frame, 0, 1));
314 }
315
316 /* *INDENT-OFF* */
317 VLIB_REGISTER_NODE (ip4_qos_record_node) = {
318   .function = ip4_qos_record,
319   .name = "ip4-qos-record",
320   .vector_size = sizeof (u32),
321   .format_trace = format_qos_record_trace,
322   .type = VLIB_NODE_TYPE_INTERNAL,
323
324   .n_errors = 0,
325   .n_next_nodes = 1,
326
327   .next_nodes = {
328     [0] = "ip4-drop",
329   },
330 };
331
332 VLIB_NODE_FUNCTION_MULTIARCH (ip4_qos_record_node, ip4_qos_record);
333
334 VNET_FEATURE_INIT (ip4_qos_record_node, static) = {
335     .arc_name = "ip4-unicast",
336     .node_name = "ip4-qos-record",
337 };
338 VNET_FEATURE_INIT (ip4m_qos_record_node, static) = {
339     .arc_name = "ip4-multicast",
340     .node_name = "ip4-qos-record",
341 };
342
343 VLIB_REGISTER_NODE (ip6_qos_record_node) = {
344   .function = ip6_qos_record,
345   .name = "ip6-qos-record",
346   .vector_size = sizeof (u32),
347   .format_trace = format_qos_record_trace,
348   .type = VLIB_NODE_TYPE_INTERNAL,
349
350   .n_errors = 0,
351   .n_next_nodes = 1,
352
353   .next_nodes = {
354     [0] = "ip6-drop",
355   },
356 };
357
358 VLIB_NODE_FUNCTION_MULTIARCH (ip6_qos_record_node, ip6_qos_record);
359
360 VNET_FEATURE_INIT (ip6_qos_record_node, static) = {
361     .arc_name = "ip6-unicast",
362     .node_name = "ip6-qos-record",
363 };
364 VNET_FEATURE_INIT (ip6m_qos_record_node, static) = {
365     .arc_name = "ip6-multicast",
366     .node_name = "ip6-qos-record",
367 };
368
369 VLIB_REGISTER_NODE (mpls_qos_record_node) = {
370   .function = mpls_qos_record,
371   .name = "mpls-qos-record",
372   .vector_size = sizeof (u32),
373   .format_trace = format_qos_record_trace,
374   .type = VLIB_NODE_TYPE_INTERNAL,
375
376   .n_errors = 0,
377   .n_next_nodes = 1,
378
379   .next_nodes = {
380     [0] = "mpls-drop",
381   },
382 };
383
384 VLIB_NODE_FUNCTION_MULTIARCH (mpls_qos_record_node, mpls_qos_record);
385
386 VNET_FEATURE_INIT (mpls_qos_record_node, static) = {
387     .arc_name = "mpls-input",
388     .node_name = "mpls-qos-record",
389 };
390
391 VLIB_REGISTER_NODE (vlan_mpls_qos_record_node) = {
392   .function = vlan_mpls_qos_record,
393   .name = "vlan-mpls-qos-record",
394   .vector_size = sizeof (u32),
395   .format_trace = format_qos_record_trace,
396   .type = VLIB_NODE_TYPE_INTERNAL,
397
398   .n_errors = 0,
399   .n_next_nodes = 1,
400
401   .next_nodes = {
402     [0] = "mpls-drop",
403   },
404 };
405
406 VLIB_NODE_FUNCTION_MULTIARCH (vlan_mpls_qos_record_node, vlan_mpls_qos_record);
407
408 VNET_FEATURE_INIT (vlan_mpls_qos_record_node, static) = {
409     .arc_name = "mpls-input",
410     .node_name = "vlan-mpls-qos-record",
411     .runs_before = VNET_FEATURES ("mpls-qos-record"),
412 };
413
414 VLIB_REGISTER_NODE (vlan_ip4_qos_record_node) = {
415   .function = vlan_ip4_qos_record,
416   .name = "vlan-ip4-qos-record",
417   .vector_size = sizeof (u32),
418   .format_trace = format_qos_record_trace,
419   .type = VLIB_NODE_TYPE_INTERNAL,
420
421   .n_errors = 0,
422   .n_next_nodes = 1,
423
424   .next_nodes = {
425     [0] = "ip4-drop",
426   },
427 };
428
429 VLIB_NODE_FUNCTION_MULTIARCH (vlan_ip4_qos_record_node, vlan_ip4_qos_record);
430
431 VNET_FEATURE_INIT (vlan_ip4_qos_record_node, static) = {
432     .arc_name = "ip4-unicast",
433     .node_name = "vlan-ip4-qos-record",
434     .runs_before = VNET_FEATURES ("ip4-qos-record"),
435 };
436 VNET_FEATURE_INIT (vlan_ip4m_qos_record_node, static) = {
437     .arc_name = "ip4-multicast",
438     .node_name = "vlan-ip4-qos-record",
439     .runs_before = VNET_FEATURES ("ip4-qos-record"),
440 };
441
442 VLIB_REGISTER_NODE (vlan_ip6_qos_record_node) = {
443   .function = vlan_ip6_qos_record,
444   .name = "vlan-ip6-qos-record",
445   .vector_size = sizeof (u32),
446   .format_trace = format_qos_record_trace,
447   .type = VLIB_NODE_TYPE_INTERNAL,
448
449   .n_errors = 0,
450   .n_next_nodes = 1,
451
452   .next_nodes = {
453     [0] = "ip6-drop",
454   },
455 };
456
457 VLIB_NODE_FUNCTION_MULTIARCH (vlan_ip6_qos_record_node, vlan_ip6_qos_record);
458
459 VNET_FEATURE_INIT (vlan_ip6_qos_record_node, static) = {
460     .arc_name = "ip6-unicast",
461     .node_name = "vlan-ip6-qos-record",
462     .runs_before = VNET_FEATURES ("ip6-qos-record"),
463 };
464 VNET_FEATURE_INIT (vlan_ip6m_qos_record_node, static) = {
465     .arc_name = "ip6-multicast",
466     .node_name = "vlan-ip6-qos-record",
467     .runs_before = VNET_FEATURES ("ip6-qos-record"),
468 };
469
470 VLIB_REGISTER_NODE (l2_ip_qos_record_node, static) = {
471   .function = l2_ip_qos_record,
472   .name = "l2-ip-qos-record",
473   .vector_size = sizeof (u32),
474   .format_trace = format_qos_record_trace,
475   .type = VLIB_NODE_TYPE_INTERNAL,
476
477   .n_errors = 0,
478   .n_next_nodes = 1,
479
480   /* Consider adding error "no IP after L2, no recording" */
481   .next_nodes = {
482     [0] = "error-drop",
483   },
484 };
485
486 VLIB_NODE_FUNCTION_MULTIARCH (l2_ip_qos_record_node, l2_ip_qos_record);
487
488 /* *INDENT-ON* */
489
490 clib_error_t *
491 l2_ip_qos_init (vlib_main_t * vm)
492 {
493   /* Initialize the feature next-node indexes */
494   feat_bitmap_init_next_nodes (vm,
495                                l2_ip_qos_record_node.index,
496                                L2INPUT_N_FEAT,
497                                l2input_get_feat_names (),
498                                l2_qos_input_next[QOS_SOURCE_IP]);
499   return 0;
500 }
501
502 VLIB_INIT_FUNCTION (l2_ip_qos_init);
503
504 static clib_error_t *
505 qos_record_cli (vlib_main_t * vm,
506                 unformat_input_t * input, vlib_cli_command_t * cmd)
507 {
508   vnet_main_t *vnm = vnet_get_main ();
509   u32 sw_if_index, qs;
510   u8 enable;
511
512   qs = 0xff;
513   enable = 1;
514   sw_if_index = ~0;
515
516   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
517     {
518       if (unformat (input, "%U", unformat_vnet_sw_interface,
519                     vnm, &sw_if_index))
520         ;
521       else if (unformat (input, "%U", unformat_qos_source, &qs))
522         ;
523       else if (unformat (input, "enable"))
524         enable = 1;
525       else if (unformat (input, "disable"))
526         enable = 0;
527       else
528         break;
529     }
530
531   if (~0 == sw_if_index)
532     return clib_error_return (0, "interface must be specified");
533   if (0xff == qs)
534     return clib_error_return (0, "input location must be specified");
535
536   if (enable)
537     qos_record_enable (sw_if_index, qs);
538   else
539     qos_record_disable (sw_if_index, qs);
540
541   return (NULL);
542 }
543
544 /*?
545  * Enable QoS bit recording on an interface using the packet's input DSCP bits
546  * Which input QoS bits to use are either; IP, MPLS or VLAN. If more than
547  * one protocol is chosen (which is foolish) the higher layers override the
548  * lower.
549  *
550  * @cliexpar
551  * @cliexcmd{qos record ip GigEthernet0/1/0}
552  ?*/
553 /* *INDENT-OFF* */
554 VLIB_CLI_COMMAND (qos_record_command, static) = {
555   .path = "qos record",
556   .short_help = "qos record <record-source> <INTERFACE> [disable]",
557   .function = qos_record_cli,
558   .is_mp_safe = 1,
559 };
560 /* *INDENT-ON* */
561
562
563 /*
564  * fd.io coding-style-patch-verification: ON
565  *
566  * Local Variables:
567  * eval: (c-set-style "gnu")
568  * End:
569  */