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