GBP: use sclass in the DP for policy
[vpp.git] / src / plugins / gbp / gbp_bridge_domain.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/gbp/gbp_bridge_domain.h>
17 #include <plugins/gbp/gbp_endpoint.h>
18 #include <plugins/gbp/gbp_learn.h>
19
20 #include <vnet/dpo/dvr_dpo.h>
21 #include <vnet/fib/fib_table.h>
22 #include <vnet/l2/l2_input.h>
23 #include <vnet/l2/feat_bitmap.h>
24 #include <vnet/l2/l2_bvi.h>
25 #include <vnet/l2/l2_fib.h>
26
27 /**
28  * Pool of GBP bridge_domains
29  */
30 gbp_bridge_domain_t *gbp_bridge_domain_pool;
31
32 /**
33  * DB of bridge_domains
34  */
35 gbp_bridge_domain_db_t gbp_bridge_domain_db;
36
37 /**
38  * logger
39  */
40 vlib_log_class_t gb_logger;
41
42 #define GBP_BD_DBG(...)                           \
43     vlib_log_debug (gb_logger, __VA_ARGS__);
44
45 index_t
46 gbp_bridge_domain_index (const gbp_bridge_domain_t * gbd)
47 {
48   return (gbd - gbp_bridge_domain_pool);
49 }
50
51 static void
52 gbp_bridge_domain_lock (index_t i)
53 {
54   gbp_bridge_domain_t *gb;
55
56   gb = gbp_bridge_domain_get (i);
57   gb->gb_locks++;
58 }
59
60 u32
61 gbp_bridge_domain_get_bd_id (index_t gbdi)
62 {
63   gbp_bridge_domain_t *gb;
64
65   gb = gbp_bridge_domain_get (gbdi);
66
67   return (gb->gb_bd_id);
68 }
69
70 static index_t
71 gbp_bridge_domain_find (u32 bd_id)
72 {
73   uword *p;
74
75   p = hash_get (gbp_bridge_domain_db.gbd_by_bd_id, bd_id);
76
77   if (NULL != p)
78     return p[0];
79
80   return (INDEX_INVALID);
81 }
82
83 index_t
84 gbp_bridge_domain_find_and_lock (u32 bd_id)
85 {
86   uword *p;
87
88   p = hash_get (gbp_bridge_domain_db.gbd_by_bd_id, bd_id);
89
90   if (NULL != p)
91     {
92       gbp_bridge_domain_lock (p[0]);
93       return p[0];
94     }
95   return (INDEX_INVALID);
96 }
97
98 static void
99 gbp_bridge_domain_db_add (gbp_bridge_domain_t * gb)
100 {
101   index_t gbi = gb - gbp_bridge_domain_pool;
102
103   hash_set (gbp_bridge_domain_db.gbd_by_bd_id, gb->gb_bd_id, gbi);
104   vec_validate_init_empty (gbp_bridge_domain_db.gbd_by_bd_index,
105                            gb->gb_bd_index, INDEX_INVALID);
106   gbp_bridge_domain_db.gbd_by_bd_index[gb->gb_bd_index] = gbi;
107 }
108
109 static void
110 gbp_bridge_domain_db_remove (gbp_bridge_domain_t * gb)
111 {
112   hash_unset (gbp_bridge_domain_db.gbd_by_bd_id, gb->gb_bd_id);
113   gbp_bridge_domain_db.gbd_by_bd_index[gb->gb_bd_index] = INDEX_INVALID;
114 }
115
116 u8 *
117 format_gbp_bridge_domain_flags (u8 * s, va_list * args)
118 {
119   gbp_bridge_domain_flags_t gf = va_arg (*args, gbp_bridge_domain_flags_t);
120
121   if (gf)
122     {
123       if (gf & GBP_BD_FLAG_DO_NOT_LEARN)
124         s = format (s, "do-not-learn");
125     }
126   else
127     {
128       s = format (s, "none");
129     }
130   return (s);
131 }
132
133 static u8 *
134 format_gbp_bridge_domain_ptr (u8 * s, va_list * args)
135 {
136   gbp_bridge_domain_t *gb = va_arg (*args, gbp_bridge_domain_t *);
137   vnet_main_t *vnm = vnet_get_main ();
138
139   if (NULL != gb)
140     s = format (s, "[%d] bd:[%d,%d], bvi:%U uu-flood:%U flags:%U locks:%d",
141                 gb - gbp_bridge_domain_pool,
142                 gb->gb_bd_id,
143                 gb->gb_bd_index,
144                 format_vnet_sw_if_index_name, vnm, gb->gb_bvi_sw_if_index,
145                 format_vnet_sw_if_index_name, vnm, gb->gb_uu_fwd_sw_if_index,
146                 format_gbp_bridge_domain_flags, gb->gb_flags, gb->gb_locks);
147   else
148     s = format (s, "NULL");
149
150   return (s);
151 }
152
153 u8 *
154 format_gbp_bridge_domain (u8 * s, va_list * args)
155 {
156   index_t gbi = va_arg (*args, index_t);
157
158   s =
159     format (s, "%U", format_gbp_bridge_domain_ptr,
160             gbp_bridge_domain_get (gbi));
161
162   return (s);
163 }
164
165 int
166 gbp_bridge_domain_add_and_lock (u32 bd_id,
167                                 gbp_bridge_domain_flags_t flags,
168                                 u32 bvi_sw_if_index,
169                                 u32 uu_fwd_sw_if_index,
170                                 u32 bm_flood_sw_if_index)
171 {
172   gbp_bridge_domain_t *gb;
173   index_t gbi;
174
175   gbi = gbp_bridge_domain_find (bd_id);
176
177   if (INDEX_INVALID == gbi)
178     {
179       u32 bd_index;
180
181       bd_index = bd_find_index (&bd_main, bd_id);
182
183       if (~0 == bd_index)
184         return (VNET_API_ERROR_BD_NOT_MODIFIABLE);
185
186       /*
187        * unset learning in the bridge
188        */
189       bd_set_flags (vlib_get_main (), bd_index, L2_LEARN, 0);
190
191       pool_get (gbp_bridge_domain_pool, gb);
192       memset (gb, 0, sizeof (*gb));
193
194       gb->gb_bd_id = bd_id;
195       gb->gb_bd_index = bd_index;
196       gb->gb_uu_fwd_sw_if_index = uu_fwd_sw_if_index;
197       gb->gb_bvi_sw_if_index = bvi_sw_if_index;
198       gb->gb_bm_flood_sw_if_index = bm_flood_sw_if_index;
199       gb->gb_locks = 1;
200       gb->gb_flags = flags;
201
202       /*
203        * Set the BVI and uu-flood interfaces into the BD
204        */
205       set_int_l2_mode (vlib_get_main (), vnet_get_main (),
206                        MODE_L2_BRIDGE, gb->gb_bvi_sw_if_index,
207                        bd_index, L2_BD_PORT_TYPE_BVI, 0, 0);
208       if (~0 != gb->gb_uu_fwd_sw_if_index)
209         {
210           set_int_l2_mode (vlib_get_main (), vnet_get_main (),
211                            MODE_L2_BRIDGE, gb->gb_uu_fwd_sw_if_index,
212                            bd_index, L2_BD_PORT_TYPE_UU_FWD, 0, 0);
213         }
214       if (~0 != gb->gb_bm_flood_sw_if_index)
215         {
216           set_int_l2_mode (vlib_get_main (), vnet_get_main (),
217                            MODE_L2_BRIDGE, gb->gb_bm_flood_sw_if_index,
218                            bd_index, L2_BD_PORT_TYPE_NORMAL, 0, 0);
219           gbp_learn_enable (gb->gb_bm_flood_sw_if_index, GBP_LEARN_MODE_L2);
220         }
221
222       /*
223        * Add the BVI's MAC to the L2FIB
224        */
225       l2fib_add_entry (vnet_sw_interface_get_hw_address
226                        (vnet_get_main (), gb->gb_bvi_sw_if_index),
227                        gb->gb_bd_index, gb->gb_bvi_sw_if_index,
228                        (L2FIB_ENTRY_RESULT_FLAG_STATIC |
229                         L2FIB_ENTRY_RESULT_FLAG_BVI));
230
231       gbp_bridge_domain_db_add (gb);
232     }
233   else
234     {
235       gb = gbp_bridge_domain_get (gbi);
236       gb->gb_locks++;
237     }
238
239   GBP_BD_DBG ("add: %U", format_gbp_bridge_domain_ptr, gb);
240
241   return (0);
242 }
243
244 void
245 gbp_bridge_domain_unlock (index_t index)
246 {
247   gbp_bridge_domain_t *gb;
248
249   gb = gbp_bridge_domain_get (index);
250
251   gb->gb_locks--;
252
253   if (0 == gb->gb_locks)
254     {
255       GBP_BD_DBG ("destroy: %U", format_gbp_bridge_domain_ptr, gb);
256
257       l2fib_del_entry (vnet_sw_interface_get_hw_address
258                        (vnet_get_main (), gb->gb_bvi_sw_if_index),
259                        gb->gb_bd_index, gb->gb_bvi_sw_if_index);
260
261       set_int_l2_mode (vlib_get_main (), vnet_get_main (),
262                        MODE_L3, gb->gb_bvi_sw_if_index,
263                        gb->gb_bd_index, L2_BD_PORT_TYPE_BVI, 0, 0);
264       if (~0 != gb->gb_uu_fwd_sw_if_index)
265         {
266           set_int_l2_mode (vlib_get_main (), vnet_get_main (),
267                            MODE_L3, gb->gb_uu_fwd_sw_if_index,
268                            gb->gb_bd_index, L2_BD_PORT_TYPE_UU_FWD, 0, 0);
269         }
270       if (~0 != gb->gb_bm_flood_sw_if_index)
271         {
272           set_int_l2_mode (vlib_get_main (), vnet_get_main (),
273                            MODE_L3, gb->gb_bm_flood_sw_if_index,
274                            gb->gb_bd_index, L2_BD_PORT_TYPE_NORMAL, 0, 0);
275           gbp_learn_enable (gb->gb_bm_flood_sw_if_index, GBP_LEARN_MODE_L2);
276         }
277
278       gbp_bridge_domain_db_remove (gb);
279
280       pool_put (gbp_bridge_domain_pool, gb);
281     }
282 }
283
284 int
285 gbp_bridge_domain_delete (u32 bd_id)
286 {
287   index_t gbi;
288
289   GBP_BD_DBG ("del: %d", bd_id);
290   gbi = gbp_bridge_domain_find (bd_id);
291
292   if (INDEX_INVALID != gbi)
293     {
294       GBP_BD_DBG ("del: %U", format_gbp_bridge_domain, gbi);
295       gbp_bridge_domain_unlock (gbi);
296
297       return (0);
298     }
299
300   return (VNET_API_ERROR_NO_SUCH_ENTRY);
301 }
302
303 void
304 gbp_bridge_domain_walk (gbp_bridge_domain_cb_t cb, void *ctx)
305 {
306   gbp_bridge_domain_t *gbpe;
307
308   /* *INDENT-OFF* */
309   pool_foreach(gbpe, gbp_bridge_domain_pool,
310   {
311     if (!cb(gbpe, ctx))
312       break;
313   });
314   /* *INDENT-ON* */
315 }
316
317 static clib_error_t *
318 gbp_bridge_domain_cli (vlib_main_t * vm,
319                        unformat_input_t * input, vlib_cli_command_t * cmd)
320 {
321   vnet_main_t *vnm = vnet_get_main ();
322   u32 bm_flood_sw_if_index = ~0;
323   u32 uu_fwd_sw_if_index = ~0;
324   u32 bvi_sw_if_index = ~0;
325   u32 bd_id = ~0;
326   u8 add = 1;
327
328   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
329     {
330       if (unformat (input, "bvi %U", unformat_vnet_sw_interface,
331                     vnm, &bvi_sw_if_index))
332         ;
333       else if (unformat (input, "uu-fwd %U", unformat_vnet_sw_interface,
334                          vnm, &uu_fwd_sw_if_index))
335         ;
336       else if (unformat (input, "bm-flood %U", unformat_vnet_sw_interface,
337                          vnm, &bm_flood_sw_if_index))
338         ;
339       else if (unformat (input, "add"))
340         add = 1;
341       else if (unformat (input, "del"))
342         add = 0;
343       else if (unformat (input, "bd %d", &bd_id))
344         ;
345       else
346         break;
347     }
348
349   if (~0 == bd_id)
350     return clib_error_return (0, "EPG-ID must be specified");
351
352   if (add)
353     {
354       if (~0 == bvi_sw_if_index)
355         return clib_error_return (0, "interface must be specified");
356
357       gbp_bridge_domain_add_and_lock (bd_id, GBP_BD_FLAG_NONE,
358                                       bvi_sw_if_index,
359                                       uu_fwd_sw_if_index,
360                                       bm_flood_sw_if_index);
361     }
362   else
363     gbp_bridge_domain_delete (bd_id);
364
365   return (NULL);
366 }
367
368 /*?
369  * Configure a GBP bridge-domain
370  *
371  * @cliexpar
372  * @cliexstart{set gbp bridge-domain [del] bd <ID> bvi <interface> uu-flood <interface>}
373  * @cliexend
374  ?*/
375 /* *INDENT-OFF* */
376 VLIB_CLI_COMMAND (gbp_bridge_domain_cli_node, static) = {
377   .path = "gbp bridge-domain",
378   .short_help = "gbp bridge-domain [del] epg bd <ID> bvi <interface> uu-flood <interface>",
379   .function = gbp_bridge_domain_cli,
380 };
381
382 static int
383 gbp_bridge_domain_show_one (gbp_bridge_domain_t *gb, void *ctx)
384 {
385   vlib_main_t *vm;
386
387   vm = ctx;
388   vlib_cli_output (vm, "  %U", format_gbp_bridge_domain_ptr, gb);
389
390   return (1);
391 }
392
393 static clib_error_t *
394 gbp_bridge_domain_show (vlib_main_t * vm,
395                    unformat_input_t * input, vlib_cli_command_t * cmd)
396 {
397   vlib_cli_output (vm, "Bridge-Domains:");
398   gbp_bridge_domain_walk (gbp_bridge_domain_show_one, vm);
399
400   return (NULL);
401 }
402
403
404 /*?
405  * Show Group Based Policy Bridge_Domains and derived information
406  *
407  * @cliexpar
408  * @cliexstart{show gbp bridge_domain}
409  * @cliexend
410  ?*/
411 /* *INDENT-OFF* */
412 VLIB_CLI_COMMAND (gbp_bridge_domain_show_node, static) = {
413   .path = "show gbp bridge-domain",
414   .short_help = "show gbp bridge-domain\n",
415   .function = gbp_bridge_domain_show,
416 };
417 /* *INDENT-ON* */
418
419 static clib_error_t *
420 gbp_bridge_domain_init (vlib_main_t * vm)
421 {
422   gb_logger = vlib_log_register_class ("gbp", "bd");
423
424   return (NULL);
425 }
426
427 VLIB_INIT_FUNCTION (gbp_bridge_domain_init);
428
429 /*
430  * fd.io coding-style-patch-verification: ON
431  *
432  * Local Variables:
433  * eval: (c-set-style "gnu")
434  * End:
435  */