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