ip: Replace Sematics for Interface IP addresses
[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 (l3xc->l3xc_pl,
71                                        (FIB_PROTOCOL_IP4 == l3xc->l3xc_proto ?
72                                         FIB_FORW_CHAIN_TYPE_UNICAST_IP4 :
73                                         FIB_FORW_CHAIN_TYPE_UNICAST_IP6),
74                                        FIB_PATH_LIST_FWD_FLAG_NONE, &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
188       l3xc_db_remove (l3xc->l3xc_sw_if_index, fproto);
189       pool_put (l3xc_pool, l3xc);
190     }
191
192   return (0);
193 }
194
195 static clib_error_t *
196 l3xc_cmd (vlib_main_t * vm,
197           unformat_input_t * main_input, vlib_cli_command_t * cmd)
198 {
199   unformat_input_t _line_input, *line_input = &_line_input;
200   fib_route_path_t *rpaths = NULL, rpath;
201   u32 sw_if_index, is_del, is_ip6;
202   dpo_proto_t payload_proto;
203   vnet_main_t *vnm;
204   int rv = 0;
205
206   is_ip6 = is_del = 0;
207   sw_if_index = ~0;
208   vnm = vnet_get_main ();
209
210   /* Get a line of input. */
211   if (!unformat_user (main_input, unformat_line_input, line_input))
212     return 0;
213
214   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
215     {
216       if (unformat
217           (line_input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index))
218         ;
219       else if (unformat (line_input, "ip6"))
220         is_ip6 = 1;
221       else if (unformat (line_input, "ip4"))
222         is_ip6 = 0;
223       else if (unformat (line_input, "del"))
224         is_del = 1;
225       else if (unformat (line_input, "add"))
226         is_del = 0;
227       else if (unformat (line_input, "via %U",
228                          unformat_fib_route_path, &rpath, &payload_proto))
229         vec_add1 (rpaths, rpath);
230       else
231         return (clib_error_return (0, "unknown input '%U'",
232                                    format_unformat_error, line_input));
233     }
234
235   if (~0 == sw_if_index)
236     {
237       vlib_cli_output (vm, "Specify an input interface");
238       goto out;
239     }
240   if (vec_len (rpaths) == 0)
241     {
242       vlib_cli_output (vm, "Specify some paths");
243       goto out;
244     }
245
246   if (!is_del)
247     {
248       rv = l3xc_update (sw_if_index, is_ip6, rpaths);
249
250       if (rv)
251         {
252           vlib_cli_output (vm, "Failed: %d", rv);
253           goto out;
254         }
255     }
256   else
257     {
258       l3xc_delete (sw_if_index, is_ip6);
259     }
260
261 out:
262   unformat_free (line_input);
263   return (NULL);
264 }
265
266 /* *INDENT-OFF* */
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 /* *INDENT-ON* */
277
278 static u8 *
279 format_l3xc (u8 * s, va_list * args)
280 {
281   l3xc_t *l3xc = va_arg (*args, l3xc_t *);
282   vnet_main_t *vnm = vnet_get_main ();
283
284   s = format (s, "l3xc:[%d]: %U",
285               l3xc - l3xc_pool, format_vnet_sw_if_index_name,
286               vnm, l3xc->l3xc_sw_if_index);
287   s = format (s, "\n");
288   if (FIB_NODE_INDEX_INVALID == l3xc->l3xc_pl)
289     {
290       s = format (s, "no forwarding");
291     }
292   else
293     {
294       s = fib_path_list_format (l3xc->l3xc_pl, s);
295
296       s = format (s, "\n  %U", format_dpo_id, &l3xc->l3xc_dpo, 4);
297     }
298
299   return (s);
300 }
301
302 void
303 l3xc_walk (l3xc_walk_cb_t cb, void *ctx)
304 {
305   u32 l3xci;
306
307   /* *INDENT-OFF* */
308   pool_foreach_index(l3xci, l3xc_pool,
309   ({
310     if (!cb(l3xci, ctx))
311       break;
312   }));
313   /* *INDENT-ON* */
314 }
315
316 static clib_error_t *
317 l3xc_show_cmd (vlib_main_t * vm,
318                unformat_input_t * input, vlib_cli_command_t * cmd)
319 {
320   l3xc_t *l3xc;
321
322   /* *INDENT-OFF* */
323   pool_foreach(l3xc, l3xc_pool,
324   ({
325     vlib_cli_output(vm, "%U", format_l3xc, l3xc);
326   }));
327   /* *INDENT-ON* */
328
329   return (NULL);
330 }
331
332 /* *INDENT-OFF* */
333 VLIB_CLI_COMMAND (l3xc_show_cmd_node, static) = {
334   .path = "show l3xc",
335   .function = l3xc_show_cmd,
336   .short_help = "show l3xc",
337   .is_mp_safe = 1,
338 };
339 /* *INDENT-ON* */
340
341 static fib_node_t *
342 l3xc_get_node (fib_node_index_t index)
343 {
344   l3xc_t *l3xc = l3xc_get (index);
345   return (&(l3xc->l3xc_node));
346 }
347
348 static l3xc_t *
349 l3xc_get_from_node (fib_node_t * node)
350 {
351   return ((l3xc_t *) (((char *) node) -
352                       STRUCT_OFFSET_OF (l3xc_t, l3xc_node)));
353 }
354
355 static void
356 l3xc_last_lock_gone (fib_node_t * node)
357 {
358 }
359
360 /*
361  * A back walk has reached this L3XC policy
362  */
363 static fib_node_back_walk_rc_t
364 l3xc_back_walk_notify (fib_node_t * node, fib_node_back_walk_ctx_t * ctx)
365 {
366   l3xc_stack (l3xc_get_from_node (node));
367
368   return (FIB_NODE_BACK_WALK_CONTINUE);
369 }
370
371 /*
372  * The BIER fmask's graph node virtual function table
373  */
374 static const fib_node_vft_t l3xc_vft = {
375   .fnv_get = l3xc_get_node,
376   .fnv_last_lock = l3xc_last_lock_gone,
377   .fnv_back_walk = l3xc_back_walk_notify,
378 };
379
380 static clib_error_t *
381 l3xc_init (vlib_main_t * vm)
382 {
383   l3xc_fib_node_type = fib_node_register_new_type (&l3xc_vft);
384
385   return (NULL);
386 }
387
388 VLIB_INIT_FUNCTION (l3xc_init);
389
390 /*
391  * fd.io coding-style-patch-verification: ON
392  *
393  * Local Variables:
394  * eval: (c-set-style "gnu")
395  * End:
396  */