L3 Span
[vpp.git] / src / plugins / l3span / l3_span_dpo.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 <plugins/l3span/l3_span_dpo.h>
17 #include <vnet/fib/fib_path_list.h>
18 #include <vnet/dpo/replicate_dpo.h>
19 #include <vnet/dpo/load_balance.h>
20
21 /**
22  * ID registered for L3 span DPOs
23  */
24 dpo_type_t l3_span_dpo_type;
25
26 /**
27  * pool of all L3 Span DPOs
28  */
29 l3_span_dpo_t *l3_span_dpo_pool;
30
31 static l3_span_dpo_t *
32 l3_span_dpo_alloc (void)
33 {
34     l3_span_dpo_t *l3sd;
35
36     pool_get_aligned(l3_span_dpo_pool, l3sd, CLIB_CACHE_LINE_BYTES);
37     memset(l3sd, 0, sizeof(*l3sd));
38
39     return (l3sd);
40 }
41
42 static index_t
43 l3_span_dpo_get_index (l3_span_dpo_t *l3s)
44 {
45     return (l3s - l3_span_dpo_pool);
46 }
47
48 static fib_forward_chain_type_t
49 l3_span_dpo_proto_to_chain_type (dpo_proto_t dproto)
50 {
51     switch (dproto)
52     {
53     case DPO_PROTO_IP4:
54         return (FIB_FORW_CHAIN_TYPE_UNICAST_IP4);
55     case DPO_PROTO_IP6:
56         return (FIB_FORW_CHAIN_TYPE_UNICAST_IP6);
57     default:
58         ASSERT(0);
59     }
60
61     return (FIB_FORW_CHAIN_TYPE_UNICAST_IP4);
62 }
63
64 void
65 l3_span_dpo_create_and_lock (dpo_proto_t dproto,
66                              fib_node_index_t pl,
67                              index_t counter,
68                              dpo_id_t *dpo)
69 {
70     l3_span_dpo_t *l3sd;
71
72     /*
73      * alloc an L3 span DPO and a replicate DPO
74      */
75     l3sd = l3_span_dpo_alloc();
76
77     /*
78      * have the path-list contribute a load-balance DPO we can clone
79      */
80     fib_path_list_contribute_forwarding
81         (pl,
82          l3_span_dpo_proto_to_chain_type(dproto),
83          FIB_PATH_LIST_FWD_FLAG_NONE,
84          &l3sd->l3sd_dpo);
85
86     ASSERT(DPO_LOAD_BALANCE == l3sd->l3sd_dpo.dpoi_type);
87
88     dpo_set(dpo, l3_span_dpo_type, dproto, l3_span_dpo_get_index(l3sd));
89 }
90
91 void
92 l3_span_dpo_update (index_t l3sdi,
93                     fib_node_index_t pl)
94 {
95     l3_span_dpo_t *l3sd;
96
97     l3sd = l3_span_dpo_get(l3sdi);
98
99     fib_path_list_contribute_forwarding
100         (pl,
101          l3_span_dpo_proto_to_chain_type(l3sd->l3sd_dpo.dpoi_proto),
102          FIB_PATH_LIST_FWD_FLAG_NONE,
103          &l3sd->l3sd_dpo);
104 }
105
106 u8*
107 format_l3_span_dpo (u8 *s, va_list *args)
108 {
109     index_t index = va_arg (*args, index_t);
110     u32 indent = va_arg (*args, u32);
111     l3_span_dpo_t *l3sd;
112
113     s = format(s, "l3-span:[%d]:", index);
114
115     if (pool_is_free_index(l3_span_dpo_pool, index))
116     {
117         /*
118          * the packet trace can be printed after the DPO has been deleted
119          */
120         return (s);
121     }
122
123     l3sd = l3_span_dpo_get(index);
124
125     if (l3sd->l3sd_flags & L3_SPAN_DPO_FLAG_CLONE)
126         s = format(s, " orig:%d", l3sd->l3sd_orig);
127     s = format(s, "\n%U", format_white_space, indent);
128     s = format(s, "%U", format_dpo_id, &l3sd->l3sd_dpo, indent+2);
129
130     return (s);
131 }
132
133 static void
134 l3_span_dpo_lock (dpo_id_t *dpo)
135 {
136     l3_span_dpo_t *l3sd;
137
138     l3sd = l3_span_dpo_get(dpo->dpoi_index);
139
140     l3sd->l3sd_locks++;
141 }
142
143 static void
144 l3_span_dpo_unlock_dpo (dpo_id_t *dpo)
145 {
146     l3_span_dpo_unlock(dpo->dpoi_index);
147 }
148
149 void
150 l3_span_dpo_unlock (index_t l3sdi)
151 {
152     l3_span_dpo_t *l3sd;
153
154     l3sd = l3_span_dpo_get(l3sdi);
155
156     l3sd->l3sd_locks--;
157
158     if (0 == l3sd->l3sd_locks)
159     {
160         dpo_reset(&l3sd->l3sd_dpo);
161         pool_put(l3_span_dpo_pool, l3sd);
162     }
163 }
164
165 /**
166  * @brief A struct to hold tracing information for the span
167  * node.
168  */
169 typedef struct l3_span_trace_t_
170 {
171     /**
172      * The index of the original L3-span
173      */
174     index_t orig;
175 } l3_span_trace_t;
176
177 always_inline uword
178 l3_span_dpo_inline (vlib_main_t * vm,
179                     vlib_node_runtime_t * node,
180                     vlib_frame_t * from_frame)
181 {
182     u32 n_left_from, next_index, * from, * to_next;
183
184     from = vlib_frame_vector_args (from_frame);
185     n_left_from = from_frame->n_vectors;
186
187     next_index = node->cached_next_index;
188
189     while (n_left_from > 0)
190     {
191         u32 n_left_to_next;
192
193         vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
194
195         while (n_left_from > 0 && n_left_to_next > 0)
196         {
197             l3_span_dpo_t *l3sd0;
198             vlib_buffer_t * b0;
199             u32 bi0, l3sdi0;
200             u32 next0;
201
202             bi0 = from[0];
203             to_next[0] = bi0;
204             from += 1;
205             to_next += 1;
206             n_left_from -= 1;
207             n_left_to_next -= 1;
208
209             b0 = vlib_get_buffer (vm, bi0);
210
211             /* dst lookup was done by ip4 lookup */
212             l3sdi0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX];
213             l3sd0 = l3_span_dpo_get(l3sdi0);
214
215             next0 = l3sd0->l3sd_dpo.dpoi_next_node;
216             vnet_buffer(b0)->ip.adj_index[VLIB_TX] = l3sd0->l3sd_dpo.dpoi_index;
217
218             if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
219             {
220                 l3_span_trace_t *tr =
221                     vlib_add_trace (vm, node, b0, sizeof (*tr));
222                 tr->orig = l3sd0->l3sd_orig;
223             }
224
225             vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next,
226                                             n_left_to_next, bi0, next0);
227         }
228         vlib_put_next_frame (vm, node, next_index, n_left_to_next);
229     }
230     return from_frame->n_vectors;
231 }
232
233 static u8 *
234 format_l3_span_trace (u8 * s, va_list * args)
235 {
236     CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
237     CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
238     l3_span_trace_t * t;
239
240     t = va_arg (*args, l3_span_trace_t *);
241
242     s = format (s, "original:%d", t->orig);
243
244     return (s);
245 }
246
247 static uword
248 ip4_l3_span (vlib_main_t * vm,
249                            vlib_node_runtime_t * node,
250                            vlib_frame_t * frame)
251 {
252     return (l3_span_dpo_inline(vm, node, frame));
253 }
254
255 VLIB_REGISTER_NODE (ip4_l3_span_node) = {
256     .function = ip4_l3_span,
257     .name = "ip4-l3-span",
258     .vector_size = sizeof (u32),
259
260     .format_trace = format_l3_span_trace,
261     .n_next_nodes = 1,
262     .next_nodes = {
263         [0] = "ip4-drop",
264     }
265 };
266 VLIB_NODE_FUNCTION_MULTIARCH (ip4_l3_span_node,
267                               ip4_l3_span)
268
269 static uword
270 ip6_l3_span (vlib_main_t * vm,
271                            vlib_node_runtime_t * node,
272                            vlib_frame_t * frame)
273 {
274     return (l3_span_dpo_inline(vm, node, frame));
275 }
276
277 VLIB_REGISTER_NODE (ip6_l3_span_node) = {
278     .function = ip6_l3_span,
279     .name = "ip6-l3-span",
280     .vector_size = sizeof (u32),
281
282     .format_trace = format_l3_span_trace,
283     .n_next_nodes = 1,
284     .next_nodes = {
285         [0] = "ip6-drop",
286     }
287 };
288 VLIB_NODE_FUNCTION_MULTIARCH (ip6_l3_span_node,
289                               ip6_l3_span)
290
291 static void
292 l3_span_dpo_mk_interpose (const dpo_id_t *original,
293                           const dpo_id_t *parent,
294                           dpo_id_t *clone)
295 {
296     l3_span_dpo_t *l3sd_orig, *l3sd_clone;
297     u16 n_buckets;
298     index_t repi;
299     u16 ii;
300
301     l3sd_clone = l3_span_dpo_alloc();
302     l3sd_orig = l3_span_dpo_get(original->dpoi_index);
303     ASSERT(!(l3sd_orig->l3sd_flags & L3_SPAN_DPO_FLAG_CLONE));
304
305     l3sd_clone->l3sd_flags |= L3_SPAN_DPO_FLAG_CLONE;
306     l3sd_clone->l3sd_orig = original->dpoi_index;
307
308     n_buckets = load_balance_n_buckets(l3sd_orig->l3sd_dpo.dpoi_index);
309
310     /*
311      * create a new replicate with one extra bucket. We'll
312      * use that extra bucket to stack the parent FIB gives us
313      * during the clone.
314      */
315     repi = replicate_create(n_buckets + 1,
316                             l3sd_orig->l3sd_dpo.dpoi_proto);
317
318     /*
319      * set the first buckets in the replicte to match the original's.
320      * these are the paths to the collectors
321      */
322     for (ii = 0; ii <n_buckets; ii++)
323     {
324         replicate_set_bucket
325             (repi, ii,
326              load_balance_get_bucket(l3sd_orig->l3sd_dpo.dpoi_index, ii));
327     }
328
329     /*
330      * in the last bucket stack the DPO given by FIB - this is how
331      * the FIB entry will forward.
332      */
333     replicate_set_bucket(repi, n_buckets, parent);
334
335     /*
336      * save the replicate we just created on the cloned span
337      */
338     dpo_set(&l3sd_clone->l3sd_dpo,
339             DPO_REPLICATE,
340             l3sd_orig->l3sd_dpo.dpoi_proto,
341             repi);
342
343     /*
344      * construct the Span clone for return to the caller
345      */
346     dpo_set(clone,
347             l3_span_dpo_type,
348             l3sd_orig->l3sd_dpo.dpoi_proto,
349             l3_span_dpo_get_index(l3sd_clone));
350 }
351
352
353 static void
354 l3_span_dpo_mem_show (void)
355 {
356     fib_show_memory_usage("L3 Span",
357                           pool_elts(l3_span_dpo_pool),
358                           pool_len(l3_span_dpo_pool),
359                           sizeof(l3_span_dpo_t));
360 }
361
362 const static dpo_vft_t l3sd_vft = {
363     .dv_lock = l3_span_dpo_lock,
364     .dv_unlock = l3_span_dpo_unlock_dpo,
365     .dv_format = format_l3_span_dpo,
366     .dv_mem_show = l3_span_dpo_mem_show,
367     .dv_mk_interpose = l3_span_dpo_mk_interpose,
368 };
369
370 const static char* const l3_span_ip4_nodes[] =
371 {
372     "ip4-l3-span",
373     NULL,
374 };
375 const static char* const l3_span_ip6_nodes[] =
376 {
377     "ip6-l3-span",
378     NULL,
379 };
380
381 const static char* const * const l3_span_dpo_nodes[DPO_PROTO_NUM] =
382 {
383     [DPO_PROTO_IP4]  = l3_span_ip4_nodes,
384     [DPO_PROTO_IP6]  = l3_span_ip6_nodes,
385 };
386
387 void
388 l3_span_dpo_module_init (void)
389 {
390     l3_span_dpo_type =
391         dpo_register_new_type(&l3sd_vft, l3_span_dpo_nodes);
392 }