Line data Source code
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 90 : mpls_disp_dpo_alloc (void)
29 : {
30 : mpls_disp_dpo_t *mdd;
31 :
32 90 : pool_get_aligned(mpls_disp_dpo_pool, mdd, CLIB_CACHE_LINE_BYTES);
33 90 : clib_memset(mdd, 0, sizeof(*mdd));
34 :
35 90 : dpo_reset(&mdd->mdd_dpo);
36 :
37 90 : return (mdd);
38 : }
39 :
40 : static index_t
41 90 : mpls_disp_dpo_get_index (mpls_disp_dpo_t *mdd)
42 : {
43 90 : return (mdd - mpls_disp_dpo_pool);
44 : }
45 :
46 : void
47 90 : 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 90 : mdd = mpls_disp_dpo_alloc();
57 :
58 90 : mdd->mdd_payload_proto = payload_proto;
59 90 : mdd->mdd_rpf_id = rpf_id;
60 90 : mdd->mdd_mode = mode;
61 90 : dtype = (FIB_MPLS_LSP_MODE_PIPE == mode ?
62 90 : DPO_MPLS_DISPOSITION_PIPE :
63 : DPO_MPLS_DISPOSITION_UNIFORM);
64 :
65 : /*
66 : * stack this disposition object on the parent given
67 : */
68 90 : dpo_stack(dtype,
69 90 : 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 90 : dpo_set(dpo,
77 : dtype,
78 : payload_proto,
79 : mpls_disp_dpo_get_index(mdd));
80 90 : }
81 :
82 : u8*
83 865 : format_mpls_disp_dpo (u8 *s, va_list *args)
84 : {
85 865 : index_t index = va_arg(*args, index_t);
86 865 : u32 indent = va_arg(*args, u32);
87 : mpls_disp_dpo_t *mdd;
88 :
89 865 : mdd = mpls_disp_dpo_get(index);
90 :
91 865 : s = format(s, "mpls-disposition:[%d]:[", index);
92 :
93 865 : if (0 != mdd->mdd_rpf_id)
94 865 : s = format(s, "rpf-id:%d ", mdd->mdd_rpf_id);
95 :
96 865 : s = format(s, "%U, %U]",
97 865 : format_dpo_proto, mdd->mdd_payload_proto,
98 865 : format_fib_mpls_lsp_mode, mdd->mdd_mode);
99 :
100 865 : s = format(s, "\n%U", format_white_space, indent);
101 865 : s = format(s, "%U", format_dpo_id, &mdd->mdd_dpo, indent+2);
102 :
103 865 : return (s);
104 : }
105 :
106 : static void
107 318 : mpls_disp_dpo_lock (dpo_id_t *dpo)
108 : {
109 : mpls_disp_dpo_t *mdd;
110 :
111 318 : mdd = mpls_disp_dpo_get(dpo->dpoi_index);
112 :
113 318 : mdd->mdd_locks++;
114 318 : }
115 :
116 : static void
117 318 : mpls_disp_dpo_unlock (dpo_id_t *dpo)
118 : {
119 : mpls_disp_dpo_t *mdd;
120 :
121 318 : mdd = mpls_disp_dpo_get(dpo->dpoi_index);
122 :
123 318 : mdd->mdd_locks--;
124 :
125 318 : if (0 == mdd->mdd_locks)
126 : {
127 90 : dpo_reset(&mdd->mdd_dpo);
128 90 : pool_put(mpls_disp_dpo_pool, mdd);
129 : }
130 318 : }
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 61 : 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 61 : if (payload_is_ip4)
161 : {
162 31 : if (FIB_MPLS_LSP_MODE_PIPE == mode)
163 : error_node =
164 27 : vlib_node_get_runtime(vm, ip4_mpls_label_disposition_pipe_node.index);
165 : else
166 : error_node =
167 4 : vlib_node_get_runtime(vm, ip4_mpls_label_disposition_uniform_node.index);
168 : }
169 : else
170 : {
171 30 : if (FIB_MPLS_LSP_MODE_PIPE == mode)
172 : error_node =
173 28 : vlib_node_get_runtime(vm, ip6_mpls_label_disposition_pipe_node.index);
174 : else
175 : error_node =
176 2 : vlib_node_get_runtime(vm, ip6_mpls_label_disposition_uniform_node.index);
177 : }
178 61 : from = vlib_frame_vector_args(from_frame);
179 61 : n_left_from = from_frame->n_vectors;
180 :
181 61 : next_index = node->cached_next_index;
182 :
183 122 : while (n_left_from > 0)
184 : {
185 : u32 n_left_to_next;
186 :
187 61 : vlib_get_next_frame(vm, node, next_index, to_next, n_left_to_next);
188 :
189 3169 : 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 3108 : bi0 = to_next[0] = from[0];
197 3108 : bi1 = to_next[1] = from[1];
198 :
199 : /* Prefetch next iteration. */
200 : {
201 : vlib_buffer_t * p2, * p3;
202 :
203 3108 : p2 = vlib_get_buffer(vm, from[2]);
204 3108 : p3 = vlib_get_buffer(vm, from[3]);
205 :
206 3108 : vlib_prefetch_buffer_header(p2, STORE);
207 3108 : vlib_prefetch_buffer_header(p3, STORE);
208 :
209 3108 : CLIB_PREFETCH(p2->data, sizeof(ip6_header_t), STORE);
210 3108 : CLIB_PREFETCH(p3->data, sizeof(ip6_header_t), STORE);
211 : }
212 :
213 3108 : from += 2;
214 3108 : to_next += 2;
215 3108 : n_left_from -= 2;
216 3108 : n_left_to_next -= 2;
217 :
218 3108 : b0 = vlib_get_buffer(vm, bi0);
219 3108 : b1 = vlib_get_buffer(vm, bi1);
220 :
221 : /* dst lookup was done by ip4 lookup */
222 3108 : mddi0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX];
223 3108 : mddi1 = vnet_buffer(b1)->ip.adj_index[VLIB_TX];
224 3108 : mdd0 = mpls_disp_dpo_get(mddi0);
225 3108 : mdd1 = mpls_disp_dpo_get(mddi1);
226 :
227 3108 : next0 = mdd0->mdd_dpo.dpoi_next_node;
228 3108 : next1 = mdd1->mdd_dpo.dpoi_next_node;
229 :
230 3108 : if (payload_is_ip4)
231 : {
232 : ip4_header_t *ip0, *ip1;
233 :
234 1490 : ip0 = vlib_buffer_get_current(b0);
235 1490 : ip1 = vlib_buffer_get_current(b1);
236 :
237 : /*
238 : * IPv4 input checks on the exposed IP header
239 : * including checksum
240 : */
241 1490 : ip4_input_check_x2(vm, error_node,
242 : b0, b1, ip0, ip1,
243 : &next0, &next1, 1);
244 :
245 1490 : 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 254 : ip0->ttl = vnet_buffer(b0)->mpls.ttl;
252 254 : ip1->ttl = vnet_buffer(b1)->mpls.ttl;
253 254 : ip0->tos = mpls_exp_to_ip_dscp(vnet_buffer(b0)->mpls.exp);
254 254 : ip1->tos = mpls_exp_to_ip_dscp(vnet_buffer(b1)->mpls.exp);
255 :
256 254 : ip0->checksum = ip4_header_checksum(ip0);
257 254 : ip1->checksum = ip4_header_checksum(ip1);
258 : }
259 : }
260 1618 : else if (payload_is_ip6)
261 : {
262 : ip6_header_t *ip0, *ip1;
263 :
264 1618 : ip0 = vlib_buffer_get_current(b0);
265 1618 : ip1 = vlib_buffer_get_current(b1);
266 :
267 : /*
268 : * IPv6 input checks on the exposed IP header
269 : */
270 1618 : ip6_input_check_x2(vm, error_node,
271 : b0, b1, ip0, ip1,
272 : &next0, &next1);
273 :
274 1618 : if (FIB_MPLS_LSP_MODE_UNIFORM == mode)
275 : {
276 : /*
277 : * Copy the TTL from the MPLS packet into the
278 : * exposed IP
279 : */
280 127 : ip0->hop_limit = vnet_buffer(b0)->mpls.ttl;
281 127 : ip1->hop_limit = vnet_buffer(b1)->mpls.ttl;
282 :
283 127 : ip6_set_traffic_class_network_order(
284 : ip0,
285 127 : mpls_exp_to_ip_dscp(vnet_buffer(b0)->mpls.exp));
286 127 : ip6_set_traffic_class_network_order(
287 : ip1,
288 127 : mpls_exp_to_ip_dscp(vnet_buffer(b1)->mpls.exp));
289 : }
290 : }
291 :
292 3108 : vnet_buffer(b0)->ip.adj_index[VLIB_TX] = mdd0->mdd_dpo.dpoi_index;
293 3108 : vnet_buffer(b1)->ip.adj_index[VLIB_TX] = mdd1->mdd_dpo.dpoi_index;
294 3108 : vnet_buffer(b0)->ip.rpf_id = mdd0->mdd_rpf_id;
295 3108 : vnet_buffer(b1)->ip.rpf_id = mdd1->mdd_rpf_id;
296 :
297 3108 : if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
298 : {
299 : mpls_label_disposition_trace_t *tr =
300 3108 : vlib_add_trace(vm, node, b0, sizeof(*tr));
301 :
302 3108 : tr->mddt_payload_proto = mdd0->mdd_payload_proto;
303 3108 : tr->mddt_rpf_id = mdd0->mdd_rpf_id;
304 3108 : tr->mddt_mode = mdd0->mdd_mode;
305 : }
306 3108 : if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED))
307 : {
308 : mpls_label_disposition_trace_t *tr =
309 3108 : vlib_add_trace(vm, node, b1, sizeof(*tr));
310 3108 : tr->mddt_payload_proto = mdd1->mdd_payload_proto;
311 3108 : tr->mddt_rpf_id = mdd1->mdd_rpf_id;
312 3108 : tr->mddt_mode = mdd1->mdd_mode;
313 : }
314 :
315 3108 : vlib_validate_buffer_enqueue_x2(vm, node, next_index, to_next,
316 : n_left_to_next,
317 : bi0, bi1, next0, next1);
318 : }
319 :
320 188 : 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 127 : bi0 = from[0];
328 127 : to_next[0] = bi0;
329 127 : from += 1;
330 127 : to_next += 1;
331 127 : n_left_from -= 1;
332 127 : n_left_to_next -= 1;
333 :
334 127 : b0 = vlib_get_buffer(vm, bi0);
335 :
336 : /* dst lookup was done by ip4 lookup */
337 127 : mddi0 = vnet_buffer(b0)->ip.adj_index[VLIB_TX];
338 127 : mdd0 = mpls_disp_dpo_get(mddi0);
339 127 : next0 = mdd0->mdd_dpo.dpoi_next_node;
340 :
341 127 : if (payload_is_ip4)
342 : {
343 : ip4_header_t *ip0;
344 :
345 67 : ip0 = vlib_buffer_get_current(b0);
346 :
347 : /*
348 : * IPv4 input checks on the exposed IP header
349 : * including checksum
350 : */
351 67 : ip4_input_check_x1(vm, error_node, b0, ip0, &next0, 1);
352 :
353 67 : 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 6 : ip0->ttl = vnet_buffer(b0)->mpls.ttl;
360 6 : ip0->tos = mpls_exp_to_ip_dscp(vnet_buffer(b0)->mpls.exp);
361 6 : ip0->checksum = ip4_header_checksum(ip0);
362 : }
363 : }
364 60 : else if (payload_is_ip6)
365 : {
366 : ip6_header_t *ip0;
367 :
368 60 : ip0 = vlib_buffer_get_current(b0);
369 :
370 : /*
371 : * IPv6 input checks on the exposed IP header
372 : */
373 60 : ip6_input_check_x1(vm, error_node, b0, ip0, &next0);
374 :
375 60 : if (FIB_MPLS_LSP_MODE_UNIFORM == mode)
376 : {
377 : /*
378 : * Copy the TTL from the MPLS packet into the
379 : * exposed IP
380 : */
381 3 : ip0->hop_limit = vnet_buffer(b0)->mpls.ttl;
382 :
383 3 : ip6_set_traffic_class_network_order(
384 : ip0,
385 3 : mpls_exp_to_ip_dscp(vnet_buffer(b0)->mpls.exp));
386 : }
387 : }
388 :
389 127 : vnet_buffer(b0)->ip.adj_index[VLIB_TX] = mdd0->mdd_dpo.dpoi_index;
390 127 : vnet_buffer(b0)->ip.rpf_id = mdd0->mdd_rpf_id;
391 :
392 127 : if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
393 : {
394 : mpls_label_disposition_trace_t *tr =
395 127 : vlib_add_trace(vm, node, b0, sizeof(*tr));
396 127 : tr->mddt_payload_proto = mdd0->mdd_payload_proto;
397 127 : tr->mddt_rpf_id = mdd0->mdd_rpf_id;
398 127 : tr->mddt_mode = mdd0->mdd_mode;
399 : }
400 :
401 127 : vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next,
402 : n_left_to_next, bi0, next0);
403 : }
404 61 : vlib_put_next_frame(vm, node, next_index, n_left_to_next);
405 : }
406 61 : return from_frame->n_vectors;
407 : }
408 :
409 : static u8 *
410 2803 : format_mpls_label_disposition_trace (u8 * s, va_list * args)
411 : {
412 2803 : CLIB_UNUSED(vlib_main_t * vm) = va_arg(*args, vlib_main_t *);
413 2803 : CLIB_UNUSED(vlib_node_t * node) = va_arg(*args, vlib_node_t *);
414 : CLIB_UNUSED(mpls_label_disposition_trace_t * t);
415 :
416 2803 : t = va_arg(*args, mpls_label_disposition_trace_t *);
417 :
418 2803 : s = format(s, "rpf-id:%d %U, %U",
419 : t->mddt_rpf_id,
420 2803 : format_dpo_proto, t->mddt_payload_proto,
421 2803 : format_fib_mpls_lsp_mode, t->mddt_mode);
422 :
423 2803 : return (s);
424 : }
425 :
426 2327 : 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 27 : return (mpls_label_disposition_inline(vm, node, frame, 1, 0,
431 : FIB_MPLS_LSP_MODE_PIPE));
432 : }
433 :
434 183788 : 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_counters = ip4_error_counters,
442 : };
443 :
444 2328 : 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 28 : return (mpls_label_disposition_inline(vm, node, frame, 0, 1,
449 : FIB_MPLS_LSP_MODE_PIPE));
450 : }
451 :
452 183788 : 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_counters = ip6_error_counters,
460 : };
461 :
462 2304 : 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 4 : return (mpls_label_disposition_inline(vm, node, frame, 1, 0,
467 : FIB_MPLS_LSP_MODE_UNIFORM));
468 : }
469 :
470 183788 : 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_counters = ip4_error_counters,
478 : };
479 :
480 2302 : 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 2 : return (mpls_label_disposition_inline(vm, node, frame, 0, 1,
485 : FIB_MPLS_LSP_MODE_UNIFORM));
486 : }
487 :
488 183788 : 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_counters = ip6_error_counters,
496 : };
497 :
498 : #ifndef CLIB_MARCH_VARIANT
499 : static void
500 0 : mpls_disp_dpo_mem_show (void)
501 : {
502 0 : fib_show_memory_usage("MPLS label",
503 0 : pool_elts(mpls_disp_dpo_pool),
504 0 : pool_len(mpls_disp_dpo_pool),
505 : sizeof(mpls_disp_dpo_t));
506 0 : }
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 575 : mpls_disp_dpo_module_init(void)
550 : {
551 575 : dpo_register(DPO_MPLS_DISPOSITION_PIPE, &mdd_vft,
552 : mpls_label_disp_pipe_nodes);
553 575 : dpo_register(DPO_MPLS_DISPOSITION_UNIFORM, &mdd_vft,
554 : mpls_label_disp_uniform_nodes);
555 575 : }
556 : #endif /* CLIB_MARCH_VARIANT */
|