LISP statistics
[vpp.git] / src / vnet / lisp-gpe / lisp_gpe_fwd_entry.c
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <vnet/lisp-gpe/lisp_gpe_fwd_entry.h>
17 #include <vnet/lisp-gpe/lisp_gpe_adjacency.h>
18 #include <vnet/lisp-gpe/lisp_gpe_tenant.h>
19 #include <vnet/lisp-cp/lisp_cp_dpo.h>
20 #include <vnet/fib/fib_table.h>
21 #include <vnet/fib/fib_entry.h>
22 #include <vnet/fib/fib_path_list.h>
23 #include <vnet/fib/ip6_fib.h>
24 #include <vnet/fib/ip4_fib.h>
25 #include <vnet/dpo/drop_dpo.h>
26 #include <vnet/dpo/lookup_dpo.h>
27 #include <vnet/dpo/load_balance.h>
28 #include <vnet/adj/adj_midchain.h>
29
30 /**
31  * @brief Add route to IP4 or IP6 Destination FIB.
32  *
33  * Add a route to the destination FIB that results in the lookup
34  * in the SRC FIB. The SRC FIB is created is it does not yet exist.
35  *
36  * @param[in]   dst_table_id    Destination FIB Table-ID
37  * @param[in]   dst_prefix      Destination IP prefix.
38  *
39  * @return  src_fib_index   The index/ID of the SRC FIB created.
40  */
41 static u32
42 ip_dst_fib_add_route (u32 dst_fib_index, const ip_prefix_t * dst_prefix)
43 {
44   fib_node_index_t src_fib_index;
45   fib_prefix_t dst_fib_prefix;
46   fib_node_index_t dst_fei;
47
48   ASSERT (NULL != dst_prefix);
49
50   ip_prefix_to_fib_prefix (dst_prefix, &dst_fib_prefix);
51
52   /*
53    * lookup the destination prefix in the VRF table and retrieve the
54    * LISP associated data
55    */
56   dst_fei = fib_table_lookup_exact_match (dst_fib_index, &dst_fib_prefix);
57
58   /*
59    * If the FIB entry is not present, or not LISP sourced, add it
60    */
61   if (dst_fei == FIB_NODE_INDEX_INVALID ||
62       NULL == fib_entry_get_source_data (dst_fei, FIB_SOURCE_LISP))
63     {
64       dpo_id_t src_lkup_dpo = DPO_INVALID;
65
66       /* create a new src FIB.  */
67       src_fib_index =
68         fib_table_create_and_lock (dst_fib_prefix.fp_proto,
69                                    "LISP-src for [%d,%U]",
70                                    dst_fib_index,
71                                    format_fib_prefix, &dst_fib_prefix);
72       /*
73        * add src fib default route
74        */
75       fib_prefix_t prefix = {
76         .fp_proto = dst_fib_prefix.fp_proto,
77       };
78       fib_table_entry_special_dpo_add (src_fib_index, &prefix,
79                                        FIB_SOURCE_LISP,
80                                        FIB_ENTRY_FLAG_EXCLUSIVE,
81                                        lisp_cp_dpo_get (fib_proto_to_dpo
82                                                         (dst_fib_prefix.fp_proto)));
83       /*
84        * create a data-path object to perform the source address lookup
85        * in the SRC FIB
86        */
87       lookup_dpo_add_or_lock_w_fib_index (src_fib_index,
88                                           (ip_prefix_version (dst_prefix) ==
89                                            IP6 ? DPO_PROTO_IP6 :
90                                            DPO_PROTO_IP4),
91                                           LOOKUP_INPUT_SRC_ADDR,
92                                           LOOKUP_TABLE_FROM_CONFIG,
93                                           &src_lkup_dpo);
94
95       /*
96        * add the entry to the destination FIB that uses the lookup DPO
97        */
98       dst_fei = fib_table_entry_special_dpo_add (dst_fib_index,
99                                                  &dst_fib_prefix,
100                                                  FIB_SOURCE_LISP,
101                                                  FIB_ENTRY_FLAG_EXCLUSIVE,
102                                                  &src_lkup_dpo);
103
104       /*
105        * the DPO is locked by the FIB entry, and we have no further
106        * need for it.
107        */
108       dpo_unlock (&src_lkup_dpo);
109
110       /*
111        * save the SRC FIB index on the entry so we can retrieve it for
112        * subsequent routes.
113        */
114       fib_entry_set_source_data (dst_fei, FIB_SOURCE_LISP, &src_fib_index);
115     }
116   else
117     {
118       /*
119        * destination FIB entry already present
120        */
121       src_fib_index = *(u32 *) fib_entry_get_source_data (dst_fei,
122                                                           FIB_SOURCE_LISP);
123     }
124
125   return (src_fib_index);
126 }
127
128 /**
129  * @brief Del route to IP4 or IP6 SD FIB.
130  *
131  * Remove routes from both destination and source FIBs.
132  *
133  * @param[in]   src_fib_index   The index/ID of the SRC FIB
134  * @param[in]   src_prefix      Source IP prefix.
135  * @param[in]   dst_fib_index   The index/ID of the DST FIB
136  * @param[in]   dst_prefix      Destination IP prefix.
137  */
138 static void
139 ip_src_dst_fib_del_route (u32 src_fib_index,
140                           const ip_prefix_t * src_prefix,
141                           u32 dst_fib_index, const ip_prefix_t * dst_prefix)
142 {
143   fib_prefix_t dst_fib_prefix, src_fib_prefix;
144   u8 have_default = 0;
145   u32 n_entries;
146
147   ASSERT (NULL != dst_prefix);
148   ASSERT (NULL != src_prefix);
149
150   ip_prefix_to_fib_prefix (dst_prefix, &dst_fib_prefix);
151   ip_prefix_to_fib_prefix (src_prefix, &src_fib_prefix);
152
153   fib_table_entry_delete (src_fib_index, &src_fib_prefix, FIB_SOURCE_LISP);
154
155   /* check if only default left or empty */
156   fib_prefix_t default_pref = {
157     .fp_proto = dst_fib_prefix.fp_proto
158   };
159
160   if (fib_table_lookup_exact_match (src_fib_index,
161                                     &default_pref) != FIB_NODE_INDEX_INVALID)
162     have_default = 1;
163
164   n_entries = fib_table_get_num_entries (src_fib_index,
165                                          src_fib_prefix.fp_proto,
166                                          FIB_SOURCE_LISP);
167   if (n_entries == 0 || (have_default && n_entries == 1))
168     {
169       /*
170        * remove src FIB default route
171        */
172       if (have_default)
173         fib_table_entry_special_remove (src_fib_index, &default_pref,
174                                         FIB_SOURCE_LISP);
175
176       /*
177        * there's nothing left now, unlock the source FIB and the
178        * destination route
179        */
180       fib_table_entry_special_remove (dst_fib_index,
181                                       &dst_fib_prefix, FIB_SOURCE_LISP);
182       fib_table_unlock (src_fib_index, src_fib_prefix.fp_proto);
183     }
184 }
185
186 /**
187  * @brief Add route to IP4 or IP6 SRC FIB.
188  *
189  * Adds a route to in the LISP SRC FIB with the result of the route
190  * being the DPO passed.
191  *
192  * @param[in]   src_fib_index   The index/ID of the SRC FIB
193  * @param[in]   src_prefix      Source IP prefix.
194  * @param[in]   src_dpo         The DPO the route will link to.
195  */
196 static void
197 ip_src_fib_add_route_w_dpo (u32 src_fib_index,
198                             const ip_prefix_t * src_prefix,
199                             const dpo_id_t * src_dpo)
200 {
201   fib_prefix_t src_fib_prefix;
202
203   ip_prefix_to_fib_prefix (src_prefix, &src_fib_prefix);
204
205   /*
206    * add the entry into the source fib.
207    */
208   fib_node_index_t src_fei;
209
210   src_fei = fib_table_lookup_exact_match (src_fib_index, &src_fib_prefix);
211
212   if (FIB_NODE_INDEX_INVALID == src_fei ||
213       !fib_entry_is_sourced (src_fei, FIB_SOURCE_LISP))
214     {
215       fib_table_entry_special_dpo_add (src_fib_index,
216                                        &src_fib_prefix,
217                                        FIB_SOURCE_LISP,
218                                        FIB_ENTRY_FLAG_EXCLUSIVE, src_dpo);
219     }
220 }
221
222 static fib_route_path_t *
223 lisp_gpe_mk_fib_paths (const lisp_fwd_path_t * paths)
224 {
225   const lisp_gpe_adjacency_t *ladj;
226   fib_route_path_t *rpaths = NULL;
227   u8 best_priority;
228   u32 ii;
229
230   vec_validate (rpaths, vec_len (paths) - 1);
231
232   best_priority = paths[0].priority;
233
234   vec_foreach_index (ii, paths)
235   {
236     if (paths[0].priority != best_priority)
237       break;
238
239     ladj = lisp_gpe_adjacency_get (paths[ii].lisp_adj);
240
241     ip_address_to_46 (&ladj->remote_rloc,
242                       &rpaths[ii].frp_addr, &rpaths[ii].frp_proto);
243
244     rpaths[ii].frp_sw_if_index = ladj->sw_if_index;
245     rpaths[ii].frp_weight = (paths[ii].weight ? paths[ii].weight : 1);
246   }
247
248   ASSERT (0 != vec_len (rpaths));
249
250   return (rpaths);
251 }
252
253 /**
254  * @brief Add route to IP4 or IP6 SRC FIB.
255  *
256  * Adds a route to in the LISP SRC FIB for the tunnel.
257  *
258  * @param[in]   src_fib_index   The index/ID of the SRC FIB
259  * @param[in]   src_prefix      Source IP prefix.
260  * @param[in]   paths           The paths from which to construct the
261  *                              load balance
262  */
263 static void
264 ip_src_fib_add_route (u32 src_fib_index,
265                       const ip_prefix_t * src_prefix,
266                       const lisp_fwd_path_t * paths)
267 {
268   fib_prefix_t src_fib_prefix;
269   fib_route_path_t *rpaths;
270
271   ip_prefix_to_fib_prefix (src_prefix, &src_fib_prefix);
272
273   rpaths = lisp_gpe_mk_fib_paths (paths);
274
275   fib_table_entry_update (src_fib_index,
276                           &src_fib_prefix,
277                           FIB_SOURCE_LISP, FIB_ENTRY_FLAG_NONE, rpaths);
278   vec_free (rpaths);
279 }
280
281
282 static void
283 create_fib_entries (lisp_gpe_fwd_entry_t * lfe)
284 {
285   dpo_proto_t dproto;
286
287   dproto = (ip_prefix_version (&lfe->key->rmt.ippref) == IP4 ?
288             DPO_PROTO_IP4 : DPO_PROTO_IP6);
289
290   lfe->src_fib_index = ip_dst_fib_add_route (lfe->eid_fib_index,
291                                              &lfe->key->rmt.ippref);
292
293   if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE == lfe->type)
294     {
295       dpo_id_t dpo = DPO_INVALID;
296
297       switch (lfe->action)
298         {
299         case LISP_NO_ACTION:
300           /* TODO update timers? */
301         case LISP_FORWARD_NATIVE:
302           /* TODO check if route/next-hop for eid exists in fib and add
303            * more specific for the eid with the next-hop found */
304         case LISP_SEND_MAP_REQUEST:
305           /* insert tunnel that always sends map-request */
306           dpo_copy (&dpo, lisp_cp_dpo_get (dproto));
307           break;
308         case LISP_DROP:
309           /* for drop fwd entries, just add route, no need to add encap tunnel */
310           dpo_copy (&dpo, drop_dpo_get (dproto));
311           break;
312         }
313       ip_src_fib_add_route_w_dpo (lfe->src_fib_index,
314                                   &lfe->key->lcl.ippref, &dpo);
315       dpo_reset (&dpo);
316     }
317   else
318     {
319       ip_src_fib_add_route (lfe->src_fib_index,
320                             &lfe->key->lcl.ippref, lfe->paths);
321     }
322 }
323
324 static void
325 delete_fib_entries (lisp_gpe_fwd_entry_t * lfe)
326 {
327   ip_src_dst_fib_del_route (lfe->src_fib_index,
328                             &lfe->key->lcl.ippref,
329                             lfe->eid_fib_index, &lfe->key->rmt.ippref);
330 }
331
332 static lisp_gpe_fwd_entry_t *
333 find_fwd_entry (lisp_gpe_main_t * lgm,
334                 vnet_lisp_gpe_add_del_fwd_entry_args_t * a,
335                 lisp_gpe_fwd_entry_key_t * key)
336 {
337   uword *p;
338
339   memset (key, 0, sizeof (*key));
340
341   if (GID_ADDR_IP_PREFIX == gid_address_type (&a->rmt_eid))
342     {
343       /*
344        * the ip version of the source is not set to ip6 when the
345        * source is all zeros. force it.
346        */
347       ip_prefix_version (&gid_address_ippref (&a->lcl_eid)) =
348         ip_prefix_version (&gid_address_ippref (&a->rmt_eid));
349     }
350
351   gid_to_dp_address (&a->rmt_eid, &key->rmt);
352   gid_to_dp_address (&a->lcl_eid, &key->lcl);
353   key->vni = a->vni;
354
355   p = hash_get_mem (lgm->lisp_gpe_fwd_entries, key);
356
357   if (NULL != p)
358     {
359       return (pool_elt_at_index (lgm->lisp_fwd_entry_pool, p[0]));
360     }
361   return (NULL);
362 }
363
364 static int
365 lisp_gpe_fwd_entry_path_sort (void *a1, void *a2)
366 {
367   lisp_fwd_path_t *p1 = a1, *p2 = a2;
368
369   return (p1->priority - p2->priority);
370 }
371
372 static void
373 lisp_gpe_fwd_entry_mk_paths (lisp_gpe_fwd_entry_t * lfe,
374                              vnet_lisp_gpe_add_del_fwd_entry_args_t * a)
375 {
376   lisp_fwd_path_t *path;
377   u32 index;
378
379   vec_validate (lfe->paths, vec_len (a->locator_pairs) - 1);
380
381   vec_foreach_index (index, a->locator_pairs)
382   {
383     path = &lfe->paths[index];
384
385     path->priority = a->locator_pairs[index].priority;
386     path->weight = a->locator_pairs[index].weight;
387
388     path->lisp_adj =
389       lisp_gpe_adjacency_find_or_create_and_lock (&a->locator_pairs
390                                                   [index],
391                                                   a->dp_table, lfe->key->vni);
392   }
393   vec_sort_with_function (lfe->paths, lisp_gpe_fwd_entry_path_sort);
394 }
395
396 /**
397  * @brief Add/Delete LISP IP forwarding entry.
398  *
399  * creation of forwarding entries for IP LISP overlay:
400  *
401  * @param[in]   lgm     Reference to @ref lisp_gpe_main_t.
402  * @param[in]   a       Parameters for building the forwarding entry.
403  *
404  * @return 0 on success.
405  */
406 static int
407 add_ip_fwd_entry (lisp_gpe_main_t * lgm,
408                   vnet_lisp_gpe_add_del_fwd_entry_args_t * a)
409 {
410   lisp_gpe_fwd_entry_key_t key;
411   lisp_gpe_fwd_entry_t *lfe;
412   fib_protocol_t fproto;
413
414   lfe = find_fwd_entry (lgm, a, &key);
415
416   if (NULL != lfe)
417     /* don't support updates */
418     return VNET_API_ERROR_INVALID_VALUE;
419
420   pool_get (lgm->lisp_fwd_entry_pool, lfe);
421   memset (lfe, 0, sizeof (*lfe));
422   lfe->key = clib_mem_alloc (sizeof (key));
423   memcpy (lfe->key, &key, sizeof (key));
424
425   hash_set_mem (lgm->lisp_gpe_fwd_entries, lfe->key,
426                 lfe - lgm->lisp_fwd_entry_pool);
427
428   fproto = (IP4 == ip_prefix_version (&fid_addr_ippref (&lfe->key->rmt)) ?
429             FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6);
430
431   lfe->type = (a->is_negative ?
432                LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE :
433                LISP_GPE_FWD_ENTRY_TYPE_NORMAL);
434   lfe->tenant = lisp_gpe_tenant_find_or_create (lfe->key->vni);
435   lfe->eid_table_id = a->table_id;
436   lfe->eid_fib_index = fib_table_find_or_create_and_lock (fproto,
437                                                           lfe->eid_table_id);
438
439   if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type)
440     {
441       lisp_gpe_fwd_entry_mk_paths (lfe, a);
442     }
443
444   create_fib_entries (lfe);
445
446   return (0);
447 }
448
449 static void
450 del_ip_fwd_entry_i (lisp_gpe_main_t * lgm, lisp_gpe_fwd_entry_t * lfe)
451 {
452   lisp_fwd_path_t *path;
453   fib_protocol_t fproto;
454
455   vec_foreach (path, lfe->paths)
456   {
457     lisp_gpe_adjacency_unlock (path->lisp_adj);
458   }
459
460   delete_fib_entries (lfe);
461
462   fproto = (IP4 == ip_prefix_version (&fid_addr_ippref (&lfe->key->rmt)) ?
463             FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6);
464   fib_table_unlock (lfe->eid_fib_index, fproto);
465
466   hash_unset_mem (lgm->lisp_gpe_fwd_entries, lfe->key);
467   clib_mem_free (lfe->key);
468   pool_put (lgm->lisp_fwd_entry_pool, lfe);
469 }
470
471 /**
472  * @brief Add/Delete LISP IP forwarding entry.
473  *
474  * removal of forwarding entries for IP LISP overlay:
475  *
476  * @param[in]   lgm     Reference to @ref lisp_gpe_main_t.
477  * @param[in]   a       Parameters for building the forwarding entry.
478  *
479  * @return 0 on success.
480  */
481 static int
482 del_ip_fwd_entry (lisp_gpe_main_t * lgm,
483                   vnet_lisp_gpe_add_del_fwd_entry_args_t * a)
484 {
485   lisp_gpe_fwd_entry_key_t key;
486   lisp_gpe_fwd_entry_t *lfe;
487
488   lfe = find_fwd_entry (lgm, a, &key);
489
490   if (NULL == lfe)
491     /* no such entry */
492     return VNET_API_ERROR_INVALID_VALUE;
493
494   del_ip_fwd_entry_i (lgm, lfe);
495
496   return (0);
497 }
498
499 static void
500 make_mac_fib_key (BVT (clib_bihash_kv) * kv, u16 bd_index, u8 src_mac[6],
501                   u8 dst_mac[6])
502 {
503   kv->key[0] = (((u64) bd_index) << 48) | mac_to_u64 (dst_mac);
504   kv->key[1] = mac_to_u64 (src_mac);
505   kv->key[2] = 0;
506 }
507
508 /**
509  * @brief Lookup L2 SD FIB entry
510  *
511  * Does a vni + dest + source lookup in the L2 LISP FIB. If the lookup fails
512  * it tries a second time with source set to 0 (i.e., a simple dest lookup).
513  *
514  * @param[in]   lgm             Reference to @ref lisp_gpe_main_t.
515  * @param[in]   bd_index        Bridge domain index.
516  * @param[in]   src_mac         Source mac address.
517  * @param[in]   dst_mac         Destination mac address.
518  *
519  * @return index of mapping matching the lookup key.
520  */
521 index_t
522 lisp_l2_fib_lookup (lisp_gpe_main_t * lgm, u16 bd_index, u8 src_mac[6],
523                     u8 dst_mac[6])
524 {
525   int rv;
526   BVT (clib_bihash_kv) kv, value;
527
528   make_mac_fib_key (&kv, bd_index, src_mac, dst_mac);
529   rv = BV (clib_bihash_search_inline_2) (&lgm->l2_fib, &kv, &value);
530
531   /* no match, try with src 0, catch all for dst */
532   if (rv != 0)
533     {
534       kv.key[1] = 0;
535       rv = BV (clib_bihash_search_inline_2) (&lgm->l2_fib, &kv, &value);
536       if (rv == 0)
537         return value.value;
538     }
539   else
540     return value.value;
541
542   return lisp_gpe_main.l2_lb_cp_lkup.dpoi_index;
543 }
544
545 /**
546  * @brief Add/del L2 SD FIB entry
547  *
548  * Inserts value in L2 FIB keyed by vni + dest + source. If entry is
549  * overwritten the associated value is returned.
550  *
551  * @param[in]   lgm             Reference to @ref lisp_gpe_main_t.
552  * @param[in]   bd_index        Bridge domain index.
553  * @param[in]   src_mac         Source mac address.
554  * @param[in]   dst_mac         Destination mac address.
555  * @param[in]   val             Value to add.
556  * @param[in]   is_add          Add/del flag.
557  *
558  * @return ~0 or value of overwritten entry.
559  */
560 static u32
561 lisp_l2_fib_add_del_entry (u16 bd_index, u8 src_mac[6],
562                            u8 dst_mac[6], const dpo_id_t * dpo, u8 is_add)
563 {
564   lisp_gpe_main_t *lgm = &lisp_gpe_main;
565   BVT (clib_bihash_kv) kv, value;
566   u32 old_val = ~0;
567
568   make_mac_fib_key (&kv, bd_index, src_mac, dst_mac);
569
570   if (BV (clib_bihash_search) (&lgm->l2_fib, &kv, &value) == 0)
571     old_val = value.value;
572
573   if (!is_add)
574     BV (clib_bihash_add_del) (&lgm->l2_fib, &kv, 0 /* is_add */ );
575   else
576     {
577       kv.value = dpo->dpoi_index;
578       BV (clib_bihash_add_del) (&lgm->l2_fib, &kv, 1 /* is_add */ );
579     }
580   return old_val;
581 }
582
583 #define L2_FIB_DEFAULT_HASH_NUM_BUCKETS (64 * 1024)
584 #define L2_FIB_DEFAULT_HASH_MEMORY_SIZE (32<<20)
585
586 static void
587 l2_fib_init (lisp_gpe_main_t * lgm)
588 {
589   index_t lbi;
590
591   BV (clib_bihash_init) (&lgm->l2_fib, "l2 fib",
592                          1 << max_log2 (L2_FIB_DEFAULT_HASH_NUM_BUCKETS),
593                          L2_FIB_DEFAULT_HASH_MEMORY_SIZE);
594
595   /*
596    * the result from a 'miss' in a L2 Table
597    */
598   lbi = load_balance_create (1, DPO_PROTO_ETHERNET, 0);
599   load_balance_set_bucket (lbi, 0, lisp_cp_dpo_get (DPO_PROTO_ETHERNET));
600
601   dpo_set (&lgm->l2_lb_cp_lkup, DPO_LOAD_BALANCE, DPO_PROTO_ETHERNET, lbi);
602 }
603
604 static void
605 del_l2_fwd_entry_i (lisp_gpe_main_t * lgm, lisp_gpe_fwd_entry_t * lfe)
606 {
607   lisp_fwd_path_t *path;
608
609   if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type)
610     {
611       vec_foreach (path, lfe->paths)
612       {
613         lisp_gpe_adjacency_unlock (path->lisp_adj);
614       }
615       fib_path_list_child_remove (lfe->l2.path_list_index,
616                                   lfe->l2.child_index);
617     }
618
619   lisp_l2_fib_add_del_entry (lfe->l2.eid_bd_index,
620                              fid_addr_mac (&lfe->key->lcl),
621                              fid_addr_mac (&lfe->key->rmt), NULL, 0);
622
623   hash_unset_mem (lgm->lisp_gpe_fwd_entries, lfe->key);
624   clib_mem_free (lfe->key);
625   pool_put (lgm->lisp_fwd_entry_pool, lfe);
626 }
627
628 /**
629  * @brief Delete LISP L2 forwarding entry.
630  *
631  * Coordinates the removal of forwarding entries for L2 LISP overlay:
632  *
633  * @param[in]   lgm     Reference to @ref lisp_gpe_main_t.
634  * @param[in]   a       Parameters for building the forwarding entry.
635  *
636  * @return 0 on success.
637  */
638 static int
639 del_l2_fwd_entry (lisp_gpe_main_t * lgm,
640                   vnet_lisp_gpe_add_del_fwd_entry_args_t * a)
641 {
642   lisp_gpe_fwd_entry_key_t key;
643   lisp_gpe_fwd_entry_t *lfe;
644
645   lfe = find_fwd_entry (lgm, a, &key);
646
647   if (NULL == lfe)
648     return VNET_API_ERROR_INVALID_VALUE;
649
650   del_l2_fwd_entry_i (lgm, lfe);
651
652   return (0);
653 }
654
655 /**
656  * @brief Construct and insert the forwarding information used by an L2 entry
657  */
658 static void
659 lisp_gpe_l2_update_fwding (lisp_gpe_fwd_entry_t * lfe)
660 {
661   lisp_gpe_main_t *lgm = &lisp_gpe_main;
662   dpo_id_t dpo = DPO_INVALID;
663
664   if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type)
665     {
666       fib_path_list_contribute_forwarding (lfe->l2.path_list_index,
667                                            FIB_FORW_CHAIN_TYPE_ETHERNET,
668                                            &lfe->l2.dpo);
669       dpo_copy (&dpo, &lfe->l2.dpo);
670     }
671   else
672     {
673       switch (lfe->action)
674         {
675         case SEND_MAP_REQUEST:
676           dpo_copy (&dpo, &lgm->l2_lb_cp_lkup);
677           break;
678         case NO_ACTION:
679         case FORWARD_NATIVE:
680         case DROP:
681           dpo_copy (&dpo, drop_dpo_get (DPO_PROTO_ETHERNET));
682         }
683     }
684
685   /* add entry to l2 lisp fib */
686   lisp_l2_fib_add_del_entry (lfe->l2.eid_bd_index,
687                              fid_addr_mac (&lfe->key->lcl),
688                              fid_addr_mac (&lfe->key->rmt), &dpo, 1);
689
690   dpo_reset (&dpo);
691 }
692
693 /**
694  * @brief Add LISP L2 forwarding entry.
695  *
696  * Coordinates the creation of forwarding entries for L2 LISP overlay:
697  * creates lisp-gpe tunnel and injects new entry in Source/Dest L2 FIB.
698  *
699  * @param[in]   lgm     Reference to @ref lisp_gpe_main_t.
700  * @param[in]   a       Parameters for building the forwarding entry.
701  *
702  * @return 0 on success.
703  */
704 static int
705 add_l2_fwd_entry (lisp_gpe_main_t * lgm,
706                   vnet_lisp_gpe_add_del_fwd_entry_args_t * a)
707 {
708   lisp_gpe_fwd_entry_key_t key;
709   bd_main_t *bdm = &bd_main;
710   lisp_gpe_fwd_entry_t *lfe;
711   uword *bd_indexp;
712
713   bd_indexp = hash_get (bdm->bd_index_by_bd_id, a->bd_id);
714   if (!bd_indexp)
715     {
716       clib_warning ("bridge domain %d doesn't exist", a->bd_id);
717       return -1;
718     }
719
720   lfe = find_fwd_entry (lgm, a, &key);
721
722   if (NULL != lfe)
723     /* don't support updates */
724     return VNET_API_ERROR_INVALID_VALUE;
725
726   pool_get (lgm->lisp_fwd_entry_pool, lfe);
727   memset (lfe, 0, sizeof (*lfe));
728   lfe->key = clib_mem_alloc (sizeof (key));
729   memcpy (lfe->key, &key, sizeof (key));
730
731   hash_set_mem (lgm->lisp_gpe_fwd_entries, lfe->key,
732                 lfe - lgm->lisp_fwd_entry_pool);
733
734   lfe->type = (a->is_negative ?
735                LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE :
736                LISP_GPE_FWD_ENTRY_TYPE_NORMAL);
737   lfe->l2.eid_bd_id = a->bd_id;
738   lfe->l2.eid_bd_index = bd_indexp[0];
739   lfe->tenant = lisp_gpe_tenant_find_or_create (lfe->key->vni);
740
741   if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type)
742     {
743       fib_route_path_t *rpaths;
744
745       /*
746        * Make the sorted array of LISP paths with their resp. adjacency
747        */
748       lisp_gpe_fwd_entry_mk_paths (lfe, a);
749
750       /*
751        * From the LISP paths, construct a FIB path list that will
752        * contribute a load-balance.
753        */
754       rpaths = lisp_gpe_mk_fib_paths (lfe->paths);
755
756       lfe->l2.path_list_index =
757         fib_path_list_create (FIB_PATH_LIST_FLAG_NONE, rpaths);
758
759       /*
760        * become a child of the path-list so we receive updates when
761        * its forwarding state changes. this includes an implicit lock.
762        */
763       lfe->l2.child_index =
764         fib_path_list_child_add (lfe->l2.path_list_index,
765                                  FIB_NODE_TYPE_LISP_GPE_FWD_ENTRY,
766                                  lfe - lgm->lisp_fwd_entry_pool);
767     }
768   else
769     {
770       lfe->action = a->action;
771     }
772
773   lisp_gpe_l2_update_fwding (lfe);
774
775   return 0;
776 }
777
778 /**
779  * @brief Lookup NSH SD FIB entry
780  *
781  * Does an SPI+SI lookup in the NSH LISP FIB.
782  *
783  * @param[in]   lgm             Reference to @ref lisp_gpe_main_t.
784  * @param[in]   spi_si          SPI + SI.
785  *
786  * @return next node index.
787  */
788 const dpo_id_t *
789 lisp_nsh_fib_lookup (lisp_gpe_main_t * lgm, u32 spi_si_net_order)
790 {
791   int rv;
792   BVT (clib_bihash_kv) kv, value;
793
794   memset (&kv, 0, sizeof (kv));
795   kv.key[0] = spi_si_net_order;
796   rv = BV (clib_bihash_search_inline_2) (&lgm->nsh_fib, &kv, &value);
797
798   if (rv != 0)
799     {
800       return lgm->nsh_cp_lkup;
801     }
802   else
803     {
804       lisp_gpe_fwd_entry_t *lfe;
805       lfe = pool_elt_at_index (lgm->lisp_fwd_entry_pool, value.value);
806       return &lfe->nsh.choice;
807     }
808 }
809
810 /**
811  * @brief Add/del NSH FIB entry
812  *
813  * Inserts value in NSH FIB keyed by SPI+SI. If entry is
814  * overwritten the associated value is returned.
815  *
816  * @param[in]   lgm             Reference to @ref lisp_gpe_main_t.
817  * @param[in]   spi_si          SPI + SI.
818  * @param[in]   dpo             Load balanced mapped to SPI + SI
819  *
820  * @return ~0 or value of overwritten entry.
821  */
822 static u32
823 lisp_nsh_fib_add_del_entry (u32 spi_si_host_order, u32 lfei, u8 is_add)
824 {
825   lisp_gpe_main_t *lgm = &lisp_gpe_main;
826   BVT (clib_bihash_kv) kv, value;
827   u32 old_val = ~0;
828
829   memset (&kv, 0, sizeof (kv));
830   kv.key[0] = clib_host_to_net_u32 (spi_si_host_order);
831   kv.value = 0ULL;
832
833   if (BV (clib_bihash_search) (&lgm->nsh_fib, &kv, &value) == 0)
834     old_val = value.value;
835
836   if (!is_add)
837     BV (clib_bihash_add_del) (&lgm->nsh_fib, &kv, 0 /* is_add */ );
838   else
839     {
840       kv.value = lfei;
841       BV (clib_bihash_add_del) (&lgm->nsh_fib, &kv, 1 /* is_add */ );
842     }
843   return old_val;
844 }
845
846 #define NSH_FIB_DEFAULT_HASH_NUM_BUCKETS (64 * 1024)
847 #define NSH_FIB_DEFAULT_HASH_MEMORY_SIZE (32<<20)
848
849 static void
850 nsh_fib_init (lisp_gpe_main_t * lgm)
851 {
852   BV (clib_bihash_init) (&lgm->nsh_fib, "nsh fib",
853                          1 << max_log2 (NSH_FIB_DEFAULT_HASH_NUM_BUCKETS),
854                          NSH_FIB_DEFAULT_HASH_MEMORY_SIZE);
855
856   /*
857    * the result from a 'miss' in a NSH Table
858    */
859   lgm->nsh_cp_lkup = lisp_cp_dpo_get (DPO_PROTO_NSH);
860 }
861
862 static void
863 del_nsh_fwd_entry_i (lisp_gpe_main_t * lgm, lisp_gpe_fwd_entry_t * lfe)
864 {
865   lisp_fwd_path_t *path;
866
867   if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type)
868     {
869       vec_foreach (path, lfe->paths)
870       {
871         lisp_gpe_adjacency_unlock (path->lisp_adj);
872       }
873       fib_path_list_child_remove (lfe->nsh.path_list_index,
874                                   lfe->nsh.child_index);
875       dpo_reset (&lfe->nsh.choice);
876     }
877
878   lisp_nsh_fib_add_del_entry (fid_addr_nsh (&lfe->key->rmt), (u32) ~ 0, 0);
879
880   hash_unset_mem (lgm->lisp_gpe_fwd_entries, lfe->key);
881   clib_mem_free (lfe->key);
882   pool_put (lgm->lisp_fwd_entry_pool, lfe);
883 }
884
885 /**
886  * @brief Delete LISP NSH forwarding entry.
887  *
888  * Coordinates the removal of forwarding entries for NSH LISP overlay:
889  *
890  * @param[in]   lgm     Reference to @ref lisp_gpe_main_t.
891  * @param[in]   a       Parameters for building the forwarding entry.
892  *
893  * @return 0 on success.
894  */
895 static int
896 del_nsh_fwd_entry (lisp_gpe_main_t * lgm,
897                    vnet_lisp_gpe_add_del_fwd_entry_args_t * a)
898 {
899   lisp_gpe_fwd_entry_key_t key;
900   lisp_gpe_fwd_entry_t *lfe;
901
902   lfe = find_fwd_entry (lgm, a, &key);
903
904   if (NULL == lfe)
905     return VNET_API_ERROR_INVALID_VALUE;
906
907   del_nsh_fwd_entry_i (lgm, lfe);
908
909   return (0);
910 }
911
912 /**
913  * @brief Construct and insert the forwarding information used by an NSH entry
914  */
915 static void
916 lisp_gpe_nsh_update_fwding (lisp_gpe_fwd_entry_t * lfe)
917 {
918   lisp_gpe_main_t *lgm = vnet_lisp_gpe_get_main ();
919   dpo_id_t dpo = DPO_INVALID;
920   vnet_hw_interface_t *hi;
921   uword *hip;
922
923   if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type)
924     {
925       fib_path_list_contribute_forwarding (lfe->nsh.path_list_index,
926                                            FIB_FORW_CHAIN_TYPE_NSH,
927                                            &lfe->nsh.dpo);
928
929       /*
930        * LISP encap is always the same for this SPI+SI so we do that hash now
931        * and stack on the choice.
932        */
933       if (DPO_LOAD_BALANCE == lfe->nsh.dpo.dpoi_type)
934         {
935           const dpo_id_t *tmp;
936           const load_balance_t *lb;
937           int hash;
938
939           lb = load_balance_get (lfe->nsh.dpo.dpoi_index);
940           hash = fid_addr_nsh (&lfe->key->rmt) % lb->lb_n_buckets;
941           tmp =
942             load_balance_get_bucket_i (lb, hash & lb->lb_n_buckets_minus_1);
943
944           dpo_copy (&dpo, tmp);
945         }
946     }
947   else
948     {
949       switch (lfe->action)
950         {
951         case SEND_MAP_REQUEST:
952           dpo_copy (&dpo, lgm->nsh_cp_lkup);
953           break;
954         case NO_ACTION:
955         case FORWARD_NATIVE:
956         case DROP:
957           dpo_copy (&dpo, drop_dpo_get (DPO_PROTO_NSH));
958         }
959     }
960
961   /* We have only one nsh-lisp interface (no NSH virtualization) */
962   hip = hash_get (lgm->nsh_ifaces.hw_if_index_by_dp_table, 0);
963   if (hip)
964     {
965       hi = vnet_get_hw_interface (lgm->vnet_main, hip[0]);
966       dpo_stack_from_node (hi->tx_node_index, &lfe->nsh.choice, &dpo);
967     }
968   /* add entry to nsh lisp fib */
969   lisp_nsh_fib_add_del_entry (fid_addr_nsh (&lfe->key->rmt),
970                               lfe - lgm->lisp_fwd_entry_pool, 1);
971   dpo_reset (&dpo);
972
973 }
974
975 /**
976  * @brief Add LISP NSH forwarding entry.
977  *
978  * Coordinates the creation of forwarding entries for L2 LISP overlay:
979  * creates lisp-gpe tunnel and injects new entry in Source/Dest L2 FIB.
980  *
981  * @param[in]   lgm     Reference to @ref lisp_gpe_main_t.
982  * @param[in]   a       Parameters for building the forwarding entry.
983  *
984  * @return 0 on success.
985  */
986 static int
987 add_nsh_fwd_entry (lisp_gpe_main_t * lgm,
988                    vnet_lisp_gpe_add_del_fwd_entry_args_t * a)
989 {
990   lisp_gpe_fwd_entry_key_t key;
991   lisp_gpe_fwd_entry_t *lfe;
992
993   lfe = find_fwd_entry (lgm, a, &key);
994
995   if (NULL != lfe)
996     /* don't support updates */
997     return VNET_API_ERROR_INVALID_VALUE;
998
999   pool_get (lgm->lisp_fwd_entry_pool, lfe);
1000   memset (lfe, 0, sizeof (*lfe));
1001   lfe->key = clib_mem_alloc (sizeof (key));
1002   memcpy (lfe->key, &key, sizeof (key));
1003
1004   hash_set_mem (lgm->lisp_gpe_fwd_entries, lfe->key,
1005                 lfe - lgm->lisp_fwd_entry_pool);
1006
1007   lfe->type = (a->is_negative ?
1008                LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE :
1009                LISP_GPE_FWD_ENTRY_TYPE_NORMAL);
1010   lfe->tenant = 0;
1011
1012   if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type)
1013     {
1014       fib_route_path_t *rpaths;
1015
1016       /*
1017        * Make the sorted array of LISP paths with their resp. adjacency
1018        */
1019       lisp_gpe_fwd_entry_mk_paths (lfe, a);
1020
1021       /*
1022        * From the LISP paths, construct a FIB path list that will
1023        * contribute a load-balance.
1024        */
1025       rpaths = lisp_gpe_mk_fib_paths (lfe->paths);
1026
1027       lfe->nsh.path_list_index =
1028         fib_path_list_create (FIB_PATH_LIST_FLAG_NONE, rpaths);
1029
1030       /*
1031        * become a child of the path-list so we receive updates when
1032        * its forwarding state changes. this includes an implicit lock.
1033        */
1034       lfe->nsh.child_index =
1035         fib_path_list_child_add (lfe->nsh.path_list_index,
1036                                  FIB_NODE_TYPE_LISP_GPE_FWD_ENTRY,
1037                                  lfe - lgm->lisp_fwd_entry_pool);
1038     }
1039   else
1040     {
1041       lfe->action = a->action;
1042     }
1043
1044   lisp_gpe_nsh_update_fwding (lfe);
1045
1046   return 0;
1047 }
1048
1049 /**
1050  * @brief conver from the embedded fib_node_t struct to the LSIP entry
1051  */
1052 static lisp_gpe_fwd_entry_t *
1053 lisp_gpe_fwd_entry_from_fib_node (fib_node_t * node)
1054 {
1055   return ((lisp_gpe_fwd_entry_t *) (((char *) node) -
1056                                     STRUCT_OFFSET_OF (lisp_gpe_fwd_entry_t,
1057                                                       node)));
1058 }
1059
1060 /**
1061  * @brief Function invoked during a backwalk of the FIB graph
1062  */
1063 static fib_node_back_walk_rc_t
1064 lisp_gpe_fib_node_back_walk (fib_node_t * node,
1065                              fib_node_back_walk_ctx_t * ctx)
1066 {
1067   lisp_gpe_fwd_entry_t *lfe = lisp_gpe_fwd_entry_from_fib_node (node);
1068
1069   if (fid_addr_type (&lfe->key->rmt) == FID_ADDR_MAC)
1070     lisp_gpe_l2_update_fwding (lfe);
1071   else if (fid_addr_type (&lfe->key->rmt) == FID_ADDR_NSH)
1072     lisp_gpe_nsh_update_fwding (lfe);
1073
1074   return (FIB_NODE_BACK_WALK_CONTINUE);
1075 }
1076
1077 /**
1078  * @brief Get a fib_node_t struct from the index of a LISP fwd entry
1079  */
1080 static fib_node_t *
1081 lisp_gpe_fwd_entry_get_fib_node (fib_node_index_t index)
1082 {
1083   lisp_gpe_main_t *lgm = &lisp_gpe_main;
1084   lisp_gpe_fwd_entry_t *lfe;
1085
1086   lfe = pool_elt_at_index (lgm->lisp_fwd_entry_pool, index);
1087
1088   return (&(lfe->node));
1089 }
1090
1091 /**
1092  * @brief An indication from the graph that the last lock has gone
1093  */
1094 static void
1095 lisp_gpe_fwd_entry_fib_node_last_lock_gone (fib_node_t * node)
1096 {
1097   /* We don't manage the locks of the LISP objects via the graph, since
1098    * this object has no children. so this is a no-op. */
1099 }
1100
1101 /**
1102  * @brief Virtual function table to register with FIB for the LISP type
1103  */
1104 const static fib_node_vft_t lisp_fwd_vft = {
1105   .fnv_get = lisp_gpe_fwd_entry_get_fib_node,
1106   .fnv_last_lock = lisp_gpe_fwd_entry_fib_node_last_lock_gone,
1107   .fnv_back_walk = lisp_gpe_fib_node_back_walk,
1108 };
1109
1110 /**
1111  * @brief Forwarding entry create/remove dispatcher.
1112  *
1113  * Calls l2 or l3 forwarding entry add/del function based on input data.
1114  *
1115  * @param[in]   a       Forwarding entry parameters.
1116  * @param[out]  hw_if_indexp    NOT USED
1117  *
1118  * @return 0 on success.
1119  */
1120 int
1121 vnet_lisp_gpe_add_del_fwd_entry (vnet_lisp_gpe_add_del_fwd_entry_args_t * a,
1122                                  u32 * hw_if_indexp)
1123 {
1124   lisp_gpe_main_t *lgm = &lisp_gpe_main;
1125   u8 type;
1126
1127   if (vnet_lisp_gpe_enable_disable_status () == 0)
1128     {
1129       clib_warning ("LISP is disabled!");
1130       return VNET_API_ERROR_LISP_DISABLED;
1131     }
1132
1133   type = gid_address_type (&a->rmt_eid);
1134   switch (type)
1135     {
1136     case GID_ADDR_IP_PREFIX:
1137       if (a->is_add)
1138         return add_ip_fwd_entry (lgm, a);
1139       else
1140         return del_ip_fwd_entry (lgm, a);
1141       break;
1142     case GID_ADDR_MAC:
1143       if (a->is_add)
1144         return add_l2_fwd_entry (lgm, a);
1145       else
1146         return del_l2_fwd_entry (lgm, a);
1147     case GID_ADDR_NSH:
1148       if (a->is_add)
1149         return add_nsh_fwd_entry (lgm, a);
1150       else
1151         return del_nsh_fwd_entry (lgm, a);
1152     default:
1153       clib_warning ("Forwarding entries for type %d not supported!", type);
1154       return -1;
1155     }
1156 }
1157
1158 void
1159 vnet_lisp_flush_stats (void)
1160 {
1161   lisp_gpe_main_t *lgm = vnet_lisp_gpe_get_main ();
1162   lisp_stats_t *stat;
1163
1164   /* *INDENT-OFF* */
1165   pool_foreach (stat, lgm->lisp_stats_pool,
1166   {
1167     stat->pkt_count = 0;
1168     stat->bytes = 0;
1169   });
1170   /* *INDENT-ON* */
1171 }
1172
1173 static void
1174 lisp_del_adj_stats (lisp_gpe_main_t * lgm, u32 fwd_entry_index, u32 ti)
1175 {
1176   hash_pair_t *hp;
1177   lisp_stats_key_t key;
1178   void *key_copy;
1179   uword *p;
1180   lisp_stats_t *s;
1181
1182   memset (&key, 0, sizeof (key));
1183   key.fwd_entry_index = fwd_entry_index;
1184   key.tunnel_index = ti;
1185
1186   p = hash_get_mem (lgm->lisp_stats_index_by_key, &key);
1187   if (p)
1188     {
1189       s = pool_elt_at_index (lgm->lisp_stats_pool, p[0]);
1190       hp = hash_get_pair (lgm->lisp_stats_index_by_key, &key);
1191       key_copy = (void *) (hp->key);
1192       hash_unset_mem (lgm->lisp_stats_index_by_key, &key);
1193       clib_mem_free (key_copy);
1194       pool_put (lgm->lisp_stats_pool, s);
1195     }
1196 }
1197
1198 void
1199 vnet_lisp_del_fwd_stats (vnet_lisp_gpe_add_del_fwd_entry_args_t * a,
1200                          u32 fwd_entry_index)
1201 {
1202   lisp_gpe_main_t *lgm = &lisp_gpe_main;
1203   lisp_gpe_fwd_entry_key_t fe_key;
1204   lisp_gpe_fwd_entry_t *lfe;
1205   lisp_fwd_path_t *path;
1206   const lisp_gpe_adjacency_t *ladj;
1207
1208   lfe = find_fwd_entry (lgm, a, &fe_key);
1209   if (!lfe)
1210     return;
1211
1212   if (LISP_GPE_FWD_ENTRY_TYPE_NORMAL != lfe->type)
1213     return;
1214
1215   vec_foreach (path, lfe->paths)
1216   {
1217     ladj = lisp_gpe_adjacency_get (path->lisp_adj);
1218     lisp_del_adj_stats (lgm, fwd_entry_index, ladj->tunnel_index);
1219   }
1220 }
1221
1222 /**
1223  * @brief Flush all the forwrding entries
1224  */
1225 void
1226 vnet_lisp_gpe_fwd_entry_flush (void)
1227 {
1228   lisp_gpe_main_t *lgm = &lisp_gpe_main;
1229   lisp_gpe_fwd_entry_t *lfe;
1230
1231   /* *INDENT-OFF* */
1232   pool_foreach (lfe, lgm->lisp_fwd_entry_pool,
1233   ({
1234     switch (fid_addr_type(&lfe->key->rmt))
1235       {
1236       case FID_ADDR_MAC:
1237         del_l2_fwd_entry_i (lgm, lfe);
1238         break;
1239       case FID_ADDR_IP_PREF:
1240         del_ip_fwd_entry_i (lgm, lfe);
1241         break;
1242       case FID_ADDR_NSH:
1243         del_nsh_fwd_entry_i (lgm, lfe);
1244         break;
1245       }
1246   }));
1247   /* *INDENT-ON* */
1248 }
1249
1250 static u8 *
1251 format_lisp_fwd_path (u8 * s, va_list ap)
1252 {
1253   lisp_fwd_path_t *lfp = va_arg (ap, lisp_fwd_path_t *);
1254
1255   s = format (s, "weight:%d ", lfp->weight);
1256   s = format (s, "adj:[%U]\n",
1257               format_lisp_gpe_adjacency,
1258               lisp_gpe_adjacency_get (lfp->lisp_adj),
1259               LISP_GPE_ADJ_FORMAT_FLAG_NONE);
1260
1261   return (s);
1262 }
1263
1264 typedef enum lisp_gpe_fwd_entry_format_flag_t_
1265 {
1266   LISP_GPE_FWD_ENTRY_FORMAT_NONE = (0 << 0),
1267   LISP_GPE_FWD_ENTRY_FORMAT_DETAIL = (1 << 1),
1268 } lisp_gpe_fwd_entry_format_flag_t;
1269
1270
1271 static u8 *
1272 format_lisp_gpe_fwd_entry (u8 * s, va_list ap)
1273 {
1274   lisp_gpe_main_t *lgm = &lisp_gpe_main;
1275   lisp_gpe_fwd_entry_t *lfe = va_arg (ap, lisp_gpe_fwd_entry_t *);
1276   lisp_gpe_fwd_entry_format_flag_t flags =
1277     va_arg (ap, lisp_gpe_fwd_entry_format_flag_t);
1278
1279   s = format (s, "VNI:%d VRF:%d EID: %U -> %U  [index:%d]",
1280               lfe->key->vni, lfe->eid_table_id,
1281               format_fid_address, &lfe->key->lcl,
1282               format_fid_address, &lfe->key->rmt,
1283               lfe - lgm->lisp_fwd_entry_pool);
1284
1285   if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE == lfe->type)
1286     {
1287       s = format (s, "\n Negative - action:%U",
1288                   format_negative_mapping_action, lfe->action);
1289     }
1290   else
1291     {
1292       lisp_fwd_path_t *path;
1293
1294       s = format (s, "\n via:");
1295       vec_foreach (path, lfe->paths)
1296       {
1297         s = format (s, "\n  %U", format_lisp_fwd_path, path);
1298       }
1299     }
1300
1301   if (flags & LISP_GPE_FWD_ENTRY_FORMAT_DETAIL)
1302     {
1303       switch (fid_addr_type (&lfe->key->rmt))
1304         {
1305         case FID_ADDR_MAC:
1306           s = format (s, " fib-path-list:%d\n", lfe->l2.path_list_index);
1307           s = format (s, " dpo:%U\n", format_dpo_id, &lfe->l2.dpo, 0);
1308           break;
1309         case FID_ADDR_NSH:
1310           s = format (s, " fib-path-list:%d\n", lfe->nsh.path_list_index);
1311           s = format (s, " dpo:%U\n", format_dpo_id, &lfe->nsh.dpo, 0);
1312           break;
1313         case FID_ADDR_IP_PREF:
1314           break;
1315         }
1316     }
1317
1318   return (s);
1319 }
1320
1321 static clib_error_t *
1322 lisp_gpe_fwd_entry_show (vlib_main_t * vm,
1323                          unformat_input_t * input, vlib_cli_command_t * cmd)
1324 {
1325   lisp_gpe_main_t *lgm = &lisp_gpe_main;
1326   lisp_gpe_fwd_entry_t *lfe;
1327   index_t index;
1328   u32 vni = ~0;
1329
1330   if (unformat (input, "vni %d", &vni))
1331     ;
1332   else if (unformat (input, "%d", &index))
1333     {
1334       if (!pool_is_free_index (lgm->lisp_fwd_entry_pool, index))
1335         {
1336           lfe = pool_elt_at_index (lgm->lisp_fwd_entry_pool, index);
1337
1338           vlib_cli_output (vm, "[%d@] %U",
1339                            index,
1340                            format_lisp_gpe_fwd_entry, lfe,
1341                            LISP_GPE_FWD_ENTRY_FORMAT_DETAIL);
1342         }
1343       else
1344         {
1345           vlib_cli_output (vm, "entry %d invalid", index);
1346         }
1347
1348       return (NULL);
1349     }
1350
1351   /* *INDENT-OFF* */
1352   pool_foreach (lfe, lgm->lisp_fwd_entry_pool,
1353   ({
1354     if ((vni == ~0) ||
1355         (lfe->key->vni == vni))
1356       vlib_cli_output (vm, "%U", format_lisp_gpe_fwd_entry, lfe,
1357                        LISP_GPE_FWD_ENTRY_FORMAT_NONE);
1358   }));
1359   /* *INDENT-ON* */
1360
1361   return (NULL);
1362 }
1363
1364 /* *INDENT-OFF* */
1365 VLIB_CLI_COMMAND (lisp_gpe_fwd_entry_show_command, static) = {
1366   .path = "show gpe entry",
1367   .short_help = "show gpe entry vni <vni> vrf <vrf> [leid <leid>] reid <reid>",
1368   .function = lisp_gpe_fwd_entry_show,
1369 };
1370 /* *INDENT-ON* */
1371
1372 clib_error_t *
1373 lisp_gpe_fwd_entry_init (vlib_main_t * vm)
1374 {
1375   lisp_gpe_main_t *lgm = &lisp_gpe_main;
1376   clib_error_t *error = NULL;
1377
1378   if ((error = vlib_call_init_function (vm, lisp_cp_dpo_module_init)))
1379     return (error);
1380
1381   l2_fib_init (lgm);
1382   nsh_fib_init (lgm);
1383
1384   fib_node_register_type (FIB_NODE_TYPE_LISP_GPE_FWD_ENTRY, &lisp_fwd_vft);
1385
1386   return (error);
1387 }
1388
1389 lisp_api_gpe_fwd_entry_t *
1390 vnet_lisp_gpe_fwd_entries_get_by_vni (u32 vni)
1391 {
1392   lisp_gpe_main_t *lgm = &lisp_gpe_main;
1393   lisp_gpe_fwd_entry_t *lfe;
1394   lisp_api_gpe_fwd_entry_t *entries = 0, e;
1395
1396   /* *INDENT-OFF* */
1397   pool_foreach (lfe, lgm->lisp_fwd_entry_pool,
1398   ({
1399     if (lfe->key->vni == vni)
1400       {
1401         memset (&e, 0, sizeof (e));
1402         e.dp_table = lfe->eid_table_id;
1403         e.vni = lfe->key->vni;
1404         e.fwd_entry_index = lfe - lgm->lisp_fwd_entry_pool;
1405         memcpy (&e.reid, &lfe->key->rmt, sizeof (e.reid));
1406         memcpy (&e.leid, &lfe->key->lcl, sizeof (e.leid));
1407         vec_add1 (entries, e);
1408       }
1409   }));
1410   /* *INDENT-ON* */
1411
1412   return entries;
1413 }
1414
1415 VLIB_INIT_FUNCTION (lisp_gpe_fwd_entry_init);
1416
1417 /*
1418  * fd.io coding-style-patch-verification: ON
1419  *
1420  * Local Variables:
1421  * eval: (c-set-style "gnu")
1422  * End:
1423  */