FIB: encode the label stack in the FIB path during table dump
[vpp.git] / src / vnet / qos / qos_mark.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/ip/ip.h>
17 #include <vnet/feature/feature.h>
18 #include <vnet/qos/qos_egress_map.h>
19 #include <vnet/qos/qos_mark.h>
20
21 /**
22  * per-interface vector of which MAP is used by which interface
23  * for each output source
24  */
25 index_t *qos_mark_configs[QOS_N_SOURCES];
26
27 void
28 qos_mark_ip_enable_disable (u32 sw_if_index, u8 enable)
29 {
30   vnet_feature_enable_disable ("ip6-output", "ip6-qos-mark",
31                                sw_if_index, enable, NULL, 0);
32   vnet_feature_enable_disable ("ip4-output", "ip4-qos-mark",
33                                sw_if_index, enable, NULL, 0);
34 }
35
36 void
37 qos_mark_vlan_enable_disable (u32 sw_if_index, u8 enable)
38 {
39   /*
40    * one cannot run a feature on a sub-interface, so we need
41    * to enable a feature on all the L3 output paths
42    */
43   vnet_feature_enable_disable ("ip6-output", "vlan-ip6-qos-mark",
44                                sw_if_index, enable, NULL, 0);
45   vnet_feature_enable_disable ("ip4-output", "vlan-ip4-qos-mark",
46                                sw_if_index, enable, NULL, 0);
47   vnet_feature_enable_disable ("mpls-output", "vlan-mpls-qos-mark",
48                                sw_if_index, enable, NULL, 0);
49 }
50
51 void
52 qos_mark_mpls_enable_disable (u32 sw_if_index, u8 enable)
53 {
54   vnet_feature_enable_disable ("mpls-output", "mpls-qos-mark",
55                                sw_if_index, enable, NULL, 0);
56 }
57
58 static void
59 qos_egress_map_feature_config (u32 sw_if_index, qos_source_t qs, u8 enable)
60 {
61   switch (qs)
62     {
63     case QOS_SOURCE_EXT:
64       ASSERT (0);
65       break;
66     case QOS_SOURCE_VLAN:
67       qos_mark_vlan_enable_disable (sw_if_index, enable);
68       break;
69     case QOS_SOURCE_MPLS:
70       qos_mark_mpls_enable_disable (sw_if_index, enable);
71       break;
72     case QOS_SOURCE_IP:
73       qos_mark_ip_enable_disable (sw_if_index, enable);
74       break;
75     }
76 }
77
78 always_inline qos_egress_map_t *
79 qos_egress_map_interface (u32 sw_if_index, qos_source_t output_source)
80 {
81   ASSERT (vec_len (qos_mark_configs[output_source]) > sw_if_index);
82
83   return pool_elt_at_index (qem_pool,
84                             qos_mark_configs[output_source][sw_if_index]);
85 }
86
87 /**
88  * per-packet trace data
89  */
90 typedef struct qos_mark_trace_t_
91 {
92   /* per-pkt trace data */
93   qos_bits_t bits;
94   qos_source_t input;
95   u32 used;
96 } qos_mark_trace_t;
97
98 static inline uword
99 qos_mark_inline (vlib_main_t * vm,
100                  vlib_node_runtime_t * node,
101                  vlib_frame_t * frame, qos_source_t output_source, int is_ip6)
102 {
103   u32 n_left_from, *from, *to_next, next_index;
104
105   next_index = 0;
106   n_left_from = frame->n_vectors;
107   from = vlib_frame_vector_args (frame);
108
109   while (n_left_from > 0)
110     {
111       u32 n_left_to_next;
112
113       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
114
115       while (n_left_from > 0 && n_left_to_next > 0)
116         {
117           qos_source_t input_source0;
118           ethernet_vlan_header_t *vlan0;
119           u32 sw_if_index0, next0, bi0;
120           qos_egress_map_t *qem0;
121           ip4_header_t *ip4_0;
122           ip6_header_t *ip6_0;
123           vlib_buffer_t *b0;
124           qos_bits_t qos0;
125           u8 *mpls_bytes_0;
126           u8 eos0;
127
128           next0 = 0;
129           bi0 = from[0];
130           to_next[0] = bi0;
131           from += 1;
132           to_next += 1;
133           n_left_from -= 1;
134           n_left_to_next -= 1;
135
136           b0 = vlib_get_buffer (vm, bi0);
137           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX];
138           input_source0 = vnet_buffer2 (b0)->qos.source;
139
140           qem0 = qos_egress_map_interface (sw_if_index0, output_source);
141           qos0 = qem0->qem_output[input_source0][vnet_buffer2 (b0)->qos.bits];
142
143           if (PREDICT_TRUE (b0->flags & VNET_BUFFER_F_QOS_DATA_VALID))
144             {
145               /* there is a source of QoS recording for this packet */
146               if (QOS_SOURCE_IP == output_source)
147                 {
148                   if (is_ip6)
149                     {
150                       ip6_0 = (vlib_buffer_get_current (b0) +
151                                vnet_buffer (b0)->ip.save_rewrite_length);
152
153                       ip6_set_traffic_class_network_order (ip6_0, qos0);
154                     }
155                   else
156                     {
157                       ip4_0 = (vlib_buffer_get_current (b0) +
158                                vnet_buffer (b0)->ip.save_rewrite_length);
159                       if (PREDICT_FALSE (qos0 != ip4_0->tos))
160                         {
161                           ip4_0->tos = qos0;
162                           ip4_0->checksum = ip4_header_checksum (ip4_0);
163                         }
164                     }
165                 }
166               else if (QOS_SOURCE_MPLS == output_source)
167                 {
168                   mpls_bytes_0 = (vlib_buffer_get_current (b0) +
169                                   vnet_buffer (b0)->mpls.save_rewrite_length);
170
171                   /* apply to all the labels in the stack */
172                   do
173                     {
174                       /* clear out the old COS bts */
175                       mpls_bytes_0[2] &= 0xf1;
176                       /* OR in 3 bits of the mapped value */
177                       mpls_bytes_0[2] |= (qos0 & 0x7) << 1;
178                       eos0 = mpls_bytes_0[2] & 0x1;
179                       mpls_bytes_0 += 4;
180                     }
181                   while (!eos0);
182                 }
183               else if (QOS_SOURCE_VLAN == output_source)
184                 {
185                   vlan0 = (vlib_buffer_get_current (b0) +
186                            sizeof (ethernet_header_t));
187
188                   ethernet_vlan_header_set_priority_net_order (vlan0, qos0);
189                 }
190             }
191           vnet_feature_next (&next0, b0);
192
193           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
194             {
195               qos_mark_trace_t *t =
196                 vlib_add_trace (vm, node, b0, sizeof (*t));
197               t->bits = qos0;
198               t->input = input_source0;
199               t->used = (b0->flags & VNET_BUFFER_F_QOS_DATA_VALID);
200             }
201
202           /* verify speculative enqueue, maybe switch current next frame */
203           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
204                                            to_next, n_left_to_next,
205                                            bi0, next0);
206         }
207
208       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
209     }
210
211   return frame->n_vectors;
212 }
213
214 /* packet trace format function */
215 static u8 *
216 format_qos_mark_trace (u8 * s, va_list * args)
217 {
218   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
219   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
220   qos_mark_trace_t *t = va_arg (*args, qos_mark_trace_t *);
221
222   s = format (s, "source:%U qos:%d used:%s",
223               format_qos_source, t->input, t->bits, (t->used ? "yes" : "no"));
224
225   return s;
226 }
227
228 static inline uword
229 ip4_qos_mark (vlib_main_t * vm, vlib_node_runtime_t * node,
230               vlib_frame_t * frame)
231 {
232   return (qos_mark_inline (vm, node, frame, QOS_SOURCE_IP, 0));
233 }
234
235 static inline uword
236 ip6_qos_mark (vlib_main_t * vm, vlib_node_runtime_t * node,
237               vlib_frame_t * frame)
238 {
239   return (qos_mark_inline (vm, node, frame, QOS_SOURCE_IP, 1));
240 }
241
242 static inline uword
243 mpls_qos_mark (vlib_main_t * vm, vlib_node_runtime_t * node,
244                vlib_frame_t * frame)
245 {
246   return (qos_mark_inline (vm, node, frame, QOS_SOURCE_MPLS, 0));
247 }
248
249 static inline uword
250 vlan_mpls_qos_mark (vlib_main_t * vm, vlib_node_runtime_t * node,
251                     vlib_frame_t * frame)
252 {
253   return (qos_mark_inline (vm, node, frame, QOS_SOURCE_VLAN, 0));
254 }
255
256 static inline uword
257 vlan_ip4_qos_mark (vlib_main_t * vm, vlib_node_runtime_t * node,
258                    vlib_frame_t * frame)
259 {
260   return (qos_mark_inline (vm, node, frame, QOS_SOURCE_VLAN, 0));
261 }
262
263 static inline uword
264 vlan_ip6_qos_mark (vlib_main_t * vm, vlib_node_runtime_t * node,
265                    vlib_frame_t * frame)
266 {
267   return (qos_mark_inline (vm, node, frame, QOS_SOURCE_VLAN, 0));
268 }
269
270 /* *INDENT-OFF* */
271 VLIB_REGISTER_NODE (ip4_qos_mark_node) = {
272   .function = ip4_qos_mark,
273   .name = "ip4-qos-mark",
274   .vector_size = sizeof (u32),
275   .format_trace = format_qos_mark_trace,
276   .type = VLIB_NODE_TYPE_INTERNAL,
277
278   .n_errors = 0,
279   .n_next_nodes = 1,
280
281   .next_nodes = {
282     [0] = "ip4-drop",
283   },
284 };
285
286 VLIB_NODE_FUNCTION_MULTIARCH (ip4_qos_mark_node, ip4_qos_mark);
287
288 VNET_FEATURE_INIT (ip4_qos_mark_node, static) = {
289     .arc_name = "ip4-output",
290     .node_name = "ip4-qos-mark",
291 };
292
293 VLIB_REGISTER_NODE (ip6_qos_mark_node) = {
294   .function = ip6_qos_mark,
295   .name = "ip6-qos-mark",
296   .vector_size = sizeof (u32),
297   .format_trace = format_qos_mark_trace,
298   .type = VLIB_NODE_TYPE_INTERNAL,
299
300   .n_errors = 0,
301   .n_next_nodes = 1,
302
303   .next_nodes = {
304     [0] = "ip6-drop",
305   },
306 };
307
308 VLIB_NODE_FUNCTION_MULTIARCH (ip6_qos_mark_node, ip6_qos_mark);
309
310 VNET_FEATURE_INIT (ip6_qos_mark_node, static) = {
311     .arc_name = "ip6-output",
312     .node_name = "ip6-qos-mark",
313 };
314
315 VLIB_REGISTER_NODE (mpls_qos_mark_node) = {
316   .function = mpls_qos_mark,
317   .name = "mpls-qos-mark",
318   .vector_size = sizeof (u32),
319   .format_trace = format_qos_mark_trace,
320   .type = VLIB_NODE_TYPE_INTERNAL,
321
322   .n_errors = 0,
323   .n_next_nodes = 1,
324
325   .next_nodes = {
326     [0] = "mpls-drop",
327   },
328 };
329
330 VLIB_NODE_FUNCTION_MULTIARCH (mpls_qos_mark_node, mpls_qos_mark);
331
332 VNET_FEATURE_INIT (mpls_qos_mark_node, static) = {
333     .arc_name = "mpls-output",
334     .node_name = "mpls-qos-mark",
335 };
336
337 VLIB_REGISTER_NODE (vlan_ip4_qos_mark_node) = {
338   .function = vlan_ip4_qos_mark,
339   .name = "vlan-ip4-qos-mark",
340   .vector_size = sizeof (u32),
341   .format_trace = format_qos_mark_trace,
342   .type = VLIB_NODE_TYPE_INTERNAL,
343
344   .n_errors = 0,
345   .n_next_nodes = 1,
346
347   .next_nodes = {
348     [0] = "error-drop",
349   },
350 };
351
352 VLIB_NODE_FUNCTION_MULTIARCH (vlan_ip4_qos_mark_node, vlan_ip4_qos_mark);
353
354 VNET_FEATURE_INIT (vlan_ip4_qos_mark_node, static) = {
355     .arc_name = "ip4-output",
356     .node_name = "vlan-ip4-qos-mark",
357     .runs_after = VNET_FEATURES ("ip4-qos-mark"),
358 };
359
360 VLIB_REGISTER_NODE (vlan_ip6_qos_mark_node) = {
361   .function = vlan_ip6_qos_mark,
362   .name = "vlan-ip6-qos-mark",
363   .vector_size = sizeof (u32),
364   .format_trace = format_qos_mark_trace,
365   .type = VLIB_NODE_TYPE_INTERNAL,
366
367   .n_errors = 0,
368   .n_next_nodes = 1,
369
370   .next_nodes = {
371     [0] = "error-drop",
372   },
373 };
374
375 VLIB_NODE_FUNCTION_MULTIARCH (vlan_ip6_qos_mark_node, vlan_ip6_qos_mark);
376
377 VNET_FEATURE_INIT (vlan_ip6_qos_mark_node, static) = {
378     .arc_name = "ip6-output",
379     .node_name = "vlan-ip6-qos-mark",
380     .runs_after = VNET_FEATURES ("ip6-qos-mark"),
381 };
382
383 VLIB_REGISTER_NODE (vlan_mpls_qos_mark_node) = {
384   .function = vlan_mpls_qos_mark,
385   .name = "vlan-mpls-qos-mark",
386   .vector_size = sizeof (u32),
387   .format_trace = format_qos_mark_trace,
388   .type = VLIB_NODE_TYPE_INTERNAL,
389
390   .n_errors = 0,
391   .n_next_nodes = 1,
392
393   .next_nodes = {
394     [0] = "error-drop",
395   },
396 };
397
398 VLIB_NODE_FUNCTION_MULTIARCH (vlan_mpls_qos_mark_node, vlan_mpls_qos_mark);
399
400 VNET_FEATURE_INIT (vlan_mpls_qos_mark_node, static) = {
401     .arc_name = "mpls-output",
402     .node_name = "vlan-mpls-qos-mark",
403     .runs_after = VNET_FEATURES ("mpls-qos-mark"),
404 };
405 /* *INDENT-ON* */
406
407 int
408 qos_mark_enable (u32 sw_if_index,
409                  qos_source_t output_source, qos_egress_map_id_t mid)
410 {
411   index_t qemi;
412
413   vec_validate_init_empty (qos_mark_configs[output_source],
414                            sw_if_index, INDEX_INVALID);
415
416   qemi = qos_egress_map_find (mid);
417
418   if (INDEX_INVALID == qemi)
419     return VNET_API_ERROR_NO_SUCH_TABLE;
420
421   if (INDEX_INVALID == qos_mark_configs[output_source][sw_if_index])
422     {
423       qos_egress_map_feature_config (sw_if_index, output_source, 1);
424     }
425
426   qos_mark_configs[output_source][sw_if_index] = qemi;
427
428   return (0);
429 }
430
431 int
432 qos_mark_disable (u32 sw_if_index, qos_source_t output_source)
433 {
434   if (vec_len (qos_mark_configs[output_source]) <= sw_if_index)
435     return VNET_API_ERROR_NO_MATCHING_INTERFACE;
436   if (INDEX_INVALID == qos_mark_configs[output_source][sw_if_index])
437     return VNET_API_ERROR_VALUE_EXIST;
438
439   if (INDEX_INVALID != qos_mark_configs[output_source][sw_if_index])
440     {
441       qos_egress_map_feature_config (sw_if_index, output_source, 0);
442     }
443
444   qos_mark_configs[output_source][sw_if_index] = INDEX_INVALID;
445
446   return (0);
447 }
448
449 static clib_error_t *
450 qos_mark_cli (vlib_main_t * vm,
451               unformat_input_t * input, vlib_cli_command_t * cmd)
452 {
453   qos_egress_map_id_t map_id;
454   u32 sw_if_index, qs;
455   vnet_main_t *vnm;
456   int rv, enable;
457
458   vnm = vnet_get_main ();
459   map_id = ~0;
460   qs = 0xff;
461   enable = 1;
462
463   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
464     {
465       if (unformat (input, "id %d", &map_id))
466         ;
467       else if (unformat (input, "disable"))
468         enable = 0;
469       else if (unformat (input, "%U", unformat_qos_source, &qs))
470         ;
471       else if (unformat (input, "%U",
472                          unformat_vnet_sw_interface, vnm, &sw_if_index))
473         ;
474       else
475         break;
476     }
477
478   if (~0 == sw_if_index)
479     return clib_error_return (0, "interface must be specified");
480   if (0xff == qs)
481     return clib_error_return (0, "output location must be specified");
482
483   if (enable)
484     rv = qos_mark_enable (sw_if_index, qs, map_id);
485   else
486     rv = qos_mark_disable (sw_if_index, qs);
487
488   if (0 == rv)
489     return (NULL);
490
491   return clib_error_return (0, "Failed to map interface");
492 }
493
494 /*?
495  * Apply a QoS egress mapping table to an interface for QoS marking packets
496  * at the given output protocol.
497  *
498  * @cliexpar
499  * @cliexcmd{qos egress interface GigEthernet0/9/0 id 0 output ip}
500  ?*/
501 /* *INDENT-OFF* */
502 VLIB_CLI_COMMAND (qos_egress_map_interface_command, static) = {
503   .path = "qos mark",
504   .short_help = "qos mark <SOURCE> <INTERFACE> id <MAP>",
505   .function = qos_mark_cli,
506   .is_mp_safe = 1,
507 };
508 /* *INDENT-ON* */
509
510 /*
511  * fd.io coding-style-patch-verification: ON
512  *
513 * Local Variables:
514 * eval: (c-set-style "gnu")
515 * End:
516 *
517 */