fib: DPO layout add u64 parameter.
[vpp.git] / src / vnet / dpo / dpo.c
1 /*
2  * Copyright (c) 2016 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  * @brief
17  * A Data-Path Object is an object that represents actions that are
18  * applied to packets are they are switched through VPP.
19  *
20  * The DPO is a base class that is specialised by other objects to provide
21  * concrete actions
22  *
23  * The VLIB graph nodes are graph of types, the DPO graph is a graph of instances.
24  */
25
26 #include <vnet/dpo/dpo.h>
27 #include <vnet/ip/lookup.h>
28 #include <vnet/ip/format.h>
29 #include <vnet/adj/adj.h>
30
31 #include <vnet/dpo/load_balance.h>
32 #include <vnet/dpo/mpls_label_dpo.h>
33 #include <vnet/dpo/lookup_dpo.h>
34 #include <vnet/dpo/drop_dpo.h>
35 #include <vnet/dpo/receive_dpo.h>
36 #include <vnet/dpo/punt_dpo.h>
37 #include <vnet/dpo/classify_dpo.h>
38 #include <vnet/dpo/ip_null_dpo.h>
39 #include <vnet/dpo/replicate_dpo.h>
40 #include <vnet/dpo/interface_rx_dpo.h>
41 #include <vnet/dpo/interface_tx_dpo.h>
42 #include <vnet/dpo/mpls_disposition.h>
43 #include <vnet/dpo/dvr_dpo.h>
44 #include <vnet/dpo/l3_proxy_dpo.h>
45 #include <vnet/dpo/ip6_ll_dpo.h>
46 #include <vnet/dpo/pw_cw.h>
47
48 /**
49  * Array of char* names for the DPO types and protos
50  */
51 static const char* dpo_type_names[] = DPO_TYPES;
52 static const char* dpo_proto_names[] = DPO_PROTOS;
53
54 /**
55  * @brief Vector of virtual function tables for the DPO types
56  *
57  * This is a vector so we can dynamically register new DPO types in plugins.
58  */
59 static dpo_vft_t *dpo_vfts;
60
61 /**
62  * @brief vector of graph node names associated with each DPO type and protocol.
63  *
64  *   dpo_nodes[child_type][child_proto][node_X] = node_name;
65  * i.e.
66  *   dpo_node[DPO_LOAD_BALANCE][DPO_PROTO_IP4][0] = "ip4-lookup"
67  *   dpo_node[DPO_LOAD_BALANCE][DPO_PROTO_IP4][1] = "ip4-load-balance"
68  *
69  * This is a vector so we can dynamically register new DPO types in plugins.
70  */
71 static const char* const * const ** dpo_nodes;
72
73 /**
74  * @brief Vector of edge indicies from parent DPO nodes to child
75  *
76  * dpo_edges[child_type][child_proto][parent_type][parent_proto] = edge_index
77  *
78  * This array is derived at init time from the dpo_nodes above. Note that
79  * the third dimension in dpo_nodes is lost, hence, the edge index from each
80  * node MUST be the same.
81  * Including both the child and parent protocol is required to support the
82  * case where it changes as the graph is traversed, most notably when an
83  * MPLS label is popped.
84  *
85  * Note that this array is child type specific, not child instance specific.
86  */
87 static u32 ****dpo_edges;
88
89 /**
90  * @brief The DPO type value that can be assigned to the next dynamic
91  *        type registration.
92  */
93 static dpo_type_t dpo_dynamic = DPO_LAST;
94
95 dpo_proto_t
96 vnet_link_to_dpo_proto (vnet_link_t linkt)
97 {
98     switch (linkt)
99     {
100     case VNET_LINK_IP6:
101         return (DPO_PROTO_IP6);
102     case VNET_LINK_IP4:
103         return (DPO_PROTO_IP4);
104     case VNET_LINK_MPLS:
105         return (DPO_PROTO_MPLS);
106     case VNET_LINK_ETHERNET:
107         return (DPO_PROTO_ETHERNET);
108     case VNET_LINK_NSH:
109         return (DPO_PROTO_NSH);
110     case VNET_LINK_ARP:
111         break;
112     }
113     ASSERT(0);
114     return (0);
115 }
116
117 vnet_link_t
118 dpo_proto_to_link (dpo_proto_t dp)
119 {
120     switch (dp)
121     {
122     case DPO_PROTO_IP6:
123         return (VNET_LINK_IP6);
124     case DPO_PROTO_IP4:
125         return (VNET_LINK_IP4);
126     case DPO_PROTO_MPLS:
127     case DPO_PROTO_BIER:
128         return (VNET_LINK_MPLS);
129     case DPO_PROTO_ETHERNET:
130         return (VNET_LINK_ETHERNET);
131     case DPO_PROTO_NSH:
132         return (VNET_LINK_NSH);
133     }
134     return (~0);
135 }
136
137 u8 *
138 format_dpo_type (u8 * s, va_list * args)
139 {
140     dpo_type_t type = va_arg (*args, int);
141
142     s = format(s, "%s", dpo_type_names[type]);
143
144     return (s);
145 }
146
147 u8 *
148 format_dpo_id (u8 * s, va_list * args)
149 {
150     dpo_id_t *dpo = va_arg (*args, dpo_id_t*);
151     u32 indent = va_arg (*args, u32);
152
153     s = format(s, "[@%d]: ", dpo->dpoi_next_node);
154
155     if (NULL != dpo_vfts[dpo->dpoi_type].dv_format)
156     {
157         s = format(s, "%U",
158                    dpo_vfts[dpo->dpoi_type].dv_format,
159                    dpo->dpoi_index,
160                    indent);
161     }
162     else
163     {
164         switch (dpo->dpoi_type)
165         {
166         case DPO_FIRST:
167             s = format(s, "unset");
168             break;
169         default:
170             s = format(s, "unknown");
171             break;
172         }
173     }
174     return (s);
175 }
176
177 u8 *
178 format_dpo_proto (u8 * s, va_list * args)
179 {
180     dpo_proto_t proto = va_arg (*args, int);
181
182     return (format(s, "%s", dpo_proto_names[proto]));
183 }
184
185 void
186 dpo_set (dpo_id_t *dpo,
187          dpo_type_t type,
188          dpo_proto_t proto,
189          index_t index)
190 {
191     dpo_id_t tmp = *dpo;
192
193     dpo->dpoi_type = type;
194     dpo->dpoi_proto = proto,
195     dpo->dpoi_index = index;
196
197     if (DPO_ADJACENCY == type)
198     {
199         /*
200          * set the adj subtype
201          */
202         ip_adjacency_t *adj;
203
204         adj = adj_get(index);
205
206         switch (adj->lookup_next_index)
207         {
208         case IP_LOOKUP_NEXT_ARP:
209             dpo->dpoi_type = DPO_ADJACENCY_INCOMPLETE;
210             break;
211         case IP_LOOKUP_NEXT_MIDCHAIN:
212             dpo->dpoi_type = DPO_ADJACENCY_MIDCHAIN;
213             break;
214         case IP_LOOKUP_NEXT_MCAST_MIDCHAIN:
215             dpo->dpoi_type = DPO_ADJACENCY_MCAST_MIDCHAIN;
216             break;
217         case IP_LOOKUP_NEXT_MCAST:
218             dpo->dpoi_type = DPO_ADJACENCY_MCAST;
219             break;
220         case IP_LOOKUP_NEXT_GLEAN:
221             dpo->dpoi_type = DPO_ADJACENCY_GLEAN;
222             break;
223         default:
224             break;
225         }
226     }
227     dpo_lock(dpo);
228     dpo_unlock(&tmp);
229 }
230
231 void
232 dpo_reset (dpo_id_t *dpo)
233 {
234     dpo_id_t tmp = DPO_INVALID;
235
236     /*
237      * use the atomic copy operation.
238      */
239     dpo_copy(dpo, &tmp);
240 }
241
242 /**
243  * \brief
244  * Compare two Data-path objects
245  *
246  * like memcmp, return 0 is matching, !0 otherwise.
247  */
248 int
249 dpo_cmp (const dpo_id_t *dpo1,
250          const dpo_id_t *dpo2)
251 {
252     int res;
253
254     res = dpo1->dpoi_type - dpo2->dpoi_type;
255
256     if (0 != res) return (res);
257
258     return (dpo1->dpoi_index - dpo2->dpoi_index);
259 }
260
261 void
262 dpo_copy (dpo_id_t *dst,
263           const dpo_id_t *src)
264 {
265     dpo_id_t tmp = {
266         .as_u64 = dst->as_u64
267     };
268
269     /*
270      * the destination is written in a single u64 write - hence atomically w.r.t
271      * any packets inflight.
272      */
273     dst->as_u64 = src->as_u64;
274
275     dpo_lock(dst);
276     dpo_unlock(&tmp);
277 }
278
279 int
280 dpo_is_adj (const dpo_id_t *dpo)
281 {
282     return ((dpo->dpoi_type == DPO_ADJACENCY) ||
283             (dpo->dpoi_type == DPO_ADJACENCY_INCOMPLETE) ||
284             (dpo->dpoi_type == DPO_ADJACENCY_GLEAN) ||
285             (dpo->dpoi_type == DPO_ADJACENCY_MCAST) ||
286             (dpo->dpoi_type == DPO_ADJACENCY_MCAST_MIDCHAIN) ||
287             (dpo->dpoi_type == DPO_ADJACENCY_MIDCHAIN) ||
288             (dpo->dpoi_type == DPO_ADJACENCY_GLEAN));
289 }
290
291 static u32 *
292 dpo_default_get_next_node (const dpo_id_t *dpo)
293 {
294     u32 *node_indices = NULL;
295     const char *node_name;
296     u32 ii = 0;
297
298     node_name = dpo_nodes[dpo->dpoi_type][dpo->dpoi_proto][ii];
299     while (NULL != node_name)
300     {
301         vlib_node_t *node;
302
303         node = vlib_get_node_by_name(vlib_get_main(), (u8*) node_name);
304         ASSERT(NULL != node);
305         vec_add1(node_indices, node->index);
306
307         ++ii;
308         node_name = dpo_nodes[dpo->dpoi_type][dpo->dpoi_proto][ii];
309     }
310
311     return (node_indices);
312 }
313
314 /**
315  * A default variant of the make interpose function that just returns
316  * the original
317  */
318 static void
319 dpo_default_mk_interpose (const dpo_id_t *original,
320                           const dpo_id_t *parent,
321                           dpo_id_t *clone)
322 {
323     dpo_copy(clone, original);
324 }
325
326 void
327 dpo_register (dpo_type_t type,
328               const dpo_vft_t *vft,
329               const char * const * const * nodes)
330 {
331     vec_validate(dpo_vfts, type);
332     dpo_vfts[type] = *vft;
333     if (NULL == dpo_vfts[type].dv_get_next_node)
334     {
335         dpo_vfts[type].dv_get_next_node = dpo_default_get_next_node;
336     }
337     if (NULL == dpo_vfts[type].dv_mk_interpose)
338     {
339         dpo_vfts[type].dv_mk_interpose = dpo_default_mk_interpose;
340     }
341
342     vec_validate(dpo_nodes, type);
343     dpo_nodes[type] = nodes;
344 }
345
346 dpo_type_t
347 dpo_register_new_type (const dpo_vft_t *vft,
348                        const char * const * const * nodes)
349 {
350     dpo_type_t type = dpo_dynamic++;
351
352     dpo_register(type, vft, nodes);
353
354     return (type);
355 }
356
357 void
358 dpo_mk_interpose (const dpo_id_t *original,
359                   const dpo_id_t *parent,
360                   dpo_id_t *clone)
361 {
362     if (!dpo_id_is_valid(original))
363         return;
364
365     dpo_vfts[original->dpoi_type].dv_mk_interpose(original, parent, clone);
366 }
367
368 void
369 dpo_lock (dpo_id_t *dpo)
370 {
371     if (!dpo_id_is_valid(dpo))
372         return;
373
374     dpo_vfts[dpo->dpoi_type].dv_lock(dpo);
375 }
376
377 void
378 dpo_unlock (dpo_id_t *dpo)
379 {
380     if (!dpo_id_is_valid(dpo))
381         return;
382
383     dpo_vfts[dpo->dpoi_type].dv_unlock(dpo);
384 }
385
386 u32
387 dpo_get_urpf(const dpo_id_t *dpo)
388 {
389     if (dpo_id_is_valid(dpo) &&
390         (NULL != dpo_vfts[dpo->dpoi_type].dv_get_urpf))
391     {
392         return (dpo_vfts[dpo->dpoi_type].dv_get_urpf(dpo));
393     }
394
395     return (~0);
396 }
397
398 static u32
399 dpo_get_next_node (dpo_type_t child_type,
400                    dpo_proto_t child_proto,
401                    const dpo_id_t *parent_dpo)
402 {
403     dpo_proto_t parent_proto;
404     dpo_type_t parent_type;
405
406     parent_type = parent_dpo->dpoi_type;
407     parent_proto = parent_dpo->dpoi_proto;
408
409     vec_validate(dpo_edges, child_type);
410     vec_validate(dpo_edges[child_type], child_proto);
411     vec_validate(dpo_edges[child_type][child_proto], parent_type);
412     vec_validate_init_empty(
413         dpo_edges[child_type][child_proto][parent_type],
414         parent_proto, ~0);
415
416     /*
417      * if the edge index has not yet been created for this node to node transition
418      */
419     if (~0 == dpo_edges[child_type][child_proto][parent_type][parent_proto])
420     {
421         vlib_node_t *child_node;
422         u32 *parent_indices;
423         vlib_main_t *vm;
424         u32 edge, *pi, cc;
425
426         vm = vlib_get_main();
427
428         ASSERT(NULL != dpo_vfts[parent_type].dv_get_next_node);
429         ASSERT(NULL != dpo_nodes[child_type]);
430         ASSERT(NULL != dpo_nodes[child_type][child_proto]);
431
432         cc = 0;
433         parent_indices = dpo_vfts[parent_type].dv_get_next_node(parent_dpo);
434
435         vlib_worker_thread_barrier_sync(vm);
436
437         /*
438          * create a graph arc from each of the child's registered node types,
439          * to each of the parent's.
440          */
441         while (NULL != dpo_nodes[child_type][child_proto][cc])
442         {
443             child_node =
444                 vlib_get_node_by_name(vm,
445                                       (u8*) dpo_nodes[child_type][child_proto][cc]);
446
447             vec_foreach(pi, parent_indices)
448             {
449                 edge = vlib_node_add_next(vm, child_node->index, *pi);
450
451                 if (~0 == dpo_edges[child_type][child_proto][parent_type][parent_proto])
452                 {
453                     dpo_edges[child_type][child_proto][parent_type][parent_proto] = edge;
454                 }
455                 else
456                 {
457                     ASSERT(dpo_edges[child_type][child_proto][parent_type][parent_proto] == edge);
458                 }
459             }
460             cc++;
461         }
462
463         vlib_worker_thread_barrier_release(vm);
464         vec_free(parent_indices);
465     }
466
467     return (dpo_edges[child_type][child_proto][parent_type][parent_proto]);
468 }
469
470 /**
471  * @brief return already stacked up next node index for a given
472  * child_type/child_proto and parent_type/patent_proto.
473  * The VLIB graph arc used is taken from the parent and child types
474  * passed.
475  */
476 u32
477 dpo_get_next_node_by_type_and_proto (dpo_type_t   child_type,
478                                      dpo_proto_t  child_proto,
479                                      dpo_type_t   parent_type,
480                                      dpo_proto_t  parent_proto)
481 {
482    return (dpo_edges[child_type][child_proto][parent_type][parent_proto]);
483 }
484
485 /**
486  * @brief Stack one DPO object on another, and thus establish a child parent
487  * relationship. The VLIB graph arc used is taken from the parent and child types
488  * passed.
489  */
490 static void
491 dpo_stack_i (u32 edge,
492              dpo_id_t *dpo,
493              const dpo_id_t *parent)
494 {
495     /*
496      * in order to get an atomic update of the parent we create a temporary,
497      * from a copy of the child, and add the next_node. then we copy to the parent
498      */
499     dpo_id_t tmp = DPO_INVALID;
500     dpo_copy(&tmp, parent);
501
502     /*
503      * get the edge index for the parent to child VLIB graph transition
504      */
505     tmp.dpoi_next_node = edge;
506
507     /*
508      * this update is atomic.
509      */
510     dpo_copy(dpo, &tmp);
511
512     dpo_reset(&tmp);
513 }
514
515 /**
516  * @brief Stack one DPO object on another, and thus establish a child-parent
517  * relationship. The VLIB graph arc used is taken from the parent and child types
518  * passed.
519  */
520 void
521 dpo_stack (dpo_type_t child_type,
522            dpo_proto_t child_proto,
523            dpo_id_t *dpo,
524            const dpo_id_t *parent)
525 {
526     dpo_stack_i(dpo_get_next_node(child_type, child_proto, parent), dpo, parent);
527 }
528
529 /**
530  * @brief Stack one DPO object on another, and thus establish a child parent
531  * relationship. A new VLIB graph arc is created from the child node passed
532  * to the nodes registered by the parent. The VLIB infra will ensure this arc
533  * is added only once.
534  */
535 void
536 dpo_stack_from_node (u32 child_node_index,
537                      dpo_id_t *dpo,
538                      const dpo_id_t *parent)
539 {
540     dpo_type_t parent_type;
541     u32 *parent_indices;
542     vlib_main_t *vm;
543     u32 edge, *pi;
544
545     edge = 0;
546     parent_type = parent->dpoi_type;
547     vm = vlib_get_main();
548
549     ASSERT(NULL != dpo_vfts[parent_type].dv_get_next_node);
550     parent_indices = dpo_vfts[parent_type].dv_get_next_node(parent);
551     ASSERT(parent_indices);
552
553     /*
554      * This loop is purposefully written with the worker thread lock in the
555      * inner loop because;
556      *  1) the likelihood that the edge does not exist is smaller
557      *  2) the likelihood there is more than one node is even smaller
558      * so we are optimising for not need to take the lock
559      */
560     vec_foreach(pi, parent_indices)
561     {
562         edge = vlib_node_get_next(vm, child_node_index, *pi);
563
564         if (~0 == edge)
565         {
566             vlib_worker_thread_barrier_sync(vm);
567
568             edge = vlib_node_add_next(vm, child_node_index, *pi);
569
570             vlib_worker_thread_barrier_release(vm);
571         }
572     }
573     dpo_stack_i(edge, dpo, parent);
574
575     /* should free this local vector to avoid memory leak */
576     vec_free(parent_indices);
577 }
578
579 static clib_error_t *
580 dpo_module_init (vlib_main_t * vm)
581 {
582     drop_dpo_module_init();
583     punt_dpo_module_init();
584     receive_dpo_module_init();
585     load_balance_module_init();
586     mpls_label_dpo_module_init();
587     classify_dpo_module_init();
588     lookup_dpo_module_init();
589     ip_null_dpo_module_init();
590     ip6_ll_dpo_module_init();
591     replicate_module_init();
592     interface_rx_dpo_module_init();
593     interface_tx_dpo_module_init();
594     mpls_disp_dpo_module_init();
595     dvr_dpo_module_init();
596     l3_proxy_dpo_module_init();
597     pw_cw_dpo_module_init();
598
599     return (NULL);
600 }
601
602 /* *INDENT-OFF* */
603 VLIB_INIT_FUNCTION(dpo_module_init) =
604 {
605     .runs_before = VLIB_INITS ("ip_main_init"),
606 };
607 /* *INDENT-ON* */
608
609 static clib_error_t *
610 dpo_memory_show (vlib_main_t * vm,
611                  unformat_input_t * input,
612                  vlib_cli_command_t * cmd)
613 {
614     dpo_vft_t *vft;
615
616     vlib_cli_output (vm, "DPO memory");
617     vlib_cli_output (vm, "%=30s %=5s %=8s/%=9s   totals",
618                      "Name","Size", "in-use", "allocated");
619
620     vec_foreach(vft, dpo_vfts)
621     {
622         if (NULL != vft->dv_mem_show)
623             vft->dv_mem_show();
624     }
625
626     return (NULL);
627 }
628
629 /* *INDENT-OFF* */
630 /*?
631  * The '<em>sh dpo memory </em>' command displays the memory usage for each
632  * data-plane object type.
633  *
634  * @cliexpar
635  * @cliexstart{show dpo memory}
636  * DPO memory
637  *             Name               Size  in-use /allocated   totals
638  *         load-balance            64     12   /    12      768/768
639  *           Adjacency            256      1   /    1       256/256
640  *            Receive              24      5   /    5       120/120
641  *            Lookup               12      0   /    0       0/0
642  *           Classify              12      0   /    0       0/0
643  *          MPLS label             24      0   /    0       0/0
644  * @cliexend
645 ?*/
646 VLIB_CLI_COMMAND (show_fib_memory, static) = {
647     .path = "show dpo memory",
648     .function = dpo_memory_show,
649     .short_help = "show dpo memory",
650 };
651 /* *INDENT-ON* */