Line data Source code
1 : /*
2 : * mpls_tunnel.c: MPLS tunnel interfaces (i.e. for RSVP-TE)
3 : *
4 : * Copyright (c) 2012 Cisco and/or its affiliates.
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at:
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 :
18 : #include <vnet/vnet.h>
19 : #include <vnet/mpls/mpls_tunnel.h>
20 : #include <vnet/mpls/mpls_types.h>
21 : #include <vnet/ip/ip.h>
22 : #include <vnet/fib/fib_path_list.h>
23 : #include <vnet/adj/adj_midchain.h>
24 : #include <vnet/adj/adj_mcast.h>
25 : #include <vnet/dpo/replicate_dpo.h>
26 : #include <vnet/fib/mpls_fib.h>
27 :
28 : /**
29 : * @brief pool of tunnel instances
30 : */
31 : static mpls_tunnel_t *mpls_tunnel_pool;
32 :
33 : /**
34 : * @brief DB of SW index to tunnel index
35 : */
36 : static u32 *mpls_tunnel_db;
37 :
38 : /**
39 : * @brief MPLS tunnel flags strings
40 : */
41 : static const char *mpls_tunnel_attribute_names[] = MPLS_TUNNEL_ATTRIBUTES;
42 :
43 : /**
44 : * @brief Packet trace structure
45 : */
46 : typedef struct mpls_tunnel_trace_t_
47 : {
48 : /**
49 : * Tunnel-id / index in tunnel vector
50 : */
51 : u32 tunnel_id;
52 : } mpls_tunnel_trace_t;
53 :
54 : static u8 *
55 222 : format_mpls_tunnel_tx_trace (u8 * s,
56 : va_list * args)
57 : {
58 222 : CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
59 222 : CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
60 222 : mpls_tunnel_trace_t * t = va_arg (*args, mpls_tunnel_trace_t *);
61 :
62 222 : s = format (s, "MPLS: tunnel %d", t->tunnel_id);
63 222 : return s;
64 : }
65 :
66 : typedef enum
67 : {
68 : MPLS_TUNNEL_ENCAP_NEXT_L2_MIDCHAIN,
69 : MPLS_TUNNEL_ENCAP_N_NEXT,
70 : } mpls_tunnel_encap_next_t;
71 :
72 : /**
73 : * @brief TX function. Only called L2. L3 traffic uses the adj-midchains
74 : */
75 565 : VLIB_NODE_FN (mpls_tunnel_tx) (vlib_main_t * vm,
76 : vlib_node_runtime_t * node,
77 : vlib_frame_t * frame)
78 : {
79 6 : u32 *from = vlib_frame_vector_args (frame);
80 : vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
81 : u16 nexts[VLIB_FRAME_SIZE], *next;
82 : u32 n_left;
83 :
84 6 : n_left = frame->n_vectors;
85 6 : b = bufs;
86 6 : next = nexts;
87 :
88 6 : vlib_get_buffers (vm, from, bufs, n_left);
89 :
90 105 : while (n_left > 2)
91 : {
92 : const mpls_tunnel_t *mt0, *mt1;
93 : u32 sw_if_index0, sw_if_index1;
94 :
95 99 : sw_if_index0 = vnet_buffer(b[0])->sw_if_index[VLIB_TX];
96 99 : sw_if_index1 = vnet_buffer(b[1])->sw_if_index[VLIB_TX];
97 :
98 99 : mt0 = pool_elt_at_index(mpls_tunnel_pool,
99 : mpls_tunnel_db[sw_if_index0]);
100 99 : mt1 = pool_elt_at_index(mpls_tunnel_pool,
101 : mpls_tunnel_db[sw_if_index1]);
102 :
103 99 : vnet_buffer(b[0])->ip.adj_index[VLIB_TX] = mt0->mt_l2_lb.dpoi_index;
104 99 : vnet_buffer(b[1])->ip.adj_index[VLIB_TX] = mt1->mt_l2_lb.dpoi_index;
105 99 : next[0] = mt0->mt_l2_lb.dpoi_next_node;
106 99 : next[1] = mt1->mt_l2_lb.dpoi_next_node;
107 :
108 : /* since we are coming out of the L2 world, where the vlib_buffer
109 : * union is used for other things, make sure it is clean for
110 : * MPLS from now on.
111 : */
112 99 : vnet_buffer(b[0])->mpls.first = 0;
113 99 : vnet_buffer(b[1])->mpls.first = 0;
114 :
115 99 : if (PREDICT_FALSE(b[0]->flags & VLIB_BUFFER_IS_TRACED))
116 : {
117 99 : mpls_tunnel_trace_t *tr = vlib_add_trace (vm, node,
118 : b[0], sizeof (*tr));
119 99 : tr->tunnel_id = mpls_tunnel_db[sw_if_index0];
120 : }
121 99 : if (PREDICT_FALSE(b[1]->flags & VLIB_BUFFER_IS_TRACED))
122 : {
123 99 : mpls_tunnel_trace_t *tr = vlib_add_trace (vm, node,
124 99 : b[1], sizeof (*tr));
125 99 : tr->tunnel_id = mpls_tunnel_db[sw_if_index1];
126 : }
127 :
128 99 : b += 2;
129 99 : n_left -= 2;
130 99 : next += 2;
131 : }
132 14 : while (n_left)
133 : {
134 : const mpls_tunnel_t *mt0;
135 : u32 sw_if_index0;
136 :
137 8 : sw_if_index0 = vnet_buffer(b[0])->sw_if_index[VLIB_TX];
138 8 : mt0 = pool_elt_at_index(mpls_tunnel_pool,
139 : mpls_tunnel_db[sw_if_index0]);
140 :
141 8 : vnet_buffer(b[0])->ip.adj_index[VLIB_TX] = mt0->mt_l2_lb.dpoi_index;
142 8 : next[0] = mt0->mt_l2_lb.dpoi_next_node;
143 :
144 : /* since we are coming out of the L2 world, where the vlib_buffer
145 : * union is used for other things, make sure it is clean for
146 : * MPLS from now on.
147 : */
148 8 : vnet_buffer(b[0])->mpls.first = 0;
149 :
150 8 : if (PREDICT_FALSE(b[0]->flags & VLIB_BUFFER_IS_TRACED))
151 : {
152 8 : mpls_tunnel_trace_t *tr = vlib_add_trace (vm, node,
153 : b[0], sizeof (*tr));
154 8 : tr->tunnel_id = mpls_tunnel_db[sw_if_index0];
155 : }
156 :
157 8 : b += 1;
158 8 : n_left -= 1;
159 8 : next += 1;
160 : }
161 :
162 6 : vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
163 :
164 6 : return frame->n_vectors;
165 : }
166 :
167 178120 : VLIB_REGISTER_NODE (mpls_tunnel_tx) =
168 : {
169 : .name = "mpls-tunnel-tx",
170 : .vector_size = sizeof (u32),
171 : .format_trace = format_mpls_tunnel_tx_trace,
172 : .type = VLIB_NODE_TYPE_INTERNAL,
173 : .n_errors = 0,
174 : .n_next_nodes = 0,
175 : /* MPLS_TUNNEL_ENCAP_N_NEXT, */
176 : /* .next_nodes = { */
177 : /* [MPLS_TUNNEL_ENCAP_NEXT_L2_MIDCHAIN] = "mpls-load-balance", */
178 : /* }, */
179 : };
180 :
181 : /**
182 : * @brief Get a tunnel object from a SW interface index
183 : */
184 : static mpls_tunnel_t*
185 1504 : mpls_tunnel_get_from_sw_if_index (u32 sw_if_index)
186 : {
187 1504 : if ((vec_len(mpls_tunnel_db) <= sw_if_index) ||
188 1504 : (~0 == mpls_tunnel_db[sw_if_index]))
189 0 : return (NULL);
190 :
191 1504 : return (pool_elt_at_index(mpls_tunnel_pool,
192 : mpls_tunnel_db[sw_if_index]));
193 : }
194 :
195 : /**
196 : * @brief Build a rewrite string for the MPLS tunnel.
197 : */
198 : static u8*
199 9 : mpls_tunnel_build_rewrite_i (void)
200 : {
201 : /*
202 : * passing the adj code a NULL rewrite means 'i don't have one cos
203 : * t'other end is unresolved'. That's not the case here. For the mpls
204 : * tunnel there are just no bytes of encap to apply in the adj. We'll impose
205 : * the label stack once we choose a path. So return a zero length rewrite.
206 : */
207 9 : u8 *rewrite = NULL;
208 :
209 9 : vec_validate(rewrite, 0);
210 9 : vec_reset_length(rewrite);
211 :
212 9 : return (rewrite);
213 : }
214 :
215 : /**
216 : * @brief Build a rewrite string for the MPLS tunnel.
217 : */
218 : static u8*
219 0 : mpls_tunnel_build_rewrite (vnet_main_t * vnm,
220 : u32 sw_if_index,
221 : vnet_link_t link_type,
222 : const void *dst_address)
223 : {
224 0 : return (mpls_tunnel_build_rewrite_i());
225 : }
226 :
227 : typedef struct mpls_tunnel_collect_forwarding_ctx_t_
228 : {
229 : load_balance_path_t * next_hops;
230 : const mpls_tunnel_t *mt;
231 : fib_forward_chain_type_t fct;
232 : } mpls_tunnel_collect_forwarding_ctx_t;
233 :
234 : static fib_path_list_walk_rc_t
235 581 : mpls_tunnel_collect_forwarding (fib_node_index_t pl_index,
236 : fib_node_index_t path_index,
237 : void *arg)
238 : {
239 : mpls_tunnel_collect_forwarding_ctx_t *ctx;
240 : fib_path_ext_t *path_ext;
241 :
242 581 : ctx = arg;
243 :
244 : /*
245 : * if the path is not resolved, don't include it.
246 : */
247 581 : if (!fib_path_is_resolved(path_index))
248 : {
249 237 : return (FIB_PATH_LIST_WALK_CONTINUE);
250 : }
251 :
252 : /*
253 : * get the matching path-extension for the path being visited.
254 : */
255 344 : path_ext = fib_path_ext_list_find_by_path_index(&ctx->mt->mt_path_exts,
256 : path_index);
257 :
258 : /*
259 : * we don't want IP TTL decrements for packets hitting the MPLS labels
260 : * we stack on, since the IP TTL decrement is done by the adj
261 : */
262 344 : path_ext->fpe_mpls_flags |= FIB_PATH_EXT_MPLS_FLAG_NO_IP_TTL_DECR;
263 :
264 : /*
265 : * found a matching extension. stack it to obtain the forwarding
266 : * info for this path.
267 : */
268 344 : ctx->next_hops =
269 344 : fib_path_ext_stack (path_ext, DPO_PROTO_MPLS, ctx->fct, ctx->next_hops);
270 :
271 344 : return (FIB_PATH_LIST_WALK_CONTINUE);
272 : }
273 :
274 : static void
275 674 : mpls_tunnel_mk_lb (mpls_tunnel_t *mt,
276 : vnet_link_t linkt,
277 : fib_forward_chain_type_t fct,
278 : dpo_id_t *dpo_lb)
279 : {
280 : dpo_proto_t lb_proto;
281 :
282 : /*
283 : * If the entry has path extensions then we construct a load-balance
284 : * by stacking the extensions on the forwarding chains of the paths.
285 : * Otherwise we use the load-balance of the path-list
286 : */
287 674 : mpls_tunnel_collect_forwarding_ctx_t ctx = {
288 : .mt = mt,
289 : .next_hops = NULL,
290 : .fct = fct,
291 : };
292 :
293 : /*
294 : * As an optimisation we allocate the vector of next-hops to be sized
295 : * equal to the maximum nuber of paths we will need, which is also the
296 : * most likely number we will need, since in most cases the paths are 'up'.
297 : */
298 674 : vec_validate(ctx.next_hops, fib_path_list_get_n_paths(mt->mt_path_list));
299 674 : vec_reset_length(ctx.next_hops);
300 :
301 674 : lb_proto = fib_forw_chain_type_to_dpo_proto(fct);
302 :
303 674 : if (FIB_NODE_INDEX_INVALID != mt->mt_path_list)
304 : {
305 570 : fib_path_list_walk(mt->mt_path_list,
306 : mpls_tunnel_collect_forwarding,
307 : &ctx);
308 : }
309 :
310 674 : if (!dpo_id_is_valid(dpo_lb))
311 : {
312 : /*
313 : * first time create
314 : */
315 674 : if (mt->mt_flags & MPLS_TUNNEL_FLAG_MCAST)
316 : {
317 11 : dpo_set(dpo_lb,
318 : DPO_REPLICATE,
319 : lb_proto,
320 : replicate_create(0, lb_proto));
321 : }
322 : else
323 : {
324 : flow_hash_config_t fhc;
325 :
326 663 : switch (linkt)
327 : {
328 648 : case VNET_LINK_MPLS:
329 648 : fhc = MPLS_FLOW_HASH_DEFAULT;
330 648 : break;
331 15 : case VNET_LINK_IP4:
332 : case VNET_LINK_IP6:
333 15 : fhc = IP_FLOW_HASH_DEFAULT;
334 15 : break;
335 0 : default:
336 0 : fhc = 0;
337 0 : break;
338 : }
339 :
340 663 : dpo_set(dpo_lb,
341 : DPO_LOAD_BALANCE,
342 : lb_proto,
343 : load_balance_create(0, lb_proto, fhc));
344 : }
345 : }
346 :
347 674 : if (mt->mt_flags & MPLS_TUNNEL_FLAG_MCAST)
348 : {
349 : /*
350 : * MPLS multicast
351 : */
352 11 : replicate_multipath_update(dpo_lb, ctx.next_hops);
353 : }
354 : else
355 : {
356 663 : load_balance_multipath_update(dpo_lb,
357 663 : ctx.next_hops,
358 : LOAD_BALANCE_FLAG_NONE);
359 663 : vec_free(ctx.next_hops);
360 : }
361 674 : }
362 :
363 : /**
364 : * mpls_tunnel_stack
365 : *
366 : * 'stack' (resolve the recursion for) the tunnel's midchain adjacency
367 : */
368 : static void
369 41 : mpls_tunnel_stack (adj_index_t ai)
370 : {
371 : ip_adjacency_t *adj;
372 : mpls_tunnel_t *mt;
373 : u32 sw_if_index;
374 :
375 41 : adj = adj_get(ai);
376 41 : sw_if_index = adj->rewrite_header.sw_if_index;
377 :
378 41 : mt = mpls_tunnel_get_from_sw_if_index(sw_if_index);
379 :
380 41 : if (NULL == mt || FIB_NODE_INDEX_INVALID == mt->mt_path_list)
381 0 : return;
382 :
383 41 : if (FIB_NODE_INDEX_INVALID == mt->mt_path_list)
384 : {
385 0 : adj_nbr_midchain_unstack(ai);
386 0 : return;
387 : }
388 :
389 : /*
390 : * while we're stacking the adj, remove the tunnel from the child list
391 : * of the path list. this breaks a circular dependency of walk updates
392 : * where the create of adjacencies in the children can lead to walks
393 : * that get back here.
394 : */
395 41 : fib_path_list_lock(mt->mt_path_list);
396 :
397 41 : fib_path_list_child_remove(mt->mt_path_list,
398 : mt->mt_sibling_index);
399 :
400 : /*
401 : * Construct the DPO (load-balance or replicate) that we can stack
402 : * the tunnel's midchain on
403 : */
404 41 : if (vnet_hw_interface_get_flags(vnet_get_main(),
405 41 : mt->mt_hw_if_index) &
406 : VNET_HW_INTERFACE_FLAG_LINK_UP)
407 : {
408 41 : dpo_id_t dpo = DPO_INVALID;
409 :
410 41 : mpls_tunnel_mk_lb(mt,
411 41 : adj->ia_link,
412 41 : fib_forw_chain_type_from_link_type(
413 41 : adj_get_link_type(ai)),
414 : &dpo);
415 :
416 41 : adj_nbr_midchain_stack(ai, &dpo);
417 41 : dpo_reset(&dpo);
418 : }
419 : else
420 : {
421 0 : adj_nbr_midchain_unstack(ai);
422 : }
423 :
424 82 : mt->mt_sibling_index = fib_path_list_child_add(mt->mt_path_list,
425 : FIB_NODE_TYPE_MPLS_TUNNEL,
426 41 : mt - mpls_tunnel_pool);
427 :
428 41 : fib_path_list_unlock(mt->mt_path_list);
429 : }
430 :
431 : /**
432 : * @brief Call back when restacking all adjacencies on a MPLS interface
433 : */
434 : static adj_walk_rc_t
435 32 : mpls_adj_walk_cb (adj_index_t ai,
436 : void *ctx)
437 : {
438 32 : mpls_tunnel_stack(ai);
439 :
440 32 : return (ADJ_WALK_RC_CONTINUE);
441 : }
442 :
443 : static void
444 1532 : mpls_tunnel_restack (mpls_tunnel_t *mt)
445 : {
446 : fib_protocol_t proto;
447 :
448 : /*
449 : * walk all the adjacencies on the MPLS interface and restack them
450 : */
451 1532 : if (mt->mt_flags & MPLS_TUNNEL_FLAG_L2)
452 : {
453 : /*
454 : * Stack a load-balance that drops, whilst we have no paths
455 : */
456 633 : dpo_id_t dpo = DPO_INVALID;
457 :
458 633 : mpls_tunnel_mk_lb(mt,
459 : VNET_LINK_MPLS,
460 : FIB_FORW_CHAIN_TYPE_ETHERNET,
461 : &dpo);
462 :
463 633 : dpo_stack_from_node(mpls_tunnel_tx.index,
464 : &mt->mt_l2_lb,
465 : &dpo);
466 633 : dpo_reset(&dpo);
467 : }
468 : else
469 : {
470 2697 : FOR_EACH_FIB_IP_PROTOCOL(proto)
471 : {
472 1798 : adj_nbr_walk(mt->mt_sw_if_index,
473 : proto,
474 : mpls_adj_walk_cb,
475 : NULL);
476 : }
477 : }
478 1532 : }
479 :
480 : static clib_error_t *
481 418 : mpls_tunnel_admin_up_down (vnet_main_t * vnm,
482 : u32 hw_if_index,
483 : u32 flags)
484 : {
485 : vnet_hw_interface_t * hi;
486 : mpls_tunnel_t *mt;
487 :
488 418 : hi = vnet_get_hw_interface (vnm, hw_if_index);
489 :
490 418 : mt = mpls_tunnel_get_from_sw_if_index(hi->sw_if_index);
491 :
492 418 : if (NULL == mt)
493 0 : return (NULL);
494 :
495 418 : if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
496 209 : vnet_hw_interface_set_flags (vnm, hw_if_index,
497 : VNET_HW_INTERFACE_FLAG_LINK_UP);
498 : else
499 209 : vnet_hw_interface_set_flags (vnm, hw_if_index, 0 /* down */);
500 :
501 418 : mpls_tunnel_restack(mt);
502 :
503 418 : return (NULL);
504 : }
505 :
506 : /**
507 : * @brief Fixup the adj rewrite post encap. This is a no-op since the
508 : * rewrite is a stack of labels.
509 : */
510 : static void
511 3598 : mpls_tunnel_fixup (vlib_main_t *vm,
512 : const ip_adjacency_t *adj,
513 : vlib_buffer_t *b0,
514 : const void*data)
515 : {
516 : /*
517 : * A no-op w.r.t. the header. but reset the 'have we pushed any
518 : * MPLS labels onto the packet' flag. That way when we enter the
519 : * tunnel we'll get a TTL set to 255
520 : */
521 3598 : vnet_buffer(b0)->mpls.first = 0;
522 3598 : }
523 :
524 : static void
525 9 : mpls_tunnel_update_adj (vnet_main_t * vnm,
526 : u32 sw_if_index,
527 : adj_index_t ai)
528 : {
529 : ip_adjacency_t *adj;
530 :
531 9 : ASSERT(ADJ_INDEX_INVALID != ai);
532 :
533 9 : adj = adj_get(ai);
534 :
535 9 : switch (adj->lookup_next_index)
536 : {
537 9 : case IP_LOOKUP_NEXT_ARP:
538 : case IP_LOOKUP_NEXT_GLEAN:
539 : case IP_LOOKUP_NEXT_BCAST:
540 9 : adj_nbr_midchain_update_rewrite(ai, mpls_tunnel_fixup,
541 : NULL,
542 : ADJ_FLAG_NONE,
543 : mpls_tunnel_build_rewrite_i());
544 9 : break;
545 0 : case IP_LOOKUP_NEXT_MCAST:
546 : /*
547 : * Construct a partial rewrite from the known ethernet mcast dest MAC
548 : * There's no MAC fixup, so the last 2 parameters are 0
549 : */
550 0 : adj_mcast_midchain_update_rewrite(ai, mpls_tunnel_fixup,
551 : NULL,
552 : ADJ_FLAG_NONE,
553 : mpls_tunnel_build_rewrite_i(),
554 : 0, 0);
555 0 : break;
556 :
557 0 : case IP_LOOKUP_NEXT_DROP:
558 : case IP_LOOKUP_NEXT_PUNT:
559 : case IP_LOOKUP_NEXT_LOCAL:
560 : case IP_LOOKUP_NEXT_REWRITE:
561 : case IP_LOOKUP_NEXT_MIDCHAIN:
562 : case IP_LOOKUP_NEXT_MCAST_MIDCHAIN:
563 : case IP_LOOKUP_NEXT_ICMP_ERROR:
564 : case IP_LOOKUP_N_NEXT:
565 0 : ASSERT (0);
566 0 : break;
567 : }
568 :
569 9 : mpls_tunnel_stack(ai);
570 9 : }
571 :
572 : static u8 *
573 416 : format_mpls_tunnel_name (u8 * s, va_list * args)
574 : {
575 416 : u32 dev_instance = va_arg (*args, u32);
576 416 : return format (s, "mpls-tunnel%d", dev_instance);
577 : }
578 :
579 : static u8 *
580 207 : format_mpls_tunnel_device (u8 * s, va_list * args)
581 : {
582 207 : u32 dev_instance = va_arg (*args, u32);
583 207 : CLIB_UNUSED (int verbose) = va_arg (*args, int);
584 :
585 207 : return (format (s, "MPLS-tunnel: id %d\n", dev_instance));
586 : }
587 :
588 11199 : VNET_DEVICE_CLASS (mpls_tunnel_class) = {
589 : .name = "MPLS tunnel device",
590 : .format_device_name = format_mpls_tunnel_name,
591 : .format_device = format_mpls_tunnel_device,
592 : .format_tx_trace = format_mpls_tunnel_tx_trace,
593 : .admin_up_down_function = mpls_tunnel_admin_up_down,
594 : };
595 :
596 7279 : VNET_HW_INTERFACE_CLASS (mpls_tunnel_hw_interface_class) = {
597 : .name = "MPLS-Tunnel",
598 : .update_adjacency = mpls_tunnel_update_adj,
599 : .build_rewrite = mpls_tunnel_build_rewrite,
600 : .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,
601 : };
602 :
603 : const mpls_tunnel_t *
604 40009 : mpls_tunnel_get (u32 mti)
605 : {
606 40009 : return (pool_elt_at_index(mpls_tunnel_pool, mti));
607 : }
608 :
609 : /**
610 : * @brief Walk all the MPLS tunnels
611 : */
612 : void
613 416 : mpls_tunnel_walk (mpls_tunnel_walk_cb_t cb,
614 : void *ctx)
615 : {
616 : u32 mti;
617 :
618 40425 : pool_foreach_index (mti, mpls_tunnel_pool)
619 : {
620 40009 : cb(mti, ctx);
621 : }
622 416 : }
623 :
624 : void
625 209 : vnet_mpls_tunnel_del (u32 sw_if_index)
626 : {
627 : mpls_tunnel_t *mt;
628 :
629 209 : mt = mpls_tunnel_get_from_sw_if_index(sw_if_index);
630 :
631 209 : if (NULL == mt)
632 0 : return;
633 :
634 209 : if (FIB_NODE_INDEX_INVALID != mt->mt_path_list)
635 0 : fib_path_list_child_remove(mt->mt_path_list,
636 : mt->mt_sibling_index);
637 209 : dpo_reset(&mt->mt_l2_lb);
638 :
639 209 : vnet_reset_interface_l3_output_node (vlib_get_main (), mt->mt_sw_if_index);
640 209 : vnet_delete_hw_interface (vnet_get_main(), mt->mt_hw_if_index);
641 :
642 209 : pool_put(mpls_tunnel_pool, mt);
643 209 : mpls_tunnel_db[sw_if_index] = ~0;
644 : }
645 :
646 : u32
647 209 : vnet_mpls_tunnel_create (u8 l2_only,
648 : u8 is_multicast,
649 : u8 *tag)
650 : {
651 : vnet_hw_interface_t * hi;
652 : mpls_tunnel_t *mt;
653 : vnet_main_t * vnm;
654 : u32 mti;
655 :
656 209 : vnm = vnet_get_main();
657 209 : pool_get(mpls_tunnel_pool, mt);
658 209 : clib_memset (mt, 0, sizeof (*mt));
659 209 : mti = mt - mpls_tunnel_pool;
660 209 : fib_node_init(&mt->mt_node, FIB_NODE_TYPE_MPLS_TUNNEL);
661 209 : mt->mt_path_list = FIB_NODE_INDEX_INVALID;
662 209 : mt->mt_sibling_index = FIB_NODE_INDEX_INVALID;
663 :
664 209 : if (is_multicast)
665 1 : mt->mt_flags |= MPLS_TUNNEL_FLAG_MCAST;
666 209 : if (l2_only)
667 104 : mt->mt_flags |= MPLS_TUNNEL_FLAG_L2;
668 209 : if (tag)
669 209 : memcpy(mt->mt_tag, tag, sizeof(mt->mt_tag));
670 : else
671 0 : mt->mt_tag[0] = '\0';
672 :
673 : /*
674 : * Create a new tunnel HW interface
675 : */
676 209 : mt->mt_hw_if_index = vnet_register_interface(
677 : vnm,
678 : mpls_tunnel_class.index,
679 : mti,
680 : mpls_tunnel_hw_interface_class.index,
681 : mti);
682 209 : hi = vnet_get_hw_interface (vnm, mt->mt_hw_if_index);
683 :
684 209 : if (mt->mt_flags & MPLS_TUNNEL_FLAG_L2)
685 104 : vnet_set_interface_output_node (vnm, mt->mt_hw_if_index,
686 : mpls_tunnel_tx.index);
687 : else
688 105 : vnet_set_interface_l3_output_node (vnm->vlib_main, hi->sw_if_index,
689 : (u8 *) "tunnel-output");
690 :
691 : /* Standard default MPLS tunnel MTU. */
692 209 : vnet_sw_interface_set_mtu (vnm, hi->sw_if_index, 9000);
693 :
694 : /*
695 : * Add the new tunnel to the tunnel DB - key:SW if index
696 : */
697 209 : mt->mt_sw_if_index = hi->sw_if_index;
698 434 : vec_validate_init_empty(mpls_tunnel_db, mt->mt_sw_if_index, ~0);
699 209 : mpls_tunnel_db[mt->mt_sw_if_index] = mti;
700 :
701 209 : return (mt->mt_sw_if_index);
702 : }
703 :
704 : void
705 209 : vnet_mpls_tunnel_path_add (u32 sw_if_index,
706 : fib_route_path_t *rpaths)
707 : {
708 : fib_route_path_t *rpath;
709 : mpls_tunnel_t *mt;
710 : u32 mti;
711 :
712 209 : mt = mpls_tunnel_get_from_sw_if_index(sw_if_index);
713 :
714 209 : if (NULL == mt)
715 0 : return;
716 :
717 209 : mti = mt - mpls_tunnel_pool;
718 :
719 : /*
720 : * construct a path-list from the path provided
721 : */
722 209 : if (FIB_NODE_INDEX_INVALID == mt->mt_path_list)
723 : {
724 209 : mt->mt_path_list = fib_path_list_create(FIB_PATH_LIST_FLAG_SHARED, rpaths);
725 209 : mt->mt_sibling_index = fib_path_list_child_add(mt->mt_path_list,
726 : FIB_NODE_TYPE_MPLS_TUNNEL,
727 : mti);
728 : }
729 : else
730 : {
731 : fib_node_index_t old_pl_index;
732 :
733 0 : old_pl_index = mt->mt_path_list;
734 :
735 0 : mt->mt_path_list =
736 0 : fib_path_list_copy_and_path_add(old_pl_index,
737 : FIB_PATH_LIST_FLAG_SHARED,
738 : rpaths);
739 :
740 0 : fib_path_list_child_remove(old_pl_index,
741 : mt->mt_sibling_index);
742 0 : mt->mt_sibling_index = fib_path_list_child_add(mt->mt_path_list,
743 : FIB_NODE_TYPE_MPLS_TUNNEL,
744 : mti);
745 : /*
746 : * re-resolve all the path-extensions with the new path-list
747 : */
748 0 : fib_path_ext_list_resolve(&mt->mt_path_exts, mt->mt_path_list);
749 : }
750 419 : vec_foreach(rpath, rpaths)
751 : {
752 210 : fib_path_ext_list_insert(&mt->mt_path_exts,
753 : mt->mt_path_list,
754 : FIB_PATH_EXT_MPLS,
755 : rpath);
756 : }
757 209 : mpls_tunnel_restack(mt);
758 : }
759 :
760 : int
761 209 : vnet_mpls_tunnel_path_remove (u32 sw_if_index,
762 : fib_route_path_t *rpaths)
763 : {
764 : mpls_tunnel_t *mt;
765 : u32 mti;
766 :
767 209 : mt = mpls_tunnel_get_from_sw_if_index(sw_if_index);
768 :
769 209 : if (NULL == mt)
770 0 : return (0);
771 :
772 209 : mti = mt - mpls_tunnel_pool;
773 :
774 : /*
775 : * construct a path-list from the path provided
776 : */
777 209 : if (FIB_NODE_INDEX_INVALID == mt->mt_path_list)
778 : {
779 : /* can't remove a path if we have onoe */
780 0 : return (0);
781 : }
782 : else
783 : {
784 : fib_node_index_t old_pl_index;
785 :
786 209 : old_pl_index = mt->mt_path_list;
787 :
788 209 : fib_path_list_lock(old_pl_index);
789 209 : mt->mt_path_list =
790 209 : fib_path_list_copy_and_path_remove(old_pl_index,
791 : FIB_PATH_LIST_FLAG_SHARED,
792 : rpaths);
793 :
794 209 : fib_path_list_child_remove(old_pl_index,
795 : mt->mt_sibling_index);
796 :
797 209 : if (FIB_NODE_INDEX_INVALID == mt->mt_path_list)
798 : {
799 : /* no paths left */
800 209 : fib_path_list_unlock(old_pl_index);
801 209 : return (0);
802 : }
803 : else
804 : {
805 0 : mt->mt_sibling_index =
806 0 : fib_path_list_child_add(mt->mt_path_list,
807 : FIB_NODE_TYPE_MPLS_TUNNEL,
808 : mti);
809 : }
810 : /*
811 : * find the matching path extension and remove it
812 : */
813 0 : fib_path_ext_list_remove(&mt->mt_path_exts,
814 : FIB_PATH_EXT_MPLS,
815 : rpaths);
816 :
817 : /*
818 : * re-resolve all the path-extensions with the new path-list
819 : */
820 0 : fib_path_ext_list_resolve(&mt->mt_path_exts,
821 : mt->mt_path_list);
822 :
823 0 : mpls_tunnel_restack(mt);
824 0 : fib_path_list_unlock(old_pl_index);
825 : }
826 :
827 0 : return (fib_path_list_get_n_paths(mt->mt_path_list));
828 : }
829 :
830 : int
831 418 : vnet_mpls_tunnel_get_index (u32 sw_if_index)
832 : {
833 : mpls_tunnel_t *mt;
834 :
835 418 : mt = mpls_tunnel_get_from_sw_if_index(sw_if_index);
836 :
837 418 : if (NULL == mt)
838 0 : return (~0);
839 :
840 418 : return (mt - mpls_tunnel_pool);
841 : }
842 :
843 : static clib_error_t *
844 0 : vnet_create_mpls_tunnel_command_fn (vlib_main_t * vm,
845 : unformat_input_t * input,
846 : vlib_cli_command_t * cmd)
847 : {
848 0 : unformat_input_t _line_input, * line_input = &_line_input;
849 0 : vnet_main_t * vnm = vnet_get_main();
850 0 : u8 is_del = 0, l2_only = 0, is_multicast =0;
851 0 : fib_route_path_t rpath, *rpaths = NULL;
852 0 : u32 sw_if_index = ~0, payload_proto;
853 0 : clib_error_t *error = NULL;
854 :
855 0 : clib_memset(&rpath, 0, sizeof(rpath));
856 0 : payload_proto = DPO_PROTO_MPLS;
857 :
858 : /* Get a line of input. */
859 0 : if (! unformat_user (input, unformat_line_input, line_input))
860 0 : return 0;
861 :
862 0 : while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
863 : {
864 0 : if (unformat (line_input, "del %U",
865 : unformat_vnet_sw_interface, vnm,
866 : &sw_if_index))
867 0 : is_del = 1;
868 0 : else if (unformat (line_input, "add %U",
869 : unformat_vnet_sw_interface, vnm,
870 : &sw_if_index))
871 0 : is_del = 0;
872 0 : else if (unformat (line_input, "add"))
873 0 : is_del = 0;
874 0 : else if (unformat (line_input, "l2-only"))
875 0 : l2_only = 1;
876 0 : else if (unformat (line_input, "multicast"))
877 0 : is_multicast = 1;
878 0 : else if (unformat (line_input, "via %U",
879 : unformat_fib_route_path,
880 : &rpath, &payload_proto))
881 0 : vec_add1(rpaths, rpath);
882 : else
883 : {
884 0 : error = clib_error_return (0, "unknown input '%U'",
885 : format_unformat_error, line_input);
886 0 : goto done;
887 : }
888 : }
889 :
890 0 : if (is_del)
891 : {
892 0 : if (NULL == rpaths)
893 : {
894 0 : vnet_mpls_tunnel_del(sw_if_index);
895 : }
896 0 : else if (!vnet_mpls_tunnel_path_remove(sw_if_index, rpaths))
897 : {
898 0 : vnet_mpls_tunnel_del(sw_if_index);
899 : }
900 : }
901 : else
902 : {
903 0 : if (0 == vec_len(rpath.frp_label_stack))
904 : {
905 0 : error = clib_error_return (0, "No Output Labels '%U'",
906 : format_unformat_error, line_input);
907 0 : goto done;
908 : }
909 :
910 0 : if (~0 == sw_if_index)
911 : {
912 0 : sw_if_index = vnet_mpls_tunnel_create(l2_only, is_multicast, NULL);
913 : }
914 0 : vnet_mpls_tunnel_path_add(sw_if_index, rpaths);
915 : }
916 :
917 0 : done:
918 0 : vec_free(rpaths);
919 0 : unformat_free (line_input);
920 :
921 0 : return error;
922 : }
923 :
924 : /*?
925 : * This command create a uni-directional MPLS tunnel
926 : *
927 : * @cliexpar
928 : * @cliexstart{create mpls tunnel}
929 : * create mpls tunnel via 10.0.0.1 GigEthernet0/8/0 out-label 33 out-label 34
930 : * @cliexend
931 : ?*/
932 272887 : VLIB_CLI_COMMAND (create_mpls_tunnel_command, static) = {
933 : .path = "mpls tunnel",
934 : .short_help =
935 : "mpls tunnel [multicast] [l2-only] via [next-hop-address] [next-hop-interface] [next-hop-table <value>] [weight <value>] [preference <value>] [udp-encap-id <value>] [ip4-lookup-in-table <value>] [ip6-lookup-in-table <value>] [mpls-lookup-in-table <value>] [resolve-via-host] [resolve-via-connected] [rx-ip4 <interface>] [out-labels <value value value>]",
936 : .function = vnet_create_mpls_tunnel_command_fn,
937 : };
938 :
939 : static u8 *
940 1 : format_mpls_tunnel (u8 * s, va_list * args)
941 : {
942 1 : mpls_tunnel_t *mt = va_arg (*args, mpls_tunnel_t *);
943 : mpls_tunnel_attribute_t attr;
944 :
945 1 : s = format(s, "mpls-tunnel%d: sw_if_index:%d hw_if_index:%d",
946 1 : mt - mpls_tunnel_pool,
947 : mt->mt_sw_if_index,
948 : mt->mt_hw_if_index);
949 1 : if (MPLS_TUNNEL_FLAG_NONE != mt->mt_flags) {
950 0 : s = format(s, " \n flags:");
951 0 : FOR_EACH_MPLS_TUNNEL_ATTRIBUTE(attr) {
952 0 : if ((1<<attr) & mt->mt_flags) {
953 0 : s = format (s, "%s,", mpls_tunnel_attribute_names[attr]);
954 : }
955 : }
956 : }
957 1 : s = format(s, "\n via:\n");
958 1 : s = fib_path_list_format(mt->mt_path_list, s);
959 1 : s = format(s, "%U", format_fib_path_ext_list, &mt->mt_path_exts);
960 1 : s = format(s, "\n");
961 :
962 1 : if (mt->mt_flags & MPLS_TUNNEL_FLAG_L2)
963 : {
964 0 : s = format(s, " forwarding: %U\n",
965 : format_fib_forw_chain_type,
966 : FIB_FORW_CHAIN_TYPE_ETHERNET);
967 0 : s = format(s, " %U\n", format_dpo_id, &mt->mt_l2_lb, 2);
968 : }
969 :
970 1 : return (s);
971 : }
972 :
973 : static clib_error_t *
974 1 : show_mpls_tunnel_command_fn (vlib_main_t * vm,
975 : unformat_input_t * input,
976 : vlib_cli_command_t * cmd)
977 : {
978 : mpls_tunnel_t * mt;
979 1 : u32 mti = ~0;
980 :
981 1 : if (pool_elts (mpls_tunnel_pool) == 0)
982 0 : vlib_cli_output (vm, "No MPLS tunnels configured...");
983 :
984 2 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
985 : {
986 1 : if (unformat (input, "%d", &mti))
987 : ;
988 : else
989 0 : break;
990 : }
991 :
992 1 : if (~0 == mti)
993 : {
994 0 : pool_foreach (mt, mpls_tunnel_pool)
995 : {
996 0 : vlib_cli_output (vm, "[@%d] %U",
997 0 : mt - mpls_tunnel_pool,
998 : format_mpls_tunnel, mt);
999 : }
1000 : }
1001 : else
1002 : {
1003 1 : if (pool_is_free_index(mpls_tunnel_pool, mti))
1004 0 : return clib_error_return (0, "Not a tunnel index %d", mti);
1005 :
1006 1 : mt = pool_elt_at_index(mpls_tunnel_pool, mti);
1007 :
1008 1 : vlib_cli_output (vm, "[@%d] %U",
1009 1 : mt - mpls_tunnel_pool,
1010 : format_mpls_tunnel, mt);
1011 : }
1012 :
1013 1 : return 0;
1014 : }
1015 :
1016 : /*?
1017 : * This command to show MPLS tunnels
1018 : *
1019 : * @cliexpar
1020 : * @cliexstart{sh mpls tunnel 2}
1021 : * [@2] mpls_tunnel2: sw_if_index:5 hw_if_index:5
1022 : * label-stack:
1023 : * 3,
1024 : * via:
1025 : * index:26 locks:1 proto:ipv4 uPRF-list:26 len:1 itfs:[2, ]
1026 : * index:26 pl-index:26 ipv4 weight=1 attached-nexthop: oper-flags:resolved,
1027 : * 10.0.0.2 loop0
1028 : * [@0]: ipv4 via 10.0.0.2 loop0: IP4: de:ad:00:00:00:00 -> 00:00:11:aa:bb:cc
1029 : * @cliexend
1030 : ?*/
1031 272887 : VLIB_CLI_COMMAND (show_mpls_tunnel_command, static) = {
1032 : .path = "show mpls tunnel",
1033 : .function = show_mpls_tunnel_command_fn,
1034 : };
1035 :
1036 : static mpls_tunnel_t *
1037 905 : mpls_tunnel_from_fib_node (fib_node_t *node)
1038 : {
1039 905 : ASSERT(FIB_NODE_TYPE_MPLS_TUNNEL == node->fn_type);
1040 905 : return ((mpls_tunnel_t*) (((char*)node) -
1041 : STRUCT_OFFSET_OF(mpls_tunnel_t, mt_node)));
1042 : }
1043 :
1044 : /**
1045 : * Function definition to backwalk a FIB node
1046 : */
1047 : static fib_node_back_walk_rc_t
1048 905 : mpls_tunnel_back_walk (fib_node_t *node,
1049 : fib_node_back_walk_ctx_t *ctx)
1050 : {
1051 905 : mpls_tunnel_restack(mpls_tunnel_from_fib_node(node));
1052 :
1053 905 : return (FIB_NODE_BACK_WALK_CONTINUE);
1054 : }
1055 :
1056 : /**
1057 : * Function definition to get a FIB node from its index
1058 : */
1059 : static fib_node_t*
1060 905 : mpls_tunnel_fib_node_get (fib_node_index_t index)
1061 : {
1062 : mpls_tunnel_t * mt;
1063 :
1064 905 : mt = pool_elt_at_index(mpls_tunnel_pool, index);
1065 :
1066 905 : return (&mt->mt_node);
1067 : }
1068 :
1069 : /**
1070 : * Function definition to inform the FIB node that its last lock has gone.
1071 : */
1072 : static void
1073 0 : mpls_tunnel_last_lock_gone (fib_node_t *node)
1074 : {
1075 : /*
1076 : * The MPLS MPLS tunnel is a root of the graph. As such
1077 : * it never has children and thus is never locked.
1078 : */
1079 0 : ASSERT(0);
1080 0 : }
1081 :
1082 : /*
1083 : * Virtual function table registered by MPLS MPLS tunnels
1084 : * for participation in the FIB object graph.
1085 : */
1086 : const static fib_node_vft_t mpls_vft = {
1087 : .fnv_get = mpls_tunnel_fib_node_get,
1088 : .fnv_last_lock = mpls_tunnel_last_lock_gone,
1089 : .fnv_back_walk = mpls_tunnel_back_walk,
1090 : };
1091 :
1092 : static clib_error_t *
1093 559 : mpls_tunnel_init (vlib_main_t *vm)
1094 : {
1095 559 : fib_node_register_type(FIB_NODE_TYPE_MPLS_TUNNEL, &mpls_vft);
1096 :
1097 559 : return 0;
1098 : }
1099 63839 : VLIB_INIT_FUNCTION(mpls_tunnel_init);
|