docs: Use newer Ubuntu LTS in tutorial
[vpp.git] / src / plugins / l3xc / l3xc.c
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 l3xc_find (u32 sw_if_index, fib_protocol_t fproto)
39 {
40   if (vec_len (l3xc_db[fproto]) <= sw_if_index)
41     return ~0;
42
43   return (l3xc_db[fproto][sw_if_index]);
44 }
45
46 static void
47 l3xc_db_add (u32 sw_if_index, fib_protocol_t fproto, index_t l3xci)
48 {
49   vec_validate_init_empty (l3xc_db[fproto], sw_if_index, ~0);
50
51   l3xc_db[fproto][sw_if_index] = l3xci;
52 }
53
54 static void
55 l3xc_db_remove (u32 sw_if_index, fib_protocol_t fproto)
56 {
57   vec_validate_init_empty (l3xc_db[fproto], sw_if_index, ~0);
58
59   l3xc_db[fproto][sw_if_index] = ~0;
60 }
61
62 static void
63 l3xc_stack (l3xc_t * l3xc)
64 {
65   /*
66    * stack the DPO on the forwarding contributed by the path-list
67    */
68   dpo_id_t via_dpo = DPO_INVALID;
69
70   fib_path_list_contribute_forwarding (
71     l3xc->l3xc_pl,
72     (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   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   dpo_reset (&via_dpo);
80 }
81
82 int
83 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   fproto = (is_ip6 ? FIB_PROTOCOL_IP6 : FIB_PROTOCOL_IP4);
90
91   l3xci = l3xc_find (sw_if_index, fproto);
92
93   if (INDEX_INVALID == l3xci)
94     {
95       /*
96        * create a new x-connect
97        */
98       pool_get_aligned_zero (l3xc_pool, l3xc, CLIB_CACHE_LINE_BYTES);
99
100       l3xci = l3xc - l3xc_pool;
101       fib_node_init (&l3xc->l3xc_node, l3xc_fib_node_type);
102       l3xc->l3xc_sw_if_index = sw_if_index;
103       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       l3xc->l3xc_pl = fib_path_list_create ((FIB_PATH_LIST_FLAG_SHARED |
110                                              FIB_PATH_LIST_FLAG_NO_URPF),
111                                             rpaths);
112       l3xc->l3xc_sibling = fib_path_list_child_add (l3xc->l3xc_pl,
113                                                     l3xc_fib_node_type,
114                                                     l3xci);
115       l3xc_stack (l3xc);
116
117       /*
118        * add this new policy to the DB and enable the feature on input interface
119        */
120       l3xc_db_add (sw_if_index, fproto, l3xci);
121
122       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                                    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       l3xc = l3xc_get (l3xci);
138
139       if (FIB_NODE_INDEX_INVALID != l3xc->l3xc_pl)
140         {
141           fib_path_list_child_remove (l3xc->l3xc_pl, l3xc->l3xc_sibling);
142         }
143
144       l3xc->l3xc_pl = fib_path_list_create ((FIB_PATH_LIST_FLAG_SHARED |
145                                              FIB_PATH_LIST_FLAG_NO_URPF),
146                                             rpaths);
147
148       l3xc->l3xc_sibling = fib_path_list_child_add (l3xc->l3xc_pl,
149                                                     l3xc_fib_node_type,
150                                                     l3xci);
151     }
152   return (0);
153 }
154
155 int
156 l3xc_delete (u32 sw_if_index, u8 is_ip6)
157 {
158   fib_protocol_t fproto;
159   l3xc_t *l3xc;
160   u32 l3xci;
161
162   fproto = (is_ip6 ? FIB_PROTOCOL_IP6 : FIB_PROTOCOL_IP4);
163
164   l3xci = l3xc_find (sw_if_index, fproto);
165
166   if (INDEX_INVALID == l3xci)
167     {
168       /*
169        * no such policy
170        */
171       return (VNET_API_ERROR_INVALID_VALUE);
172     }
173   else
174     {
175       l3xc = l3xc_get (l3xci);
176
177       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       fib_path_list_child_remove (l3xc->l3xc_pl, l3xc->l3xc_sibling);
187       dpo_reset (&l3xc->l3xc_dpo);
188
189       l3xc_db_remove (l3xc->l3xc_sw_if_index, fproto);
190       pool_put (l3xc_pool, l3xc);
191     }
192
193   return (0);
194 }
195
196 static clib_error_t *
197 l3xc_cmd (vlib_main_t * vm,
198           unformat_input_t * main_input, vlib_cli_command_t * cmd)
199 {
200   unformat_input_t _line_input, *line_input = &_line_input;
201   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   int rv = 0;
206
207   is_ip6 = is_del = 0;
208   sw_if_index = ~0;
209   vnm = vnet_get_main ();
210
211   /* Get a line of input. */
212   if (!unformat_user (main_input, unformat_line_input, line_input))
213     return 0;
214
215   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
216     {
217       if (unformat
218           (line_input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index))
219         ;
220       else if (unformat (line_input, "ip6"))
221         is_ip6 = 1;
222       else if (unformat (line_input, "ip4"))
223         is_ip6 = 0;
224       else if (unformat (line_input, "del"))
225         is_del = 1;
226       else if (unformat (line_input, "add"))
227         is_del = 0;
228       else if (unformat (line_input, "via %U",
229                          unformat_fib_route_path, &rpath, &payload_proto))
230         vec_add1 (rpaths, rpath);
231       else
232         return (clib_error_return (0, "unknown input '%U'",
233                                    format_unformat_error, line_input));
234     }
235
236   if (~0 == sw_if_index)
237     {
238       vlib_cli_output (vm, "Specify an input interface");
239       goto out;
240     }
241   if (vec_len (rpaths) == 0)
242     {
243       vlib_cli_output (vm, "Specify some paths");
244       goto out;
245     }
246
247   if (!is_del)
248     {
249       rv = l3xc_update (sw_if_index, is_ip6, rpaths);
250
251       if (rv)
252         {
253           vlib_cli_output (vm, "Failed: %d", rv);
254           goto out;
255         }
256     }
257   else
258     {
259       l3xc_delete (sw_if_index, is_ip6);
260     }
261
262 out:
263   unformat_free (line_input);
264   return (NULL);
265 }
266
267 /**
268  * Create an L3XC policy.
269  */
270 VLIB_CLI_COMMAND (l3xc_cmd_node, static) = {
271   .path = "l3xc",
272   .function = l3xc_cmd,
273   .short_help = "l3xc [add|del] <INTERFACE> via ...",
274   .is_mp_safe = 1,
275 };
276
277 static u8 *
278 format_l3xc (u8 * s, va_list * args)
279 {
280   l3xc_t *l3xc = va_arg (*args, l3xc_t *);
281   vnet_main_t *vnm = vnet_get_main ();
282
283   s = format (s, "l3xc:[%d]: %U",
284               l3xc - l3xc_pool, format_vnet_sw_if_index_name,
285               vnm, l3xc->l3xc_sw_if_index);
286   s = format (s, "\n");
287   if (FIB_NODE_INDEX_INVALID == l3xc->l3xc_pl)
288     {
289       s = format (s, "no forwarding");
290     }
291   else
292     {
293       s = fib_path_list_format (l3xc->l3xc_pl, s);
294
295       s = format (s, "\n  %U", format_dpo_id, &l3xc->l3xc_dpo, 4);
296     }
297
298   return (s);
299 }
300
301 void
302 l3xc_walk (l3xc_walk_cb_t cb, void *ctx)
303 {
304   u32 l3xci;
305
306   pool_foreach_index (l3xci, l3xc_pool)
307    {
308     if (!cb(l3xci, ctx))
309       break;
310   }
311 }
312
313 static clib_error_t *
314 l3xc_show_cmd (vlib_main_t * vm,
315                unformat_input_t * input, vlib_cli_command_t * cmd)
316 {
317   l3xc_t *l3xc;
318
319   pool_foreach (l3xc, l3xc_pool)
320    {
321     vlib_cli_output(vm, "%U", format_l3xc, l3xc);
322   }
323
324   return (NULL);
325 }
326
327 VLIB_CLI_COMMAND (l3xc_show_cmd_node, static) = {
328   .path = "show l3xc",
329   .function = l3xc_show_cmd,
330   .short_help = "show l3xc",
331   .is_mp_safe = 1,
332 };
333
334 static fib_node_t *
335 l3xc_get_node (fib_node_index_t index)
336 {
337   l3xc_t *l3xc = l3xc_get (index);
338   return (&(l3xc->l3xc_node));
339 }
340
341 static l3xc_t *
342 l3xc_get_from_node (fib_node_t * node)
343 {
344   return ((l3xc_t *) (((char *) node) -
345                       STRUCT_OFFSET_OF (l3xc_t, l3xc_node)));
346 }
347
348 static void
349 l3xc_last_lock_gone (fib_node_t * node)
350 {
351 }
352
353 /*
354  * A back walk has reached this L3XC policy
355  */
356 static fib_node_back_walk_rc_t
357 l3xc_back_walk_notify (fib_node_t * node, fib_node_back_walk_ctx_t * ctx)
358 {
359   l3xc_stack (l3xc_get_from_node (node));
360
361   return (FIB_NODE_BACK_WALK_CONTINUE);
362 }
363
364 /*
365  * The BIER fmask's graph node virtual function table
366  */
367 static const fib_node_vft_t l3xc_vft = {
368   .fnv_get = l3xc_get_node,
369   .fnv_last_lock = l3xc_last_lock_gone,
370   .fnv_back_walk = l3xc_back_walk_notify,
371 };
372
373 static clib_error_t *
374 l3xc_init (vlib_main_t * vm)
375 {
376   l3xc_fib_node_type = fib_node_register_new_type ("l3xc", &l3xc_vft);
377
378   return (NULL);
379 }
380
381 VLIB_INIT_FUNCTION (l3xc_init);
382
383 /*
384  * fd.io coding-style-patch-verification: ON
385  *
386  * Local Variables:
387  * eval: (c-set-style "gnu")
388  * End:
389  */