gbp: Ownership of dynamically created vxlan-gbp tunnels managed via gbp_itf
[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 #include <plugins/gbp/gbp_itf.h>
23
24 #include <vnet/dpo/dvr_dpo.h>
25 #include <vnet/fib/fib_table.h>
26 #include <vnet/l2/l2_input.h>
27
28 /**
29  * Pool of GBP endpoint_groups
30  */
31 gbp_endpoint_group_t *gbp_endpoint_group_pool;
32
33 /**
34  * DB of endpoint_groups
35  */
36 gbp_endpoint_group_db_t gbp_endpoint_group_db;
37
38 /**
39  * Map sclass to EPG
40  */
41 uword *gbp_epg_sclass_db;
42
43 vlib_log_class_t gg_logger;
44
45 #define GBP_EPG_DBG(...)                           \
46     vlib_log_debug (gg_logger, __VA_ARGS__);
47
48 gbp_endpoint_group_t *
49 gbp_endpoint_group_get (index_t i)
50 {
51   return (pool_elt_at_index (gbp_endpoint_group_pool, i));
52 }
53
54 void
55 gbp_endpoint_group_lock (index_t i)
56 {
57   gbp_endpoint_group_t *gg;
58
59   gg = gbp_endpoint_group_get (i);
60   gg->gg_locks++;
61 }
62
63 index_t
64 gbp_endpoint_group_find (sclass_t sclass)
65 {
66   uword *p;
67
68   p = hash_get (gbp_endpoint_group_db.gg_hash_sclass, sclass);
69
70   if (NULL != p)
71     return p[0];
72
73   return (INDEX_INVALID);
74 }
75
76 int
77 gbp_endpoint_group_add_and_lock (vnid_t vnid,
78                                  u16 sclass,
79                                  u32 bd_id,
80                                  u32 rd_id,
81                                  u32 uplink_sw_if_index,
82                                  const gbp_endpoint_retention_t * retention)
83 {
84   gbp_endpoint_group_t *gg;
85   index_t ggi;
86
87   ggi = gbp_endpoint_group_find (sclass);
88
89   if (INDEX_INVALID == ggi)
90     {
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       pool_get_zero (gbp_endpoint_group_pool, gg);
108
109       gg->gg_vnid = vnid;
110       gg->gg_rd = grdi;
111       gg->gg_gbd = gbi;
112
113       gg->gg_uplink_sw_if_index = uplink_sw_if_index;
114       gbp_itf_hdl_reset (&gg->gg_uplink_itf);
115       gg->gg_locks = 1;
116       gg->gg_sclass = sclass;
117       gg->gg_retention = *retention;
118
119       if (SCLASS_INVALID != gg->gg_sclass)
120         hash_set (gbp_epg_sclass_db, gg->gg_sclass, gg->gg_vnid);
121
122       /*
123        * an egress DVR dpo for internal subnets to use when sending
124        * on the uplink interface
125        */
126       if (~0 != gg->gg_uplink_sw_if_index)
127         {
128           FOR_EACH_FIB_IP_PROTOCOL (fproto)
129           {
130             dvr_dpo_add_or_lock (uplink_sw_if_index,
131                                  fib_proto_to_dpo (fproto),
132                                  &gg->gg_dpo[fproto]);
133           }
134
135           /*
136            * Add the uplink to the BD
137            * packets direct from the uplink have had policy applied
138            */
139           gg->gg_uplink_itf =
140             gbp_itf_l2_add_and_lock (gg->gg_uplink_sw_if_index, gbi);
141
142           gbp_itf_l2_set_input_feature (gg->gg_uplink_itf,
143                                         L2INPUT_FEAT_GBP_NULL_CLASSIFY);
144         }
145
146       hash_set (gbp_endpoint_group_db.gg_hash_sclass,
147                 gg->gg_sclass, gg - gbp_endpoint_group_pool);
148     }
149   else
150     {
151       gg = gbp_endpoint_group_get (ggi);
152       gg->gg_locks++;
153     }
154
155   GBP_EPG_DBG ("add: %U", format_gbp_endpoint_group, gg);
156
157   return (0);
158 }
159
160 void
161 gbp_endpoint_group_unlock (index_t ggi)
162 {
163   gbp_endpoint_group_t *gg;
164
165   if (INDEX_INVALID == ggi)
166     return;
167
168   gg = gbp_endpoint_group_get (ggi);
169
170   gg->gg_locks--;
171
172   if (0 == gg->gg_locks)
173     {
174       fib_protocol_t fproto;
175
176       gg = pool_elt_at_index (gbp_endpoint_group_pool, ggi);
177
178       gbp_itf_unlock (&gg->gg_uplink_itf);
179
180       FOR_EACH_FIB_IP_PROTOCOL (fproto)
181       {
182         dpo_reset (&gg->gg_dpo[fproto]);
183       }
184       gbp_bridge_domain_unlock (gg->gg_gbd);
185       gbp_route_domain_unlock (gg->gg_rd);
186
187       if (SCLASS_INVALID != gg->gg_sclass)
188         hash_unset (gbp_epg_sclass_db, gg->gg_sclass);
189       hash_unset (gbp_endpoint_group_db.gg_hash_sclass, gg->gg_sclass);
190
191       pool_put (gbp_endpoint_group_pool, gg);
192     }
193 }
194
195 int
196 gbp_endpoint_group_delete (sclass_t sclass)
197 {
198   index_t ggi;
199
200   ggi = gbp_endpoint_group_find (sclass);
201
202   if (INDEX_INVALID != ggi)
203     {
204       GBP_EPG_DBG ("del: %U", format_gbp_endpoint_group,
205                    gbp_endpoint_group_get (ggi));
206       gbp_endpoint_group_unlock (ggi);
207
208       return (0);
209     }
210
211   return (VNET_API_ERROR_NO_SUCH_ENTRY);
212 }
213
214 u32
215 gbp_endpoint_group_get_bd_id (const gbp_endpoint_group_t * gg)
216 {
217   const gbp_bridge_domain_t *gb;
218
219   gb = gbp_bridge_domain_get (gg->gg_gbd);
220
221   return (gb->gb_bd_id);
222 }
223
224 index_t
225 gbp_endpoint_group_get_fib_index (const gbp_endpoint_group_t * gg,
226                                   fib_protocol_t fproto)
227 {
228   const gbp_route_domain_t *grd;
229
230   grd = gbp_route_domain_get (gg->gg_rd);
231
232   return (grd->grd_fib_index[fproto]);
233 }
234
235 void
236 gbp_endpoint_group_walk (gbp_endpoint_group_cb_t cb, void *ctx)
237 {
238   gbp_endpoint_group_t *gbpe;
239
240   /* *INDENT-OFF* */
241   pool_foreach(gbpe, gbp_endpoint_group_pool,
242   {
243     if (!cb(gbpe, ctx))
244       break;
245   });
246   /* *INDENT-ON* */
247 }
248
249 static clib_error_t *
250 gbp_endpoint_group_cli (vlib_main_t * vm,
251                         unformat_input_t * input, vlib_cli_command_t * cmd)
252 {
253   gbp_endpoint_retention_t retention = { 0 };
254   vnid_t vnid = VNID_INVALID, sclass;
255   vnet_main_t *vnm = vnet_get_main ();
256   u32 uplink_sw_if_index = ~0;
257   u32 bd_id = ~0;
258   u32 rd_id = ~0;
259   u8 add = 1;
260
261   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
262     {
263       if (unformat (input, "%U", unformat_vnet_sw_interface,
264                     vnm, &uplink_sw_if_index))
265         ;
266       else if (unformat (input, "add"))
267         add = 1;
268       else if (unformat (input, "del"))
269         add = 0;
270       else if (unformat (input, "epg %d", &vnid))
271         ;
272       else if (unformat (input, "sclass %d", &sclass))
273         ;
274       else if (unformat (input, "bd %d", &bd_id))
275         ;
276       else if (unformat (input, "rd %d", &rd_id))
277         ;
278       else
279         break;
280     }
281
282   if (VNID_INVALID == vnid)
283     return clib_error_return (0, "EPG-ID must be specified");
284
285   if (add)
286     {
287       if (~0 == bd_id)
288         return clib_error_return (0, "Bridge-domain must be specified");
289       if (~0 == rd_id)
290         return clib_error_return (0, "route-domain must be specified");
291
292       gbp_endpoint_group_add_and_lock (vnid, sclass, bd_id, rd_id,
293                                        uplink_sw_if_index, &retention);
294     }
295   else
296     gbp_endpoint_group_delete (vnid);
297
298   return (NULL);
299 }
300
301 /*?
302  * Configure a GBP Endpoint Group
303  *
304  * @cliexpar
305  * @cliexstart{gbp endpoint-group [del] epg <ID> bd <ID> rd <ID> [sclass <ID>] [<interface>]}
306  * @cliexend
307  ?*/
308 /* *INDENT-OFF* */
309 VLIB_CLI_COMMAND (gbp_endpoint_group_cli_node, static) = {
310   .path = "gbp endpoint-group",
311   .short_help = "gbp endpoint-group [del] epg <ID> bd <ID> rd <ID> [sclass <ID>] [<interface>]",
312   .function = gbp_endpoint_group_cli,
313 };
314
315 static u8 *
316 format_gbp_endpoint_retention (u8 * s, va_list * args)
317 {
318   gbp_endpoint_retention_t *rt = va_arg (*args, gbp_endpoint_retention_t*);
319
320   s = format (s, "[remote-EP-timeout:%d]", rt->remote_ep_timeout);
321
322   return (s);
323 }
324
325 u8 *
326 format_gbp_endpoint_group (u8 * s, va_list * args)
327 {
328   gbp_endpoint_group_t *gg = va_arg (*args, gbp_endpoint_group_t*);
329
330   if (NULL != gg)
331     s = format (s, "[%d] %d, sclass:%d bd:%d rd:%d uplink:%U retention:%U locks:%d",
332                 gg - gbp_endpoint_group_pool,
333                 gg->gg_vnid,
334                 gg->gg_sclass,
335                 gg->gg_gbd,
336                 gg->gg_rd,
337                 format_gbp_itf_hdl, gg->gg_uplink_itf,
338                 format_gbp_endpoint_retention, &gg->gg_retention,
339                 gg->gg_locks);
340   else
341     s = format (s, "NULL");
342
343   return (s);
344 }
345
346 static int
347 gbp_endpoint_group_show_one (gbp_endpoint_group_t *gg, void *ctx)
348 {
349   vlib_main_t *vm;
350
351   vm = ctx;
352   vlib_cli_output (vm, "  %U",format_gbp_endpoint_group, gg);
353
354   return (1);
355 }
356
357 static clib_error_t *
358 gbp_endpoint_group_show (vlib_main_t * vm,
359                    unformat_input_t * input, vlib_cli_command_t * cmd)
360 {
361   vlib_cli_output (vm, "Endpoint-Groups:");
362   gbp_endpoint_group_walk (gbp_endpoint_group_show_one, vm);
363
364   return (NULL);
365 }
366
367
368 /*?
369  * Show Group Based Policy Endpoint_Groups and derived information
370  *
371  * @cliexpar
372  * @cliexstart{show gbp endpoint_group}
373  * @cliexend
374  ?*/
375 /* *INDENT-OFF* */
376 VLIB_CLI_COMMAND (gbp_endpoint_group_show_node, static) = {
377   .path = "show gbp endpoint-group",
378   .short_help = "show gbp endpoint-group\n",
379   .function = gbp_endpoint_group_show,
380 };
381 /* *INDENT-ON* */
382
383 static clib_error_t *
384 gbp_endpoint_group_init (vlib_main_t * vm)
385 {
386   gg_logger = vlib_log_register_class ("gbp", "epg");
387
388   return (NULL);
389 }
390
391 VLIB_INIT_FUNCTION (gbp_endpoint_group_init);
392
393 /*
394  * fd.io coding-style-patch-verification: ON
395  *
396  * Local Variables:
397  * eval: (c-set-style "gnu")
398  * End:
399  */