MPLS Mcast
[vpp.git] / src / vnet / dpo / mpls_disposition.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 #include <vnet/ip/ip.h>
17 #include <vnet/dpo/mpls_disposition.h>
18 #include <vnet/mpls/mpls.h>
19
20 /*
21  * pool of all MPLS Label DPOs
22  */
23 mpls_disp_dpo_t *mpls_disp_dpo_pool;
24
25 static mpls_disp_dpo_t *
26 mpls_disp_dpo_alloc (void)
27 {
28     mpls_disp_dpo_t *mdd;
29
30     pool_get_aligned(mpls_disp_dpo_pool, mdd, CLIB_CACHE_LINE_BYTES);
31     memset(mdd, 0, sizeof(*mdd));
32
33     dpo_reset(&mdd->mdd_dpo);
34
35     return (mdd);
36 }
37
38 static index_t
39 mpls_disp_dpo_get_index (mpls_disp_dpo_t *mdd)
40 {
41     return (mdd - mpls_disp_dpo_pool);
42 }
43
44 index_t
45 mpls_disp_dpo_create (dpo_proto_t payload_proto,
46                       fib_rpf_id_t rpf_id,
47                       const dpo_id_t *dpo)
48 {
49     mpls_disp_dpo_t *mdd;
50
51     mdd = mpls_disp_dpo_alloc();
52
53     mdd->mdd_payload_proto = payload_proto;
54     mdd->mdd_rpf_id = rpf_id;
55
56     dpo_stack(DPO_MPLS_DISPOSITION,
57               mdd->mdd_payload_proto,
58               &mdd->mdd_dpo,
59               dpo);
60
61     return (mpls_disp_dpo_get_index(mdd));
62 }
63
64 u8*
65 format_mpls_disp_dpo (u8 *s, va_list *args)
66 {
67     index_t index = va_arg (*args, index_t);
68     u32 indent = va_arg (*args, u32);
69     mpls_disp_dpo_t *mdd;
70
71     mdd = mpls_disp_dpo_get(index);
72
73     s = format(s, "mpls-disposition:[%d]:[%U]",
74                index,
75                format_dpo_proto, mdd->mdd_payload_proto);
76
77     s = format(s, "\n%U", format_white_space, indent);
78     s = format(s, "%U", format_dpo_id, &mdd->mdd_dpo, indent+2);
79
80     return (s);
81 }
82
83 static void
84 mpls_disp_dpo_lock (dpo_id_t *dpo)
85 {
86     mpls_disp_dpo_t *mdd;
87
88     mdd = mpls_disp_dpo_get(dpo->dpoi_index);
89
90     mdd->mdd_locks++;
91 }
92
93 static void
94 mpls_disp_dpo_unlock (dpo_id_t *dpo)
95 {
96     mpls_disp_dpo_t *mdd;
97
98     mdd = mpls_disp_dpo_get(dpo->dpoi_index);
99
100     mdd->mdd_locks--;
101
102     if (0 == mdd->mdd_locks)
103     {
104         dpo_reset(&mdd->mdd_dpo);
105         pool_put(mpls_disp_dpo_pool, mdd);
106     }
107 }
108
109 /**
110  * @brief A struct to hold tracing information for the MPLS label disposition
111  * node.
112  */
113 typedef struct mpls_label_disposition_trace_t_
114 {
115     index_t mdd;
116 } mpls_label_disposition_trace_t;
117
118 always_inline uword
119 mpls_label_disposition_inline (vlib_main_t * vm,
120                               vlib_node_runtime_t * node,
121                               vlib_frame_t * from_frame,
122                               u8 payload_is_ip4,
123                               u8 payload_is_ip6)
124 {
125     u32 n_left_from, next_index, * from, * to_next;
126
127     from = vlib_frame_vector_args (from_frame);
128     n_left_from = from_frame->n_vectors;
129
130     next_index = node->cached_next_index;
131
132     while (n_left_from > 0)
133     {
134         u32 n_left_to_next;
135
136         vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
137
138         while (n_left_from >= 4 && n_left_to_next >= 2)
139         {
140             mpls_disp_dpo_t *mdd0, *mdd1;
141             u32 bi0, mddi0, bi1, mddi1;
142             vlib_buffer_t * b0, *b1;
143             u32 next0, next1;
144
145             bi0 = to_next[0] = from[0];
146             bi1 = to_next[1] = from[1];
147
148             /* Prefetch next iteration. */
149             {
150                 vlib_buffer_t * p2, * p3;
151
152                 p2 = vlib_get_buffer (vm, from[2]);
153                 p3 = vlib_get_buffer (vm, from[3]);
154
155                 vlib_prefetch_buffer_header (p2, STORE);
156                 vlib_prefetch_buffer_header (p3, STORE);
157
158                 CLIB_PREFETCH (p2->data, sizeof (ip6_header_t), STORE);
159                 CLIB_PREFETCH (p3->data, sizeof (ip6_header_t), STORE);
160             }
161
162             from += 2;
163             to_next += 2;
164             n_left_from -= 2;
165             n_left_to_next -= 2;
166
167             b0 = vlib_get_buffer (vm, bi0);
168             b1 = vlib_get_buffer (vm, bi1);
169
170             /* dst lookup was done by ip4 lookup */
171             mddi0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX];
172             mddi1 = vnet_buffer(b1)->ip.adj_index[VLIB_TX];
173             mdd0 = mpls_disp_dpo_get(mddi0);
174             mdd1 = mpls_disp_dpo_get(mddi1);
175
176             if (payload_is_ip4)
177             {
178                 /*
179                  * decrement the TTL on ingress to the LSP
180                  */
181             }
182             else if (payload_is_ip6)
183             {
184                 /*
185                  * decrement the TTL on ingress to the LSP
186                  */
187             }
188  
189             next0 = mdd0->mdd_dpo.dpoi_next_node;
190             next1 = mdd1->mdd_dpo.dpoi_next_node;
191             vnet_buffer(b0)->ip.adj_index[VLIB_TX] = mdd0->mdd_dpo.dpoi_index;
192             vnet_buffer(b1)->ip.adj_index[VLIB_TX] = mdd1->mdd_dpo.dpoi_index;
193             vnet_buffer(b0)->ip.rpf_id = mdd0->mdd_rpf_id;
194             vnet_buffer(b1)->ip.rpf_id = mdd1->mdd_rpf_id;
195
196             if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
197             {
198                 mpls_label_disposition_trace_t *tr =
199                     vlib_add_trace (vm, node, b0, sizeof (*tr));
200
201                 tr->mdd = mddi0;
202             }
203             if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED))
204             {
205                 mpls_label_disposition_trace_t *tr =
206                     vlib_add_trace (vm, node, b1, sizeof (*tr));
207                 tr->mdd = mddi1;
208             }
209
210             vlib_validate_buffer_enqueue_x2(vm, node, next_index, to_next,
211                                             n_left_to_next,
212                                             bi0, bi1, next0, next1);
213         }
214
215         while (n_left_from > 0 && n_left_to_next > 0)
216         {
217             mpls_disp_dpo_t *mdd0;
218             vlib_buffer_t * b0;
219             u32 bi0, mddi0;
220             u32 next0;
221
222             bi0 = from[0];
223             to_next[0] = bi0;
224             from += 1;
225             to_next += 1;
226             n_left_from -= 1;
227             n_left_to_next -= 1;
228
229             b0 = vlib_get_buffer (vm, bi0);
230
231             /* dst lookup was done by ip4 lookup */
232             mddi0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX];
233             mdd0 = mpls_disp_dpo_get(mddi0);
234
235             if (payload_is_ip4)
236             {
237                 /*
238                  * decrement the TTL on ingress to the LSP
239                  */
240             }
241             else if (payload_is_ip6)
242             {
243                 /*
244                  * decrement the TTL on ingress to the LSP
245                  */
246             }
247             else
248             {
249             }
250
251             next0 = mdd0->mdd_dpo.dpoi_next_node;
252             vnet_buffer(b0)->ip.adj_index[VLIB_TX] = mdd0->mdd_dpo.dpoi_index;
253             vnet_buffer(b0)->ip.rpf_id = mdd0->mdd_rpf_id;
254
255             if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
256             {
257                 mpls_label_disposition_trace_t *tr =
258                     vlib_add_trace (vm, node, b0, sizeof (*tr));
259                 tr->mdd = mddi0;
260             }
261
262             vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next,
263                                             n_left_to_next, bi0, next0);
264         }
265         vlib_put_next_frame (vm, node, next_index, n_left_to_next);
266     }
267     return from_frame->n_vectors;
268 }
269
270 static u8 *
271 format_mpls_label_disposition_trace (u8 * s, va_list * args)
272 {
273     CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
274     CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
275     CLIB_UNUSED (mpls_label_disposition_trace_t * t);
276
277     t = va_arg (*args, mpls_label_disposition_trace_t *);
278
279     s = format(s, "disp:%d", t->mdd);
280     return (s);
281 }
282
283 static uword
284 ip4_mpls_label_disposition (vlib_main_t * vm,
285                            vlib_node_runtime_t * node,
286                            vlib_frame_t * frame)
287 {
288     return (mpls_label_disposition_inline(vm, node, frame, 1, 0));
289 }
290
291 VLIB_REGISTER_NODE (ip4_mpls_label_disposition_node) = {
292     .function = ip4_mpls_label_disposition,
293     .name = "ip4-mpls-label-disposition",
294     .vector_size = sizeof (u32),
295
296     .format_trace = format_mpls_label_disposition_trace,
297     .n_next_nodes = 1,
298     .next_nodes = {
299         [0] = "ip4-drop",
300     }
301 };
302 VLIB_NODE_FUNCTION_MULTIARCH (ip4_mpls_label_disposition_node,
303                               ip4_mpls_label_disposition)
304
305 static uword
306 ip6_mpls_label_disposition (vlib_main_t * vm,
307                            vlib_node_runtime_t * node,
308                            vlib_frame_t * frame)
309 {
310     return (mpls_label_disposition_inline(vm, node, frame, 0, 1));
311 }
312
313 VLIB_REGISTER_NODE (ip6_mpls_label_disposition_node) = {
314     .function = ip6_mpls_label_disposition,
315     .name = "ip6-mpls-label-disposition",
316     .vector_size = sizeof (u32),
317
318     .format_trace = format_mpls_label_disposition_trace,
319     .n_next_nodes = 1,
320     .next_nodes = {
321         [0] = "ip6-drop",
322     }
323 };
324 VLIB_NODE_FUNCTION_MULTIARCH (ip6_mpls_label_disposition_node,
325                               ip6_mpls_label_disposition)
326
327 static void
328 mpls_disp_dpo_mem_show (void)
329 {
330     fib_show_memory_usage("MPLS label",
331                           pool_elts(mpls_disp_dpo_pool),
332                           pool_len(mpls_disp_dpo_pool),
333                           sizeof(mpls_disp_dpo_t));
334 }
335
336 const static dpo_vft_t mdd_vft = {
337     .dv_lock = mpls_disp_dpo_lock,
338     .dv_unlock = mpls_disp_dpo_unlock,
339     .dv_format = format_mpls_disp_dpo,
340     .dv_mem_show = mpls_disp_dpo_mem_show,
341 };
342
343 const static char* const mpls_label_disp_ip4_nodes[] =
344 {
345     "ip4-mpls-label-disposition",
346     NULL,
347 };
348 const static char* const mpls_label_disp_ip6_nodes[] =
349 {
350     "ip6-mpls-label-disposition",
351     NULL,
352 };
353 const static char* const * const mpls_label_disp_nodes[DPO_PROTO_NUM] =
354 {
355     [DPO_PROTO_IP4]  = mpls_label_disp_ip4_nodes,
356     [DPO_PROTO_IP6]  = mpls_label_disp_ip6_nodes,
357 };
358
359
360 void
361 mpls_disp_dpo_module_init (void)
362 {
363     dpo_register(DPO_MPLS_DISPOSITION, &mdd_vft, mpls_label_disp_nodes);
364 }