L2 over MPLS
[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  * concreate 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_dpo.h>
41 #include <vnet/dpo/mpls_disposition.h>
42
43 /**
44  * Array of char* names for the DPO types and protos
45  */
46 static const char* dpo_type_names[] = DPO_TYPES;
47 static const char* dpo_proto_names[] = DPO_PROTOS;
48
49 /**
50  * @brief Vector of virtual function tables for the DPO types
51  *
52  * This is a vector so we can dynamically register new DPO types in plugins.
53  */
54 static dpo_vft_t *dpo_vfts;
55
56 /**
57  * @brief vector of graph node names associated with each DPO type and protocol.
58  *
59  *   dpo_nodes[child_type][child_proto][node_X] = node_name;
60  * i.e.
61  *   dpo_node[DPO_LOAD_BALANCE][DPO_PROTO_IP4][0] = "ip4-lookup"
62  *   dpo_node[DPO_LOAD_BALANCE][DPO_PROTO_IP4][1] = "ip4-load-balance"
63  *
64  * This is a vector so we can dynamically register new DPO types in plugins.
65  */
66 static const char* const * const ** dpo_nodes;
67
68 /**
69  * @brief Vector of edge indicies from parent DPO nodes to child
70  *
71  * dpo_edges[child_type][child_proto][parent_type][parent_proto] = edge_index
72  *
73  * This array is derived at init time from the dpo_nodes above. Note that
74  * the third dimension in dpo_nodes is lost, hence, the edge index from each
75  * node MUST be the same.
76  * Including both the child and parent protocol is required to support the
77  * case where it changes as the grapth is traversed, most notablly when an
78  * MPLS label is popped.
79  *
80  * Note that this array is child type specific, not child instance specific.
81  */
82 static u32 ****dpo_edges;
83
84 /**
85  * @brief The DPO type value that can be assigend to the next dynamic
86  *        type registration.
87  */
88 static dpo_type_t dpo_dynamic = DPO_LAST;
89
90 dpo_proto_t
91 vnet_link_to_dpo_proto (vnet_link_t linkt)
92 {
93     switch (linkt)
94     {
95     case VNET_LINK_IP6:
96         return (DPO_PROTO_IP6);
97     case VNET_LINK_IP4:
98         return (DPO_PROTO_IP4);
99     case VNET_LINK_MPLS:
100         return (DPO_PROTO_MPLS);
101     case VNET_LINK_ETHERNET:
102         return (DPO_PROTO_ETHERNET);
103     case VNET_LINK_NSH:
104         return (DPO_PROTO_NSH);
105     case VNET_LINK_ARP:
106         break;
107     }
108     ASSERT(0);
109     return (0);
110 }
111
112 vnet_link_t
113 dpo_proto_to_link (dpo_proto_t dp)
114 {
115     switch (dp)
116     {
117     case DPO_PROTO_IP6:
118         return (VNET_LINK_IP6);
119     case DPO_PROTO_IP4:
120         return (VNET_LINK_IP4);
121     case DPO_PROTO_MPLS:
122         return (VNET_LINK_MPLS);
123     case DPO_PROTO_ETHERNET:
124         return (VNET_LINK_ETHERNET);
125     case DPO_PROTO_NSH:
126         return (VNET_LINK_NSH);
127     }
128     return (~0);
129 }
130
131 u8 *
132 format_dpo_type (u8 * s, va_list * args)
133 {
134     dpo_type_t type = va_arg (*args, int);
135
136     s = format(s, "%s", dpo_type_names[type]);
137
138     return (s);
139 }
140
141 u8 *
142 format_dpo_id (u8 * s, va_list * args)
143 {
144     dpo_id_t *dpo = va_arg (*args, dpo_id_t*);
145     u32 indent = va_arg (*args, u32);
146
147     s = format(s, "[@%d]: ", dpo->dpoi_next_node);
148
149     if (NULL != dpo_vfts[dpo->dpoi_type].dv_format)
150     {
151         return (format(s, "%U",
152                        dpo_vfts[dpo->dpoi_type].dv_format,
153                        dpo->dpoi_index,
154                        indent));
155     }
156
157     switch (dpo->dpoi_type)
158     {
159     case DPO_FIRST:
160         s = format(s, "unset");
161         break;
162     default:
163         s = format(s, "unknown");
164         break;
165     }
166     return (s);
167 }
168
169 u8 *
170 format_dpo_proto (u8 * s, va_list * args)
171 {
172     dpo_proto_t proto = va_arg (*args, int);
173
174     return (format(s, "%s", dpo_proto_names[proto]));
175 }
176
177 void
178 dpo_set (dpo_id_t *dpo,
179          dpo_type_t type,
180          dpo_proto_t proto,
181          index_t index)
182 {
183     dpo_id_t tmp = *dpo;
184
185     dpo->dpoi_type = type;
186     dpo->dpoi_proto = proto,
187     dpo->dpoi_index = index;
188
189     if (DPO_ADJACENCY == type)
190     {
191         /*
192          * set the adj subtype
193          */
194         ip_adjacency_t *adj;
195
196         adj = adj_get(index);
197
198         switch (adj->lookup_next_index)
199         {
200         case IP_LOOKUP_NEXT_ARP:
201             dpo->dpoi_type = DPO_ADJACENCY_INCOMPLETE;
202             break;
203         case IP_LOOKUP_NEXT_MIDCHAIN:
204             dpo->dpoi_type = DPO_ADJACENCY_MIDCHAIN;
205             break;
206         case IP_LOOKUP_NEXT_MCAST_MIDCHAIN:
207             dpo->dpoi_type = DPO_ADJACENCY_MCAST_MIDCHAIN;
208             break;
209         case IP_LOOKUP_NEXT_MCAST:
210             dpo->dpoi_type = DPO_ADJACENCY_MCAST;
211             break;
212         case IP_LOOKUP_NEXT_GLEAN:
213             dpo->dpoi_type = DPO_ADJACENCY_GLEAN;
214             break;
215         default:
216             break;
217         }
218     }
219     dpo_lock(dpo);
220     dpo_unlock(&tmp);
221 }
222
223 void
224 dpo_reset (dpo_id_t *dpo)
225 {
226     dpo_id_t tmp = DPO_INVALID;
227
228     /*
229      * use the atomic copy operation.
230      */
231     dpo_copy(dpo, &tmp);
232 }
233
234 /**
235  * \brief
236  * Compare two Data-path objects
237  *
238  * like memcmp, return 0 is matching, !0 otherwise.
239  */
240 int
241 dpo_cmp (const dpo_id_t *dpo1,
242          const dpo_id_t *dpo2)
243 {
244     int res;
245
246     res = dpo1->dpoi_type - dpo2->dpoi_type;
247
248     if (0 != res) return (res);
249
250     return (dpo1->dpoi_index - dpo2->dpoi_index);
251 }
252
253 void
254 dpo_copy (dpo_id_t *dst,
255           const dpo_id_t *src)
256 {
257     dpo_id_t tmp = *dst;
258
259     /*
260      * the destination is written in a single u64 write - hence atomically w.r.t
261      * any packets inflight.
262      */
263     *((u64*)dst) = *(u64*)src; 
264
265     dpo_lock(dst);
266     dpo_unlock(&tmp);    
267 }
268
269 int
270 dpo_is_adj (const dpo_id_t *dpo)
271 {
272     return ((dpo->dpoi_type == DPO_ADJACENCY) ||
273             (dpo->dpoi_type == DPO_ADJACENCY_INCOMPLETE) ||
274             (dpo->dpoi_type == DPO_ADJACENCY_MIDCHAIN) ||
275             (dpo->dpoi_type == DPO_ADJACENCY_GLEAN));
276 }
277
278 void
279 dpo_register (dpo_type_t type,
280               const dpo_vft_t *vft,
281               const char * const * const * nodes)
282 {
283     vec_validate(dpo_vfts, type);
284     dpo_vfts[type] = *vft;
285
286     vec_validate(dpo_nodes, type);
287     dpo_nodes[type] = nodes;
288 }
289
290 dpo_type_t
291 dpo_register_new_type (const dpo_vft_t *vft,
292                        const char * const * const * nodes)
293 {
294     dpo_type_t type = dpo_dynamic++;
295
296     dpo_register(type, vft, nodes);
297
298     return (type);
299 }
300
301 void
302 dpo_lock (dpo_id_t *dpo)
303 {
304     if (!dpo_id_is_valid(dpo))
305         return;
306
307     dpo_vfts[dpo->dpoi_type].dv_lock(dpo);
308 }
309
310 void
311 dpo_unlock (dpo_id_t *dpo)
312 {
313     if (!dpo_id_is_valid(dpo))
314         return;
315
316     dpo_vfts[dpo->dpoi_type].dv_unlock(dpo);
317 }
318
319
320 static u32
321 dpo_get_next_node (dpo_type_t child_type,
322                    dpo_proto_t child_proto,
323                    const dpo_id_t *parent_dpo)
324 {
325     dpo_proto_t parent_proto;
326     dpo_type_t parent_type;
327
328     parent_type = parent_dpo->dpoi_type;
329     parent_proto = parent_dpo->dpoi_proto;
330
331     vec_validate(dpo_edges, child_type);
332     vec_validate(dpo_edges[child_type], child_proto);
333     vec_validate(dpo_edges[child_type][child_proto], parent_type);
334     vec_validate_init_empty(
335         dpo_edges[child_type][child_proto][parent_type],
336         parent_proto, ~0);
337
338     /*
339      * if the edge index has not yet been created for this node to node transistion
340      */
341     if (~0 == dpo_edges[child_type][child_proto][parent_type][parent_proto])
342     {
343         vlib_node_t *parent_node, *child_node;
344         vlib_main_t *vm;
345         u32 edge ,pp, cc;
346
347         vm = vlib_get_main();
348
349         vlib_worker_thread_barrier_sync(vm);
350
351         ASSERT(NULL != dpo_nodes[child_type]);
352         ASSERT(NULL != dpo_nodes[child_type][child_proto]);
353         ASSERT(NULL != dpo_nodes[parent_type]);
354         ASSERT(NULL != dpo_nodes[parent_type][parent_proto]);
355
356         cc = 0;
357
358         /*
359          * create a graph arc from each of the parent's registered node types,
360          * to each of the childs.
361          */
362         while (NULL != dpo_nodes[child_type][child_proto][cc])
363         {
364             child_node =
365                 vlib_get_node_by_name(vm,
366                                       (u8*) dpo_nodes[child_type][child_proto][cc]);
367
368             pp = 0;
369
370             while (NULL != dpo_nodes[parent_type][parent_proto][pp])
371             {
372                 parent_node =
373                     vlib_get_node_by_name(vm,
374                                           (u8*) dpo_nodes[parent_type][parent_proto][pp]);
375
376                 edge = vlib_node_add_next(vm,
377                                           child_node->index,
378                                           parent_node->index);
379
380                 if (~0 == dpo_edges[child_type][child_proto][parent_type][parent_proto])
381                 {
382                     dpo_edges[child_type][child_proto][parent_type][parent_proto] = edge;
383                 }
384                 else
385                 {
386                     ASSERT(dpo_edges[child_type][child_proto][parent_type][parent_proto] == edge);
387                 }
388                 pp++;
389             }
390             cc++;
391         }
392
393         vlib_worker_thread_barrier_release(vm);
394     }
395
396     return (dpo_edges[child_type][child_proto][parent_type][parent_proto]);
397 }
398
399 /**
400  * @brief Stack one DPO object on another, and thus establish a child parent
401  * relationship. The VLIB graph arc used is taken from the parent and child types
402  * passed.
403  */
404 static void
405 dpo_stack_i (u32 edge,
406              dpo_id_t *dpo,
407              const dpo_id_t *parent)
408 {
409     /*
410      * in order to get an atomic update of the parent we create a temporary,
411      * from a copy of the child, and add the next_node. then we copy to the parent
412      */
413     dpo_id_t tmp = DPO_INVALID;
414     dpo_copy(&tmp, parent);
415
416     /*
417      * get the edge index for the parent to child VLIB graph transisition
418      */
419     tmp.dpoi_next_node = edge;
420
421     /*
422      * this update is atomic.
423      */
424     dpo_copy(dpo, &tmp);
425
426     dpo_reset(&tmp);
427 }
428
429 /**
430  * @brief Stack one DPO object on another, and thus establish a child-parent
431  * relationship. The VLIB graph arc used is taken from the parent and child types
432  * passed.
433  */
434 void
435 dpo_stack (dpo_type_t child_type,
436            dpo_proto_t child_proto,
437            dpo_id_t *dpo,
438            const dpo_id_t *parent)
439 {
440     dpo_stack_i(dpo_get_next_node(child_type, child_proto, parent), dpo, parent);
441 }
442
443 /**
444  * @brief Stack one DPO object on another, and thus establish a child parent
445  * relationship. A new VLIB graph arc is created from the child node passed
446  * to the nodes registered by the parent. The VLIB infra will ensure this arc
447  * is added only once.
448  */
449 void
450 dpo_stack_from_node (u32 child_node_index,
451                      dpo_id_t *dpo,
452                      const dpo_id_t *parent)
453 {
454     dpo_proto_t parent_proto;
455     vlib_node_t *parent_node;
456     dpo_type_t parent_type;
457     vlib_main_t *vm;
458     u32 edge;
459
460     parent_type = parent->dpoi_type;
461     parent_proto = parent->dpoi_proto;
462
463     vm = vlib_get_main();
464
465     ASSERT(NULL != dpo_nodes[parent_type]);
466     ASSERT(NULL != dpo_nodes[parent_type][parent_proto]);
467
468     parent_node =
469         vlib_get_node_by_name(vm, (u8*) dpo_nodes[parent_type][parent_proto][0]);
470
471     edge = vlib_node_get_next(vm,
472                               child_node_index,
473                               parent_node->index);
474
475     if (~0 == edge)
476     {
477         vlib_worker_thread_barrier_sync(vm);
478
479         edge = vlib_node_add_next(vm,
480                                   child_node_index,
481                                   parent_node->index);
482
483         vlib_worker_thread_barrier_release(vm);
484     }
485
486     dpo_stack_i(edge, dpo, parent);
487 }
488
489 static clib_error_t *
490 dpo_module_init (vlib_main_t * vm)
491 {
492     drop_dpo_module_init();
493     punt_dpo_module_init();
494     receive_dpo_module_init();
495     load_balance_module_init();
496     mpls_label_dpo_module_init();
497     classify_dpo_module_init();
498     lookup_dpo_module_init();
499     ip_null_dpo_module_init();
500     replicate_module_init();
501     interface_dpo_module_init();
502     mpls_disp_dpo_module_init();
503
504     return (NULL);
505 }
506
507 VLIB_INIT_FUNCTION(dpo_module_init);
508
509 static clib_error_t *
510 dpo_memory_show (vlib_main_t * vm,
511                  unformat_input_t * input,
512                  vlib_cli_command_t * cmd)
513 {
514     dpo_vft_t *vft;
515
516     vlib_cli_output (vm, "DPO memory");
517     vlib_cli_output (vm, "%=30s %=5s %=8s/%=9s   totals",
518                      "Name","Size", "in-use", "allocated");
519
520     vec_foreach(vft, dpo_vfts)
521     {
522         if (NULL != vft->dv_mem_show)
523             vft->dv_mem_show();
524     }
525
526     return (NULL);
527 }
528
529 /* *INDENT-OFF* */
530 /*?
531  * The '<em>sh dpo memory </em>' command displays the memory usage for each
532  * data-plane object type.
533  *
534  * @cliexpar
535  * @cliexstart{show dpo memory}
536  * DPO memory
537  *             Name               Size  in-use /allocated   totals
538  *         load-balance            64     12   /    12      768/768
539  *           Adjacency            256      1   /    1       256/256
540  *            Receive              24      5   /    5       120/120
541  *            Lookup               12      0   /    0       0/0
542  *           Classify              12      0   /    0       0/0
543  *          MPLS label             24      0   /    0       0/0
544  * @cliexend
545 ?*/
546 VLIB_CLI_COMMAND (show_fib_memory, static) = {
547     .path = "show dpo memory",
548     .function = dpo_memory_show,
549     .short_help = "show dpo memory",
550 };
551 /* *INDENT-ON* */