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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 #include <plugins/svs/svs.h>
18 #include <vlib/vlib.h>
19 #include <vnet/plugin/plugin.h>
20 #include <vnet/fib/fib_table.h>
21 #include <vnet/fib/ip6_fib.h>
22 #include <vnet/fib/ip4_fib.h>
23 #include <vnet/dpo/lookup_dpo.h>
24 #include <vnet/dpo/load_balance.h>
25 #include <vnet/dpo/load_balance_map.h>
27 u32 *svs_itf_db[FIB_PROTOCOL_IP_MAX];
29 static fib_source_t svs_fib_src;
32 svs_table_add (fib_protocol_t fproto, u32 table_id)
34 fib_table_find_or_create_and_lock (fproto, table_id, svs_fib_src);
40 svs_table_delete (fib_protocol_t fproto, u32 table_id)
44 fib_index = fib_table_find (fproto, table_id);
46 vec_foreach_index (ii, svs_itf_db[fproto])
48 if (svs_itf_db[fproto][ii] == fib_index)
49 return VNET_API_ERROR_INSTANCE_IN_USE;
53 return VNET_API_ERROR_NO_SUCH_FIB;
55 fib_table_unlock (fib_index, fproto, svs_fib_src);
61 svs_route_add_i (u32 fib_index, const fib_prefix_t * pfx, u32 src_fib_index)
63 dpo_id_t dpo = DPO_INVALID;
66 lookup_dpo_add_or_lock_w_fib_index (src_fib_index,
67 fib_proto_to_dpo (pfx->fp_proto),
69 LOOKUP_INPUT_SRC_ADDR,
70 LOOKUP_TABLE_FROM_CONFIG, &dpo);
72 fib_table_entry_special_dpo_add (fib_index, pfx,
74 FIB_ENTRY_FLAG_EXCLUSIVE, &dpo);
82 svs_route_add (u32 table_id, const fib_prefix_t * pfx, u32 source_table_id)
84 u32 fib_index, src_fib_index;
87 fib_index = fib_table_find (pfx->fp_proto, table_id);
90 return VNET_API_ERROR_NO_SUCH_FIB;
92 src_fib_index = fib_table_find (pfx->fp_proto, source_table_id);
94 if (~0 == src_fib_index)
95 return (VNET_API_ERROR_NO_SUCH_FIB);
97 rv = svs_route_add_i (fib_index, pfx, src_fib_index);
103 svs_route_delete (u32 table_id, const fib_prefix_t * pfx)
107 fib_index = fib_table_find (pfx->fp_proto, table_id);
110 return VNET_API_ERROR_NO_SUCH_FIB;
112 fib_table_entry_special_remove (fib_index, pfx, svs_fib_src);
118 svs_enable (fib_protocol_t fproto, u32 table_id, u32 sw_if_index)
125 fib_index = fib_table_find (fproto, table_id);
128 return VNET_API_ERROR_NO_SUCH_FIB;
131 * now we know which interface the table will serve, we can add the default
132 * route to use the table that the interface is bound to.
134 svs_route_add_i (fib_index, &pfx,
135 fib_table_get_index_for_sw_if_index (fproto, sw_if_index));
137 vec_validate_init_empty (svs_itf_db[fproto], sw_if_index, ~0);
139 svs_itf_db[fproto][sw_if_index] = fib_index;
141 vnet_feature_enable_disable ((FIB_PROTOCOL_IP4 == fproto ?
144 (FIB_PROTOCOL_IP4 == fproto ?
146 "svs-ip6"), sw_if_index, 1, NULL, 0);
152 svs_table_bind (fib_protocol_t fproto, u32 sw_if_index, u32 itf_fib_index)
155 * update the default route to use the interface's newly bound FIB
159 if (sw_if_index >= vec_len (svs_itf_db[FIB_PROTOCOL_IP6]))
162 svs_fib_index = svs_itf_db[FIB_PROTOCOL_IP6][sw_if_index];
164 if (~0 != svs_fib_index)
170 svs_route_add (svs_fib_index, &pfx, itf_fib_index);
174 * no SVS enable on this interface
179 svs_ip6_table_bind (ip6_main_t * im,
181 u32 sw_if_index, u32 new_fib_index, u32 old_fib_index)
183 svs_table_bind (FIB_PROTOCOL_IP6, sw_if_index, new_fib_index);
187 svs_ip4_table_bind (ip4_main_t * im,
189 u32 sw_if_index, u32 new_fib_index, u32 old_fib_index)
191 svs_table_bind (FIB_PROTOCOL_IP4, sw_if_index, new_fib_index);
195 svs_disable (fib_protocol_t fproto, u32 table_id, u32 sw_if_index)
202 fib_index = fib_table_find (fproto, table_id);
205 return VNET_API_ERROR_NO_SUCH_FIB;
207 if (sw_if_index >= vec_len (svs_itf_db[fproto]))
208 return VNET_API_ERROR_INVALID_SW_IF_INDEX;
210 svs_itf_db[fproto][sw_if_index] = ~0;
212 vnet_feature_enable_disable ((FIB_PROTOCOL_IP4 == fproto ?
215 (FIB_PROTOCOL_IP4 == fproto ?
217 "svs-ip6"), sw_if_index, 0, NULL, 0);
219 fib_table_entry_special_remove (fib_index, &pfx, svs_fib_src);
225 svs_walk (svs_walk_fn_t fn, void *ctx)
227 fib_protocol_t fproto;
230 FOR_EACH_FIB_IP_PROTOCOL (fproto)
232 vec_foreach_index (ii, svs_itf_db[fproto])
234 fib_index = svs_itf_db[fproto][ii];
238 if (WALK_CONTINUE != fn (fproto,
239 fib_table_get_table_id (fib_index, fproto),
247 typedef enum svs_next_t_
253 typedef struct svs_input_trace_t_
259 svs_input_inline (vlib_main_t * vm,
260 vlib_node_runtime_t * node,
261 vlib_frame_t * frame, fib_protocol_t fproto)
263 u32 n_left_from, *from, *to_next, next_index;
265 from = vlib_frame_vector_args (frame);
266 n_left_from = frame->n_vectors;
267 next_index = node->cached_next_index;
269 while (n_left_from > 0)
273 vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
275 while (n_left_from > 0 && n_left_to_next > 0)
277 const load_balance_t *lb0;
278 const lookup_dpo_t *lk0;
279 u32 bi0, sw_if_index0;
280 const dpo_id_t *dpo0;
292 b0 = vlib_get_buffer (vm, bi0);
293 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
295 if (FIB_PROTOCOL_IP4 == fproto)
299 ip0 = vlib_buffer_get_current (b0);
301 ip4_fib_forwarding_lookup (svs_itf_db[fproto][sw_if_index0],
308 ip0 = vlib_buffer_get_current (b0);
309 lbi0 = ip6_fib_table_fwding_lookup (svs_itf_db[fproto]
313 lb0 = load_balance_get (lbi0);
314 dpo0 = load_balance_get_fwd_bucket (lb0, 0);
315 lk0 = lookup_dpo_get (dpo0->dpoi_index);
317 vnet_buffer (b0)->sw_if_index[VLIB_TX] = lk0->lkd_fib_index;
319 vnet_feature_next (&next0, b0);
321 if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
323 svs_input_trace_t *tr;
325 tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
326 tr->fib_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
329 /* verify speculative enqueue, maybe switch current next frame */
330 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
331 to_next, n_left_to_next, bi0,
335 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
338 return frame->n_vectors;
342 svs_input_ip4 (vlib_main_t * vm,
343 vlib_node_runtime_t * node, vlib_frame_t * frame)
345 return svs_input_inline (vm, node, frame, FIB_PROTOCOL_IP4);
349 svs_input_ip6 (vlib_main_t * vm,
350 vlib_node_runtime_t * node, vlib_frame_t * frame)
352 return svs_input_inline (vm, node, frame, FIB_PROTOCOL_IP6);
356 format_svs_input_trace (u8 * s, va_list * args)
358 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
359 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
360 svs_input_trace_t *t = va_arg (*args, svs_input_trace_t *);
362 s = format (s, " fib_index %d", t->fib_index);
366 VLIB_REGISTER_NODE (svs_ip4_node) =
368 .function = svs_input_ip4,
370 .vector_size = sizeof (u32),
371 .format_trace = format_svs_input_trace,
372 .type = VLIB_NODE_TYPE_INTERNAL,
373 .n_next_nodes = SVS_N_NEXT,
376 [SVS_NEXT_DROP] = "error-drop",
380 VLIB_REGISTER_NODE (svs_ip6_node) =
382 .function = svs_input_ip6,
384 .vector_size = sizeof (u32),
385 .format_trace = format_svs_input_trace,
386 .type = VLIB_NODE_TYPE_INTERNAL,
389 [SVS_NEXT_DROP] = "error-drop",
393 VNET_FEATURE_INIT (svs_ip4_feat, static) =
395 .arc_name = "ip4-unicast",
396 .node_name = "svs-ip4",
399 VNET_FEATURE_INIT (svs_ip6_feat, static) =
401 .arc_name = "ip6-unicast",
402 .node_name = "svs-ip6",
405 static clib_error_t *
406 svs_table_cli (vlib_main_t * vm,
407 unformat_input_t * input, vlib_cli_command_t * cmd)
409 fib_protocol_t fproto;
413 fproto = FIB_PROTOCOL_IP4;
417 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
419 if (unformat (input, "add"))
421 else if (unformat (input, "del"))
423 else if (unformat (input, "ip4"))
424 fproto = FIB_PROTOCOL_IP4;
425 else if (unformat (input, "ip6"))
426 fproto = FIB_PROTOCOL_IP6;
427 else if (unformat (input, "table-id %d", &table_id))
434 return clib_error_return (0, "table-id must be specified");
437 svs_table_add (fproto, table_id);
439 svs_table_delete (fproto, table_id);
444 VLIB_CLI_COMMAND (svs_table_cmd_cli, static) = {
446 .short_help = "Source VRF select table [add|delete] [ip4|ip6] table-id X",
447 .function = svs_table_cli,
450 static clib_error_t *
451 svs_enable_cli (vlib_main_t * vm,
452 unformat_input_t * input, vlib_cli_command_t * cmd)
454 u32 sw_if_index, table_id;
455 fib_protocol_t fproto;
459 vnm = vnet_get_main ();
460 sw_if_index = table_id = ~0;
461 fproto = FIB_PROTOCOL_IP4;
464 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
466 if (unformat (input, "%U", unformat_vnet_sw_interface,
469 else if (unformat (input, "enable"))
471 else if (unformat (input, "disable"))
473 else if (unformat (input, "ip4"))
474 fproto = FIB_PROTOCOL_IP4;
475 else if (unformat (input, "ip6"))
476 fproto = FIB_PROTOCOL_IP6;
477 else if (unformat (input, "table-id %d", &table_id))
483 if (~0 == sw_if_index)
484 return clib_error_return (0, "interface must be specified");
486 return clib_error_return (0, "table-id must be specified");
489 svs_enable (fproto, table_id, sw_if_index);
491 svs_disable (fproto, table_id, sw_if_index);
496 VLIB_CLI_COMMAND (svs_enable_cli_cmd, static) = {
497 .path = "svs enable",
498 .short_help = "Source VRF select [enable|disable] [ip4|ip6] <table-id> X <interface>",
499 .function = svs_enable_cli,
502 static clib_error_t *
503 svs_route_cli (vlib_main_t * vm,
504 unformat_input_t * input, vlib_cli_command_t * cmd)
506 u32 table_id, src_table_id;
511 src_table_id = table_id = ~0;
514 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
516 if (unformat (input, "add"))
518 else if (unformat (input, "del"))
520 else if (unformat (input, "table-id %d", &table_id))
522 else if (unformat (input, "src-table-id %d", &src_table_id))
524 else if (unformat (input, "%U/%d",
525 unformat_ip4_address, &pfx.fp_addr.ip4, &pfx.fp_len))
527 pfx.fp_proto = FIB_PROTOCOL_IP4;
529 else if (unformat (input, "%U/%d",
530 unformat_ip6_address, &pfx.fp_addr.ip6, &pfx.fp_len))
532 pfx.fp_proto = FIB_PROTOCOL_IP6;
539 return clib_error_return (0, "table-id must be specified");
540 if (~0 == src_table_id)
541 return clib_error_return (0, "src-table-id must be specified");
544 rv = svs_route_add (table_id, &pfx, src_table_id);
546 rv = svs_route_delete (table_id, &pfx);
549 return clib_error_return (0,
551 (int) rv, format_vnet_api_errno, rv);
556 VLIB_CLI_COMMAND (svs_route_cmd_cli, static) = {
558 .short_help = "Source VRF select route [add|delete] <table-id> <prefix> <src-table-id>",
559 .function = svs_route_cli,
562 static clib_error_t *
563 svs_show_cli (vlib_main_t * vm,
564 unformat_input_t * input, vlib_cli_command_t * cmd)
566 fib_protocol_t fproto;
569 vlib_cli_output (vm, "Source VRF select interface to fib-index mappings:");
570 FOR_EACH_FIB_IP_PROTOCOL (fproto)
572 vlib_cli_output (vm, " %U", format_fib_protocol, fproto);
573 vec_foreach_index (ii, svs_itf_db[fproto])
575 if (~0 != svs_itf_db[fproto][ii])
576 vlib_cli_output (vm, " %U -> %d", format_vnet_sw_if_index_name,
577 vnet_get_main (), ii, svs_itf_db[fproto][ii]);
583 VLIB_CLI_COMMAND (svs_show_cli_cmd, static) = {
585 .short_help = "Source VRF select show",
586 .function = svs_show_cli,
589 static clib_error_t *
590 svs_init (vlib_main_t * vm)
592 ip6_table_bind_callback_t cbt6 = {
593 .function = svs_ip6_table_bind,
595 vec_add1 (ip6_main.table_bind_callbacks, cbt6);
597 ip4_table_bind_callback_t cbt4 = {
598 .function = svs_ip4_table_bind,
600 vec_add1 (ip4_main.table_bind_callbacks, cbt4);
602 svs_fib_src = fib_source_allocate ("svs",
603 FIB_SOURCE_PRIORITY_LOW,
604 FIB_SOURCE_BH_SIMPLE);
609 VLIB_INIT_FUNCTION (svs_init);
612 * fd.io coding-style-patch-verification: ON
615 * eval: (c-set-style "gnu")