L2 BVI/FIB: Update L2 FIB table when BVI's MAC changes
[vpp.git] / src / plugins / gbp / gbp_endpoint.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.h>
19 #include <plugins/gbp/gbp_endpoint_group.h>
20
21 #include <vnet/ethernet/arp_packet.h>
22 #include <vnet/l2/l2_input.h>
23 #include <vnet/l2/l2_output.h>
24 #include <vnet/l2/feat_bitmap.h>
25
26 /**
27  * IP4 destintion address to destination EPG mapping table
28  */
29 typedef struct gbp_ip4_to_epg_db_t_
30 {
31   /**
32    * use a simple hash table
33    */
34   uword *g4ie_hash;
35 } gbp_ip4_to_epg_db_t;
36
37 static gbp_ip4_to_epg_db_t gbp_ip4_to_epg_db;
38
39 /**
40  * IP6 destintion address to destination EPG mapping table
41  */
42 typedef struct gbp_ip6_to_epg_db_t_
43 {
44   /**
45    * use a memroy hash table
46    */
47   uword *g6ie_hash;
48 } gbp_ip6_to_epg_db_t;
49
50 static gbp_ip6_to_epg_db_t gbp_ip6_to_epg_db;
51
52
53 const static gbp_itf_t ITF_INVALID = {
54   .gi_epg = EPG_INVALID,
55   .gi_ref_count = 0,
56 };
57
58 gbp_itf_to_epg_db_t gbp_itf_to_epg_db;
59
60 /**
61  * Pool of GBP endpoints
62  */
63 static gbp_endpoint_t *gbp_endpoint_pool;
64
65 /**
66  * DB of endpoints
67  */
68 static uword *gbp_endpoint_db;
69
70 static void
71 gbp_ip_epg_update (const ip46_address_t * ip, epg_id_t epg_id)
72 {
73   /*
74    * we are dealing only with addresses here so this limited
75    * is_ip4 check is ok
76    */
77   if (ip46_address_is_ip4 (ip))
78     {
79       hash_set (gbp_ip4_to_epg_db.g4ie_hash, ip->ip4.as_u32, epg_id);
80     }
81   else
82     {
83       hash_set_mem (gbp_ip6_to_epg_db.g6ie_hash, &ip->ip6, epg_id);
84     }
85 }
86
87 static void
88 gbp_ip_epg_delete (const ip46_address_t * ip)
89 {
90   if (ip46_address_is_ip4 (ip))
91     {
92       hash_unset (gbp_ip4_to_epg_db.g4ie_hash, ip->ip4.as_u32);
93     }
94   else
95     {
96       hash_unset_mem (gbp_ip6_to_epg_db.g6ie_hash, &ip->ip6);
97     }
98 }
99
100 void
101 gbp_itf_epg_update (u32 sw_if_index, epg_id_t src_epg, u8 do_policy)
102 {
103   vec_validate_init_empty (gbp_itf_to_epg_db.gte_vec,
104                            sw_if_index, ITF_INVALID);
105
106   if (0 == gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count)
107     {
108       l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_GBP_SRC_CLASSIFY,
109                                   1);
110       l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_GBP_FWD, 1);
111       if (do_policy)
112         l2output_intf_bitmap_enable (sw_if_index, L2OUTPUT_FEAT_GBP_POLICY,
113                                      1);
114     }
115   gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg = src_epg;
116   gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count++;
117 }
118
119 void
120 gbp_itf_epg_delete (u32 sw_if_index)
121 {
122   if (vec_len (gbp_itf_to_epg_db.gte_vec) <= sw_if_index)
123     return;
124
125   if (1 == gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count)
126     {
127       gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg = EPG_INVALID;
128
129       l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_GBP_SRC_CLASSIFY,
130                                   0);
131       l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_GBP_FWD, 0);
132       l2output_intf_bitmap_enable (sw_if_index, L2OUTPUT_FEAT_GBP_POLICY, 0);
133     }
134   gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count--;
135 }
136
137 int
138 gbp_endpoint_update (u32 sw_if_index,
139                      const ip46_address_t * ip, epg_id_t epg_id)
140 {
141   gbp_endpoint_key_t key = {
142     .gek_ip = *ip,
143     .gek_sw_if_index = sw_if_index,
144   };
145   gbp_endpoint_group_t *gepg;
146   gbp_endpoint_t *gbpe;
147   uword *p;
148
149   gepg = gbp_endpoint_group_find (epg_id);
150
151   if (NULL == gepg)
152     return (VNET_API_ERROR_NO_SUCH_ENTRY);
153
154   p = hash_get_mem (gbp_endpoint_db, &key);
155
156   if (p)
157     {
158       gbpe = pool_elt_at_index (gbp_endpoint_pool, p[0]);
159     }
160   else
161     {
162       pool_get (gbp_endpoint_pool, gbpe);
163
164       gbpe->ge_key = clib_mem_alloc (sizeof (gbp_endpoint_key_t));
165       clib_memcpy (gbpe->ge_key, &key, sizeof (gbp_endpoint_key_t));
166
167       hash_set_mem (gbp_endpoint_db, gbpe->ge_key, gbpe - gbp_endpoint_pool);
168     }
169
170   gbpe->ge_epg_id = epg_id;
171
172   gbp_itf_epg_update (gbpe->ge_key->gek_sw_if_index, gbpe->ge_epg_id, 1);
173
174   if (!ip46_address_is_zero (&gbpe->ge_key->gek_ip))
175     gbp_ip_epg_update (&gbpe->ge_key->gek_ip, gbpe->ge_epg_id);
176
177   /*
178    * send a gratuitous ARP on the EPG's uplink. this is done so that if
179    * this EP has moved from some other place in the 'fabric', upstream
180    * devices are informed
181    */
182   if (ip46_address_is_ip4 (&gbpe->ge_key->gek_ip))
183     send_ip4_garp_w_addr (vlib_get_main (),
184                           &gbpe->ge_key->gek_ip.ip4,
185                           gepg->gepg_uplink_sw_if_index);
186   else
187     send_ip6_na_w_addr (vlib_get_main (),
188                         &gbpe->ge_key->gek_ip.ip6,
189                         gepg->gepg_uplink_sw_if_index);
190
191   return (0);
192 }
193
194 void
195 gbp_endpoint_delete (u32 sw_if_index, const ip46_address_t * ip)
196 {
197   gbp_endpoint_key_t key = {
198     .gek_ip = *ip,
199     .gek_sw_if_index = sw_if_index,
200   };
201   gbp_endpoint_t *gbpe;
202   uword *p;
203
204   p = hash_get_mem (gbp_endpoint_db, &key);
205
206   if (p)
207     {
208       gbpe = pool_elt_at_index (gbp_endpoint_pool, p[0]);
209
210       hash_unset_mem (gbp_endpoint_db, gbpe->ge_key);
211
212       gbp_itf_epg_delete (gbpe->ge_key->gek_sw_if_index);
213       if (!ip46_address_is_zero (&gbpe->ge_key->gek_ip))
214         gbp_ip_epg_delete (&gbpe->ge_key->gek_ip);
215
216       clib_mem_free (gbpe->ge_key);
217
218       pool_put (gbp_endpoint_pool, gbpe);
219     }
220 }
221
222 void
223 gbp_endpoint_walk (gbp_endpoint_cb_t cb, void *ctx)
224 {
225   gbp_endpoint_t *gbpe;
226
227   /* *INDENT-OFF* */
228   pool_foreach(gbpe, gbp_endpoint_pool,
229   {
230     if (!cb(gbpe, ctx))
231       break;
232   });
233   /* *INDENT-ON* */
234 }
235
236 static clib_error_t *
237 gbp_endpoint_cli (vlib_main_t * vm,
238                   unformat_input_t * input, vlib_cli_command_t * cmd)
239 {
240   vnet_main_t *vnm = vnet_get_main ();
241   epg_id_t epg_id = EPG_INVALID;
242   ip46_address_t ip = { };
243   u32 sw_if_index = ~0;
244   u8 add = 1;
245
246   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
247     {
248       if (unformat (input, "%U", unformat_vnet_sw_interface,
249                     vnm, &sw_if_index))
250         ;
251       else if (unformat (input, "add"))
252         add = 1;
253       else if (unformat (input, "del"))
254         add = 0;
255       else if (unformat (input, "epg %d", &epg_id))
256         ;
257       else if (unformat (input, "ip %U", unformat_ip4_address, &ip.ip4))
258         ;
259       else if (unformat (input, "ip %U", unformat_ip6_address, &ip.ip6))
260         ;
261       else
262         break;
263     }
264
265   if (~0 == sw_if_index)
266     return clib_error_return (0, "interface must be specified");
267   if (EPG_INVALID == epg_id)
268     return clib_error_return (0, "EPG-ID must be specified");
269   if (ip46_address_is_zero (&ip))
270     return clib_error_return (0, "IP address must be specified");
271
272   if (add)
273     gbp_endpoint_update (sw_if_index, &ip, epg_id);
274   else
275     gbp_endpoint_delete (sw_if_index, &ip);
276
277   return (NULL);
278 }
279
280
281 /*?
282  * Configure a GBP Endpoint
283  *
284  * @cliexpar
285  * @cliexstart{set gbp endpoint [del] <interface> epg <ID> ip <IP>}
286  * @cliexend
287  ?*/
288 /* *INDENT-OFF* */
289 VLIB_CLI_COMMAND (gbp_endpoint_cli_node, static) = {
290   .path = "gbp endpoint",
291   .short_help = "gbp endpoint [del] <interface> epg <ID> ip <IP>",
292   .function = gbp_endpoint_cli,
293 };
294 /* *INDENT-ON* */
295
296 static int
297 gbp_endpoint_show_one (gbp_endpoint_t * gbpe, void *ctx)
298 {
299   vnet_main_t *vnm = vnet_get_main ();
300   vlib_main_t *vm;
301
302   vm = ctx;
303   vlib_cli_output (vm, "  {%U, %U} -> %d",
304                    format_vnet_sw_if_index_name, vnm,
305                    gbpe->ge_key->gek_sw_if_index,
306                    format_ip46_address, &gbpe->ge_key->gek_ip, IP46_TYPE_ANY,
307                    gbpe->ge_epg_id);
308
309   return (1);
310 }
311
312 static clib_error_t *
313 gbp_endpoint_show (vlib_main_t * vm,
314                    unformat_input_t * input, vlib_cli_command_t * cmd)
315 {
316   vnet_main_t *vnm = vnet_get_main ();
317   ip46_address_t ip, *ipp;
318   epg_id_t epg_id;
319   u32 sw_if_index;
320
321   vlib_cli_output (vm, "Endpoints:");
322   gbp_endpoint_walk (gbp_endpoint_show_one, vm);
323
324   vlib_cli_output (vm, "\nSource interface to EPG:");
325
326   vec_foreach_index (sw_if_index, gbp_itf_to_epg_db.gte_vec)
327   {
328     if (EPG_INVALID != gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg)
329       {
330         vlib_cli_output (vm, "  %U -> %d",
331                          format_vnet_sw_if_index_name, vnm, sw_if_index,
332                          gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg);
333       }
334   }
335
336   vlib_cli_output (vm, "\nDestination IP4 to EPG:");
337
338   /* *INDENT-OFF* */
339   hash_foreach (ip.ip4.as_u32, epg_id, gbp_ip4_to_epg_db.g4ie_hash,
340   {
341     vlib_cli_output (vm, "  %U -> %d", format_ip46_address, &ip,
342                      IP46_TYPE_IP4, epg_id);
343   });
344   /* *INDENT-ON* */
345
346   vlib_cli_output (vm, "\nDestination IP6 to EPG:");
347
348   /* *INDENT-OFF* */
349   hash_foreach_mem (ipp, epg_id, gbp_ip6_to_epg_db.g6ie_hash,
350   {
351     vlib_cli_output (vm, "  %U -> %d", format_ip46_address, ipp,
352                      IP46_TYPE_IP6, epg_id);
353   });
354   /* *INDENT-ON* */
355
356   return (NULL);
357 }
358
359
360 /*?
361  * Show Group Based Policy Endpoints and derived information
362  *
363  * @cliexpar
364  * @cliexstart{show gbp endpoint}
365  * @cliexend
366  ?*/
367 /* *INDENT-OFF* */
368 VLIB_CLI_COMMAND (gbp_endpoint_show_node, static) = {
369   .path = "show gbp endpoint",
370   .short_help = "show gbp endpoint\n",
371   .function = gbp_endpoint_show,
372 };
373 /* *INDENT-ON* */
374
375 static clib_error_t *
376 gbp_endpoint_init (vlib_main_t * vm)
377 {
378   gbp_endpoint_db = hash_create_mem (0,
379                                      sizeof (gbp_endpoint_key_t),
380                                      sizeof (u32));
381   gbp_ip6_to_epg_db.g6ie_hash =
382     hash_create_mem (0, sizeof (ip6_address_t), sizeof (u32));
383   return 0;
384 }
385
386 VLIB_INIT_FUNCTION (gbp_endpoint_init);
387
388 /*
389  * fd.io coding-style-patch-verification: ON
390  *
391  * Local Variables:
392  * eval: (c-set-style "gnu")
393  * End:
394  */