fib: fib api updates
[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/ip4_input.h>
17 #include <vnet/ip/ip6_input.h>
18 #include <vnet/dpo/mpls_disposition.h>
19 #include <vnet/mpls/mpls.h>
20
21 #ifndef CLIB_MARCH_VARIANT
22 /*
23  * pool of all MPLS Label DPOs
24  */
25 mpls_disp_dpo_t *mpls_disp_dpo_pool;
26
27 static mpls_disp_dpo_t *
28 mpls_disp_dpo_alloc (void)
29 {
30     mpls_disp_dpo_t *mdd;
31
32     pool_get_aligned(mpls_disp_dpo_pool, mdd, CLIB_CACHE_LINE_BYTES);
33     clib_memset(mdd, 0, sizeof(*mdd));
34
35     dpo_reset(&mdd->mdd_dpo);
36
37     return (mdd);
38 }
39
40 static index_t
41 mpls_disp_dpo_get_index (mpls_disp_dpo_t *mdd)
42 {
43     return (mdd - mpls_disp_dpo_pool);
44 }
45
46 void
47 mpls_disp_dpo_create (dpo_proto_t payload_proto,
48                       fib_rpf_id_t rpf_id,
49                       fib_mpls_lsp_mode_t mode,
50                       const dpo_id_t *parent,
51                       dpo_id_t *dpo)
52 {
53     mpls_disp_dpo_t *mdd;
54     dpo_type_t dtype;
55
56     mdd = mpls_disp_dpo_alloc();
57
58     mdd->mdd_payload_proto = payload_proto;
59     mdd->mdd_rpf_id = rpf_id;
60     mdd->mdd_mode = mode;
61     dtype = (FIB_MPLS_LSP_MODE_PIPE == mode ?
62              DPO_MPLS_DISPOSITION_PIPE :
63              DPO_MPLS_DISPOSITION_UNIFORM);
64
65     /*
66      * stack this disposition object on the parent given
67      */
68     dpo_stack(dtype,
69               mdd->mdd_payload_proto,
70               &mdd->mdd_dpo,
71               parent);
72
73     /*
74      * set up the return DPO to refer to this object
75      */
76     dpo_set(dpo,
77             dtype,
78             payload_proto,
79             mpls_disp_dpo_get_index(mdd));
80 }
81
82 u8*
83 format_mpls_disp_dpo (u8 *s, va_list *args)
84 {
85     index_t index = va_arg(*args, index_t);
86     u32 indent = va_arg(*args, u32);
87     mpls_disp_dpo_t *mdd;
88
89     mdd = mpls_disp_dpo_get(index);
90
91     s = format(s, "mpls-disposition:[%d]:[", index);
92
93     if (0 != mdd->mdd_rpf_id)
94         s = format(s, "rpf-id:%d ", mdd->mdd_rpf_id);
95
96     s = format(s, "%U, %U]",
97                format_dpo_proto, mdd->mdd_payload_proto,
98                format_fib_mpls_lsp_mode, mdd->mdd_mode);
99
100     s = format(s, "\n%U", format_white_space, indent);
101     s = format(s, "%U", format_dpo_id, &mdd->mdd_dpo, indent+2);
102
103     return (s);
104 }
105
106 static void
107 mpls_disp_dpo_lock (dpo_id_t *dpo)
108 {
109     mpls_disp_dpo_t *mdd;
110
111     mdd = mpls_disp_dpo_get(dpo->dpoi_index);
112
113     mdd->mdd_locks++;
114 }
115
116 static void
117 mpls_disp_dpo_unlock (dpo_id_t *dpo)
118 {
119     mpls_disp_dpo_t *mdd;
120
121     mdd = mpls_disp_dpo_get(dpo->dpoi_index);
122
123     mdd->mdd_locks--;
124
125     if (0 == mdd->mdd_locks)
126     {
127         dpo_reset(&mdd->mdd_dpo);
128         pool_put(mpls_disp_dpo_pool, mdd);
129     }
130 }
131 #endif /* CLIB_MARCH_VARIANT */
132
133 /**
134  * @brief A struct to hold tracing information for the MPLS label disposition
135  * node.
136  */
137 typedef struct mpls_label_disposition_trace_t_
138 {
139     dpo_proto_t mddt_payload_proto;
140     fib_rpf_id_t mddt_rpf_id;
141     fib_mpls_lsp_mode_t mddt_mode;
142 } mpls_label_disposition_trace_t;
143
144 extern vlib_node_registration_t ip4_mpls_label_disposition_pipe_node;
145 extern vlib_node_registration_t ip6_mpls_label_disposition_pipe_node;
146 extern vlib_node_registration_t ip4_mpls_label_disposition_uniform_node;
147 extern vlib_node_registration_t ip6_mpls_label_disposition_uniform_node;
148
149 always_inline uword
150 mpls_label_disposition_inline (vlib_main_t * vm,
151                                vlib_node_runtime_t * node,
152                                vlib_frame_t * from_frame,
153                                u8 payload_is_ip4,
154                                u8 payload_is_ip6,
155                                fib_mpls_lsp_mode_t mode)
156 {
157     u32 n_left_from, next_index, * from, * to_next;
158     vlib_node_runtime_t *error_node;
159
160     if (payload_is_ip4)
161     {
162         if (FIB_MPLS_LSP_MODE_PIPE == mode)
163             error_node =
164                 vlib_node_get_runtime(vm, ip4_mpls_label_disposition_pipe_node.index);
165         else
166             error_node =
167                 vlib_node_get_runtime(vm, ip4_mpls_label_disposition_uniform_node.index);
168     }
169     else
170     {
171         if (FIB_MPLS_LSP_MODE_PIPE == mode)
172             error_node =
173                 vlib_node_get_runtime(vm, ip6_mpls_label_disposition_pipe_node.index);
174         else
175             error_node =
176                 vlib_node_get_runtime(vm, ip6_mpls_label_disposition_uniform_node.index);
177     }
178     from = vlib_frame_vector_args(from_frame);
179     n_left_from = from_frame->n_vectors;
180
181     next_index = node->cached_next_index;
182
183     while (n_left_from > 0)
184     {
185         u32 n_left_to_next;
186
187         vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
188
189         while (n_left_from >= 4 && n_left_to_next >= 2)
190         {
191             mpls_disp_dpo_t *mdd0, *mdd1;
192             u32 bi0, mddi0, bi1, mddi1;
193             vlib_buffer_t * b0, *b1;
194             u32 next0, next1;
195
196             bi0 = to_next[0] = from[0];
197             bi1 = to_next[1] = from[1];
198
199             /* Prefetch next iteration. */
200             {
201                 vlib_buffer_t * p2, * p3;
202
203                 p2 = vlib_get_buffer(vm, from[2]);
204                 p3 = vlib_get_buffer(vm, from[3]);
205
206                 vlib_prefetch_buffer_header(p2, STORE);
207                 vlib_prefetch_buffer_header(p3, STORE);
208
209                 CLIB_PREFETCH(p2->data, sizeof(ip6_header_t), STORE);
210                 CLIB_PREFETCH(p3->data, sizeof(ip6_header_t), STORE);
211             }
212
213             from += 2;
214             to_next += 2;
215             n_left_from -= 2;
216             n_left_to_next -= 2;
217
218             b0 = vlib_get_buffer(vm, bi0);
219             b1 = vlib_get_buffer(vm, bi1);
220
221             /* dst lookup was done by ip4 lookup */
222             mddi0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX];
223             mddi1 = vnet_buffer(b1)->ip.adj_index[VLIB_TX];
224             mdd0 = mpls_disp_dpo_get(mddi0);
225             mdd1 = mpls_disp_dpo_get(mddi1);
226
227             next0 = mdd0->mdd_dpo.dpoi_next_node;
228             next1 = mdd1->mdd_dpo.dpoi_next_node;
229
230             if (payload_is_ip4)
231             {
232                 ip4_header_t *ip0, *ip1;
233
234                 ip0 = vlib_buffer_get_current(b0);
235                 ip1 = vlib_buffer_get_current(b1);
236
237                 /*
238                  * IPv4 input checks on the exposed IP header
239                  * including checksum
240                  */
241                 ip4_input_check_x2(vm, error_node,
242                                    b0, b1, ip0, ip1,
243                                    &next0, &next1, 1);
244
245                 if (FIB_MPLS_LSP_MODE_UNIFORM == mode)
246                 {
247                     /*
248                      * Copy the TTL from the MPLS packet into the
249                      * exposed IP. recalc the chksum
250                      */
251                     ip0->ttl = vnet_buffer(b0)->mpls.ttl;
252                     ip1->ttl = vnet_buffer(b1)->mpls.ttl;
253                     ip0->tos = mpls_exp_to_ip_dscp(vnet_buffer(b0)->mpls.exp);
254                     ip1->tos = mpls_exp_to_ip_dscp(vnet_buffer(b1)->mpls.exp);
255
256                     ip0->checksum = ip4_header_checksum(ip0);
257                     ip1->checksum = ip4_header_checksum(ip1);
258                 }
259             }
260             else if (payload_is_ip6)
261             {
262                 ip6_header_t *ip0, *ip1;
263
264                 ip0 = vlib_buffer_get_current(b0);
265                 ip1 = vlib_buffer_get_current(b1);
266
267                 /*
268                  * IPv6 input checks on the exposed IP header
269                  */
270                 ip6_input_check_x2(vm, error_node,
271                                    b0, b1, ip0, ip1,
272                                    &next0, &next1);
273
274                 if (FIB_MPLS_LSP_MODE_UNIFORM == mode)
275                 {
276                     /*
277                      * Copy the TTL from the MPLS packet into the
278                      * exposed IP
279                      */
280                     ip0->hop_limit = vnet_buffer(b0)->mpls.ttl;
281                     ip1->hop_limit = vnet_buffer(b1)->mpls.ttl;
282
283                     ip6_set_traffic_class_network_order(
284                         ip0,
285                         mpls_exp_to_ip_dscp(vnet_buffer(b0)->mpls.exp));
286                     ip6_set_traffic_class_network_order(
287                         ip1,
288                         mpls_exp_to_ip_dscp(vnet_buffer(b1)->mpls.exp));
289                 }
290             }
291
292             vnet_buffer(b0)->ip.adj_index[VLIB_TX] = mdd0->mdd_dpo.dpoi_index;
293             vnet_buffer(b1)->ip.adj_index[VLIB_TX] = mdd1->mdd_dpo.dpoi_index;
294             vnet_buffer(b0)->ip.rpf_id = mdd0->mdd_rpf_id;
295             vnet_buffer(b1)->ip.rpf_id = mdd1->mdd_rpf_id;
296
297             if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
298             {
299                 mpls_label_disposition_trace_t *tr =
300                     vlib_add_trace(vm, node, b0, sizeof(*tr));
301
302                 tr->mddt_payload_proto = mdd0->mdd_payload_proto;
303                 tr->mddt_rpf_id = mdd0->mdd_rpf_id;
304                 tr->mddt_mode = mdd0->mdd_mode;
305             }
306             if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED))
307             {
308                 mpls_label_disposition_trace_t *tr =
309                     vlib_add_trace(vm, node, b1, sizeof(*tr));
310                 tr->mddt_payload_proto = mdd1->mdd_payload_proto;
311                 tr->mddt_rpf_id = mdd1->mdd_rpf_id;
312                 tr->mddt_mode = mdd1->mdd_mode;
313             }
314
315             vlib_validate_buffer_enqueue_x2(vm, node, next_index, to_next,
316                                             n_left_to_next,
317                                             bi0, bi1, next0, next1);
318         }
319
320         while (n_left_from > 0 && n_left_to_next > 0)
321         {
322             mpls_disp_dpo_t *mdd0;
323             vlib_buffer_t * b0;
324             u32 bi0, mddi0;
325             u32 next0;
326
327             bi0 = from[0];
328             to_next[0] = bi0;
329             from += 1;
330             to_next += 1;
331             n_left_from -= 1;
332             n_left_to_next -= 1;
333
334             b0 = vlib_get_buffer(vm, bi0);
335
336             /* dst lookup was done by ip4 lookup */
337             mddi0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX];
338             mdd0 = mpls_disp_dpo_get(mddi0);
339             next0 = mdd0->mdd_dpo.dpoi_next_node;
340
341             if (payload_is_ip4)
342             {
343                 ip4_header_t *ip0;
344
345                 ip0 = vlib_buffer_get_current(b0);
346
347                 /*
348                  * IPv4 input checks on the exposed IP header
349                  * including checksum
350                  */
351                 ip4_input_check_x1(vm, error_node, b0, ip0, &next0, 1);
352
353                 if (FIB_MPLS_LSP_MODE_UNIFORM == mode)
354                 {
355                     /*
356                      * Copy the TTL from the MPLS packet into the
357                      * exposed IP. recalc the chksum
358                      */
359                     ip0->ttl = vnet_buffer(b0)->mpls.ttl;
360                     ip0->tos = mpls_exp_to_ip_dscp(vnet_buffer(b0)->mpls.exp);
361                     ip0->checksum = ip4_header_checksum(ip0);
362                 }
363             }
364             else if (payload_is_ip6)
365             {
366                 ip6_header_t *ip0;
367
368                 ip0 = vlib_buffer_get_current(b0);
369
370                 /*
371                  * IPv6 input checks on the exposed IP header
372                  */
373                 ip6_input_check_x1(vm, error_node, b0, ip0, &next0);
374
375                 if (FIB_MPLS_LSP_MODE_UNIFORM == mode)
376                 {
377                     /*
378                      * Copy the TTL from the MPLS packet into the
379                      * exposed IP
380                      */
381                     ip0->hop_limit = vnet_buffer(b0)->mpls.ttl;
382
383                     ip6_set_traffic_class_network_order(
384                         ip0,
385                         mpls_exp_to_ip_dscp(vnet_buffer(b0)->mpls.exp));
386                 }
387             }
388
389             vnet_buffer(b0)->ip.adj_index[VLIB_TX] = mdd0->mdd_dpo.dpoi_index;
390             vnet_buffer(b0)->ip.rpf_id = mdd0->mdd_rpf_id;
391
392             if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
393             {
394                 mpls_label_disposition_trace_t *tr =
395                     vlib_add_trace(vm, node, b0, sizeof(*tr));
396                 tr->mddt_payload_proto = mdd0->mdd_payload_proto;
397                 tr->mddt_rpf_id = mdd0->mdd_rpf_id;
398                 tr->mddt_mode = mdd0->mdd_mode;
399             }
400
401             vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next,
402                                             n_left_to_next, bi0, next0);
403         }
404         vlib_put_next_frame(vm, node, next_index, n_left_to_next);
405     }
406     return from_frame->n_vectors;
407 }
408
409 static u8 *
410 format_mpls_label_disposition_trace (u8 * s, va_list * args)
411 {
412     CLIB_UNUSED(vlib_main_t * vm) = va_arg(*args, vlib_main_t *);
413     CLIB_UNUSED(vlib_node_t * node) = va_arg(*args, vlib_node_t *);
414     CLIB_UNUSED(mpls_label_disposition_trace_t * t);
415
416     t = va_arg(*args, mpls_label_disposition_trace_t *);
417
418     s = format(s, "rpf-id:%d %U, %U",
419                t->mddt_rpf_id,
420                format_dpo_proto, t->mddt_payload_proto,
421                format_fib_mpls_lsp_mode, t->mddt_mode);
422
423     return (s);
424 }
425
426 VLIB_NODE_FN (ip4_mpls_label_disposition_pipe_node) (vlib_main_t * vm,
427                                  vlib_node_runtime_t * node,
428                                  vlib_frame_t * frame)
429 {
430     return (mpls_label_disposition_inline(vm, node, frame, 1, 0,
431                                           FIB_MPLS_LSP_MODE_PIPE));
432 }
433
434 VLIB_REGISTER_NODE(ip4_mpls_label_disposition_pipe_node) = {
435     .name = "ip4-mpls-label-disposition-pipe",
436     .vector_size = sizeof(u32),
437
438     .format_trace = format_mpls_label_disposition_trace,
439     .sibling_of = "ip4-input",
440     .n_errors = IP4_N_ERROR,
441     .error_strings = ip4_error_strings,
442 };
443
444 VLIB_NODE_FN (ip6_mpls_label_disposition_pipe_node) (vlib_main_t * vm,
445                                  vlib_node_runtime_t * node,
446                                  vlib_frame_t * frame)
447 {
448     return (mpls_label_disposition_inline(vm, node, frame, 0, 1,
449                                           FIB_MPLS_LSP_MODE_PIPE));
450 }
451
452 VLIB_REGISTER_NODE(ip6_mpls_label_disposition_pipe_node) = {
453     .name = "ip6-mpls-label-disposition-pipe",
454     .vector_size = sizeof(u32),
455
456     .format_trace = format_mpls_label_disposition_trace,
457     .sibling_of = "ip6-input",
458     .n_errors = IP6_N_ERROR,
459     .error_strings = ip6_error_strings,
460 };
461
462 VLIB_NODE_FN (ip4_mpls_label_disposition_uniform_node) (vlib_main_t * vm,
463                                  vlib_node_runtime_t * node,
464                                  vlib_frame_t * frame)
465 {
466     return (mpls_label_disposition_inline(vm, node, frame, 1, 0,
467                                           FIB_MPLS_LSP_MODE_UNIFORM));
468 }
469
470 VLIB_REGISTER_NODE(ip4_mpls_label_disposition_uniform_node) = {
471     .name = "ip4-mpls-label-disposition-uniform",
472     .vector_size = sizeof(u32),
473
474     .format_trace = format_mpls_label_disposition_trace,
475     .sibling_of = "ip4-input",
476     .n_errors = IP4_N_ERROR,
477     .error_strings = ip4_error_strings,
478 };
479
480 VLIB_NODE_FN (ip6_mpls_label_disposition_uniform_node) (vlib_main_t * vm,
481                                     vlib_node_runtime_t * node,
482                                     vlib_frame_t * frame)
483 {
484     return (mpls_label_disposition_inline(vm, node, frame, 0, 1,
485                                           FIB_MPLS_LSP_MODE_UNIFORM));
486 }
487
488 VLIB_REGISTER_NODE(ip6_mpls_label_disposition_uniform_node) = {
489     .name = "ip6-mpls-label-disposition-uniform",
490     .vector_size = sizeof(u32),
491
492     .format_trace = format_mpls_label_disposition_trace,
493     .sibling_of = "ip6-input",
494     .n_errors = IP6_N_ERROR,
495     .error_strings = ip6_error_strings,
496 };
497
498 #ifndef CLIB_MARCH_VARIANT
499 static void
500 mpls_disp_dpo_mem_show (void)
501 {
502     fib_show_memory_usage("MPLS label",
503                           pool_elts(mpls_disp_dpo_pool),
504                           pool_len(mpls_disp_dpo_pool),
505                           sizeof(mpls_disp_dpo_t));
506 }
507
508 const static dpo_vft_t mdd_vft = {
509     .dv_lock = mpls_disp_dpo_lock,
510     .dv_unlock = mpls_disp_dpo_unlock,
511     .dv_format = format_mpls_disp_dpo,
512     .dv_mem_show = mpls_disp_dpo_mem_show,
513 };
514
515 const static char* const mpls_label_disp_pipe_ip4_nodes[] =
516 {
517     "ip4-mpls-label-disposition-pipe",
518     NULL,
519 };
520 const static char* const mpls_label_disp_pipe_ip6_nodes[] =
521 {
522     "ip6-mpls-label-disposition-pipe",
523     NULL,
524 };
525 const static char* const * const mpls_label_disp_pipe_nodes[DPO_PROTO_NUM] =
526 {
527     [DPO_PROTO_IP4]  = mpls_label_disp_pipe_ip4_nodes,
528     [DPO_PROTO_IP6]  = mpls_label_disp_pipe_ip6_nodes,
529 };
530
531 const static char* const mpls_label_disp_uniform_ip4_nodes[] =
532 {
533     "ip4-mpls-label-disposition-uniform",
534     NULL,
535 };
536 const static char* const mpls_label_disp_uniform_ip6_nodes[] =
537 {
538     "ip6-mpls-label-disposition-uniform",
539     NULL,
540 };
541 const static char* const * const mpls_label_disp_uniform_nodes[DPO_PROTO_NUM] =
542 {
543     [DPO_PROTO_IP4]  = mpls_label_disp_uniform_ip4_nodes,
544     [DPO_PROTO_IP6]  = mpls_label_disp_uniform_ip6_nodes,
545 };
546
547
548 void
549 mpls_disp_dpo_module_init(void)
550 {
551     dpo_register(DPO_MPLS_DISPOSITION_PIPE, &mdd_vft,
552                  mpls_label_disp_pipe_nodes);
553     dpo_register(DPO_MPLS_DISPOSITION_UNIFORM, &mdd_vft,
554                  mpls_label_disp_uniform_nodes);
555 }
556 #endif /* CLIB_MARCH_VARIANT */