GBP: Sclass to src-epg conversions
[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, u32 rd_id, u32 uplink_sw_if_index)
79 {
80   gbp_endpoint_group_t *gg;
81   index_t ggi;
82
83   ggi = gbp_endpoint_group_find (epg_id);
84
85   if (INDEX_INVALID == ggi)
86     {
87       gbp_bridge_domain_t *gb;
88       fib_protocol_t fproto;
89       index_t gbi, grdi;
90
91       gbi = gbp_bridge_domain_find_and_lock (bd_id);
92
93       if (~0 == gbi)
94         return (VNET_API_ERROR_BD_NOT_MODIFIABLE);
95
96       grdi = gbp_route_domain_find_and_lock (rd_id);
97
98       if (~0 == grdi)
99         {
100           gbp_bridge_domain_unlock (gbi);
101           return (VNET_API_ERROR_NO_SUCH_FIB);
102         }
103
104       gb = gbp_bridge_domain_get (gbi);
105
106       pool_get_zero (gbp_endpoint_group_pool, gg);
107
108       gg->gg_id = epg_id;
109       gg->gg_rd = grdi;
110       gg->gg_gbd = gbi;
111       gg->gg_bd_index = gb->gb_bd_index;
112
113       gg->gg_uplink_sw_if_index = uplink_sw_if_index;
114       gg->gg_locks = 1;
115       gg->gg_sclass = sclass;
116
117       if (SCLASS_INVALID != gg->gg_sclass)
118         hash_set (gbp_epg_sclass_db, gg->gg_sclass, gg->gg_id);
119
120       /*
121        * an egress DVR dpo for internal subnets to use when sending
122        * on the uplink interface
123        */
124       if (~0 != gg->gg_uplink_sw_if_index)
125         {
126           FOR_EACH_FIB_IP_PROTOCOL (fproto)
127           {
128             dvr_dpo_add_or_lock (uplink_sw_if_index,
129                                  fib_proto_to_dpo (fproto),
130                                  &gg->gg_dpo[fproto]);
131           }
132
133           /*
134            * Add the uplink to the BD
135            * packets direct from the uplink have had policy applied
136            */
137           set_int_l2_mode (vlib_get_main (), vnet_get_main (),
138                            MODE_L2_BRIDGE, gg->gg_uplink_sw_if_index,
139                            gg->gg_bd_index, L2_BD_PORT_TYPE_NORMAL, 0, 0);
140           l2input_intf_bitmap_enable (gg->gg_uplink_sw_if_index,
141                                       L2INPUT_FEAT_GBP_NULL_CLASSIFY, 1);
142         }
143
144       hash_set (gbp_endpoint_group_db.gg_hash,
145                 gg->gg_id, gg - gbp_endpoint_group_pool);
146
147     }
148   else
149     {
150       gg = gbp_endpoint_group_get (ggi);
151       gg->gg_locks++;
152     }
153
154   GBP_EPG_DBG ("add: %U", format_gbp_endpoint_group, gg);
155
156   return (0);
157 }
158
159 void
160 gbp_endpoint_group_unlock (index_t ggi)
161 {
162   gbp_endpoint_group_t *gg;
163
164   if (INDEX_INVALID == ggi)
165     return;
166
167   gg = gbp_endpoint_group_get (ggi);
168
169   gg->gg_locks--;
170
171   if (0 == gg->gg_locks)
172     {
173       fib_protocol_t fproto;
174
175       gg = pool_elt_at_index (gbp_endpoint_group_pool, ggi);
176
177       if (~0 != gg->gg_uplink_sw_if_index)
178         {
179           set_int_l2_mode (vlib_get_main (), vnet_get_main (),
180                            MODE_L3, gg->gg_uplink_sw_if_index,
181                            gg->gg_bd_index, L2_BD_PORT_TYPE_NORMAL, 0, 0);
182
183           l2input_intf_bitmap_enable (gg->gg_uplink_sw_if_index,
184                                       L2INPUT_FEAT_GBP_NULL_CLASSIFY, 0);
185         }
186       FOR_EACH_FIB_IP_PROTOCOL (fproto)
187       {
188         dpo_reset (&gg->gg_dpo[fproto]);
189       }
190       gbp_bridge_domain_unlock (gg->gg_gbd);
191       gbp_route_domain_unlock (gg->gg_rd);
192
193       if (SCLASS_INVALID != gg->gg_sclass)
194         hash_unset (gbp_epg_sclass_db, gg->gg_sclass);
195       hash_unset (gbp_endpoint_group_db.gg_hash, gg->gg_id);
196
197       pool_put (gbp_endpoint_group_pool, gg);
198     }
199 }
200
201 int
202 gbp_endpoint_group_delete (epg_id_t epg_id)
203 {
204   index_t ggi;
205
206   ggi = gbp_endpoint_group_find (epg_id);
207
208   if (INDEX_INVALID != ggi)
209     {
210       GBP_EPG_DBG ("del: %U", format_gbp_endpoint_group,
211                    gbp_endpoint_group_get (ggi));
212       gbp_endpoint_group_unlock (ggi);
213
214       return (0);
215     }
216
217   return (VNET_API_ERROR_NO_SUCH_ENTRY);
218 }
219
220 u32
221 gbp_endpoint_group_get_bd_id (const gbp_endpoint_group_t * gg)
222 {
223   const gbp_bridge_domain_t *gb;
224
225   gb = gbp_bridge_domain_get (gg->gg_gbd);
226
227   return (gb->gb_bd_id);
228 }
229
230 index_t
231 gbp_endpoint_group_get_fib_index (const gbp_endpoint_group_t * gg,
232                                   fib_protocol_t fproto)
233 {
234   const gbp_route_domain_t *grd;
235
236   grd = gbp_route_domain_get (gg->gg_rd);
237
238   return (grd->grd_fib_index[fproto]);
239 }
240
241 void
242 gbp_endpoint_group_walk (gbp_endpoint_group_cb_t cb, void *ctx)
243 {
244   gbp_endpoint_group_t *gbpe;
245
246   /* *INDENT-OFF* */
247   pool_foreach(gbpe, gbp_endpoint_group_pool,
248   {
249     if (!cb(gbpe, ctx))
250       break;
251   });
252   /* *INDENT-ON* */
253 }
254
255 static clib_error_t *
256 gbp_endpoint_group_cli (vlib_main_t * vm,
257                         unformat_input_t * input, vlib_cli_command_t * cmd)
258 {
259   epg_id_t epg_id = EPG_INVALID, sclass;
260   vnet_main_t *vnm = vnet_get_main ();
261   u32 uplink_sw_if_index = ~0;
262   u32 bd_id = ~0;
263   u32 rd_id = ~0;
264   u8 add = 1;
265
266   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
267     {
268       if (unformat (input, "%U", unformat_vnet_sw_interface,
269                     vnm, &uplink_sw_if_index))
270         ;
271       else if (unformat (input, "add"))
272         add = 1;
273       else if (unformat (input, "del"))
274         add = 0;
275       else if (unformat (input, "epg %d", &epg_id))
276         ;
277       else if (unformat (input, "sclass %d", &sclass))
278         ;
279       else if (unformat (input, "bd %d", &bd_id))
280         ;
281       else if (unformat (input, "rd %d", &rd_id))
282         ;
283       else
284         break;
285     }
286
287   if (EPG_INVALID == epg_id)
288     return clib_error_return (0, "EPG-ID must be specified");
289
290   if (add)
291     {
292       if (~0 == uplink_sw_if_index)
293         return clib_error_return (0, "interface must be specified");
294       if (~0 == bd_id)
295         return clib_error_return (0, "Bridge-domain must be specified");
296       if (~0 == rd_id)
297         return clib_error_return (0, "route-domain must be specified");
298
299       gbp_endpoint_group_add_and_lock (epg_id, sclass, bd_id, rd_id,
300                                        uplink_sw_if_index);
301     }
302   else
303     gbp_endpoint_group_delete (epg_id);
304
305   return (NULL);
306 }
307
308 /*?
309  * Configure a GBP Endpoint Group
310  *
311  * @cliexpar
312  * @cliexstart{set gbp endpoint-group [del] epg <ID> bd <ID> <interface>}
313  * @cliexend
314  ?*/
315 /* *INDENT-OFF* */
316 VLIB_CLI_COMMAND (gbp_endpoint_group_cli_node, static) = {
317   .path = "gbp endpoint-group",
318   .short_help = "gbp endpoint-group [del] epg <ID> bd <ID> rd <ID> <interface>",
319   .function = gbp_endpoint_group_cli,
320 };
321
322 u8 *
323 format_gbp_endpoint_group (u8 * s, va_list * args)
324 {
325   gbp_endpoint_group_t *gg = va_arg (*args, gbp_endpoint_group_t*);
326   vnet_main_t *vnm = vnet_get_main ();
327
328   if (NULL != gg)
329     s = format (s, "%d, bd:[%d,%d], rd:[%d] uplink:%U locks:%d",
330                 gg->gg_id,
331                 gbp_endpoint_group_get_bd_id(gg), gg->gg_bd_index,
332                 gg->gg_rd,
333                 format_vnet_sw_if_index_name, vnm, gg->gg_uplink_sw_if_index,
334                 gg->gg_locks);
335   else
336     s = format (s, "NULL");
337
338   return (s);
339 }
340
341 static int
342 gbp_endpoint_group_show_one (gbp_endpoint_group_t *gg, void *ctx)
343 {
344   vlib_main_t *vm;
345
346   vm = ctx;
347   vlib_cli_output (vm, "  %U",format_gbp_endpoint_group, gg);
348
349   return (1);
350 }
351
352 static clib_error_t *
353 gbp_endpoint_group_show (vlib_main_t * vm,
354                    unformat_input_t * input, vlib_cli_command_t * cmd)
355 {
356   vlib_cli_output (vm, "Endpoint-Groups:");
357   gbp_endpoint_group_walk (gbp_endpoint_group_show_one, vm);
358
359   return (NULL);
360 }
361
362
363 /*?
364  * Show Group Based Policy Endpoint_Groups and derived information
365  *
366  * @cliexpar
367  * @cliexstart{show gbp endpoint_group}
368  * @cliexend
369  ?*/
370 /* *INDENT-OFF* */
371 VLIB_CLI_COMMAND (gbp_endpoint_group_show_node, static) = {
372   .path = "show gbp endpoint-group",
373   .short_help = "show gbp endpoint-group\n",
374   .function = gbp_endpoint_group_show,
375 };
376 /* *INDENT-ON* */
377
378 static clib_error_t *
379 gbp_endpoint_group_init (vlib_main_t * vm)
380 {
381   gg_logger = vlib_log_register_class ("gbp", "epg");
382
383   return (NULL);
384 }
385
386 VLIB_INIT_FUNCTION (gbp_endpoint_group_init);
387
388 /*
389  * fd.io coding-style-patch-verification: ON
390  *
391  * Local Variables:
392  * eval: (c-set-style "gnu")
393  * End:
394  */