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