bond: send gratuitous arp when the active slave went down in active-backup mode
[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                           gepg->gepg_uplink_sw_if_index);
183   else
184     send_ip6_na_w_addr (vlib_get_main (),
185                         &gbpe->ge_key->gek_ip.ip6,
186                         gepg->gepg_uplink_sw_if_index);
187
188   return (0);
189 }
190
191 void
192 gbp_endpoint_delete (u32 sw_if_index, const ip46_address_t * ip)
193 {
194   gbp_endpoint_key_t key = {
195     .gek_ip = *ip,
196     .gek_sw_if_index = sw_if_index,
197   };
198   gbp_endpoint_t *gbpe;
199   uword *p;
200
201   p = hash_get_mem (gbp_endpoint_db, &key);
202
203   if (p)
204     {
205       gbpe = pool_elt_at_index (gbp_endpoint_pool, p[0]);
206
207       hash_unset_mem (gbp_endpoint_db, gbpe->ge_key);
208
209       gbp_itf_epg_delete (gbpe->ge_key->gek_sw_if_index);
210       if (!ip46_address_is_zero (&gbpe->ge_key->gek_ip))
211         gbp_ip_epg_delete (&gbpe->ge_key->gek_ip);
212
213       clib_mem_free (gbpe->ge_key);
214
215       pool_put (gbp_endpoint_pool, gbpe);
216     }
217 }
218
219 void
220 gbp_endpoint_walk (gbp_endpoint_cb_t cb, void *ctx)
221 {
222   gbp_endpoint_t *gbpe;
223
224   /* *INDENT-OFF* */
225   pool_foreach(gbpe, gbp_endpoint_pool,
226   {
227     if (!cb(gbpe, ctx))
228       break;
229   });
230   /* *INDENT-ON* */
231 }
232
233 static clib_error_t *
234 gbp_endpoint_cli (vlib_main_t * vm,
235                   unformat_input_t * input, vlib_cli_command_t * cmd)
236 {
237   vnet_main_t *vnm = vnet_get_main ();
238   epg_id_t epg_id = EPG_INVALID;
239   ip46_address_t ip = { };
240   u32 sw_if_index = ~0;
241   u8 add = 1;
242
243   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
244     {
245       if (unformat (input, "%U", unformat_vnet_sw_interface,
246                     vnm, &sw_if_index))
247         ;
248       else if (unformat (input, "add"))
249         add = 1;
250       else if (unformat (input, "del"))
251         add = 0;
252       else if (unformat (input, "epg %d", &epg_id))
253         ;
254       else if (unformat (input, "ip %U", unformat_ip4_address, &ip.ip4))
255         ;
256       else if (unformat (input, "ip %U", unformat_ip6_address, &ip.ip6))
257         ;
258       else
259         break;
260     }
261
262   if (~0 == sw_if_index)
263     return clib_error_return (0, "interface must be specified");
264   if (EPG_INVALID == epg_id)
265     return clib_error_return (0, "EPG-ID must be specified");
266   if (ip46_address_is_zero (&ip))
267     return clib_error_return (0, "IP address must be specified");
268
269   if (add)
270     gbp_endpoint_update (sw_if_index, &ip, epg_id);
271   else
272     gbp_endpoint_delete (sw_if_index, &ip);
273
274   return (NULL);
275 }
276
277
278 /*?
279  * Configure a GBP Endpoint
280  *
281  * @cliexpar
282  * @cliexstart{set gbp endpoint [del] <interface> epg <ID> ip <IP>}
283  * @cliexend
284  ?*/
285 /* *INDENT-OFF* */
286 VLIB_CLI_COMMAND (gbp_endpoint_cli_node, static) = {
287   .path = "gbp endpoint",
288   .short_help = "gbp endpoint [del] <interface> epg <ID> ip <IP>",
289   .function = gbp_endpoint_cli,
290 };
291 /* *INDENT-ON* */
292
293 static int
294 gbp_endpoint_show_one (gbp_endpoint_t * gbpe, void *ctx)
295 {
296   vnet_main_t *vnm = vnet_get_main ();
297   vlib_main_t *vm;
298
299   vm = ctx;
300   vlib_cli_output (vm, "  {%U, %U} -> %d",
301                    format_vnet_sw_if_index_name, vnm,
302                    gbpe->ge_key->gek_sw_if_index,
303                    format_ip46_address, &gbpe->ge_key->gek_ip, IP46_TYPE_ANY,
304                    gbpe->ge_epg_id);
305
306   return (1);
307 }
308
309 static clib_error_t *
310 gbp_endpoint_show (vlib_main_t * vm,
311                    unformat_input_t * input, vlib_cli_command_t * cmd)
312 {
313   vnet_main_t *vnm = vnet_get_main ();
314   ip46_address_t ip, *ipp;
315   epg_id_t epg_id;
316   u32 sw_if_index;
317
318   vlib_cli_output (vm, "Endpoints:");
319   gbp_endpoint_walk (gbp_endpoint_show_one, vm);
320
321   vlib_cli_output (vm, "\nSource interface to EPG:");
322
323   vec_foreach_index (sw_if_index, gbp_itf_to_epg_db.gte_vec)
324   {
325     if (EPG_INVALID != gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg)
326       {
327         vlib_cli_output (vm, "  %U -> %d",
328                          format_vnet_sw_if_index_name, vnm, sw_if_index,
329                          gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg);
330       }
331   }
332
333   vlib_cli_output (vm, "\nDestination IP4 to EPG:");
334
335   /* *INDENT-OFF* */
336   hash_foreach (ip.ip4.as_u32, epg_id, gbp_ip4_to_epg_db.g4ie_hash,
337   {
338     vlib_cli_output (vm, "  %U -> %d", format_ip46_address, &ip,
339                      IP46_TYPE_IP4, epg_id);
340   });
341   /* *INDENT-ON* */
342
343   vlib_cli_output (vm, "\nDestination IP6 to EPG:");
344
345   /* *INDENT-OFF* */
346   hash_foreach_mem (ipp, epg_id, gbp_ip6_to_epg_db.g6ie_hash,
347   {
348     vlib_cli_output (vm, "  %U -> %d", format_ip46_address, ipp,
349                      IP46_TYPE_IP6, epg_id);
350   });
351   /* *INDENT-ON* */
352
353   return (NULL);
354 }
355
356
357 /*?
358  * Show Group Based Policy Endpoints and derived information
359  *
360  * @cliexpar
361  * @cliexstart{show gbp endpoint}
362  * @cliexend
363  ?*/
364 /* *INDENT-OFF* */
365 VLIB_CLI_COMMAND (gbp_endpoint_show_node, static) = {
366   .path = "show gbp endpoint",
367   .short_help = "show gbp endpoint\n",
368   .function = gbp_endpoint_show,
369 };
370 /* *INDENT-ON* */
371
372 static clib_error_t *
373 gbp_endpoint_init (vlib_main_t * vm)
374 {
375   gbp_endpoint_db = hash_create_mem (0,
376                                      sizeof (gbp_endpoint_key_t),
377                                      sizeof (u32));
378   gbp_ip6_to_epg_db.g6ie_hash =
379     hash_create_mem (0, sizeof (ip6_address_t), sizeof (u32));
380   return 0;
381 }
382
383 VLIB_INIT_FUNCTION (gbp_endpoint_init);
384
385 /*
386  * fd.io coding-style-patch-verification: ON
387  *
388  * Local Variables:
389  * eval: (c-set-style "gnu")
390  * End:
391  */