GBP Endpoint Updates
[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 gbp_ep_by_itf_db_t gbp_ep_by_itf_db;
27 gbp_ep_by_mac_itf_db_t gbp_ep_by_mac_itf_db;
28 gbp_ep_by_ip_itf_db_t gbp_ep_by_ip_itf_db;
29
30 /**
31  * Pool of GBP endpoints
32  */
33 gbp_endpoint_t *gbp_endpoint_pool;
34
35 /* void */
36 /* gbp_itf_epg_update (u32 sw_if_index, epg_id_t src_epg, u8 do_policy) */
37 /* { */
38 /*   vec_validate_init_empty (gbp_itf_to_epg_db.gte_vec, */
39 /*                         sw_if_index, ITF_INVALID); */
40
41 /*   if (0 == gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count) */
42 /*     { */
43 /*       l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_GBP_SRC_CLASSIFY, */
44 /*                                1); */
45 /*       l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_GBP_FWD, 1); */
46 /*       if (do_policy) */
47 /*      l2output_intf_bitmap_enable (sw_if_index, L2OUTPUT_FEAT_GBP_POLICY, */
48 /*                                   1); */
49 /*     } */
50 /*   gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg = src_epg; */
51 /*   gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count++; */
52 /* } */
53
54 /* void */
55 /* gbp_itf_epg_delete (u32 sw_if_index) */
56 /* { */
57 /*   if (vec_len (gbp_itf_to_epg_db.gte_vec) <= sw_if_index) */
58 /*     return; */
59
60 /*   if (1 == gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count) */
61 /*     { */
62 /*       gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg = EPG_INVALID; */
63
64 /*       l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_GBP_SRC_CLASSIFY, */
65 /*                                0); */
66 /*       l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_GBP_FWD, 0); */
67 /*       l2output_intf_bitmap_enable (sw_if_index, L2OUTPUT_FEAT_GBP_POLICY, 0); */
68 /*     } */
69 /*   gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count--; */
70 /* } */
71
72 static void
73 gbp_endpoint_mk_key_mac_itf (const mac_address_t * mac,
74                              u32 sw_if_index, clib_bihash_kv_16_8_t * key)
75 {
76   key->key[0] = mac_address_as_u64 (mac);
77   key->key[1] = sw_if_index;
78 }
79
80 static void
81 gbp_endpoint_extract_key_mac_itf (const clib_bihash_kv_16_8_t * key,
82                                   mac_address_t * mac, u32 * sw_if_index)
83 {
84   mac_address_from_u64 (key->key[0], mac);
85   *sw_if_index = key->key[1];
86 }
87
88 gbp_endpoint_t *
89 gbp_endpoint_find_mac_itf (const mac_address_t * mac, u32 sw_if_index)
90 {
91   clib_bihash_kv_16_8_t key, value;
92   int rv;
93
94   gbp_endpoint_mk_key_mac_itf (mac, sw_if_index, &key);
95
96   rv =
97     clib_bihash_search_16_8 (&gbp_ep_by_mac_itf_db.gte_table, &key, &value);
98
99   if (0 != rv)
100     return NULL;
101
102   return (gbp_endpoint_get (value.value));
103 }
104
105 static void
106 gbp_endpoint_mk_key_ip_itf (const ip46_address_t * ip,
107                             u32 sw_if_index, clib_bihash_kv_24_8_t * key)
108 {
109   key->key[0] = ip->as_u64[0];
110   key->key[1] = ip->as_u64[1];
111   key->key[2] = sw_if_index;
112 }
113
114 static void
115 gbp_endpoint_extract_key_ip_itf (const clib_bihash_kv_24_8_t * key,
116                                  ip46_address_t * ip, u32 * sw_if_index)
117 {
118   ip->as_u64[0] = key->key[0];
119   ip->as_u64[1] = key->key[1];
120   *sw_if_index = key->key[2];
121 }
122
123 gbp_endpoint_t *
124 gbp_endpoint_find_ip_itf (const ip46_address_t * ip, u32 sw_if_index)
125 {
126   clib_bihash_kv_24_8_t key, value;
127   int rv;
128
129   gbp_endpoint_mk_key_ip_itf (ip, sw_if_index, &key);
130
131   rv = clib_bihash_search_24_8 (&gbp_ep_by_ip_itf_db.gte_table, &key, &value);
132
133   if (0 != rv)
134     return NULL;
135
136   return (gbp_endpoint_get (value.value));
137 }
138
139 gbp_endpoint_t *
140 gbp_endpoint_find_itf (u32 sw_if_index)
141 {
142   /* if (vec_len(gbp_ep_by_itf_db.gte_vec) >= sw_if_index) */
143   /*   return NULL; */
144
145   /* vec_search(gbp_ep_by_itf_db.gte_vec[sw_if_index],  */
146   /* return (gbp_endpoint_get(gbp_ep_by_itf_db.gte_vec[sw_if_index][0])); */
147   return (NULL);
148 }
149
150 static bool
151 gbp_endpoint_add_mac_itf (const mac_address_t * mac,
152                           u32 sw_if_index, index_t gbpei)
153 {
154   clib_bihash_kv_16_8_t key;
155   int rv;
156
157   gbp_endpoint_mk_key_mac_itf (mac, sw_if_index, &key);
158   key.value = gbpei;
159
160   rv = clib_bihash_add_del_16_8 (&gbp_ep_by_mac_itf_db.gte_table, &key, 1);
161
162   return (0 == rv);
163 }
164
165 static bool
166 gbp_endpoint_add_ip_itf (const ip46_address_t * ip,
167                          u32 sw_if_index, index_t gbpei)
168 {
169   clib_bihash_kv_24_8_t key;
170   int rv;
171
172   gbp_endpoint_mk_key_ip_itf (ip, sw_if_index, &key);
173   key.value = gbpei;
174
175   rv = clib_bihash_add_del_24_8 (&gbp_ep_by_ip_itf_db.gte_table, &key, 1);
176
177   return (0 == rv);
178 }
179
180 static void
181 gbp_endpoint_add_itf (u32 sw_if_index, index_t gbpei)
182 {
183   vec_validate_init_empty (gbp_ep_by_itf_db.gte_vec, sw_if_index,
184                            INDEX_INVALID);
185
186   if (INDEX_INVALID == gbp_ep_by_itf_db.gte_vec[sw_if_index])
187     {
188       l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_GBP_SRC_CLASSIFY,
189                                   1);
190       l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_GBP_FWD, 1);
191       l2output_intf_bitmap_enable (sw_if_index, L2OUTPUT_FEAT_GBP_POLICY, 1);
192     }
193   gbp_ep_by_itf_db.gte_vec[sw_if_index] = gbpei;
194 }
195
196 static void
197 gbp_endpoint_del_mac_itf (const mac_address_t * mac, u32 sw_if_index)
198 {
199   clib_bihash_kv_16_8_t key;
200
201   gbp_endpoint_mk_key_mac_itf (mac, sw_if_index, &key);
202
203   clib_bihash_add_del_16_8 (&gbp_ep_by_mac_itf_db.gte_table, &key, 0);
204 }
205
206 static void
207 gbp_endpoint_del_ip_itf (const ip46_address_t * ip, u32 sw_if_index)
208 {
209   clib_bihash_kv_24_8_t key;
210
211   gbp_endpoint_mk_key_ip_itf (ip, sw_if_index, &key);
212
213   clib_bihash_add_del_24_8 (&gbp_ep_by_ip_itf_db.gte_table, &key, 0);
214 }
215
216 static void
217 gbp_endpoint_del_itf (u32 sw_if_index)
218 {
219   if (vec_len (gbp_ep_by_itf_db.gte_vec) <= sw_if_index)
220     return;
221
222   l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_GBP_SRC_CLASSIFY, 0);
223   l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_GBP_FWD, 0);
224   l2output_intf_bitmap_enable (sw_if_index, L2OUTPUT_FEAT_GBP_POLICY, 0);
225
226   gbp_ep_by_itf_db.gte_vec[sw_if_index] = INDEX_INVALID;
227 }
228
229 static index_t
230 gbp_endpoint_index (const gbp_endpoint_t * gbpe)
231 {
232   return (gbpe - gbp_endpoint_pool);
233 }
234
235 int
236 gbp_endpoint_update (u32 sw_if_index,
237                      const ip46_address_t * ips,
238                      const mac_address_t * mac, epg_id_t epg_id, u32 * handle)
239 {
240   gbp_endpoint_group_t *gepg;
241   const ip46_address_t *ip;
242   gbp_endpoint_t *gbpe;
243
244   gbpe = NULL;
245   gepg = gbp_endpoint_group_find (epg_id);
246
247   if (NULL == gepg)
248     return (VNET_API_ERROR_NO_SUCH_ENTRY);
249
250   /*
251    * find an existing endpoint matching one of the key types
252    */
253   if (NULL != mac)
254     {
255       gbpe = gbp_endpoint_find_mac_itf (mac, sw_if_index);
256     }
257   if (NULL == gbpe && NULL != ips)
258     {
259       vec_foreach (ip, ips)
260       {
261         gbpe = gbp_endpoint_find_ip_itf (ip, sw_if_index);
262
263         if (NULL != gbpe)
264           break;
265       }
266     }
267   if (NULL == gbpe)
268     {
269       gbpe = gbp_endpoint_find_itf (sw_if_index);
270     }
271
272   if (NULL == gbpe)
273     {
274       index_t gbpei;
275       u32 ii;
276       /*
277        * new entry
278        */
279       pool_get (gbp_endpoint_pool, gbpe);
280       gbpei = gbp_endpoint_index (gbpe);
281
282       gbpe->ge_epg_id = epg_id;
283       gbpe->ge_sw_if_index = sw_if_index;
284       gbp_endpoint_add_itf (gbpe->ge_sw_if_index, gbpei);
285
286       if (NULL != mac)
287         {
288           gbpe->ge_mac = *mac;
289
290           // FIXME ERROR
291           gbp_endpoint_add_mac_itf (mac, sw_if_index, gbpei);
292         }
293
294       if (NULL != ips)
295         {
296           vec_validate (gbpe->ge_ips, vec_len (ips) - 1);
297           vec_foreach_index (ii, ips)
298           {
299             ip46_address_copy (&gbpe->ge_ips[ii], &ips[ii]);
300
301             // FIXME ERROR
302             gbp_endpoint_add_ip_itf (&ips[ii], sw_if_index, gbpei);
303
304             /*
305              * send a gratuitous ARP on the EPG's uplink. this is done so
306              * that if this EP has moved from some other place in the
307              * 'fabric', upstream devices are informed
308              */
309             if (ip46_address_is_ip4 (&ips[ii]))
310               send_ip4_garp_w_addr (vlib_get_main (),
311                                     &ips[ii].ip4,
312                                     gepg->gepg_uplink_sw_if_index);
313             else
314               send_ip6_na_w_addr (vlib_get_main (),
315                                   &ips[ii].ip6,
316                                   gepg->gepg_uplink_sw_if_index);
317           }
318         }
319     }
320   else
321     {
322       /*
323        * update existing entry..
324        */
325       ASSERT (0);
326     }
327
328   *handle = (gbpe - gbp_endpoint_pool);
329
330   return (0);
331 }
332
333 void
334 gbp_endpoint_delete (u32 handle)
335 {
336   gbp_endpoint_t *gbpe;
337
338   if (pool_is_free_index (gbp_endpoint_pool, handle))
339     return;
340
341   gbpe = pool_elt_at_index (gbp_endpoint_pool, handle);
342
343   gbp_endpoint_del_itf (gbpe->ge_sw_if_index);
344
345   if (!mac_address_is_zero (&gbpe->ge_mac))
346     {
347       gbp_endpoint_del_mac_itf (&gbpe->ge_mac, gbpe->ge_sw_if_index);
348     }
349
350   if (NULL != gbpe->ge_ips)
351     {
352       const ip46_address_t *ip;
353
354       vec_foreach (ip, gbpe->ge_ips)
355       {
356         gbp_endpoint_del_ip_itf (ip, gbpe->ge_sw_if_index);
357       }
358     }
359   pool_put (gbp_endpoint_pool, gbpe);
360 }
361
362 void
363 gbp_endpoint_walk (gbp_endpoint_cb_t cb, void *ctx)
364 {
365   gbp_endpoint_t *gbpe;
366
367   /* *INDENT-OFF* */
368   pool_foreach(gbpe, gbp_endpoint_pool,
369   {
370     if (!cb(gbpe, ctx))
371       break;
372   });
373   /* *INDENT-ON* */
374 }
375
376 static clib_error_t *
377 gbp_endpoint_cli (vlib_main_t * vm,
378                   unformat_input_t * input, vlib_cli_command_t * cmd)
379 {
380   ip46_address_t ip = ip46_address_initializer, *ips = NULL;
381   mac_address_t mac = ZERO_MAC_ADDRESS;
382   vnet_main_t *vnm = vnet_get_main ();
383   epg_id_t epg_id = EPG_INVALID;
384   u32 handle = INDEX_INVALID;
385   u32 sw_if_index = ~0;
386   u8 add = 1;
387   int rv;
388
389   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
390     {
391       ip46_address_reset (&ip);
392
393       if (unformat (input, "%U", unformat_vnet_sw_interface,
394                     vnm, &sw_if_index))
395         ;
396       else if (unformat (input, "add"))
397         add = 1;
398       else if (unformat (input, "del"))
399         add = 0;
400       else if (unformat (input, "epg %d", &epg_id))
401         ;
402       else if (unformat (input, "handle %d", &handle))
403         ;
404       else if (unformat (input, "ip %U", unformat_ip4_address, &ip.ip4))
405         vec_add1 (ips, ip);
406       else if (unformat (input, "ip %U", unformat_ip6_address, &ip.ip6))
407         vec_add1 (ips, ip);
408       else if (unformat (input, "mac %U", unformat_mac_address, &mac))
409         ;
410       else
411         break;
412     }
413
414   if (add)
415     {
416       if (~0 == sw_if_index)
417         return clib_error_return (0, "interface must be specified");
418       if (EPG_INVALID == epg_id)
419         return clib_error_return (0, "EPG-ID must be specified");
420
421       rv = gbp_endpoint_update (sw_if_index, ips, &mac, epg_id, &handle);
422
423       if (rv)
424         return clib_error_return (0, "GBP Endpoint update returned %d", rv);
425       else
426         vlib_cli_output (vm, "handle %d\n", handle);
427     }
428   else
429     {
430       if (INDEX_INVALID == handle)
431         return clib_error_return (0, "handle must be specified");
432
433       gbp_endpoint_delete (handle);
434     }
435
436   vec_free (ips);
437
438   return (NULL);
439 }
440
441
442 /*?
443  * Configure a GBP Endpoint
444  *
445  * @cliexpar
446  * @cliexstart{set gbp endpoint [del] <interface> epg <ID> ip <IP>}
447  * @cliexend
448  ?*/
449 /* *INDENT-OFF* */
450 VLIB_CLI_COMMAND (gbp_endpoint_cli_node, static) = {
451   .path = "gbp endpoint",
452   .short_help = "gbp endpoint [del] <interface> epg <ID> ip <IP> mac <MAC>",
453   .function = gbp_endpoint_cli,
454 };
455 /* *INDENT-ON* */
456
457 u8 *
458 format_gbp_endpoint (u8 * s, va_list * args)
459 {
460   index_t gbpei = va_arg (*args, index_t);
461   vnet_main_t *vnm = vnet_get_main ();
462   const ip46_address_t *ip;
463   gbp_endpoint_t *gbpe;
464
465   gbpe = gbp_endpoint_get (gbpei);
466
467   s = format (s, "[@%d] ", gbpei);
468   s =
469     format (s, "%U", format_vnet_sw_if_index_name, vnm, gbpe->ge_sw_if_index);
470   s = format (s, ", IPs:[");
471
472   vec_foreach (ip, gbpe->ge_ips)
473   {
474     s = format (s, "%U, ", format_ip46_address, ip, IP46_TYPE_ANY);
475   }
476   s = format (s, "]");
477
478   s = format (s, " MAC:%U", format_mac_address_t, &gbpe->ge_mac);
479   s = format (s, " EPG-ID:%d", gbpe->ge_epg_id);
480
481   return s;
482 }
483
484 static walk_rc_t
485 gbp_endpoint_show_one (gbp_endpoint_t * gbpe, void *ctx)
486 {
487   vlib_main_t *vm;
488
489   vm = ctx;
490   vlib_cli_output (vm, " %U", format_gbp_endpoint, gbp_endpoint_index (gbpe));
491
492   return (WALK_CONTINUE);
493 }
494
495 static void
496 gbp_endpoint_walk_ip_itf (const clib_bihash_kv_24_8_t * kvp, void *arg)
497 {
498   ip46_address_t ip;
499   vlib_main_t *vm;
500   u32 sw_if_index;
501
502   vm = arg;
503
504   gbp_endpoint_extract_key_ip_itf (kvp, &ip, &sw_if_index);
505
506   vlib_cli_output (vm, " {%U, %U} -> %d",
507                    format_ip46_address, &ip, IP46_TYPE_ANY,
508                    format_vnet_sw_if_index_name, vnet_get_main (),
509                    sw_if_index, kvp->value);
510 }
511
512 static void
513 gbp_endpoint_walk_mac_itf (const clib_bihash_kv_16_8_t * kvp, void *arg)
514 {
515   mac_address_t mac;
516   vlib_main_t *vm;
517   u32 sw_if_index;
518
519   vm = arg;
520
521   gbp_endpoint_extract_key_mac_itf (kvp, &mac, &sw_if_index);
522
523   vlib_cli_output (vm, " {%U, %U} -> %d",
524                    format_mac_address_t, &mac,
525                    format_vnet_sw_if_index_name, vnet_get_main (),
526                    sw_if_index, kvp->value);
527 }
528
529 static clib_error_t *
530 gbp_endpoint_show (vlib_main_t * vm,
531                    unformat_input_t * input, vlib_cli_command_t * cmd)
532 {
533   u32 sw_if_index, show_dbs, handle;
534
535   handle = INDEX_INVALID;
536   show_dbs = 0;
537
538   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
539     {
540       if (unformat (input, "%d", &handle))
541         ;
542       else if (unformat (input, "db", &handle))
543         show_dbs = 1;
544       else
545         break;
546     }
547
548   if (INDEX_INVALID != handle)
549     {
550       vlib_cli_output (vm, "%U", format_gbp_endpoint, handle);
551     }
552   else if (show_dbs)
553     {
554       vlib_cli_output (vm, "\nDatabases:");
555       clib_bihash_foreach_key_value_pair_24_8 (&gbp_ep_by_ip_itf_db.gte_table,
556                                                gbp_endpoint_walk_ip_itf, vm);
557       clib_bihash_foreach_key_value_pair_16_8
558         (&gbp_ep_by_mac_itf_db.gte_table, gbp_endpoint_walk_mac_itf, vm);
559
560       vec_foreach_index (sw_if_index, gbp_ep_by_itf_db.gte_vec)
561       {
562         if (INDEX_INVALID != gbp_ep_by_itf_db.gte_vec[sw_if_index])
563           vlib_cli_output (vm, " {%U} -> %d",
564                            format_vnet_sw_if_index_name, vnet_get_main (),
565                            sw_if_index,
566                            gbp_ep_by_itf_db.gte_vec[sw_if_index]);
567       }
568     }
569   else
570     {
571       vlib_cli_output (vm, "Endpoints:");
572       gbp_endpoint_walk (gbp_endpoint_show_one, vm);
573     }
574
575   return (NULL);
576 }
577
578 /*?
579  * Show Group Based Policy Endpoints and derived information
580  *
581  * @cliexpar
582  * @cliexstart{show gbp endpoint}
583  * @cliexend
584  ?*/
585 /* *INDENT-OFF* */
586 VLIB_CLI_COMMAND (gbp_endpoint_show_node, static) = {
587   .path = "show gbp endpoint",
588   .short_help = "show gbp endpoint\n",
589   .function = gbp_endpoint_show,
590 };
591 /* *INDENT-ON* */
592
593 #define GBP_EP_HASH_NUM_BUCKETS (2 * 1024)
594 #define GBP_EP_HASH_MEMORY_SIZE (1 << 20)
595
596 static clib_error_t *
597 gbp_endpoint_init (vlib_main_t * vm)
598 {
599   clib_bihash_init_24_8 (&gbp_ep_by_ip_itf_db.gte_table,
600                          "GBP Endpoints - IP/Interface",
601                          GBP_EP_HASH_NUM_BUCKETS, GBP_EP_HASH_MEMORY_SIZE);
602
603   clib_bihash_init_16_8 (&gbp_ep_by_mac_itf_db.gte_table,
604                          "GBP Endpoints - MAC/Interface",
605                          GBP_EP_HASH_NUM_BUCKETS, GBP_EP_HASH_MEMORY_SIZE);
606
607   return (NULL);
608 }
609
610 VLIB_INIT_FUNCTION (gbp_endpoint_init);
611
612 /*
613  * fd.io coding-style-patch-verification: ON
614  *
615  * Local Variables:
616  * eval: (c-set-style "gnu")
617  * End:
618  */