QoS: fix always recoding as IP
[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,
139                    qos_source_t qos_src, dpo_proto_t dproto, int is_l2)
140 {
141   u32 n_left_from, *from, *to_next, next_index;
142
143   next_index = 0;
144   n_left_from = frame->n_vectors;
145   from = vlib_frame_vector_args (frame);
146
147   while (n_left_from > 0)
148     {
149       u32 n_left_to_next;
150
151       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
152
153       while (n_left_from > 0 && n_left_to_next > 0)
154         {
155           ip4_header_t *ip4_0;
156           ip6_header_t *ip6_0;
157           vlib_buffer_t *b0;
158           u32 next0, bi0;
159           qos_bits_t qos0;
160           u8 l2_len;
161
162           next0 = 0;
163           bi0 = from[0];
164           to_next[0] = bi0;
165           from += 1;
166           to_next += 1;
167           n_left_from -= 1;
168           n_left_to_next -= 1;
169
170           b0 = vlib_get_buffer (vm, bi0);
171
172           if (is_l2)
173             {
174               l2_len = vnet_buffer (b0)->l2.l2_len;
175               u8 *l3h;
176               u16 ethertype;
177
178               vlib_buffer_advance (b0, l2_len);
179
180               l3h = vlib_buffer_get_current (b0);
181               ethertype = clib_net_to_host_u16 (*(u16 *) (l3h - 2));
182
183               if (ethertype == ETHERNET_TYPE_IP4)
184                 dproto = DPO_PROTO_IP4;
185               else if (ethertype == ETHERNET_TYPE_IP6)
186                 dproto = DPO_PROTO_IP6;
187               else if (ethertype == ETHERNET_TYPE_MPLS)
188                 dproto = DPO_PROTO_MPLS;
189               else
190                 goto non_ip;
191             }
192
193           if (DPO_PROTO_IP6 == dproto)
194             {
195               ip6_0 = vlib_buffer_get_current (b0);
196               qos0 = ip6_traffic_class_network_order (ip6_0);
197             }
198           else if (DPO_PROTO_IP4 == dproto)
199             {
200               ip4_0 = vlib_buffer_get_current (b0);
201               qos0 = ip4_0->tos;
202             }
203           else if (DPO_PROTO_ETHERNET == dproto)
204             {
205               ethernet_vlan_header_t *vlan0;
206
207               vlan0 = (vlib_buffer_get_current (b0) -
208                        sizeof (ethernet_vlan_header_t));
209
210               qos0 = ethernet_vlan_header_get_priority_net_order (vlan0);
211             }
212           else if (DPO_PROTO_MPLS)
213             {
214               mpls_unicast_header_t *mh;
215
216               mh = vlib_buffer_get_current (b0);
217               qos0 = vnet_mpls_uc_get_exp (mh->label_exp_s_ttl);
218             }
219
220           vnet_buffer2 (b0)->qos.bits = qos0;
221           vnet_buffer2 (b0)->qos.source = qos_src;
222           b0->flags |= VNET_BUFFER_F_QOS_DATA_VALID;
223
224           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
225                              (b0->flags & VLIB_BUFFER_IS_TRACED)))
226             {
227               qos_record_trace_t *t =
228                 vlib_add_trace (vm, node, b0, sizeof (*t));
229               t->bits = qos0;
230             }
231
232         non_ip:
233           if (is_l2)
234             {
235               vlib_buffer_advance (b0, -l2_len);
236               next0 = vnet_l2_feature_next (b0,
237                                             l2_qos_input_next[qos_src],
238                                             L2INPUT_FEAT_L2_IP_QOS_RECORD);
239             }
240           else
241             vnet_feature_next (&next0, b0);
242
243           /* verify speculative enqueue, maybe switch current next frame */
244           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
245                                            to_next, n_left_to_next,
246                                            bi0, next0);
247         }
248
249       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
250     }
251
252   return frame->n_vectors;
253 }
254
255 /* packet trace format function */
256 static u8 *
257 format_qos_record_trace (u8 * s, va_list * args)
258 {
259   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
260   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
261   qos_record_trace_t *t = va_arg (*args, qos_record_trace_t *);
262
263   s = format (s, "qos:%d", t->bits);
264
265   return s;
266 }
267
268 static inline uword
269 ip4_qos_record (vlib_main_t * vm, vlib_node_runtime_t * node,
270                 vlib_frame_t * frame)
271 {
272   return (qos_record_inline (vm, node, frame, QOS_SOURCE_IP,
273                              DPO_PROTO_IP4, 0));
274 }
275
276 static inline uword
277 ip6_qos_record (vlib_main_t * vm, vlib_node_runtime_t * node,
278                 vlib_frame_t * frame)
279 {
280   return (qos_record_inline (vm, node, frame, QOS_SOURCE_IP,
281                              DPO_PROTO_IP6, 0));
282 }
283
284 static inline uword
285 mpls_qos_record (vlib_main_t * vm, vlib_node_runtime_t * node,
286                  vlib_frame_t * frame)
287 {
288   return (qos_record_inline (vm, node, frame, QOS_SOURCE_MPLS,
289                              DPO_PROTO_MPLS, 0));
290 }
291
292 static inline uword
293 vlan_ip4_qos_record (vlib_main_t * vm, vlib_node_runtime_t * node,
294                      vlib_frame_t * frame)
295 {
296   return (qos_record_inline (vm, node, frame, QOS_SOURCE_VLAN,
297                              DPO_PROTO_ETHERNET, 0));
298 }
299
300 static inline uword
301 vlan_ip6_qos_record (vlib_main_t * vm, vlib_node_runtime_t * node,
302                      vlib_frame_t * frame)
303 {
304   return (qos_record_inline (vm, node, frame, QOS_SOURCE_VLAN,
305                              DPO_PROTO_ETHERNET, 0));
306 }
307
308 static inline uword
309 vlan_mpls_qos_record (vlib_main_t * vm, vlib_node_runtime_t * node,
310                       vlib_frame_t * frame)
311 {
312   return (qos_record_inline (vm, node, frame, QOS_SOURCE_VLAN,
313                              DPO_PROTO_ETHERNET, 0));
314 }
315
316 static inline uword
317 l2_ip_qos_record (vlib_main_t * vm, vlib_node_runtime_t * node,
318                   vlib_frame_t * frame)
319 {
320   return (qos_record_inline (vm, node, frame, QOS_SOURCE_VLAN, 0, 1));
321 }
322
323 /* *INDENT-OFF* */
324 VLIB_REGISTER_NODE (ip4_qos_record_node) = {
325   .function = ip4_qos_record,
326   .name = "ip4-qos-record",
327   .vector_size = sizeof (u32),
328   .format_trace = format_qos_record_trace,
329   .type = VLIB_NODE_TYPE_INTERNAL,
330
331   .n_errors = 0,
332   .n_next_nodes = 1,
333
334   .next_nodes = {
335     [0] = "ip4-drop",
336   },
337 };
338
339 VLIB_NODE_FUNCTION_MULTIARCH (ip4_qos_record_node, ip4_qos_record);
340
341 VNET_FEATURE_INIT (ip4_qos_record_node, static) = {
342     .arc_name = "ip4-unicast",
343     .node_name = "ip4-qos-record",
344 };
345 VNET_FEATURE_INIT (ip4m_qos_record_node, static) = {
346     .arc_name = "ip4-multicast",
347     .node_name = "ip4-qos-record",
348 };
349
350 VLIB_REGISTER_NODE (ip6_qos_record_node) = {
351   .function = ip6_qos_record,
352   .name = "ip6-qos-record",
353   .vector_size = sizeof (u32),
354   .format_trace = format_qos_record_trace,
355   .type = VLIB_NODE_TYPE_INTERNAL,
356
357   .n_errors = 0,
358   .n_next_nodes = 1,
359
360   .next_nodes = {
361     [0] = "ip6-drop",
362   },
363 };
364
365 VLIB_NODE_FUNCTION_MULTIARCH (ip6_qos_record_node, ip6_qos_record);
366
367 VNET_FEATURE_INIT (ip6_qos_record_node, static) = {
368     .arc_name = "ip6-unicast",
369     .node_name = "ip6-qos-record",
370 };
371 VNET_FEATURE_INIT (ip6m_qos_record_node, static) = {
372     .arc_name = "ip6-multicast",
373     .node_name = "ip6-qos-record",
374 };
375
376 VLIB_REGISTER_NODE (mpls_qos_record_node) = {
377   .function = mpls_qos_record,
378   .name = "mpls-qos-record",
379   .vector_size = sizeof (u32),
380   .format_trace = format_qos_record_trace,
381   .type = VLIB_NODE_TYPE_INTERNAL,
382
383   .n_errors = 0,
384   .n_next_nodes = 1,
385
386   .next_nodes = {
387     [0] = "mpls-drop",
388   },
389 };
390
391 VLIB_NODE_FUNCTION_MULTIARCH (mpls_qos_record_node, mpls_qos_record);
392
393 VNET_FEATURE_INIT (mpls_qos_record_node, static) = {
394     .arc_name = "mpls-input",
395     .node_name = "mpls-qos-record",
396 };
397
398 VLIB_REGISTER_NODE (vlan_mpls_qos_record_node) = {
399   .function = vlan_mpls_qos_record,
400   .name = "vlan-mpls-qos-record",
401   .vector_size = sizeof (u32),
402   .format_trace = format_qos_record_trace,
403   .type = VLIB_NODE_TYPE_INTERNAL,
404
405   .n_errors = 0,
406   .n_next_nodes = 1,
407
408   .next_nodes = {
409     [0] = "mpls-drop",
410   },
411 };
412
413 VLIB_NODE_FUNCTION_MULTIARCH (vlan_mpls_qos_record_node, vlan_mpls_qos_record);
414
415 VNET_FEATURE_INIT (vlan_mpls_qos_record_node, static) = {
416     .arc_name = "mpls-input",
417     .node_name = "vlan-mpls-qos-record",
418     .runs_before = VNET_FEATURES ("mpls-qos-record"),
419 };
420
421 VLIB_REGISTER_NODE (vlan_ip4_qos_record_node) = {
422   .function = vlan_ip4_qos_record,
423   .name = "vlan-ip4-qos-record",
424   .vector_size = sizeof (u32),
425   .format_trace = format_qos_record_trace,
426   .type = VLIB_NODE_TYPE_INTERNAL,
427
428   .n_errors = 0,
429   .n_next_nodes = 1,
430
431   .next_nodes = {
432     [0] = "ip4-drop",
433   },
434 };
435
436 VLIB_NODE_FUNCTION_MULTIARCH (vlan_ip4_qos_record_node, vlan_ip4_qos_record);
437
438 VNET_FEATURE_INIT (vlan_ip4_qos_record_node, static) = {
439     .arc_name = "ip4-unicast",
440     .node_name = "vlan-ip4-qos-record",
441     .runs_before = VNET_FEATURES ("ip4-qos-record"),
442 };
443 VNET_FEATURE_INIT (vlan_ip4m_qos_record_node, static) = {
444     .arc_name = "ip4-multicast",
445     .node_name = "vlan-ip4-qos-record",
446     .runs_before = VNET_FEATURES ("ip4-qos-record"),
447 };
448
449 VLIB_REGISTER_NODE (vlan_ip6_qos_record_node) = {
450   .function = vlan_ip6_qos_record,
451   .name = "vlan-ip6-qos-record",
452   .vector_size = sizeof (u32),
453   .format_trace = format_qos_record_trace,
454   .type = VLIB_NODE_TYPE_INTERNAL,
455
456   .n_errors = 0,
457   .n_next_nodes = 1,
458
459   .next_nodes = {
460     [0] = "ip6-drop",
461   },
462 };
463
464 VLIB_NODE_FUNCTION_MULTIARCH (vlan_ip6_qos_record_node, vlan_ip6_qos_record);
465
466 VNET_FEATURE_INIT (vlan_ip6_qos_record_node, static) = {
467     .arc_name = "ip6-unicast",
468     .node_name = "vlan-ip6-qos-record",
469     .runs_before = VNET_FEATURES ("ip6-qos-record"),
470 };
471 VNET_FEATURE_INIT (vlan_ip6m_qos_record_node, static) = {
472     .arc_name = "ip6-multicast",
473     .node_name = "vlan-ip6-qos-record",
474     .runs_before = VNET_FEATURES ("ip6-qos-record"),
475 };
476
477 VLIB_REGISTER_NODE (l2_ip_qos_record_node, static) = {
478   .function = l2_ip_qos_record,
479   .name = "l2-ip-qos-record",
480   .vector_size = sizeof (u32),
481   .format_trace = format_qos_record_trace,
482   .type = VLIB_NODE_TYPE_INTERNAL,
483
484   .n_errors = 0,
485   .n_next_nodes = 1,
486
487   /* Consider adding error "no IP after L2, no recording" */
488   .next_nodes = {
489     [0] = "error-drop",
490   },
491 };
492
493 VLIB_NODE_FUNCTION_MULTIARCH (l2_ip_qos_record_node, l2_ip_qos_record);
494
495 /* *INDENT-ON* */
496
497 clib_error_t *
498 l2_ip_qos_init (vlib_main_t * vm)
499 {
500   qos_source_t qs;
501
502   /* Initialize the feature next-node indexes */
503   FOR_EACH_QOS_SOURCE (qs)
504     feat_bitmap_init_next_nodes (vm,
505                                  l2_ip_qos_record_node.index,
506                                  L2INPUT_N_FEAT,
507                                  l2input_get_feat_names (),
508                                  l2_qos_input_next[qs]);
509   return 0;
510 }
511
512 VLIB_INIT_FUNCTION (l2_ip_qos_init);
513
514 static clib_error_t *
515 qos_record_cli (vlib_main_t * vm,
516                 unformat_input_t * input, vlib_cli_command_t * cmd)
517 {
518   vnet_main_t *vnm = vnet_get_main ();
519   u32 sw_if_index, qs;
520   u8 enable;
521
522   qs = 0xff;
523   enable = 1;
524   sw_if_index = ~0;
525
526   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
527     {
528       if (unformat (input, "%U", unformat_vnet_sw_interface,
529                     vnm, &sw_if_index))
530         ;
531       else if (unformat (input, "%U", unformat_qos_source, &qs))
532         ;
533       else if (unformat (input, "enable"))
534         enable = 1;
535       else if (unformat (input, "disable"))
536         enable = 0;
537       else
538         break;
539     }
540
541   if (~0 == sw_if_index)
542     return clib_error_return (0, "interface must be specified");
543   if (0xff == qs)
544     return clib_error_return (0, "input location must be specified");
545
546   if (enable)
547     qos_record_enable (sw_if_index, qs);
548   else
549     qos_record_disable (sw_if_index, qs);
550
551   return (NULL);
552 }
553
554 /*?
555  * Enable QoS bit recording on an interface using the packet's input DSCP bits
556  * Which input QoS bits to use are either; IP, MPLS or VLAN. If more than
557  * one protocol is chosen (which is foolish) the higher layers override the
558  * lower.
559  *
560  * @cliexpar
561  * @cliexcmd{qos record ip GigEthernet0/1/0}
562  ?*/
563 /* *INDENT-OFF* */
564 VLIB_CLI_COMMAND (qos_record_command, static) = {
565   .path = "qos record",
566   .short_help = "qos record <record-source> <INTERFACE> [disable]",
567   .function = qos_record_cli,
568   .is_mp_safe = 1,
569 };
570 /* *INDENT-ON* */
571
572
573 /*
574  * fd.io coding-style-patch-verification: ON
575  *
576  * Local Variables:
577  * eval: (c-set-style "gnu")
578  * End:
579  */