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/l3xc/l3xc.h>
17 :
18 : #include <vlib/vlib.h>
19 : #include <vnet/plugin/plugin.h>
20 : #include <vnet/fib/fib_path_list.h>
21 :
22 : /**
23 : * FIB node type the attachment is registered
24 : */
25 : fib_node_type_t l3xc_fib_node_type;
26 :
27 : /**
28 : * Pool of L3XC objects
29 : */
30 : l3xc_t *l3xc_pool;
31 :
32 : /**
33 : * DB of L3XC objects
34 : */
35 : static u32 *l3xc_db[FIB_PROTOCOL_IP_MAX];
36 :
37 : index_t
38 10 : l3xc_find (u32 sw_if_index, fib_protocol_t fproto)
39 : {
40 10 : if (vec_len (l3xc_db[fproto]) <= sw_if_index)
41 5 : return ~0;
42 :
43 5 : return (l3xc_db[fproto][sw_if_index]);
44 : }
45 :
46 : static void
47 2 : l3xc_db_add (u32 sw_if_index, fib_protocol_t fproto, index_t l3xci)
48 : {
49 6 : vec_validate_init_empty (l3xc_db[fproto], sw_if_index, ~0);
50 :
51 2 : l3xc_db[fproto][sw_if_index] = l3xci;
52 2 : }
53 :
54 : static void
55 2 : l3xc_db_remove (u32 sw_if_index, fib_protocol_t fproto)
56 : {
57 2 : vec_validate_init_empty (l3xc_db[fproto], sw_if_index, ~0);
58 :
59 2 : l3xc_db[fproto][sw_if_index] = ~0;
60 2 : }
61 :
62 : static void
63 5 : l3xc_stack (l3xc_t * l3xc)
64 : {
65 : /*
66 : * stack the DPO on the forwarding contributed by the path-list
67 : */
68 5 : dpo_id_t via_dpo = DPO_INVALID;
69 :
70 5 : fib_path_list_contribute_forwarding (
71 : l3xc->l3xc_pl,
72 5 : (FIB_PROTOCOL_IP4 == l3xc->l3xc_proto ? FIB_FORW_CHAIN_TYPE_UNICAST_IP4 :
73 : FIB_FORW_CHAIN_TYPE_UNICAST_IP6),
74 : FIB_PATH_LIST_FWD_FLAG_COLLAPSE, &via_dpo);
75 :
76 5 : dpo_stack_from_node ((FIB_PROTOCOL_IP4 == l3xc->l3xc_proto ?
77 : l3xc_ip4_node.index :
78 : l3xc_ip6_node.index), &l3xc->l3xc_dpo, &via_dpo);
79 5 : dpo_reset (&via_dpo);
80 5 : }
81 :
82 : int
83 2 : l3xc_update (u32 sw_if_index, u8 is_ip6, const fib_route_path_t * rpaths)
84 : {
85 : fib_protocol_t fproto;
86 : l3xc_t *l3xc;
87 : u32 l3xci;
88 :
89 2 : fproto = (is_ip6 ? FIB_PROTOCOL_IP6 : FIB_PROTOCOL_IP4);
90 :
91 2 : l3xci = l3xc_find (sw_if_index, fproto);
92 :
93 2 : if (INDEX_INVALID == l3xci)
94 : {
95 : /*
96 : * create a new x-connect
97 : */
98 2 : pool_get_aligned_zero (l3xc_pool, l3xc, CLIB_CACHE_LINE_BYTES);
99 :
100 2 : l3xci = l3xc - l3xc_pool;
101 2 : fib_node_init (&l3xc->l3xc_node, l3xc_fib_node_type);
102 2 : l3xc->l3xc_sw_if_index = sw_if_index;
103 2 : l3xc->l3xc_proto = fproto;
104 :
105 : /*
106 : * create and become a child of a path list so we get poked when
107 : * the forwarding changes and stack on the DPO the path-list provides
108 : */
109 2 : l3xc->l3xc_pl = fib_path_list_create ((FIB_PATH_LIST_FLAG_SHARED |
110 : FIB_PATH_LIST_FLAG_NO_URPF),
111 : rpaths);
112 2 : l3xc->l3xc_sibling = fib_path_list_child_add (l3xc->l3xc_pl,
113 : l3xc_fib_node_type,
114 : l3xci);
115 2 : l3xc_stack (l3xc);
116 :
117 : /*
118 : * add this new policy to the DB and enable the feature on input interface
119 : */
120 2 : l3xc_db_add (sw_if_index, fproto, l3xci);
121 :
122 2 : vnet_feature_enable_disable ((FIB_PROTOCOL_IP4 == fproto ?
123 : "ip4-unicast" :
124 : "ip6-unicast"),
125 : (FIB_PROTOCOL_IP4 == fproto ?
126 : "l3xc-input-ip4" :
127 : "l3xc-input-ip6"),
128 2 : l3xc->l3xc_sw_if_index,
129 : 1, &l3xci, sizeof (l3xci));
130 : }
131 : else
132 : {
133 : /*
134 : * update an existing x-connect.
135 : * - add the path to the path-list and swap our ancestry
136 : */
137 0 : l3xc = l3xc_get (l3xci);
138 :
139 0 : if (FIB_NODE_INDEX_INVALID != l3xc->l3xc_pl)
140 : {
141 0 : fib_path_list_child_remove (l3xc->l3xc_pl, l3xc->l3xc_sibling);
142 : }
143 :
144 0 : l3xc->l3xc_pl = fib_path_list_create ((FIB_PATH_LIST_FLAG_SHARED |
145 : FIB_PATH_LIST_FLAG_NO_URPF),
146 : rpaths);
147 :
148 0 : l3xc->l3xc_sibling = fib_path_list_child_add (l3xc->l3xc_pl,
149 : l3xc_fib_node_type,
150 : l3xci);
151 : }
152 2 : return (0);
153 : }
154 :
155 : int
156 2 : l3xc_delete (u32 sw_if_index, u8 is_ip6)
157 : {
158 : fib_protocol_t fproto;
159 : l3xc_t *l3xc;
160 : u32 l3xci;
161 :
162 2 : fproto = (is_ip6 ? FIB_PROTOCOL_IP6 : FIB_PROTOCOL_IP4);
163 :
164 2 : l3xci = l3xc_find (sw_if_index, fproto);
165 :
166 2 : if (INDEX_INVALID == l3xci)
167 : {
168 : /*
169 : * no such policy
170 : */
171 0 : return (VNET_API_ERROR_INVALID_VALUE);
172 : }
173 : else
174 : {
175 2 : l3xc = l3xc_get (l3xci);
176 :
177 2 : vnet_feature_enable_disable ((FIB_PROTOCOL_IP4 == fproto ?
178 : "ip4-unicast" :
179 : "ip6-unicast"),
180 : (FIB_PROTOCOL_IP4 == fproto ?
181 : "l3xc-input-ip4" :
182 : "l3xc-input-ip6"),
183 : l3xc->l3xc_sw_if_index,
184 : 0, &l3xci, sizeof (l3xci));
185 :
186 2 : fib_path_list_child_remove (l3xc->l3xc_pl, l3xc->l3xc_sibling);
187 2 : dpo_reset (&l3xc->l3xc_dpo);
188 :
189 2 : l3xc_db_remove (l3xc->l3xc_sw_if_index, fproto);
190 2 : pool_put (l3xc_pool, l3xc);
191 : }
192 :
193 2 : return (0);
194 : }
195 :
196 : static clib_error_t *
197 0 : l3xc_cmd (vlib_main_t * vm,
198 : unformat_input_t * main_input, vlib_cli_command_t * cmd)
199 : {
200 0 : unformat_input_t _line_input, *line_input = &_line_input;
201 0 : fib_route_path_t *rpaths = NULL, rpath;
202 : u32 sw_if_index, is_del, is_ip6;
203 : dpo_proto_t payload_proto;
204 : vnet_main_t *vnm;
205 0 : int rv = 0;
206 :
207 0 : is_ip6 = is_del = 0;
208 0 : sw_if_index = ~0;
209 0 : vnm = vnet_get_main ();
210 :
211 : /* Get a line of input. */
212 0 : if (!unformat_user (main_input, unformat_line_input, line_input))
213 0 : return 0;
214 :
215 0 : while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
216 : {
217 0 : if (unformat
218 : (line_input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index))
219 : ;
220 0 : else if (unformat (line_input, "ip6"))
221 0 : is_ip6 = 1;
222 0 : else if (unformat (line_input, "ip4"))
223 0 : is_ip6 = 0;
224 0 : else if (unformat (line_input, "del"))
225 0 : is_del = 1;
226 0 : else if (unformat (line_input, "add"))
227 0 : is_del = 0;
228 0 : else if (unformat (line_input, "via %U",
229 : unformat_fib_route_path, &rpath, &payload_proto))
230 0 : vec_add1 (rpaths, rpath);
231 : else
232 0 : return (clib_error_return (0, "unknown input '%U'",
233 : format_unformat_error, line_input));
234 : }
235 :
236 0 : if (~0 == sw_if_index)
237 : {
238 0 : vlib_cli_output (vm, "Specify an input interface");
239 0 : goto out;
240 : }
241 0 : if (vec_len (rpaths) == 0)
242 : {
243 0 : vlib_cli_output (vm, "Specify some paths");
244 0 : goto out;
245 : }
246 :
247 0 : if (!is_del)
248 : {
249 0 : rv = l3xc_update (sw_if_index, is_ip6, rpaths);
250 :
251 0 : if (rv)
252 : {
253 0 : vlib_cli_output (vm, "Failed: %d", rv);
254 0 : goto out;
255 : }
256 : }
257 : else
258 : {
259 0 : l3xc_delete (sw_if_index, is_ip6);
260 : }
261 :
262 0 : out:
263 0 : unformat_free (line_input);
264 0 : return (NULL);
265 : }
266 :
267 : /* *INDENT-OFF* */
268 : /**
269 : * Create an L3XC policy.
270 : */
271 170967 : VLIB_CLI_COMMAND (l3xc_cmd_node, static) = {
272 : .path = "l3xc",
273 : .function = l3xc_cmd,
274 : .short_help = "l3xc [add|del] <INTERFACE> via ...",
275 : .is_mp_safe = 1,
276 : };
277 : /* *INDENT-ON* */
278 :
279 : static u8 *
280 2 : format_l3xc (u8 * s, va_list * args)
281 : {
282 2 : l3xc_t *l3xc = va_arg (*args, l3xc_t *);
283 2 : vnet_main_t *vnm = vnet_get_main ();
284 :
285 2 : s = format (s, "l3xc:[%d]: %U",
286 2 : l3xc - l3xc_pool, format_vnet_sw_if_index_name,
287 : vnm, l3xc->l3xc_sw_if_index);
288 2 : s = format (s, "\n");
289 2 : if (FIB_NODE_INDEX_INVALID == l3xc->l3xc_pl)
290 : {
291 0 : s = format (s, "no forwarding");
292 : }
293 : else
294 : {
295 2 : s = fib_path_list_format (l3xc->l3xc_pl, s);
296 :
297 2 : s = format (s, "\n %U", format_dpo_id, &l3xc->l3xc_dpo, 4);
298 : }
299 :
300 2 : return (s);
301 : }
302 :
303 : void
304 1 : l3xc_walk (l3xc_walk_cb_t cb, void *ctx)
305 : {
306 : u32 l3xci;
307 :
308 : /* *INDENT-OFF* */
309 3 : pool_foreach_index (l3xci, l3xc_pool)
310 : {
311 2 : if (!cb(l3xci, ctx))
312 0 : break;
313 : }
314 : /* *INDENT-ON* */
315 1 : }
316 :
317 : static clib_error_t *
318 1 : l3xc_show_cmd (vlib_main_t * vm,
319 : unformat_input_t * input, vlib_cli_command_t * cmd)
320 : {
321 : l3xc_t *l3xc;
322 :
323 : /* *INDENT-OFF* */
324 3 : pool_foreach (l3xc, l3xc_pool)
325 : {
326 2 : vlib_cli_output(vm, "%U", format_l3xc, l3xc);
327 : }
328 : /* *INDENT-ON* */
329 :
330 1 : return (NULL);
331 : }
332 :
333 : /* *INDENT-OFF* */
334 170967 : VLIB_CLI_COMMAND (l3xc_show_cmd_node, static) = {
335 : .path = "show l3xc",
336 : .function = l3xc_show_cmd,
337 : .short_help = "show l3xc",
338 : .is_mp_safe = 1,
339 : };
340 : /* *INDENT-ON* */
341 :
342 : static fib_node_t *
343 3 : l3xc_get_node (fib_node_index_t index)
344 : {
345 3 : l3xc_t *l3xc = l3xc_get (index);
346 3 : return (&(l3xc->l3xc_node));
347 : }
348 :
349 : static l3xc_t *
350 3 : l3xc_get_from_node (fib_node_t * node)
351 : {
352 3 : return ((l3xc_t *) (((char *) node) -
353 : STRUCT_OFFSET_OF (l3xc_t, l3xc_node)));
354 : }
355 :
356 : static void
357 0 : l3xc_last_lock_gone (fib_node_t * node)
358 : {
359 0 : }
360 :
361 : /*
362 : * A back walk has reached this L3XC policy
363 : */
364 : static fib_node_back_walk_rc_t
365 3 : l3xc_back_walk_notify (fib_node_t * node, fib_node_back_walk_ctx_t * ctx)
366 : {
367 3 : l3xc_stack (l3xc_get_from_node (node));
368 :
369 3 : return (FIB_NODE_BACK_WALK_CONTINUE);
370 : }
371 :
372 : /*
373 : * The BIER fmask's graph node virtual function table
374 : */
375 : static const fib_node_vft_t l3xc_vft = {
376 : .fnv_get = l3xc_get_node,
377 : .fnv_last_lock = l3xc_last_lock_gone,
378 : .fnv_back_walk = l3xc_back_walk_notify,
379 : };
380 :
381 : static clib_error_t *
382 559 : l3xc_init (vlib_main_t * vm)
383 : {
384 559 : l3xc_fib_node_type = fib_node_register_new_type ("l3xc", &l3xc_vft);
385 :
386 559 : return (NULL);
387 : }
388 :
389 1119 : VLIB_INIT_FUNCTION (l3xc_init);
390 :
391 : /*
392 : * fd.io coding-style-patch-verification: ON
393 : *
394 : * Local Variables:
395 : * eval: (c-set-style "gnu")
396 : * End:
397 : */
|