GBP: format EPG retention policy
[vpp.git] / src / plugins / gbp / gbp_endpoint_group.c
1 /*
2  * gbp.h : Group Based Policy
3  *
4  * Copyright (c) 2018 Cisco and/or its affiliates.
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #include <plugins/gbp/gbp_endpoint_group.h>
19 #include <plugins/gbp/gbp_endpoint.h>
20 #include <plugins/gbp/gbp_bridge_domain.h>
21 #include <plugins/gbp/gbp_route_domain.h>
22
23 #include <vnet/dpo/dvr_dpo.h>
24 #include <vnet/fib/fib_table.h>
25 #include <vnet/l2/l2_input.h>
26
27 /**
28  * Pool of GBP endpoint_groups
29  */
30 gbp_endpoint_group_t *gbp_endpoint_group_pool;
31
32 /**
33  * DB of endpoint_groups
34  */
35 gbp_endpoint_group_db_t gbp_endpoint_group_db;
36
37 /**
38  * Map sclass to EPG
39  */
40 uword *gbp_epg_sclass_db;
41
42 vlib_log_class_t gg_logger;
43
44 #define GBP_EPG_DBG(...)                           \
45     vlib_log_debug (gg_logger, __VA_ARGS__);
46
47 gbp_endpoint_group_t *
48 gbp_endpoint_group_get (index_t i)
49 {
50   return (pool_elt_at_index (gbp_endpoint_group_pool, i));
51 }
52
53 void
54 gbp_endpoint_group_lock (index_t i)
55 {
56   gbp_endpoint_group_t *gg;
57
58   gg = gbp_endpoint_group_get (i);
59   gg->gg_locks++;
60 }
61
62 index_t
63 gbp_endpoint_group_find (epg_id_t epg_id)
64 {
65   uword *p;
66
67   p = hash_get (gbp_endpoint_group_db.gg_hash, epg_id);
68
69   if (NULL != p)
70     return p[0];
71
72   return (INDEX_INVALID);
73 }
74
75 int
76 gbp_endpoint_group_add_and_lock (epg_id_t epg_id,
77                                  u16 sclass,
78                                  u32 bd_id,
79                                  u32 rd_id,
80                                  u32 uplink_sw_if_index,
81                                  const gbp_endpoint_retention_t * retention)
82 {
83   gbp_endpoint_group_t *gg;
84   index_t ggi;
85
86   ggi = gbp_endpoint_group_find (epg_id);
87
88   if (INDEX_INVALID == ggi)
89     {
90       gbp_bridge_domain_t *gb;
91       fib_protocol_t fproto;
92       index_t gbi, grdi;
93
94       gbi = gbp_bridge_domain_find_and_lock (bd_id);
95
96       if (~0 == gbi)
97         return (VNET_API_ERROR_BD_NOT_MODIFIABLE);
98
99       grdi = gbp_route_domain_find_and_lock (rd_id);
100
101       if (~0 == grdi)
102         {
103           gbp_bridge_domain_unlock (gbi);
104           return (VNET_API_ERROR_NO_SUCH_FIB);
105         }
106
107       gb = gbp_bridge_domain_get (gbi);
108
109       pool_get_zero (gbp_endpoint_group_pool, gg);
110
111       gg->gg_id = epg_id;
112       gg->gg_rd = grdi;
113       gg->gg_gbd = gbi;
114       gg->gg_bd_index = gb->gb_bd_index;
115
116       gg->gg_uplink_sw_if_index = uplink_sw_if_index;
117       gg->gg_locks = 1;
118       gg->gg_sclass = sclass;
119       gg->gg_retention = *retention;
120
121       if (SCLASS_INVALID != gg->gg_sclass)
122         hash_set (gbp_epg_sclass_db, gg->gg_sclass, gg->gg_id);
123
124       /*
125        * an egress DVR dpo for internal subnets to use when sending
126        * on the uplink interface
127        */
128       if (~0 != gg->gg_uplink_sw_if_index)
129         {
130           FOR_EACH_FIB_IP_PROTOCOL (fproto)
131           {
132             dvr_dpo_add_or_lock (uplink_sw_if_index,
133                                  fib_proto_to_dpo (fproto),
134                                  &gg->gg_dpo[fproto]);
135           }
136
137           /*
138            * Add the uplink to the BD
139            * packets direct from the uplink have had policy applied
140            */
141           set_int_l2_mode (vlib_get_main (), vnet_get_main (),
142                            MODE_L2_BRIDGE, gg->gg_uplink_sw_if_index,
143                            gg->gg_bd_index, L2_BD_PORT_TYPE_NORMAL, 0, 0);
144           l2input_intf_bitmap_enable (gg->gg_uplink_sw_if_index,
145                                       L2INPUT_FEAT_GBP_NULL_CLASSIFY, 1);
146         }
147
148       hash_set (gbp_endpoint_group_db.gg_hash,
149                 gg->gg_id, gg - gbp_endpoint_group_pool);
150
151     }
152   else
153     {
154       gg = gbp_endpoint_group_get (ggi);
155       gg->gg_locks++;
156     }
157
158   GBP_EPG_DBG ("add: %U", format_gbp_endpoint_group, gg);
159
160   return (0);
161 }
162
163 void
164 gbp_endpoint_group_unlock (index_t ggi)
165 {
166   gbp_endpoint_group_t *gg;
167
168   if (INDEX_INVALID == ggi)
169     return;
170
171   gg = gbp_endpoint_group_get (ggi);
172
173   gg->gg_locks--;
174
175   if (0 == gg->gg_locks)
176     {
177       fib_protocol_t fproto;
178
179       gg = pool_elt_at_index (gbp_endpoint_group_pool, ggi);
180
181       if (~0 != gg->gg_uplink_sw_if_index)
182         {
183           set_int_l2_mode (vlib_get_main (), vnet_get_main (),
184                            MODE_L3, gg->gg_uplink_sw_if_index,
185                            gg->gg_bd_index, L2_BD_PORT_TYPE_NORMAL, 0, 0);
186
187           l2input_intf_bitmap_enable (gg->gg_uplink_sw_if_index,
188                                       L2INPUT_FEAT_GBP_NULL_CLASSIFY, 0);
189         }
190       FOR_EACH_FIB_IP_PROTOCOL (fproto)
191       {
192         dpo_reset (&gg->gg_dpo[fproto]);
193       }
194       gbp_bridge_domain_unlock (gg->gg_gbd);
195       gbp_route_domain_unlock (gg->gg_rd);
196
197       if (SCLASS_INVALID != gg->gg_sclass)
198         hash_unset (gbp_epg_sclass_db, gg->gg_sclass);
199       hash_unset (gbp_endpoint_group_db.gg_hash, gg->gg_id);
200
201       pool_put (gbp_endpoint_group_pool, gg);
202     }
203 }
204
205 int
206 gbp_endpoint_group_delete (epg_id_t epg_id)
207 {
208   index_t ggi;
209
210   ggi = gbp_endpoint_group_find (epg_id);
211
212   if (INDEX_INVALID != ggi)
213     {
214       GBP_EPG_DBG ("del: %U", format_gbp_endpoint_group,
215                    gbp_endpoint_group_get (ggi));
216       gbp_endpoint_group_unlock (ggi);
217
218       return (0);
219     }
220
221   return (VNET_API_ERROR_NO_SUCH_ENTRY);
222 }
223
224 u32
225 gbp_endpoint_group_get_bd_id (const gbp_endpoint_group_t * gg)
226 {
227   const gbp_bridge_domain_t *gb;
228
229   gb = gbp_bridge_domain_get (gg->gg_gbd);
230
231   return (gb->gb_bd_id);
232 }
233
234 index_t
235 gbp_endpoint_group_get_fib_index (const gbp_endpoint_group_t * gg,
236                                   fib_protocol_t fproto)
237 {
238   const gbp_route_domain_t *grd;
239
240   grd = gbp_route_domain_get (gg->gg_rd);
241
242   return (grd->grd_fib_index[fproto]);
243 }
244
245 void
246 gbp_endpoint_group_walk (gbp_endpoint_group_cb_t cb, void *ctx)
247 {
248   gbp_endpoint_group_t *gbpe;
249
250   /* *INDENT-OFF* */
251   pool_foreach(gbpe, gbp_endpoint_group_pool,
252   {
253     if (!cb(gbpe, ctx))
254       break;
255   });
256   /* *INDENT-ON* */
257 }
258
259 static clib_error_t *
260 gbp_endpoint_group_cli (vlib_main_t * vm,
261                         unformat_input_t * input, vlib_cli_command_t * cmd)
262 {
263   gbp_endpoint_retention_t retention = { 0 };
264   epg_id_t epg_id = EPG_INVALID, sclass;
265   vnet_main_t *vnm = vnet_get_main ();
266   u32 uplink_sw_if_index = ~0;
267   u32 bd_id = ~0;
268   u32 rd_id = ~0;
269   u8 add = 1;
270
271   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
272     {
273       if (unformat (input, "%U", unformat_vnet_sw_interface,
274                     vnm, &uplink_sw_if_index))
275         ;
276       else if (unformat (input, "add"))
277         add = 1;
278       else if (unformat (input, "del"))
279         add = 0;
280       else if (unformat (input, "epg %d", &epg_id))
281         ;
282       else if (unformat (input, "sclass %d", &sclass))
283         ;
284       else if (unformat (input, "bd %d", &bd_id))
285         ;
286       else if (unformat (input, "rd %d", &rd_id))
287         ;
288       else
289         break;
290     }
291
292   if (EPG_INVALID == epg_id)
293     return clib_error_return (0, "EPG-ID must be specified");
294
295   if (add)
296     {
297       if (~0 == uplink_sw_if_index)
298         return clib_error_return (0, "interface must be specified");
299       if (~0 == bd_id)
300         return clib_error_return (0, "Bridge-domain must be specified");
301       if (~0 == rd_id)
302         return clib_error_return (0, "route-domain must be specified");
303
304       gbp_endpoint_group_add_and_lock (epg_id, sclass, bd_id, rd_id,
305                                        uplink_sw_if_index, &retention);
306     }
307   else
308     gbp_endpoint_group_delete (epg_id);
309
310   return (NULL);
311 }
312
313 /*?
314  * Configure a GBP Endpoint Group
315  *
316  * @cliexpar
317  * @cliexstart{set gbp endpoint-group [del] epg <ID> bd <ID> <interface>}
318  * @cliexend
319  ?*/
320 /* *INDENT-OFF* */
321 VLIB_CLI_COMMAND (gbp_endpoint_group_cli_node, static) = {
322   .path = "gbp endpoint-group",
323   .short_help = "gbp endpoint-group [del] epg <ID> bd <ID> rd <ID> <interface>",
324   .function = gbp_endpoint_group_cli,
325 };
326
327 static u8 *
328 format_gbp_endpoint_retention (u8 * s, va_list * args)
329 {
330   gbp_endpoint_retention_t *rt = va_arg (*args, gbp_endpoint_retention_t*);
331
332   s = format (s, "[remote-EP-timeout:%d]", rt->remote_ep_timeout);
333
334   return (s);
335 }
336
337 u8 *
338 format_gbp_endpoint_group (u8 * s, va_list * args)
339 {
340   gbp_endpoint_group_t *gg = va_arg (*args, gbp_endpoint_group_t*);
341   vnet_main_t *vnm = vnet_get_main ();
342
343   if (NULL != gg)
344     s = format (s, "[%d] %d, sclass:%d bd:[%d,%d] rd:[%d] uplink:%U retnetion:%U locks:%d",
345                 gg - gbp_endpoint_group_pool,
346                 gg->gg_id,
347                 gg->gg_sclass,
348                 gbp_endpoint_group_get_bd_id(gg), gg->gg_bd_index,
349                 gg->gg_rd,
350                 format_vnet_sw_if_index_name, vnm, gg->gg_uplink_sw_if_index,
351                 format_gbp_endpoint_retention, &gg->gg_retention,
352                 gg->gg_locks);
353   else
354     s = format (s, "NULL");
355
356   return (s);
357 }
358
359 static int
360 gbp_endpoint_group_show_one (gbp_endpoint_group_t *gg, void *ctx)
361 {
362   vlib_main_t *vm;
363
364   vm = ctx;
365   vlib_cli_output (vm, "  %U",format_gbp_endpoint_group, gg);
366
367   return (1);
368 }
369
370 static clib_error_t *
371 gbp_endpoint_group_show (vlib_main_t * vm,
372                    unformat_input_t * input, vlib_cli_command_t * cmd)
373 {
374   vlib_cli_output (vm, "Endpoint-Groups:");
375   gbp_endpoint_group_walk (gbp_endpoint_group_show_one, vm);
376
377   return (NULL);
378 }
379
380
381 /*?
382  * Show Group Based Policy Endpoint_Groups and derived information
383  *
384  * @cliexpar
385  * @cliexstart{show gbp endpoint_group}
386  * @cliexend
387  ?*/
388 /* *INDENT-OFF* */
389 VLIB_CLI_COMMAND (gbp_endpoint_group_show_node, static) = {
390   .path = "show gbp endpoint-group",
391   .short_help = "show gbp endpoint-group\n",
392   .function = gbp_endpoint_group_show,
393 };
394 /* *INDENT-ON* */
395
396 static clib_error_t *
397 gbp_endpoint_group_init (vlib_main_t * vm)
398 {
399   gg_logger = vlib_log_register_class ("gbp", "epg");
400
401   return (NULL);
402 }
403
404 VLIB_INIT_FUNCTION (gbp_endpoint_group_init);
405
406 /*
407  * fd.io coding-style-patch-verification: ON
408  *
409  * Local Variables:
410  * eval: (c-set-style "gnu")
411  * End:
412  */