Line data Source code
1 : /*
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:
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 <plugins/svs/svs.h>
17 :
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>
26 :
27 : u32 *svs_itf_db[FIB_PROTOCOL_IP_MAX];
28 :
29 : static fib_source_t svs_fib_src;
30 :
31 : int
32 4 : svs_table_add (fib_protocol_t fproto, u32 table_id)
33 : {
34 4 : fib_table_find_or_create_and_lock (fproto, table_id, svs_fib_src);
35 :
36 4 : return (0);
37 : }
38 :
39 : int
40 4 : svs_table_delete (fib_protocol_t fproto, u32 table_id)
41 : {
42 : u32 fib_index, ii;
43 :
44 4 : fib_index = fib_table_find (fproto, table_id);
45 :
46 16 : vec_foreach_index (ii, svs_itf_db[fproto])
47 : {
48 12 : if (svs_itf_db[fproto][ii] == fib_index)
49 0 : return VNET_API_ERROR_INSTANCE_IN_USE;
50 : }
51 :
52 4 : if (~0 == fib_index)
53 0 : return VNET_API_ERROR_NO_SUCH_FIB;
54 :
55 4 : fib_table_unlock (fib_index, fproto, svs_fib_src);
56 :
57 4 : return (0);
58 : }
59 :
60 : static int
61 16 : svs_route_add_i (u32 fib_index, const fib_prefix_t * pfx, u32 src_fib_index)
62 : {
63 16 : dpo_id_t dpo = DPO_INVALID;
64 :
65 :
66 16 : lookup_dpo_add_or_lock_w_fib_index (src_fib_index,
67 16 : fib_proto_to_dpo (pfx->fp_proto),
68 : LOOKUP_UNICAST,
69 : LOOKUP_INPUT_SRC_ADDR,
70 : LOOKUP_TABLE_FROM_CONFIG, &dpo);
71 :
72 16 : fib_table_entry_special_dpo_add (fib_index, pfx,
73 : svs_fib_src,
74 : FIB_ENTRY_FLAG_EXCLUSIVE, &dpo);
75 :
76 16 : dpo_unlock (&dpo);
77 :
78 16 : return (0);
79 : }
80 :
81 : int
82 12 : svs_route_add (u32 table_id, const fib_prefix_t * pfx, u32 source_table_id)
83 : {
84 : u32 fib_index, src_fib_index;
85 : int rv;
86 :
87 12 : fib_index = fib_table_find (pfx->fp_proto, table_id);
88 :
89 12 : if (~0 == fib_index)
90 0 : return VNET_API_ERROR_NO_SUCH_FIB;
91 :
92 12 : src_fib_index = fib_table_find (pfx->fp_proto, source_table_id);
93 :
94 12 : if (~0 == src_fib_index)
95 0 : return (VNET_API_ERROR_NO_SUCH_FIB);
96 :
97 12 : rv = svs_route_add_i (fib_index, pfx, src_fib_index);
98 :
99 12 : return (rv);
100 : }
101 :
102 : int
103 12 : svs_route_delete (u32 table_id, const fib_prefix_t * pfx)
104 : {
105 : u32 fib_index;
106 :
107 12 : fib_index = fib_table_find (pfx->fp_proto, table_id);
108 :
109 12 : if (~0 == fib_index)
110 0 : return VNET_API_ERROR_NO_SUCH_FIB;
111 :
112 12 : fib_table_entry_special_remove (fib_index, pfx, svs_fib_src);
113 :
114 12 : return (0);
115 : }
116 :
117 : int
118 4 : svs_enable (fib_protocol_t fproto, u32 table_id, u32 sw_if_index)
119 : {
120 4 : fib_prefix_t pfx = {
121 : .fp_proto = fproto,
122 : };
123 : u32 fib_index;
124 :
125 4 : fib_index = fib_table_find (fproto, table_id);
126 :
127 4 : if (~0 == fib_index)
128 0 : return VNET_API_ERROR_NO_SUCH_FIB;
129 :
130 : /*
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.
133 : */
134 4 : svs_route_add_i (fib_index, &pfx,
135 : fib_table_get_index_for_sw_if_index (fproto, sw_if_index));
136 :
137 10 : vec_validate_init_empty (svs_itf_db[fproto], sw_if_index, ~0);
138 :
139 4 : svs_itf_db[fproto][sw_if_index] = fib_index;
140 :
141 4 : vnet_feature_enable_disable ((FIB_PROTOCOL_IP4 == fproto ?
142 : "ip4-unicast" :
143 : "ip6-unicast"),
144 : (FIB_PROTOCOL_IP4 == fproto ?
145 : "svs-ip4" :
146 : "svs-ip6"), sw_if_index, 1, NULL, 0);
147 :
148 4 : return (0);
149 : }
150 :
151 : static void
152 1723 : svs_table_bind (fib_protocol_t fproto, u32 sw_if_index, u32 itf_fib_index)
153 : {
154 : /*
155 : * update the default route to use the interface's newly bound FIB
156 : */
157 : u32 svs_fib_index;
158 :
159 1723 : if (sw_if_index >= vec_len (svs_itf_db[FIB_PROTOCOL_IP6]))
160 1719 : return;
161 :
162 4 : svs_fib_index = svs_itf_db[FIB_PROTOCOL_IP6][sw_if_index];
163 :
164 4 : if (~0 != svs_fib_index)
165 : {
166 0 : fib_prefix_t pfx = {
167 : .fp_proto = fproto,
168 : };
169 :
170 0 : svs_route_add (svs_fib_index, &pfx, itf_fib_index);
171 : }
172 : /*
173 : * else
174 : * no SVS enable on this interface
175 : */
176 : }
177 :
178 : static void
179 777 : svs_ip6_table_bind (ip6_main_t * im,
180 : uword opaque,
181 : u32 sw_if_index, u32 new_fib_index, u32 old_fib_index)
182 : {
183 777 : svs_table_bind (FIB_PROTOCOL_IP6, sw_if_index, new_fib_index);
184 777 : }
185 :
186 : static void
187 946 : svs_ip4_table_bind (ip4_main_t * im,
188 : uword opaque,
189 : u32 sw_if_index, u32 new_fib_index, u32 old_fib_index)
190 : {
191 946 : svs_table_bind (FIB_PROTOCOL_IP4, sw_if_index, new_fib_index);
192 946 : }
193 :
194 : int
195 4 : svs_disable (fib_protocol_t fproto, u32 table_id, u32 sw_if_index)
196 : {
197 4 : fib_prefix_t pfx = {
198 : .fp_proto = fproto,
199 : };
200 : u32 fib_index;
201 :
202 4 : fib_index = fib_table_find (fproto, table_id);
203 :
204 4 : if (~0 == fib_index)
205 0 : return VNET_API_ERROR_NO_SUCH_FIB;
206 :
207 4 : if (sw_if_index >= vec_len (svs_itf_db[fproto]))
208 0 : return VNET_API_ERROR_INVALID_SW_IF_INDEX;
209 :
210 4 : svs_itf_db[fproto][sw_if_index] = ~0;
211 :
212 4 : vnet_feature_enable_disable ((FIB_PROTOCOL_IP4 == fproto ?
213 : "ip4-unicast" :
214 : "ip6-unicast"),
215 : (FIB_PROTOCOL_IP4 == fproto ?
216 : "svs-ip4" :
217 : "svs-ip6"), sw_if_index, 0, NULL, 0);
218 :
219 4 : fib_table_entry_special_remove (fib_index, &pfx, svs_fib_src);
220 :
221 4 : return (0);
222 : }
223 :
224 : void
225 2 : svs_walk (svs_walk_fn_t fn, void *ctx)
226 : {
227 : fib_protocol_t fproto;
228 : u32 ii, fib_index;
229 :
230 6 : FOR_EACH_FIB_IP_PROTOCOL (fproto)
231 : {
232 13 : vec_foreach_index (ii, svs_itf_db[fproto])
233 : {
234 9 : fib_index = svs_itf_db[fproto][ii];
235 :
236 9 : if (~0 != fib_index)
237 : {
238 4 : if (WALK_CONTINUE != fn (fproto,
239 : fib_table_get_table_id (fib_index, fproto),
240 : ii, ctx))
241 0 : return;
242 : }
243 : }
244 : }
245 : }
246 :
247 : typedef enum svs_next_t_
248 : {
249 : SVS_NEXT_DROP,
250 : SVS_N_NEXT,
251 : } svs_next_t;
252 :
253 : typedef struct svs_input_trace_t_
254 : {
255 : u32 fib_index;
256 : } svs_input_trace_t;
257 :
258 : always_inline uword
259 16 : svs_input_inline (vlib_main_t * vm,
260 : vlib_node_runtime_t * node,
261 : vlib_frame_t * frame, fib_protocol_t fproto)
262 : {
263 : u32 n_left_from, *from, *to_next, next_index;
264 :
265 16 : from = vlib_frame_vector_args (frame);
266 16 : n_left_from = frame->n_vectors;
267 16 : next_index = node->cached_next_index;
268 :
269 32 : while (n_left_from > 0)
270 : {
271 : u32 n_left_to_next;
272 :
273 16 : vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
274 :
275 1088 : while (n_left_from > 0 && n_left_to_next > 0)
276 : {
277 : const load_balance_t *lb0;
278 : const lookup_dpo_t *lk0;
279 : u32 bi0, sw_if_index0;
280 : const dpo_id_t *dpo0;
281 : vlib_buffer_t *b0;
282 : svs_next_t next0;
283 : index_t lbi0;
284 :
285 1072 : bi0 = from[0];
286 1072 : to_next[0] = bi0;
287 1072 : from += 1;
288 1072 : to_next += 1;
289 1072 : n_left_from -= 1;
290 1072 : n_left_to_next -= 1;
291 :
292 1072 : b0 = vlib_get_buffer (vm, bi0);
293 1072 : sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
294 :
295 1072 : if (FIB_PROTOCOL_IP4 == fproto)
296 : {
297 : ip4_header_t *ip0;
298 :
299 536 : ip0 = vlib_buffer_get_current (b0);
300 : lbi0 =
301 536 : ip4_fib_forwarding_lookup (svs_itf_db[fproto][sw_if_index0],
302 536 : &ip0->src_address);
303 : }
304 : else
305 : {
306 : ip6_header_t *ip0;
307 :
308 536 : ip0 = vlib_buffer_get_current (b0);
309 536 : lbi0 = ip6_fib_table_fwding_lookup (svs_itf_db[fproto]
310 536 : [sw_if_index0],
311 536 : &ip0->src_address);
312 : }
313 1072 : lb0 = load_balance_get (lbi0);
314 1072 : dpo0 = load_balance_get_fwd_bucket (lb0, 0);
315 1072 : lk0 = lookup_dpo_get (dpo0->dpoi_index);
316 :
317 1072 : vnet_buffer (b0)->sw_if_index[VLIB_TX] = lk0->lkd_fib_index;
318 :
319 1072 : vnet_feature_next (&next0, b0);
320 :
321 1072 : if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
322 : {
323 : svs_input_trace_t *tr;
324 :
325 1072 : tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
326 1072 : tr->fib_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
327 : }
328 :
329 : /* verify speculative enqueue, maybe switch current next frame */
330 1072 : vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
331 : to_next, n_left_to_next, bi0,
332 : next0);
333 : }
334 :
335 16 : vlib_put_next_frame (vm, node, next_index, n_left_to_next);
336 : }
337 :
338 16 : return frame->n_vectors;
339 : }
340 :
341 : static uword
342 8 : svs_input_ip4 (vlib_main_t * vm,
343 : vlib_node_runtime_t * node, vlib_frame_t * frame)
344 : {
345 8 : return svs_input_inline (vm, node, frame, FIB_PROTOCOL_IP4);
346 : }
347 :
348 : static uword
349 8 : svs_input_ip6 (vlib_main_t * vm,
350 : vlib_node_runtime_t * node, vlib_frame_t * frame)
351 : {
352 8 : return svs_input_inline (vm, node, frame, FIB_PROTOCOL_IP6);
353 : }
354 :
355 : static u8 *
356 934 : format_svs_input_trace (u8 * s, va_list * args)
357 : {
358 934 : CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
359 934 : CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
360 934 : svs_input_trace_t *t = va_arg (*args, svs_input_trace_t *);
361 :
362 934 : s = format (s, " fib_index %d", t->fib_index);
363 934 : return s;
364 : }
365 :
366 : /* *INDENT-OFF* */
367 29951 : VLIB_REGISTER_NODE (svs_ip4_node) =
368 : {
369 : .function = svs_input_ip4,
370 : .name = "svs-ip4",
371 : .vector_size = sizeof (u32),
372 : .format_trace = format_svs_input_trace,
373 : .type = VLIB_NODE_TYPE_INTERNAL,
374 : .n_next_nodes = SVS_N_NEXT,
375 : .next_nodes =
376 : {
377 : [SVS_NEXT_DROP] = "error-drop",
378 : }
379 : };
380 :
381 29951 : VLIB_REGISTER_NODE (svs_ip6_node) =
382 : {
383 : .function = svs_input_ip6,
384 : .name = "svs-ip6",
385 : .vector_size = sizeof (u32),
386 : .format_trace = format_svs_input_trace,
387 : .type = VLIB_NODE_TYPE_INTERNAL,
388 : .next_nodes =
389 : {
390 : [SVS_NEXT_DROP] = "error-drop",
391 : }
392 : };
393 :
394 12095 : VNET_FEATURE_INIT (svs_ip4_feat, static) =
395 : {
396 : .arc_name = "ip4-unicast",
397 : .node_name = "svs-ip4",
398 : };
399 :
400 12095 : VNET_FEATURE_INIT (svs_ip6_feat, static) =
401 : {
402 : .arc_name = "ip6-unicast",
403 : .node_name = "svs-ip6",
404 : };
405 : /* *INDENT-ON* */
406 :
407 : static clib_error_t *
408 0 : svs_table_cli (vlib_main_t * vm,
409 : unformat_input_t * input, vlib_cli_command_t * cmd)
410 : {
411 : fib_protocol_t fproto;
412 : u32 table_id;
413 : u8 add;
414 :
415 0 : fproto = FIB_PROTOCOL_IP4;
416 0 : table_id = ~0;
417 0 : add = 1;
418 :
419 0 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
420 : {
421 0 : if (unformat (input, "add"))
422 0 : add = 1;
423 0 : else if (unformat (input, "del"))
424 0 : add = 0;
425 0 : else if (unformat (input, "ip4"))
426 0 : fproto = FIB_PROTOCOL_IP4;
427 0 : else if (unformat (input, "ip6"))
428 0 : fproto = FIB_PROTOCOL_IP6;
429 0 : else if (unformat (input, "table-id %d", &table_id))
430 : ;
431 : else
432 0 : break;
433 : }
434 :
435 0 : if (~0 == table_id)
436 0 : return clib_error_return (0, "table-id must be specified");
437 :
438 0 : if (add)
439 0 : svs_table_add (fproto, table_id);
440 : else
441 0 : svs_table_delete (fproto, table_id);
442 :
443 0 : return (NULL);
444 : }
445 :
446 : /* *INDENT-OFF* */
447 38015 : VLIB_CLI_COMMAND (svs_table_cmd_cli, static) = {
448 : .path = "svs table",
449 : .short_help = "Source VRF select table [add|delete] [ip4|ip6] table-id X",
450 : .function = svs_table_cli,
451 : };
452 : /* *INDENT-ON* */
453 :
454 : static clib_error_t *
455 0 : svs_enable_cli (vlib_main_t * vm,
456 : unformat_input_t * input, vlib_cli_command_t * cmd)
457 : {
458 : u32 sw_if_index, table_id;
459 : fib_protocol_t fproto;
460 : vnet_main_t *vnm;
461 : u8 enable;
462 :
463 0 : vnm = vnet_get_main ();
464 0 : sw_if_index = table_id = ~0;
465 0 : fproto = FIB_PROTOCOL_IP4;
466 0 : enable = 1;
467 :
468 0 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
469 : {
470 0 : if (unformat (input, "%U", unformat_vnet_sw_interface,
471 : vnm, &sw_if_index))
472 : ;
473 0 : else if (unformat (input, "enable"))
474 0 : enable = 1;
475 0 : else if (unformat (input, "disable"))
476 0 : enable = 0;
477 0 : else if (unformat (input, "ip4"))
478 0 : fproto = FIB_PROTOCOL_IP4;
479 0 : else if (unformat (input, "ip6"))
480 0 : fproto = FIB_PROTOCOL_IP6;
481 0 : else if (unformat (input, "table-id %d", &table_id))
482 : ;
483 : else
484 0 : break;
485 : }
486 :
487 0 : if (~0 == sw_if_index)
488 0 : return clib_error_return (0, "interface must be specified");
489 0 : if (~0 == table_id)
490 0 : return clib_error_return (0, "table-id must be specified");
491 :
492 0 : if (enable)
493 0 : svs_enable (fproto, table_id, sw_if_index);
494 : else
495 0 : svs_disable (fproto, table_id, sw_if_index);
496 :
497 0 : return (NULL);
498 : }
499 :
500 : /* *INDENT-OFF* */
501 38015 : VLIB_CLI_COMMAND (svs_enable_cli_cmd, static) = {
502 : .path = "svs enable",
503 : .short_help = "Source VRF select [enable|disable] [ip4|ip6] <table-id> X <interface>",
504 : .function = svs_enable_cli,
505 : };
506 : /* *INDENT-ON* */
507 :
508 : static clib_error_t *
509 0 : svs_route_cli (vlib_main_t * vm,
510 : unformat_input_t * input, vlib_cli_command_t * cmd)
511 : {
512 : u32 table_id, src_table_id;
513 : fib_prefix_t pfx;
514 : int rv;
515 : u8 add;
516 :
517 0 : src_table_id = table_id = ~0;
518 0 : add = 1;
519 :
520 0 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
521 : {
522 0 : if (unformat (input, "add"))
523 0 : add = 1;
524 0 : else if (unformat (input, "del"))
525 0 : add = 0;
526 0 : else if (unformat (input, "table-id %d", &table_id))
527 : ;
528 0 : else if (unformat (input, "src-table-id %d", &src_table_id))
529 : ;
530 0 : else if (unformat (input, "%U/%d",
531 : unformat_ip4_address, &pfx.fp_addr.ip4, &pfx.fp_len))
532 : {
533 0 : pfx.fp_proto = FIB_PROTOCOL_IP4;
534 : }
535 0 : else if (unformat (input, "%U/%d",
536 : unformat_ip6_address, &pfx.fp_addr.ip6, &pfx.fp_len))
537 : {
538 0 : pfx.fp_proto = FIB_PROTOCOL_IP6;
539 : }
540 : else
541 0 : break;
542 : }
543 :
544 0 : if (~0 == table_id)
545 0 : return clib_error_return (0, "table-id must be specified");
546 0 : if (~0 == src_table_id)
547 0 : return clib_error_return (0, "src-table-id must be specified");
548 :
549 0 : if (add)
550 0 : rv = svs_route_add (table_id, &pfx, src_table_id);
551 : else
552 0 : rv = svs_route_delete (table_id, &pfx);
553 :
554 0 : if (rv != 0)
555 0 : return clib_error_return (0,
556 : "failed, rv=%d:%U",
557 : (int) rv, format_vnet_api_errno, rv);
558 :
559 0 : return (NULL);
560 : }
561 :
562 : /* *INDENT-OFF* */
563 38015 : VLIB_CLI_COMMAND (svs_route_cmd_cli, static) = {
564 : .path = "svs route",
565 : .short_help = "Source VRF select route [add|delete] <table-id> <prefix> <src-table-id>",
566 : .function = svs_route_cli,
567 : };
568 : /* *INDENT-ON* */
569 :
570 : static clib_error_t *
571 0 : svs_show_cli (vlib_main_t * vm,
572 : unformat_input_t * input, vlib_cli_command_t * cmd)
573 : {
574 : fib_protocol_t fproto;
575 : u32 ii;
576 :
577 0 : vlib_cli_output (vm, "Source VRF select interface to fib-index mappings:");
578 0 : FOR_EACH_FIB_IP_PROTOCOL (fproto)
579 : {
580 0 : vlib_cli_output (vm, " %U", format_fib_protocol, fproto);
581 0 : vec_foreach_index (ii, svs_itf_db[fproto])
582 : {
583 0 : if (~0 != svs_itf_db[fproto][ii])
584 0 : vlib_cli_output (vm, " %U -> %d", format_vnet_sw_if_index_name,
585 0 : vnet_get_main (), ii, svs_itf_db[fproto][ii]);
586 : }
587 : }
588 0 : return (NULL);
589 : }
590 :
591 : /* *INDENT-OFF* */
592 38015 : VLIB_CLI_COMMAND (svs_show_cli_cmd, static) = {
593 : .path = "show svs",
594 : .short_help = "Source VRF select show",
595 : .function = svs_show_cli,
596 : };
597 : /* *INDENT-ON* */
598 :
599 : static clib_error_t *
600 575 : svs_init (vlib_main_t * vm)
601 : {
602 575 : ip6_table_bind_callback_t cbt6 = {
603 : .function = svs_ip6_table_bind,
604 : };
605 575 : vec_add1 (ip6_main.table_bind_callbacks, cbt6);
606 :
607 575 : ip4_table_bind_callback_t cbt4 = {
608 : .function = svs_ip4_table_bind,
609 : };
610 575 : vec_add1 (ip4_main.table_bind_callbacks, cbt4);
611 :
612 575 : svs_fib_src = fib_source_allocate ("svs",
613 : FIB_SOURCE_PRIORITY_LOW,
614 : FIB_SOURCE_BH_SIMPLE);
615 :
616 575 : return (NULL);
617 : }
618 :
619 1151 : VLIB_INIT_FUNCTION (svs_init);
620 :
621 : /*
622 : * fd.io coding-style-patch-verification: ON
623 : *
624 : * Local Variables:
625 : * eval: (c-set-style "gnu")
626 : * End:
627 : */
|