GBP Endpoint Learning
[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 #include <plugins/gbp/gbp_itf.h>
21 #include <plugins/gbp/gbp_scanner.h>
22 #include <plugins/gbp/gbp_bridge_domain.h>
23 #include <plugins/gbp/gbp_route_domain.h>
24 #include <plugins/gbp/gbp_policy_dpo.h>
25 #include <plugins/gbp/gbp_vxlan.h>
26
27 #include <vnet/ethernet/arp.h>
28 #include <vnet/l2/l2_input.h>
29 #include <vnet/l2/l2_output.h>
30 #include <vnet/l2/feat_bitmap.h>
31 #include <vnet/l2/l2_fib.h>
32 #include <vnet/fib/fib_table.h>
33 #include <vnet/ip/ip_neighbor.h>
34
35 static const char *gbp_endpoint_attr_names[] = GBP_ENDPOINT_ATTR_NAMES;
36
37 /**
38  * EP DBs
39  */
40 gbp_ep_db_t gbp_ep_db;
41
42 vlib_log_class_t gbp_ep_logger;
43
44 #define GBP_ENDPOINT_DBG(...)                           \
45     vlib_log_debug (gbp_ep_logger, __VA_ARGS__);
46
47 #define GBP_ENDPOINT_INFO(...)                          \
48     vlib_log_notice (gbp_ep_logger, __VA_ARGS__);
49
50 /**
51  * GBP Endpoint inactive timeout (in seconds)
52  * If a dynamically learned Endpoint has not been heard from in this
53  * amount of time it is considered inactive and discarded
54  */
55 static u32 GBP_ENDPOINT_INACTIVE_TIME = 30;
56
57 /**
58  * Pool of GBP endpoints
59  */
60 gbp_endpoint_t *gbp_endpoint_pool;
61
62 /**
63  * A count of the number of dynamic entries
64  */
65 static u32 gbp_n_learnt_endpoints;
66
67 #define FOR_EACH_GBP_ENDPOINT_ATTR(_item)               \
68     for (_item = GBP_ENDPOINT_ATTR_FIRST;               \
69          _item < GBP_ENDPOINT_ATTR_LAST;                \
70          _item++)
71
72 u8 *
73 format_gbp_endpoint_flags (u8 * s, va_list * args)
74 {
75   gbp_endpoint_attr_t attr;
76   gbp_endpoint_flags_t flags = va_arg (*args, gbp_endpoint_flags_t);
77
78   FOR_EACH_GBP_ENDPOINT_ATTR (attr)
79   {
80     if ((1 << attr) & flags)
81       {
82         s = format (s, "%s,", gbp_endpoint_attr_names[attr]);
83       }
84   }
85
86   return (s);
87 }
88
89 int
90 gbp_endpoint_is_remote (const gbp_endpoint_t * ge)
91 {
92   return (ge->ge_flags & GBP_ENDPOINT_FLAG_REMOTE);
93 }
94
95 static void
96 gbp_endpoint_extract_key_mac_itf (const clib_bihash_kv_16_8_t * key,
97                                   mac_address_t * mac, u32 * sw_if_index)
98 {
99   mac_address_from_u64 (key->key[0], mac);
100   *sw_if_index = key->key[1];
101 }
102
103 static void
104 gbp_endpoint_extract_key_ip_itf (const clib_bihash_kv_24_8_t * key,
105                                  ip46_address_t * ip, u32 * sw_if_index)
106 {
107   ip->as_u64[0] = key->key[0];
108   ip->as_u64[1] = key->key[1];
109   *sw_if_index = key->key[2];
110 }
111
112 gbp_endpoint_t *
113 gbp_endpoint_find_ip (const ip46_address_t * ip, u32 fib_index)
114 {
115   clib_bihash_kv_24_8_t key, value;
116   int rv;
117
118   gbp_endpoint_mk_key_ip (ip, fib_index, &key);
119
120   rv = clib_bihash_search_24_8 (&gbp_ep_db.ged_by_ip_rd, &key, &value);
121
122   if (0 != rv)
123     return NULL;
124
125   return (gbp_endpoint_get (value.value));
126 }
127
128 static void
129 gbp_endpoint_add_itf (u32 sw_if_index, index_t gei)
130 {
131   vec_validate_init_empty (gbp_ep_db.ged_by_sw_if_index, sw_if_index, ~0);
132
133   gbp_ep_db.ged_by_sw_if_index[sw_if_index] = gei;
134 }
135
136 static bool
137 gbp_endpoint_add_mac (const mac_address_t * mac, u32 bd_index, index_t gei)
138 {
139   clib_bihash_kv_16_8_t key;
140   int rv;
141
142   gbp_endpoint_mk_key_mac (mac->bytes, bd_index, &key);
143   key.value = gei;
144
145   rv = clib_bihash_add_del_16_8 (&gbp_ep_db.ged_by_mac_bd, &key, 1);
146
147
148   return (0 == rv);
149 }
150
151 static bool
152 gbp_endpoint_add_ip (const ip46_address_t * ip, u32 fib_index, index_t gei)
153 {
154   clib_bihash_kv_24_8_t key;
155   int rv;
156
157   gbp_endpoint_mk_key_ip (ip, fib_index, &key);
158   key.value = gei;
159
160   rv = clib_bihash_add_del_24_8 (&gbp_ep_db.ged_by_ip_rd, &key, 1);
161
162   return (0 == rv);
163 }
164
165 static void
166 gbp_endpoint_del_mac (const mac_address_t * mac, u32 bd_index)
167 {
168   clib_bihash_kv_16_8_t key;
169
170   gbp_endpoint_mk_key_mac (mac->bytes, bd_index, &key);
171
172   clib_bihash_add_del_16_8 (&gbp_ep_db.ged_by_mac_bd, &key, 0);
173 }
174
175 static void
176 gbp_endpoint_del_ip (const ip46_address_t * ip, u32 fib_index)
177 {
178   clib_bihash_kv_24_8_t key;
179
180   gbp_endpoint_mk_key_ip (ip, fib_index, &key);
181
182   clib_bihash_add_del_24_8 (&gbp_ep_db.ged_by_ip_rd, &key, 0);
183 }
184
185 static index_t
186 gbp_endpoint_index (const gbp_endpoint_t * ge)
187 {
188   return (ge - gbp_endpoint_pool);
189 }
190
191 static ip46_type_t
192 ip46_address_get_type (const ip46_address_t * a)
193 {
194   return (ip46_address_is_ip4 (a) ? IP46_TYPE_IP4 : IP46_TYPE_IP6);
195 }
196
197 static ip46_type_t
198 ip46_address_get_len (const ip46_address_t * a)
199 {
200   return (ip46_address_is_ip4 (a) ? 32 : 128);
201 }
202
203 static gbp_endpoint_t *
204 gbp_endpoint_alloc (epg_id_t epg_id,
205                     index_t ggi, u32 sw_if_index, gbp_endpoint_flags_t flags,
206                     const ip46_address_t * tun_src,
207                     const ip46_address_t * tun_dst)
208 {
209   gbp_endpoint_t *ge;
210
211   pool_get_zero (gbp_endpoint_pool, ge);
212
213   ge->ge_epg = ggi;
214   ge->ge_epg_id = epg_id;
215   ge->ge_flags = flags;
216   ge->ge_sw_if_index = sw_if_index;
217   ge->ge_last_time = vlib_time_now (vlib_get_main ());
218
219   gbp_endpoint_group_find_and_lock (epg_id);
220
221   if (gbp_endpoint_is_remote (ge))
222     {
223       if (NULL != tun_src)
224         ip46_address_copy (&ge->tun.ge_src, tun_src);
225       if (NULL != tun_dst)
226         ip46_address_copy (&ge->tun.ge_dst, tun_dst);
227
228       /*
229        * the input interface may be the parent GBP-vxlan interface,
230        * create a child vlxan-gbp tunnel and use that as the endpoint's
231        * interface.
232        */
233       switch (gbp_vxlan_tunnel_get_type (sw_if_index))
234         {
235         case GBP_VXLAN_TEMPLATE_TUNNEL:
236           ge->tun.ge_parent_sw_if_index = sw_if_index;
237           ge->ge_sw_if_index =
238             gbp_vxlan_tunnel_clone_and_lock (sw_if_index, tun_src, tun_dst);
239           break;
240         case VXLAN_GBP_TUNNEL:
241           ge->tun.ge_parent_sw_if_index =
242             vxlan_gbp_tunnel_get_parent (sw_if_index);
243           ge->ge_sw_if_index = sw_if_index;
244           vxlan_gbp_tunnel_lock (ge->ge_sw_if_index);
245           break;
246         }
247     }
248
249   return (ge);
250 }
251
252 int
253 gbp_endpoint_update (u32 sw_if_index,
254                      const ip46_address_t * ips,
255                      const mac_address_t * mac,
256                      epg_id_t epg_id,
257                      gbp_endpoint_flags_t flags,
258                      const ip46_address_t * tun_src,
259                      const ip46_address_t * tun_dst, u32 * handle)
260 {
261   gbp_endpoint_group_t *gg;
262   gbp_endpoint_t *ge;
263   index_t ggi, gei;
264
265   if (~0 == sw_if_index)
266     return (VNET_API_ERROR_INVALID_SW_IF_INDEX);
267
268   ge = NULL;
269   ggi = gbp_endpoint_group_find_and_lock (epg_id);
270
271   if (INDEX_INVALID == ggi)
272     return (VNET_API_ERROR_NO_SUCH_ENTRY);
273
274   gg = gbp_endpoint_group_get (ggi);
275
276   /*
277    * L2 EP
278    */
279   if (NULL != mac && !mac_address_is_zero (mac))
280     {
281       /*
282        * find an existing endpoint matching one of the key types
283        */
284       ge = gbp_endpoint_find_mac (mac->bytes, gg->gg_bd_index);
285       if (NULL == ge)
286         {
287           /*
288            * new entry
289            */
290           ge = gbp_endpoint_alloc (epg_id, ggi, sw_if_index, flags,
291                                    tun_src, tun_dst);
292           gei = gbp_endpoint_index (ge);
293           mac_address_copy (&ge->ge_mac, mac);
294
295           ge->ge_itf = gbp_itf_add_and_lock (ge->ge_sw_if_index,
296                                              gg->gg_bd_index);
297
298           gbp_itf_set_l2_input_feature (ge->ge_itf, gei,
299                                         L2INPUT_FEAT_GBP_FWD);
300
301           if (gbp_endpoint_is_remote (ge))
302             {
303               gbp_itf_set_l2_output_feature (ge->ge_itf, gei,
304                                              L2OUTPUT_FEAT_GBP_POLICY_MAC);
305             }
306           else
307             {
308               gbp_endpoint_add_itf (ge->ge_sw_if_index, gei);
309               gbp_itf_set_l2_output_feature (ge->ge_itf, gei,
310                                              L2OUTPUT_FEAT_GBP_POLICY_PORT);
311             }
312
313           gbp_endpoint_add_mac (mac, gg->gg_bd_index, gei);
314
315           l2fib_add_entry (mac->bytes, gg->gg_bd_index, ge->ge_sw_if_index,
316                            L2FIB_ENTRY_RESULT_FLAG_STATIC);
317         }
318       else
319         {
320           /*
321            * update existing entry..
322            */
323           ge->ge_flags = flags;
324           gei = gbp_endpoint_index (ge);
325           goto out;
326         }
327     }
328
329   /*
330    * L3 EP
331    */
332   if (NULL != ips && !ip46_address_is_zero (ips))
333     {
334       const ip46_address_t *ip;
335       fib_protocol_t fproto;
336       gbp_endpoint_t *l3_ge;
337       u32 ii;
338
339       /*
340        * look for a matching EP by any of the address
341        * An EP's IP addresses cannot change so we can search based on
342        * the first
343        */
344       fproto = fib_proto_from_ip46 (ip46_address_get_type (&ips[0]));
345
346       l3_ge = gbp_endpoint_find_ip (&ips[0],
347                                     gbp_endpoint_group_get_fib_index (gg,
348                                                                       fproto));
349       if (NULL == l3_ge)
350         {
351           if (NULL == ge)
352             {
353               ge = gbp_endpoint_alloc (epg_id, ggi, sw_if_index, flags,
354                                        tun_src, tun_dst);
355               ge->ge_itf = gbp_itf_add_and_lock (sw_if_index, ~0);
356             }
357           else
358             /* L2 EP with IPs */
359             gei = gbp_endpoint_index (ge);
360         }
361       else
362         {
363           /* modify */
364           ge = l3_ge;
365           ge->ge_flags = flags;
366           gei = gbp_endpoint_index (ge);
367           goto out;
368         }
369
370       gei = gbp_endpoint_index (ge);
371       ge->ge_ips = ips;
372       vec_validate (ge->ge_adjs, vec_len (ips) - 1);
373
374       vec_foreach_index (ii, ge->ge_ips)
375       {
376         ethernet_header_t *eth;
377         ip46_type_t ip_type;
378         u32 ip_sw_if_index;
379         u8 *rewrite;
380
381         rewrite = NULL;
382         ip = &ge->ge_ips[ii];
383         ip_type = ip46_address_get_type (ip);
384         fproto = fib_proto_from_ip46 (ip_type);
385
386         bd_add_del_ip_mac (gg->gg_bd_index, ip_type, ip, &ge->ge_mac, 1);
387
388         // FIXME - check error
389         gbp_endpoint_add_ip (ip,
390                              gbp_endpoint_group_get_fib_index (gg, fproto),
391                              gei);
392
393         /*
394          * add a host route via the EPG's BVI we need this because the
395          * adj fib does not install, due to cover refinement check, since
396          * the BVI's prefix is /32
397          */
398         fib_prefix_t pfx = {
399           .fp_proto = fproto,
400           .fp_len = ip46_address_get_len (ip),
401           .fp_addr = *ip,
402         };
403         vec_validate (rewrite, sizeof (*eth) - 1);
404         eth = (ethernet_header_t *) rewrite;
405
406         eth->type = clib_host_to_net_u16 ((fproto == FIB_PROTOCOL_IP4 ?
407                                            ETHERNET_TYPE_IP4 :
408                                            ETHERNET_TYPE_IP6));
409
410         if (gbp_endpoint_is_remote (ge))
411           {
412             /*
413              * for dynamic EPs we msut add the IP adjacency via the learned
414              * tunnel since the BD will not contain the EP's MAC since it was
415              * L3 learned. The dst MAC address used is the 'BD's MAC'.
416              */
417             ip_sw_if_index = ge->ge_sw_if_index;
418
419             mac_address_to_bytes (gbp_route_domain_get_local_mac (),
420                                   eth->src_address);
421             mac_address_to_bytes (gbp_route_domain_get_remote_mac (),
422                                   eth->dst_address);
423           }
424         else
425           {
426             /*
427              * for the static EPs we add the IP adjacency via the BVI
428              * knowing that the BD has the MAC address to route to and
429              * that policy will be applied on egress to the EP's port
430              */
431             ip_sw_if_index = gbp_endpoint_group_get_bvi (gg);
432
433             clib_memcpy (eth->src_address,
434                          vnet_sw_interface_get_hw_address (vnet_get_main (),
435                                                            ip_sw_if_index),
436                          sizeof (eth->src_address));
437             mac_address_to_bytes (&ge->ge_mac, eth->dst_address);
438           }
439
440         fib_table_entry_path_add
441           (gbp_endpoint_group_get_fib_index (gg, fproto),
442            &pfx, FIB_SOURCE_PLUGIN_LOW,
443            FIB_ENTRY_FLAG_NONE,
444            fib_proto_to_dpo (fproto), ip, ip_sw_if_index,
445            ~0, 1, NULL, FIB_ROUTE_PATH_FLAG_NONE);
446
447         ge->ge_adjs[ii] = adj_nbr_add_or_lock_w_rewrite (fproto,
448                                                          fib_proto_to_link
449                                                          (fproto), ip,
450                                                          ip_sw_if_index,
451                                                          rewrite);
452
453         if (gbp_endpoint_is_remote (ge))
454           {
455             dpo_id_t policy_dpo = DPO_INVALID;
456
457             /*
458              * interpose a policy DPO from the endpoint so that policy
459              * is applied
460              */
461             gbp_policy_dpo_add_or_lock (fib_proto_to_dpo (fproto),
462                                         gg->gg_id, ~0, &policy_dpo);
463
464             fib_table_entry_special_dpo_add
465               (gbp_endpoint_group_get_fib_index (gg, fproto),
466                &pfx,
467                FIB_SOURCE_PLUGIN_HI, FIB_ENTRY_FLAG_INTERPOSE, &policy_dpo);
468           }
469
470         /*
471          * send a gratuitous ARP on the EPG's uplink. this is done so
472          * that if this EP has moved from some other place in the
473          * 'fabric', upstream devices are informed
474          */
475         if (!(gbp_endpoint_is_remote (ge)) && ~0 != gg->gg_uplink_sw_if_index)
476           {
477             gbp_endpoint_add_itf (sw_if_index, gei);
478             if (ip46_address_is_ip4 (ip))
479               send_ip4_garp_w_addr (vlib_get_main (),
480                                     &ip->ip4, gg->gg_uplink_sw_if_index);
481             else
482               send_ip6_na_w_addr (vlib_get_main (),
483                                   &ip->ip6, gg->gg_uplink_sw_if_index);
484           }
485       }
486     }
487
488   if (NULL == ge)
489     return (0);
490
491   /*
492    * count the number of dynamic entries and kick off the scanner
493    * process is this is our first.
494    */
495   if (gbp_endpoint_is_remote (ge))
496     {
497       gbp_n_learnt_endpoints++;
498
499       if (1 == gbp_n_learnt_endpoints)
500         {
501           vlib_process_signal_event (vlib_get_main (),
502                                      gbp_scanner_node.index,
503                                      GBP_ENDPOINT_SCAN_START, 0);
504         }
505     }
506   else
507     {
508       /*
509        * non-remote endpoints (i.e. those not arriving on iVXLAN
510        * tunnels) need to be classifed based on the the input interface.
511        * We enable the GBP-FWD feature only is the group has an uplink
512        * interface (on which the GBP-FWD feature would send UU traffic).
513        */
514       l2input_feat_masks_t feats = L2INPUT_FEAT_GBP_SRC_CLASSIFY;
515
516       if (~0 != gg->gg_uplink_sw_if_index)
517         feats |= L2INPUT_FEAT_GBP_FWD;
518       gbp_itf_set_l2_input_feature (ge->ge_itf, gbp_endpoint_index (ge),
519                                     feats);
520     }
521 out:
522
523   if (handle)
524     *handle = (ge - gbp_endpoint_pool);
525
526   gbp_endpoint_group_unlock (ggi);
527   GBP_ENDPOINT_INFO ("update: %U", format_gbp_endpoint, gei);
528
529   return (0);
530 }
531
532 void
533 gbp_endpoint_delete (index_t gei)
534 {
535   gbp_endpoint_group_t *gg;
536   gbp_endpoint_t *ge;
537
538   if (pool_is_free_index (gbp_endpoint_pool, gei))
539     return;
540
541   GBP_ENDPOINT_INFO ("delete: %U", format_gbp_endpoint, gei);
542
543   ge = gbp_endpoint_get (gei);
544   gg = gbp_endpoint_group_get (ge->ge_epg);
545
546   gbp_endpoint_del_mac (&ge->ge_mac, gg->gg_bd_index);
547   l2fib_del_entry (ge->ge_mac.bytes, gg->gg_bd_index, ge->ge_sw_if_index);
548   gbp_itf_set_l2_input_feature (ge->ge_itf, gei, (L2INPUT_FEAT_NONE));
549   gbp_itf_set_l2_output_feature (ge->ge_itf, gei, L2OUTPUT_FEAT_NONE);
550
551   if (NULL != ge->ge_ips)
552     {
553       const ip46_address_t *ip;
554       index_t *ai;
555
556       vec_foreach (ai, ge->ge_adjs)
557       {
558         adj_unlock (*ai);
559       }
560       vec_foreach (ip, ge->ge_ips)
561       {
562         fib_protocol_t fproto;
563         ip46_type_t ip_type;
564
565         ip_type = ip46_address_get_type (ip);
566         fproto = fib_proto_from_ip46 (ip_type);
567
568         gbp_endpoint_del_ip (ip,
569                              gbp_endpoint_group_get_fib_index (gg, fproto));
570
571         bd_add_del_ip_mac (gg->gg_bd_index, ip_type, ip, &ge->ge_mac, 0);
572
573         /*
574          * remove a host route via the EPG's BVI
575          */
576         fib_prefix_t pfx = {
577           .fp_proto = fproto,
578           .fp_len = ip46_address_get_len (ip),
579           .fp_addr = *ip,
580         };
581
582         if (gbp_endpoint_is_remote (ge))
583           {
584             fib_table_entry_special_remove
585               (gbp_endpoint_group_get_fib_index (gg, fproto),
586                &pfx, FIB_SOURCE_PLUGIN_HI);
587           }
588
589         fib_table_entry_path_remove
590           (gbp_endpoint_group_get_fib_index (gg, fproto),
591            &pfx, FIB_SOURCE_PLUGIN_LOW,
592            fib_proto_to_dpo (fproto), ip,
593            (gbp_endpoint_is_remote (ge) ?
594             ge->ge_sw_if_index :
595             gbp_endpoint_group_get_bvi (gg)),
596            ~0, 1, FIB_ROUTE_PATH_FLAG_NONE);
597       }
598     }
599
600   if (ge->ge_flags & GBP_ENDPOINT_FLAG_LEARNT)
601     {
602       gbp_n_learnt_endpoints--;
603
604       if (0 == gbp_n_learnt_endpoints)
605         {
606           vlib_process_signal_event (vlib_get_main (),
607                                      gbp_scanner_node.index,
608                                      GBP_ENDPOINT_SCAN_STOP, 0);
609         }
610     }
611
612   gbp_itf_unlock (ge->ge_itf);
613   if (gbp_endpoint_is_remote (ge))
614     {
615       vxlan_gbp_tunnel_unlock (ge->ge_sw_if_index);
616     }
617   gbp_endpoint_group_unlock (ge->ge_epg);
618   pool_put (gbp_endpoint_pool, ge);
619 }
620
621 typedef struct gbp_endpoint_flush_ctx_t_
622 {
623   u32 sw_if_index;
624   index_t *geis;
625 } gbp_endpoint_flush_ctx_t;
626
627 static walk_rc_t
628 gbp_endpoint_flush_cb (index_t gei, void *args)
629 {
630   gbp_endpoint_flush_ctx_t *ctx = args;
631   gbp_endpoint_t *ge;
632
633   ge = gbp_endpoint_get (gei);
634
635   if (gbp_endpoint_is_remote (ge) &&
636       ctx->sw_if_index == ge->tun.ge_parent_sw_if_index)
637     {
638       vec_add1 (ctx->geis, gei);
639     }
640
641   return (WALK_CONTINUE);
642 }
643
644 /**
645  * remove all learnt endpoints using the interface
646  */
647 void
648 gbp_endpoint_flush (u32 sw_if_index)
649 {
650   gbp_endpoint_flush_ctx_t ctx = {
651     .sw_if_index = sw_if_index,
652   };
653   index_t *gei;
654
655   gbp_endpoint_walk (gbp_endpoint_flush_cb, &ctx);
656
657   vec_foreach (gei, ctx.geis)
658   {
659     gbp_endpoint_delete (*gei);
660   }
661
662   vec_free (ctx.geis);
663 }
664
665 void
666 gbp_endpoint_walk (gbp_endpoint_cb_t cb, void *ctx)
667 {
668   u32 index;
669
670   /* *INDENT-OFF* */
671   pool_foreach_index(index, gbp_endpoint_pool,
672   {
673     if (!cb(index, ctx))
674       break;
675   });
676   /* *INDENT-ON* */
677 }
678
679 static clib_error_t *
680 gbp_endpoint_cli (vlib_main_t * vm,
681                   unformat_input_t * input, vlib_cli_command_t * cmd)
682 {
683   ip46_address_t ip = ip46_address_initializer, *ips = NULL;
684   mac_address_t mac = ZERO_MAC_ADDRESS;
685   vnet_main_t *vnm = vnet_get_main ();
686   u32 epg_id = EPG_INVALID;
687   u32 handle = INDEX_INVALID;
688   u32 sw_if_index = ~0;
689   u8 add = 1;
690   int rv;
691
692   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
693     {
694       ip46_address_reset (&ip);
695
696       if (unformat (input, "%U", unformat_vnet_sw_interface,
697                     vnm, &sw_if_index))
698         ;
699       else if (unformat (input, "add"))
700         add = 1;
701       else if (unformat (input, "del"))
702         add = 0;
703       else if (unformat (input, "epg %d", &epg_id))
704         ;
705       else if (unformat (input, "handle %d", &handle))
706         ;
707       else if (unformat (input, "ip %U", unformat_ip4_address, &ip.ip4))
708         vec_add1 (ips, ip);
709       else if (unformat (input, "ip %U", unformat_ip6_address, &ip.ip6))
710         vec_add1 (ips, ip);
711       else if (unformat (input, "mac %U", unformat_mac_address, &mac))
712         ;
713       else
714         break;
715     }
716
717   if (add)
718     {
719       if (~0 == sw_if_index)
720         return clib_error_return (0, "interface must be specified");
721       if (EPG_INVALID == epg_id)
722         return clib_error_return (0, "EPG-ID must be specified");
723
724       rv =
725         gbp_endpoint_update (sw_if_index, ips, &mac, epg_id,
726                              GBP_ENDPOINT_FLAG_NONE, NULL, NULL, &handle);
727
728       if (rv)
729         return clib_error_return (0, "GBP Endpoint update returned %d", rv);
730       else
731         vlib_cli_output (vm, "handle %d\n", handle);
732     }
733   else
734     {
735       if (INDEX_INVALID == handle)
736         return clib_error_return (0, "handle must be specified");
737
738       gbp_endpoint_delete (handle);
739     }
740
741   vec_free (ips);
742
743   return (NULL);
744 }
745
746
747 /*?
748  * Configure a GBP Endpoint
749  *
750  * @cliexpar
751  * @cliexstart{set gbp endpoint [del] <interface> epg <ID> ip <IP>}
752  * @cliexend
753  ?*/
754 /* *INDENT-OFF* */
755 VLIB_CLI_COMMAND (gbp_endpoint_cli_node, static) = {
756   .path = "gbp endpoint",
757   .short_help = "gbp endpoint [del] <interface> epg <ID> ip <IP> mac <MAC>",
758   .function = gbp_endpoint_cli,
759 };
760 /* *INDENT-ON* */
761
762 u8 *
763 format_gbp_endpoint (u8 * s, va_list * args)
764 {
765   index_t gei = va_arg (*args, index_t);
766   const ip46_address_t *ip;
767   gbp_endpoint_t *ge;
768
769   ge = gbp_endpoint_get (gei);
770
771   s = format (s, "[@%d] ", gei);
772   s = format (s, "IPs:[");
773
774   vec_foreach (ip, ge->ge_ips)
775   {
776     s = format (s, "%U, ", format_ip46_address, ip, IP46_TYPE_ANY);
777   }
778   s = format (s, "]");
779
780   s = format (s, " MAC:%U", format_mac_address_t, &ge->ge_mac);
781   s = format (s, " EPG-ID:%d", ge->ge_epg_id);
782   if (GBP_ENDPOINT_FLAG_NONE != ge->ge_flags)
783     {
784       s = format (s, " flags:%U", format_gbp_endpoint_flags, ge->ge_flags);
785     }
786
787   s = format (s, " itf:[%U]", format_gbp_itf, ge->ge_itf);
788   s = format (s, " last-time:[%f]", ge->ge_last_time);
789
790   return s;
791 }
792
793 static walk_rc_t
794 gbp_endpoint_show_one (index_t gei, void *ctx)
795 {
796   vlib_main_t *vm;
797
798   vm = ctx;
799   vlib_cli_output (vm, " %U", format_gbp_endpoint, gei);
800
801   return (WALK_CONTINUE);
802 }
803
804 static void
805 gbp_endpoint_walk_ip_itf (const clib_bihash_kv_24_8_t * kvp, void *arg)
806 {
807   ip46_address_t ip;
808   vlib_main_t *vm;
809   u32 sw_if_index;
810
811   vm = arg;
812
813   gbp_endpoint_extract_key_ip_itf (kvp, &ip, &sw_if_index);
814
815   vlib_cli_output (vm, " {%U, %U} -> %d",
816                    format_ip46_address, &ip, IP46_TYPE_ANY,
817                    format_vnet_sw_if_index_name, vnet_get_main (),
818                    sw_if_index, kvp->value);
819 }
820
821 static void
822 gbp_endpoint_walk_mac_itf (const clib_bihash_kv_16_8_t * kvp, void *arg)
823 {
824   mac_address_t mac;
825   vlib_main_t *vm;
826   u32 sw_if_index;
827
828   vm = arg;
829
830   gbp_endpoint_extract_key_mac_itf (kvp, &mac, &sw_if_index);
831
832   vlib_cli_output (vm, " {%U, %U} -> %d",
833                    format_mac_address_t, &mac,
834                    format_vnet_sw_if_index_name, vnet_get_main (),
835                    sw_if_index, kvp->value);
836 }
837
838 static clib_error_t *
839 gbp_endpoint_show (vlib_main_t * vm,
840                    unformat_input_t * input, vlib_cli_command_t * cmd)
841 {
842   u32 show_dbs, handle;
843
844   handle = INDEX_INVALID;
845   show_dbs = 0;
846
847   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
848     {
849       if (unformat (input, "%d", &handle))
850         ;
851       else if (unformat (input, "db"))
852         show_dbs = 1;
853       else
854         break;
855     }
856
857   if (INDEX_INVALID != handle)
858     {
859       vlib_cli_output (vm, "%U", format_gbp_endpoint, handle);
860     }
861   else if (show_dbs)
862     {
863       vlib_cli_output (vm, "\nDatabases:");
864       clib_bihash_foreach_key_value_pair_24_8 (&gbp_ep_db.ged_by_ip_rd,
865                                                gbp_endpoint_walk_ip_itf, vm);
866       clib_bihash_foreach_key_value_pair_16_8
867         (&gbp_ep_db.ged_by_mac_bd, gbp_endpoint_walk_mac_itf, vm);
868     }
869   else
870     {
871       vlib_cli_output (vm, "Endpoints:");
872       gbp_endpoint_walk (gbp_endpoint_show_one, vm);
873     }
874
875   return (NULL);
876 }
877
878 /*?
879  * Show Group Based Policy Endpoints and derived information
880  *
881  * @cliexpar
882  * @cliexstart{show gbp endpoint}
883  * @cliexend
884  ?*/
885 /* *INDENT-OFF* */
886 VLIB_CLI_COMMAND (gbp_endpoint_show_node, static) = {
887   .path = "show gbp endpoint",
888   .short_help = "show gbp endpoint\n",
889   .function = gbp_endpoint_show,
890 };
891 /* *INDENT-ON* */
892
893 static void
894 gbp_endpoint_check (index_t gei, f64 start_time)
895 {
896   gbp_endpoint_t *ge;
897
898   ge = gbp_endpoint_get (gei);
899
900   GBP_ENDPOINT_DBG ("scan at:%f -> %U", start_time, format_gbp_endpoint, gei);
901
902   if ((ge->ge_flags & GBP_ENDPOINT_FLAG_LEARNT) &&
903       ((start_time - ge->ge_last_time) > GBP_ENDPOINT_INACTIVE_TIME))
904     {
905       gbp_endpoint_delete (gei);
906     }
907 }
908
909 static void
910 gbp_endpoint_scan_l2 (vlib_main_t * vm)
911 {
912   clib_bihash_16_8_t *gte_table = &gbp_ep_db.ged_by_mac_bd;
913   f64 last_start, start_time, delta_t;
914   int i, j, k;
915
916   delta_t = 0;
917   last_start = start_time = vlib_time_now (vm);
918
919   for (i = 0; i < gte_table->nbuckets; i++)
920     {
921       clib_bihash_bucket_16_8_t *b;
922       clib_bihash_value_16_8_t *v;
923
924       /* allow no more than 20us without a pause */
925       delta_t = vlib_time_now (vm) - last_start;
926       if (delta_t > 20e-6)
927         {
928           /* suspend for 100 us */
929           vlib_process_suspend (vm, 100e-6);
930           last_start = vlib_time_now (vm);
931         }
932
933       b = &gte_table->buckets[i];
934       if (b->offset == 0)
935         continue;
936       v = clib_bihash_get_value_16_8 (gte_table, b->offset);
937
938       for (j = 0; j < (1 << b->log2_pages); j++)
939         {
940           for (k = 0; k < BIHASH_KVP_PER_PAGE; k++)
941             {
942               if (clib_bihash_is_free_16_8 (&v->kvp[k]))
943                 continue;
944
945               gbp_endpoint_check (v->kvp[k].value, start_time);
946
947               /*
948                * Note: we may have just freed the bucket's backing
949                * storage, so check right here...
950                */
951               if (b->offset == 0)
952                 goto doublebreak;
953             }
954           v++;
955         }
956     doublebreak:
957       ;
958     }
959 }
960
961 static void
962 gbp_endpoint_scan_l3 (vlib_main_t * vm)
963 {
964   clib_bihash_24_8_t *gte_table = &gbp_ep_db.ged_by_ip_rd;
965   f64 last_start, start_time, delta_t;
966   int i, j, k;
967
968   delta_t = 0;
969   last_start = start_time = vlib_time_now (vm);
970
971   for (i = 0; i < gte_table->nbuckets; i++)
972     {
973       clib_bihash_bucket_24_8_t *b;
974       clib_bihash_value_24_8_t *v;
975
976       /* allow no more than 20us without a pause */
977       delta_t = vlib_time_now (vm) - last_start;
978       if (delta_t > 20e-6)
979         {
980           /* suspend for 100 us */
981           vlib_process_suspend (vm, 100e-6);
982           last_start = vlib_time_now (vm);
983         }
984
985       b = &gte_table->buckets[i];
986       if (b->offset == 0)
987         continue;
988       v = clib_bihash_get_value_24_8 (gte_table, b->offset);
989
990       for (j = 0; j < (1 << b->log2_pages); j++)
991         {
992           for (k = 0; k < BIHASH_KVP_PER_PAGE; k++)
993             {
994               if (clib_bihash_is_free_24_8 (&v->kvp[k]))
995                 continue;
996
997               gbp_endpoint_check (v->kvp[k].value, start_time);
998
999               /*
1000                * Note: we may have just freed the bucket's backing
1001                * storage, so check right here...
1002                */
1003               if (b->offset == 0)
1004                 goto doublebreak;
1005             }
1006           v++;
1007         }
1008     doublebreak:
1009       ;
1010     }
1011 }
1012
1013 void
1014 gbp_endpoint_scan (vlib_main_t * vm)
1015 {
1016   gbp_endpoint_scan_l2 (vm);
1017   gbp_endpoint_scan_l3 (vm);
1018 }
1019
1020 void
1021 gbp_learn_set_inactive_threshold (u32 threshold)
1022 {
1023   GBP_ENDPOINT_INACTIVE_TIME = threshold;
1024 }
1025
1026 f64
1027 gbp_endpoint_scan_threshold (void)
1028 {
1029   return (GBP_ENDPOINT_INACTIVE_TIME);
1030 }
1031
1032 #define GBP_EP_HASH_NUM_BUCKETS (2 * 1024)
1033 #define GBP_EP_HASH_MEMORY_SIZE (1 << 20)
1034
1035 static clib_error_t *
1036 gbp_endpoint_init (vlib_main_t * vm)
1037 {
1038   clib_bihash_init_24_8 (&gbp_ep_db.ged_by_ip_rd,
1039                          "GBP Endpoints - IP/RD",
1040                          GBP_EP_HASH_NUM_BUCKETS, GBP_EP_HASH_MEMORY_SIZE);
1041
1042   clib_bihash_init_16_8 (&gbp_ep_db.ged_by_mac_bd,
1043                          "GBP Endpoints - MAC/BD",
1044                          GBP_EP_HASH_NUM_BUCKETS, GBP_EP_HASH_MEMORY_SIZE);
1045
1046   gbp_ep_logger = vlib_log_register_class ("gbp", "ep");
1047
1048   return (NULL);
1049 }
1050
1051 VLIB_INIT_FUNCTION (gbp_endpoint_init);
1052
1053 /*
1054  * fd.io coding-style-patch-verification: ON
1055  *
1056  * Local Variables:
1057  * eval: (c-set-style "gnu")
1058  * End:
1059  */