Add NSH to GPE decap path
[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 void
333 gid_to_dp_address (gid_address_t * g, dp_address_t * d)
334 {
335   switch (gid_address_type (g))
336     {
337     case GID_ADDR_IP_PREFIX:
338     case GID_ADDR_SRC_DST:
339       ip_prefix_copy (&d->ippref, &gid_address_ippref (g));
340       d->type = FID_ADDR_IP_PREF;
341       break;
342     case GID_ADDR_MAC:
343       mac_copy (&d->mac, &gid_address_mac (g));
344       d->type = FID_ADDR_MAC;
345       break;
346     case GID_ADDR_NSH:
347     default:
348       d->nsh = gid_address_nsh (g).spi << 8 | gid_address_nsh (g).si;
349       d->type = FID_ADDR_NSH;
350       break;
351     }
352 }
353
354 static lisp_gpe_fwd_entry_t *
355 find_fwd_entry (lisp_gpe_main_t * lgm,
356                 vnet_lisp_gpe_add_del_fwd_entry_args_t * a,
357                 lisp_gpe_fwd_entry_key_t * key)
358 {
359   uword *p;
360
361   memset (key, 0, sizeof (*key));
362
363   if (GID_ADDR_IP_PREFIX == gid_address_type (&a->rmt_eid))
364     {
365       /*
366        * the ip version of the source is not set to ip6 when the
367        * source is all zeros. force it.
368        */
369       ip_prefix_version (&gid_address_ippref (&a->lcl_eid)) =
370         ip_prefix_version (&gid_address_ippref (&a->rmt_eid));
371     }
372
373   gid_to_dp_address (&a->rmt_eid, &key->rmt);
374   gid_to_dp_address (&a->lcl_eid, &key->lcl);
375   key->vni = a->vni;
376
377   p = hash_get_mem (lgm->lisp_gpe_fwd_entries, key);
378
379   if (NULL != p)
380     {
381       return (pool_elt_at_index (lgm->lisp_fwd_entry_pool, p[0]));
382     }
383   return (NULL);
384 }
385
386 static int
387 lisp_gpe_fwd_entry_path_sort (void *a1, void *a2)
388 {
389   lisp_fwd_path_t *p1 = a1, *p2 = a2;
390
391   return (p1->priority - p2->priority);
392 }
393
394 static void
395 lisp_gpe_fwd_entry_mk_paths (lisp_gpe_fwd_entry_t * lfe,
396                              vnet_lisp_gpe_add_del_fwd_entry_args_t * a)
397 {
398   lisp_fwd_path_t *path;
399   u32 index;
400
401   vec_validate (lfe->paths, vec_len (a->locator_pairs) - 1);
402
403   vec_foreach_index (index, a->locator_pairs)
404   {
405     path = &lfe->paths[index];
406
407     path->priority = a->locator_pairs[index].priority;
408     path->weight = a->locator_pairs[index].weight;
409
410     path->lisp_adj =
411       lisp_gpe_adjacency_find_or_create_and_lock (&a->locator_pairs
412                                                   [index],
413                                                   a->dp_table, lfe->key->vni);
414   }
415   vec_sort_with_function (lfe->paths, lisp_gpe_fwd_entry_path_sort);
416 }
417
418 /**
419  * @brief Add/Delete LISP IP forwarding entry.
420  *
421  * creation of forwarding entries for IP LISP overlay:
422  *
423  * @param[in]   lgm     Reference to @ref lisp_gpe_main_t.
424  * @param[in]   a       Parameters for building the forwarding entry.
425  *
426  * @return 0 on success.
427  */
428 static int
429 add_ip_fwd_entry (lisp_gpe_main_t * lgm,
430                   vnet_lisp_gpe_add_del_fwd_entry_args_t * a)
431 {
432   lisp_gpe_fwd_entry_key_t key;
433   lisp_gpe_fwd_entry_t *lfe;
434   fib_protocol_t fproto;
435
436   lfe = find_fwd_entry (lgm, a, &key);
437
438   if (NULL != lfe)
439     /* don't support updates */
440     return VNET_API_ERROR_INVALID_VALUE;
441
442   pool_get (lgm->lisp_fwd_entry_pool, lfe);
443   memset (lfe, 0, sizeof (*lfe));
444   lfe->key = clib_mem_alloc (sizeof (key));
445   memcpy (lfe->key, &key, sizeof (key));
446
447   hash_set_mem (lgm->lisp_gpe_fwd_entries, lfe->key,
448                 lfe - lgm->lisp_fwd_entry_pool);
449
450   fproto = (IP4 == ip_prefix_version (&fid_addr_ippref (&lfe->key->rmt)) ?
451             FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6);
452
453   lfe->type = (a->is_negative ?
454                LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE :
455                LISP_GPE_FWD_ENTRY_TYPE_NORMAL);
456   lfe->tenant = lisp_gpe_tenant_find_or_create (lfe->key->vni);
457   lfe->eid_table_id = a->table_id;
458   lfe->eid_fib_index = fib_table_find_or_create_and_lock (fproto,
459                                                           lfe->eid_table_id);
460
461   if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type)
462     {
463       lisp_gpe_fwd_entry_mk_paths (lfe, a);
464     }
465
466   create_fib_entries (lfe);
467
468   return (0);
469 }
470
471 static void
472 del_ip_fwd_entry_i (lisp_gpe_main_t * lgm, lisp_gpe_fwd_entry_t * lfe)
473 {
474   lisp_fwd_path_t *path;
475   fib_protocol_t fproto;
476
477   vec_foreach (path, lfe->paths)
478   {
479     lisp_gpe_adjacency_unlock (path->lisp_adj);
480   }
481
482   delete_fib_entries (lfe);
483
484   fproto = (IP4 == ip_prefix_version (&fid_addr_ippref (&lfe->key->rmt)) ?
485             FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6);
486   fib_table_unlock (lfe->eid_fib_index, fproto);
487
488   hash_unset_mem (lgm->lisp_gpe_fwd_entries, lfe->key);
489   clib_mem_free (lfe->key);
490   pool_put (lgm->lisp_fwd_entry_pool, lfe);
491 }
492
493 /**
494  * @brief Add/Delete LISP IP forwarding entry.
495  *
496  * removal of forwarding entries for IP LISP overlay:
497  *
498  * @param[in]   lgm     Reference to @ref lisp_gpe_main_t.
499  * @param[in]   a       Parameters for building the forwarding entry.
500  *
501  * @return 0 on success.
502  */
503 static int
504 del_ip_fwd_entry (lisp_gpe_main_t * lgm,
505                   vnet_lisp_gpe_add_del_fwd_entry_args_t * a)
506 {
507   lisp_gpe_fwd_entry_key_t key;
508   lisp_gpe_fwd_entry_t *lfe;
509
510   lfe = find_fwd_entry (lgm, a, &key);
511
512   if (NULL == lfe)
513     /* no such entry */
514     return VNET_API_ERROR_INVALID_VALUE;
515
516   del_ip_fwd_entry_i (lgm, lfe);
517
518   return (0);
519 }
520
521 static void
522 make_mac_fib_key (BVT (clib_bihash_kv) * kv, u16 bd_index, u8 src_mac[6],
523                   u8 dst_mac[6])
524 {
525   kv->key[0] = (((u64) bd_index) << 48) | mac_to_u64 (dst_mac);
526   kv->key[1] = mac_to_u64 (src_mac);
527   kv->key[2] = 0;
528 }
529
530 /**
531  * @brief Lookup L2 SD FIB entry
532  *
533  * Does a vni + dest + source lookup in the L2 LISP FIB. If the lookup fails
534  * it tries a second time with source set to 0 (i.e., a simple dest lookup).
535  *
536  * @param[in]   lgm             Reference to @ref lisp_gpe_main_t.
537  * @param[in]   bd_index        Bridge domain index.
538  * @param[in]   src_mac         Source mac address.
539  * @param[in]   dst_mac         Destination mac address.
540  *
541  * @return index of mapping matching the lookup key.
542  */
543 index_t
544 lisp_l2_fib_lookup (lisp_gpe_main_t * lgm, u16 bd_index, u8 src_mac[6],
545                     u8 dst_mac[6])
546 {
547   int rv;
548   BVT (clib_bihash_kv) kv, value;
549
550   make_mac_fib_key (&kv, bd_index, src_mac, dst_mac);
551   rv = BV (clib_bihash_search_inline_2) (&lgm->l2_fib, &kv, &value);
552
553   /* no match, try with src 0, catch all for dst */
554   if (rv != 0)
555     {
556       kv.key[1] = 0;
557       rv = BV (clib_bihash_search_inline_2) (&lgm->l2_fib, &kv, &value);
558       if (rv == 0)
559         return value.value;
560     }
561   else
562     return value.value;
563
564   return lisp_gpe_main.l2_lb_cp_lkup.dpoi_index;
565 }
566
567 /**
568  * @brief Add/del L2 SD FIB entry
569  *
570  * Inserts value in L2 FIB keyed by vni + dest + source. If entry is
571  * overwritten the associated value is returned.
572  *
573  * @param[in]   lgm             Reference to @ref lisp_gpe_main_t.
574  * @param[in]   bd_index        Bridge domain index.
575  * @param[in]   src_mac         Source mac address.
576  * @param[in]   dst_mac         Destination mac address.
577  * @param[in]   val             Value to add.
578  * @param[in]   is_add          Add/del flag.
579  *
580  * @return ~0 or value of overwritten entry.
581  */
582 static u32
583 lisp_l2_fib_add_del_entry (u16 bd_index, u8 src_mac[6],
584                            u8 dst_mac[6], const dpo_id_t * dpo, u8 is_add)
585 {
586   lisp_gpe_main_t *lgm = &lisp_gpe_main;
587   BVT (clib_bihash_kv) kv, value;
588   u32 old_val = ~0;
589
590   make_mac_fib_key (&kv, bd_index, src_mac, dst_mac);
591
592   if (BV (clib_bihash_search) (&lgm->l2_fib, &kv, &value) == 0)
593     old_val = value.value;
594
595   if (!is_add)
596     BV (clib_bihash_add_del) (&lgm->l2_fib, &kv, 0 /* is_add */ );
597   else
598     {
599       kv.value = dpo->dpoi_index;
600       BV (clib_bihash_add_del) (&lgm->l2_fib, &kv, 1 /* is_add */ );
601     }
602   return old_val;
603 }
604
605 #define L2_FIB_DEFAULT_HASH_NUM_BUCKETS (64 * 1024)
606 #define L2_FIB_DEFAULT_HASH_MEMORY_SIZE (32<<20)
607
608 static void
609 l2_fib_init (lisp_gpe_main_t * lgm)
610 {
611   index_t lbi;
612
613   BV (clib_bihash_init) (&lgm->l2_fib, "l2 fib",
614                          1 << max_log2 (L2_FIB_DEFAULT_HASH_NUM_BUCKETS),
615                          L2_FIB_DEFAULT_HASH_MEMORY_SIZE);
616
617   /*
618    * the result from a 'miss' in a L2 Table
619    */
620   lbi = load_balance_create (1, DPO_PROTO_ETHERNET, 0);
621   load_balance_set_bucket (lbi, 0, lisp_cp_dpo_get (DPO_PROTO_ETHERNET));
622
623   dpo_set (&lgm->l2_lb_cp_lkup, DPO_LOAD_BALANCE, DPO_PROTO_ETHERNET, lbi);
624 }
625
626 static void
627 del_l2_fwd_entry_i (lisp_gpe_main_t * lgm, lisp_gpe_fwd_entry_t * lfe)
628 {
629   lisp_fwd_path_t *path;
630
631   if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type)
632     {
633       vec_foreach (path, lfe->paths)
634       {
635         lisp_gpe_adjacency_unlock (path->lisp_adj);
636       }
637       fib_path_list_child_remove (lfe->l2.path_list_index,
638                                   lfe->l2.child_index);
639     }
640
641   lisp_l2_fib_add_del_entry (lfe->l2.eid_bd_index,
642                              fid_addr_mac (&lfe->key->lcl),
643                              fid_addr_mac (&lfe->key->rmt), NULL, 0);
644
645   hash_unset_mem (lgm->lisp_gpe_fwd_entries, lfe->key);
646   clib_mem_free (lfe->key);
647   pool_put (lgm->lisp_fwd_entry_pool, lfe);
648 }
649
650 /**
651  * @brief Delete LISP L2 forwarding entry.
652  *
653  * Coordinates the removal of forwarding entries for L2 LISP overlay:
654  *
655  * @param[in]   lgm     Reference to @ref lisp_gpe_main_t.
656  * @param[in]   a       Parameters for building the forwarding entry.
657  *
658  * @return 0 on success.
659  */
660 static int
661 del_l2_fwd_entry (lisp_gpe_main_t * lgm,
662                   vnet_lisp_gpe_add_del_fwd_entry_args_t * a)
663 {
664   lisp_gpe_fwd_entry_key_t key;
665   lisp_gpe_fwd_entry_t *lfe;
666
667   lfe = find_fwd_entry (lgm, a, &key);
668
669   if (NULL == lfe)
670     return VNET_API_ERROR_INVALID_VALUE;
671
672   del_l2_fwd_entry_i (lgm, lfe);
673
674   return (0);
675 }
676
677 /**
678  * @brief Construct and insert the forwarding information used by an L2 entry
679  */
680 static void
681 lisp_gpe_l2_update_fwding (lisp_gpe_fwd_entry_t * lfe)
682 {
683   lisp_gpe_main_t *lgm = &lisp_gpe_main;
684   dpo_id_t dpo = DPO_INVALID;
685
686   if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type)
687     {
688       fib_path_list_contribute_forwarding (lfe->l2.path_list_index,
689                                            FIB_FORW_CHAIN_TYPE_ETHERNET,
690                                            &lfe->l2.dpo);
691       dpo_copy (&dpo, &lfe->l2.dpo);
692     }
693   else
694     {
695       switch (lfe->action)
696         {
697         case SEND_MAP_REQUEST:
698           dpo_copy (&dpo, &lgm->l2_lb_cp_lkup);
699           break;
700         case NO_ACTION:
701         case FORWARD_NATIVE:
702         case DROP:
703           dpo_copy (&dpo, drop_dpo_get (DPO_PROTO_ETHERNET));
704         }
705     }
706
707   /* add entry to l2 lisp fib */
708   lisp_l2_fib_add_del_entry (lfe->l2.eid_bd_index,
709                              fid_addr_mac (&lfe->key->lcl),
710                              fid_addr_mac (&lfe->key->rmt), &dpo, 1);
711
712   dpo_reset (&dpo);
713 }
714
715 /**
716  * @brief Add LISP L2 forwarding entry.
717  *
718  * Coordinates the creation of forwarding entries for L2 LISP overlay:
719  * creates lisp-gpe tunnel and injects new entry in Source/Dest L2 FIB.
720  *
721  * @param[in]   lgm     Reference to @ref lisp_gpe_main_t.
722  * @param[in]   a       Parameters for building the forwarding entry.
723  *
724  * @return 0 on success.
725  */
726 static int
727 add_l2_fwd_entry (lisp_gpe_main_t * lgm,
728                   vnet_lisp_gpe_add_del_fwd_entry_args_t * a)
729 {
730   lisp_gpe_fwd_entry_key_t key;
731   bd_main_t *bdm = &bd_main;
732   lisp_gpe_fwd_entry_t *lfe;
733   uword *bd_indexp;
734
735   bd_indexp = hash_get (bdm->bd_index_by_bd_id, a->bd_id);
736   if (!bd_indexp)
737     {
738       clib_warning ("bridge domain %d doesn't exist", a->bd_id);
739       return -1;
740     }
741
742   lfe = find_fwd_entry (lgm, a, &key);
743
744   if (NULL != lfe)
745     /* don't support updates */
746     return VNET_API_ERROR_INVALID_VALUE;
747
748   pool_get (lgm->lisp_fwd_entry_pool, lfe);
749   memset (lfe, 0, sizeof (*lfe));
750   lfe->key = clib_mem_alloc (sizeof (key));
751   memcpy (lfe->key, &key, sizeof (key));
752
753   hash_set_mem (lgm->lisp_gpe_fwd_entries, lfe->key,
754                 lfe - lgm->lisp_fwd_entry_pool);
755
756   lfe->type = (a->is_negative ?
757                LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE :
758                LISP_GPE_FWD_ENTRY_TYPE_NORMAL);
759   lfe->l2.eid_bd_id = a->bd_id;
760   lfe->l2.eid_bd_index = bd_indexp[0];
761   lfe->tenant = lisp_gpe_tenant_find_or_create (lfe->key->vni);
762
763   if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type)
764     {
765       fib_route_path_t *rpaths;
766
767       /*
768        * Make the sorted array of LISP paths with their resp. adjacency
769        */
770       lisp_gpe_fwd_entry_mk_paths (lfe, a);
771
772       /*
773        * From the LISP paths, construct a FIB path list that will
774        * contribute a load-balance.
775        */
776       rpaths = lisp_gpe_mk_fib_paths (lfe->paths);
777
778       lfe->l2.path_list_index =
779         fib_path_list_create (FIB_PATH_LIST_FLAG_NONE, rpaths);
780
781       /*
782        * become a child of the path-list so we receive updates when
783        * its forwarding state changes. this includes an implicit lock.
784        */
785       lfe->l2.child_index =
786         fib_path_list_child_add (lfe->l2.path_list_index,
787                                  FIB_NODE_TYPE_LISP_GPE_FWD_ENTRY,
788                                  lfe - lgm->lisp_fwd_entry_pool);
789     }
790   else
791     {
792       lfe->action = a->action;
793     }
794
795   lisp_gpe_l2_update_fwding (lfe);
796
797   return 0;
798 }
799
800 /**
801  * @brief Lookup NSH SD FIB entry
802  *
803  * Does an SPI+SI lookup in the NSH LISP FIB.
804  *
805  * @param[in]   lgm             Reference to @ref lisp_gpe_main_t.
806  * @param[in]   spi_si          SPI + SI.
807  *
808  * @return next node index.
809  */
810 const dpo_id_t *
811 lisp_nsh_fib_lookup (lisp_gpe_main_t * lgm, u32 spi_si_net_order)
812 {
813   int rv;
814   BVT (clib_bihash_kv) kv, value;
815
816   memset (&kv, 0, sizeof (kv));
817   kv.key[0] = spi_si_net_order;
818   rv = BV (clib_bihash_search_inline_2) (&lgm->nsh_fib, &kv, &value);
819
820   if (rv != 0)
821     {
822       return lgm->nsh_cp_lkup;
823     }
824   else
825     {
826       lisp_gpe_fwd_entry_t *lfe;
827       lfe = pool_elt_at_index (lgm->lisp_fwd_entry_pool, value.value);
828       return &lfe->nsh.choice;
829     }
830 }
831
832 /**
833  * @brief Add/del NSH FIB entry
834  *
835  * Inserts value in NSH FIB keyed by SPI+SI. If entry is
836  * overwritten the associated value is returned.
837  *
838  * @param[in]   lgm             Reference to @ref lisp_gpe_main_t.
839  * @param[in]   spi_si          SPI + SI.
840  * @param[in]   dpo             Load balanced mapped to SPI + SI
841  *
842  * @return ~0 or value of overwritten entry.
843  */
844 static u32
845 lisp_nsh_fib_add_del_entry (u32 spi_si_host_order, u32 lfei, u8 is_add)
846 {
847   lisp_gpe_main_t *lgm = &lisp_gpe_main;
848   BVT (clib_bihash_kv) kv, value;
849   u32 old_val = ~0;
850
851   memset (&kv, 0, sizeof (kv));
852   kv.key[0] = clib_host_to_net_u32 (spi_si_host_order);
853   kv.value = 0ULL;
854
855   if (BV (clib_bihash_search) (&lgm->nsh_fib, &kv, &value) == 0)
856     old_val = value.value;
857
858   if (!is_add)
859     BV (clib_bihash_add_del) (&lgm->nsh_fib, &kv, 0 /* is_add */ );
860   else
861     {
862       kv.value = lfei;
863       BV (clib_bihash_add_del) (&lgm->nsh_fib, &kv, 1 /* is_add */ );
864     }
865   return old_val;
866 }
867
868 #define NSH_FIB_DEFAULT_HASH_NUM_BUCKETS (64 * 1024)
869 #define NSH_FIB_DEFAULT_HASH_MEMORY_SIZE (32<<20)
870
871 static void
872 nsh_fib_init (lisp_gpe_main_t * lgm)
873 {
874   BV (clib_bihash_init) (&lgm->nsh_fib, "nsh fib",
875                          1 << max_log2 (NSH_FIB_DEFAULT_HASH_NUM_BUCKETS),
876                          NSH_FIB_DEFAULT_HASH_MEMORY_SIZE);
877
878   /*
879    * the result from a 'miss' in a NSH Table
880    */
881   lgm->nsh_cp_lkup = lisp_cp_dpo_get (DPO_PROTO_NSH);
882 }
883
884 static void
885 del_nsh_fwd_entry_i (lisp_gpe_main_t * lgm, lisp_gpe_fwd_entry_t * lfe)
886 {
887   lisp_fwd_path_t *path;
888
889   if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type)
890     {
891       vec_foreach (path, lfe->paths)
892       {
893         lisp_gpe_adjacency_unlock (path->lisp_adj);
894       }
895       fib_path_list_child_remove (lfe->nsh.path_list_index,
896                                   lfe->nsh.child_index);
897       dpo_reset (&lfe->nsh.choice);
898     }
899
900   lisp_nsh_fib_add_del_entry (fid_addr_nsh (&lfe->key->rmt), (u32) ~ 0, 0);
901
902   hash_unset_mem (lgm->lisp_gpe_fwd_entries, lfe->key);
903   clib_mem_free (lfe->key);
904   pool_put (lgm->lisp_fwd_entry_pool, lfe);
905 }
906
907 /**
908  * @brief Delete LISP NSH forwarding entry.
909  *
910  * Coordinates the removal of forwarding entries for NSH LISP overlay:
911  *
912  * @param[in]   lgm     Reference to @ref lisp_gpe_main_t.
913  * @param[in]   a       Parameters for building the forwarding entry.
914  *
915  * @return 0 on success.
916  */
917 static int
918 del_nsh_fwd_entry (lisp_gpe_main_t * lgm,
919                    vnet_lisp_gpe_add_del_fwd_entry_args_t * a)
920 {
921   lisp_gpe_fwd_entry_key_t key;
922   lisp_gpe_fwd_entry_t *lfe;
923
924   lfe = find_fwd_entry (lgm, a, &key);
925
926   if (NULL == lfe)
927     return VNET_API_ERROR_INVALID_VALUE;
928
929   del_nsh_fwd_entry_i (lgm, lfe);
930
931   return (0);
932 }
933
934 /**
935  * @brief Construct and insert the forwarding information used by an NSH entry
936  */
937 static void
938 lisp_gpe_nsh_update_fwding (lisp_gpe_fwd_entry_t * lfe)
939 {
940   lisp_gpe_main_t *lgm = vnet_lisp_gpe_get_main ();
941   dpo_id_t dpo = DPO_INVALID;
942   vnet_hw_interface_t *hi;
943   uword *hip;
944
945   if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type)
946     {
947       fib_path_list_contribute_forwarding (lfe->nsh.path_list_index,
948                                            FIB_FORW_CHAIN_TYPE_NSH,
949                                            &lfe->nsh.dpo);
950
951       /*
952        * LISP encap is always the same for this SPI+SI so we do that hash now
953        * and stack on the choice.
954        */
955       if (DPO_LOAD_BALANCE == lfe->nsh.dpo.dpoi_type)
956         {
957           const dpo_id_t *tmp;
958           const load_balance_t *lb;
959           int hash;
960
961           lb = load_balance_get (lfe->nsh.dpo.dpoi_index);
962           hash = fid_addr_nsh (&lfe->key->rmt) % lb->lb_n_buckets;
963           tmp =
964             load_balance_get_bucket_i (lb, hash & lb->lb_n_buckets_minus_1);
965
966           dpo_copy (&dpo, tmp);
967         }
968     }
969   else
970     {
971       switch (lfe->action)
972         {
973         case SEND_MAP_REQUEST:
974           dpo_copy (&dpo, lgm->nsh_cp_lkup);
975           break;
976         case NO_ACTION:
977         case FORWARD_NATIVE:
978         case DROP:
979           dpo_copy (&dpo, drop_dpo_get (DPO_PROTO_NSH));
980         }
981     }
982
983   /* We have only one nsh-lisp interface (no NSH virtualization) */
984   hip = hash_get (lgm->nsh_ifaces.hw_if_index_by_dp_table, 0);
985   if (hip)
986     {
987       hi = vnet_get_hw_interface (lgm->vnet_main, hip[0]);
988       dpo_stack_from_node (hi->tx_node_index, &lfe->nsh.choice, &dpo);
989     }
990   /* add entry to nsh lisp fib */
991   lisp_nsh_fib_add_del_entry (fid_addr_nsh (&lfe->key->rmt),
992                               lfe - lgm->lisp_fwd_entry_pool, 1);
993   dpo_reset (&dpo);
994
995 }
996
997 /**
998  * @brief Add LISP NSH forwarding entry.
999  *
1000  * Coordinates the creation of forwarding entries for L2 LISP overlay:
1001  * creates lisp-gpe tunnel and injects new entry in Source/Dest L2 FIB.
1002  *
1003  * @param[in]   lgm     Reference to @ref lisp_gpe_main_t.
1004  * @param[in]   a       Parameters for building the forwarding entry.
1005  *
1006  * @return 0 on success.
1007  */
1008 static int
1009 add_nsh_fwd_entry (lisp_gpe_main_t * lgm,
1010                    vnet_lisp_gpe_add_del_fwd_entry_args_t * a)
1011 {
1012   lisp_gpe_fwd_entry_key_t key;
1013   lisp_gpe_fwd_entry_t *lfe;
1014
1015   lfe = find_fwd_entry (lgm, a, &key);
1016
1017   if (NULL != lfe)
1018     /* don't support updates */
1019     return VNET_API_ERROR_INVALID_VALUE;
1020
1021   pool_get (lgm->lisp_fwd_entry_pool, lfe);
1022   memset (lfe, 0, sizeof (*lfe));
1023   lfe->key = clib_mem_alloc (sizeof (key));
1024   memcpy (lfe->key, &key, sizeof (key));
1025
1026   hash_set_mem (lgm->lisp_gpe_fwd_entries, lfe->key,
1027                 lfe - lgm->lisp_fwd_entry_pool);
1028
1029   lfe->type = (a->is_negative ?
1030                LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE :
1031                LISP_GPE_FWD_ENTRY_TYPE_NORMAL);
1032   lfe->tenant = 0;
1033
1034   if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE != lfe->type)
1035     {
1036       fib_route_path_t *rpaths;
1037
1038       /*
1039        * Make the sorted array of LISP paths with their resp. adjacency
1040        */
1041       lisp_gpe_fwd_entry_mk_paths (lfe, a);
1042
1043       /*
1044        * From the LISP paths, construct a FIB path list that will
1045        * contribute a load-balance.
1046        */
1047       rpaths = lisp_gpe_mk_fib_paths (lfe->paths);
1048
1049       lfe->nsh.path_list_index =
1050         fib_path_list_create (FIB_PATH_LIST_FLAG_NONE, rpaths);
1051
1052       /*
1053        * become a child of the path-list so we receive updates when
1054        * its forwarding state changes. this includes an implicit lock.
1055        */
1056       lfe->nsh.child_index =
1057         fib_path_list_child_add (lfe->nsh.path_list_index,
1058                                  FIB_NODE_TYPE_LISP_GPE_FWD_ENTRY,
1059                                  lfe - lgm->lisp_fwd_entry_pool);
1060     }
1061   else
1062     {
1063       lfe->action = a->action;
1064     }
1065
1066   lisp_gpe_nsh_update_fwding (lfe);
1067
1068   return 0;
1069 }
1070
1071 /**
1072  * @brief conver from the embedded fib_node_t struct to the LSIP entry
1073  */
1074 static lisp_gpe_fwd_entry_t *
1075 lisp_gpe_fwd_entry_from_fib_node (fib_node_t * node)
1076 {
1077   return ((lisp_gpe_fwd_entry_t *) (((char *) node) -
1078                                     STRUCT_OFFSET_OF (lisp_gpe_fwd_entry_t,
1079                                                       node)));
1080 }
1081
1082 /**
1083  * @brief Function invoked during a backwalk of the FIB graph
1084  */
1085 static fib_node_back_walk_rc_t
1086 lisp_gpe_fib_node_back_walk (fib_node_t * node,
1087                              fib_node_back_walk_ctx_t * ctx)
1088 {
1089   lisp_gpe_fwd_entry_t *lfe = lisp_gpe_fwd_entry_from_fib_node (node);
1090
1091   if (fid_addr_type (&lfe->key->rmt) == FID_ADDR_MAC)
1092     lisp_gpe_l2_update_fwding (lfe);
1093   else if (fid_addr_type (&lfe->key->rmt) == FID_ADDR_NSH)
1094     lisp_gpe_nsh_update_fwding (lfe);
1095
1096   return (FIB_NODE_BACK_WALK_CONTINUE);
1097 }
1098
1099 /**
1100  * @brief Get a fib_node_t struct from the index of a LISP fwd entry
1101  */
1102 static fib_node_t *
1103 lisp_gpe_fwd_entry_get_fib_node (fib_node_index_t index)
1104 {
1105   lisp_gpe_main_t *lgm = &lisp_gpe_main;
1106   lisp_gpe_fwd_entry_t *lfe;
1107
1108   lfe = pool_elt_at_index (lgm->lisp_fwd_entry_pool, index);
1109
1110   return (&(lfe->node));
1111 }
1112
1113 /**
1114  * @brief An indication from the graph that the last lock has gone
1115  */
1116 static void
1117 lisp_gpe_fwd_entry_fib_node_last_lock_gone (fib_node_t * node)
1118 {
1119   /* We don't manage the locks of the LISP objects via the graph, since
1120    * this object has no children. so this is a no-op. */
1121 }
1122
1123 /**
1124  * @brief Virtual function table to register with FIB for the LISP type
1125  */
1126 const static fib_node_vft_t lisp_fwd_vft = {
1127   .fnv_get = lisp_gpe_fwd_entry_get_fib_node,
1128   .fnv_last_lock = lisp_gpe_fwd_entry_fib_node_last_lock_gone,
1129   .fnv_back_walk = lisp_gpe_fib_node_back_walk,
1130 };
1131
1132 /**
1133  * @brief Forwarding entry create/remove dispatcher.
1134  *
1135  * Calls l2 or l3 forwarding entry add/del function based on input data.
1136  *
1137  * @param[in]   a       Forwarding entry parameters.
1138  * @param[out]  hw_if_indexp    NOT USED
1139  *
1140  * @return 0 on success.
1141  */
1142 int
1143 vnet_lisp_gpe_add_del_fwd_entry (vnet_lisp_gpe_add_del_fwd_entry_args_t * a,
1144                                  u32 * hw_if_indexp)
1145 {
1146   lisp_gpe_main_t *lgm = &lisp_gpe_main;
1147   u8 type;
1148
1149   if (vnet_lisp_gpe_enable_disable_status () == 0)
1150     {
1151       clib_warning ("LISP is disabled!");
1152       return VNET_API_ERROR_LISP_DISABLED;
1153     }
1154
1155   type = gid_address_type (&a->rmt_eid);
1156   switch (type)
1157     {
1158     case GID_ADDR_IP_PREFIX:
1159       if (a->is_add)
1160         return add_ip_fwd_entry (lgm, a);
1161       else
1162         return del_ip_fwd_entry (lgm, a);
1163       break;
1164     case GID_ADDR_MAC:
1165       if (a->is_add)
1166         return add_l2_fwd_entry (lgm, a);
1167       else
1168         return del_l2_fwd_entry (lgm, a);
1169     case GID_ADDR_NSH:
1170       if (a->is_add)
1171         return add_nsh_fwd_entry (lgm, a);
1172       else
1173         return del_nsh_fwd_entry (lgm, a);
1174     default:
1175       clib_warning ("Forwarding entries for type %d not supported!", type);
1176       return -1;
1177     }
1178 }
1179
1180 /**
1181  * @brief Flush all the forwrding entries
1182  */
1183 void
1184 vnet_lisp_gpe_fwd_entry_flush (void)
1185 {
1186   lisp_gpe_main_t *lgm = &lisp_gpe_main;
1187   lisp_gpe_fwd_entry_t *lfe;
1188
1189   /* *INDENT-OFF* */
1190   pool_foreach (lfe, lgm->lisp_fwd_entry_pool,
1191   ({
1192     switch (fid_addr_type(&lfe->key->rmt))
1193       {
1194       case FID_ADDR_MAC:
1195         del_l2_fwd_entry_i (lgm, lfe);
1196         break;
1197       case FID_ADDR_IP_PREF:
1198         del_ip_fwd_entry_i (lgm, lfe);
1199         break;
1200       case FID_ADDR_NSH:
1201         del_nsh_fwd_entry_i (lgm, lfe);
1202         break;
1203       }
1204   }));
1205   /* *INDENT-ON* */
1206 }
1207
1208 static u8 *
1209 format_lisp_fwd_path (u8 * s, va_list ap)
1210 {
1211   lisp_fwd_path_t *lfp = va_arg (ap, lisp_fwd_path_t *);
1212
1213   s = format (s, "weight:%d ", lfp->weight);
1214   s = format (s, "adj:[%U]\n",
1215               format_lisp_gpe_adjacency,
1216               lisp_gpe_adjacency_get (lfp->lisp_adj),
1217               LISP_GPE_ADJ_FORMAT_FLAG_NONE);
1218
1219   return (s);
1220 }
1221
1222 typedef enum lisp_gpe_fwd_entry_format_flag_t_
1223 {
1224   LISP_GPE_FWD_ENTRY_FORMAT_NONE = (0 << 0),
1225   LISP_GPE_FWD_ENTRY_FORMAT_DETAIL = (1 << 1),
1226 } lisp_gpe_fwd_entry_format_flag_t;
1227
1228
1229 static u8 *
1230 format_lisp_gpe_fwd_entry (u8 * s, va_list ap)
1231 {
1232   lisp_gpe_main_t *lgm = &lisp_gpe_main;
1233   lisp_gpe_fwd_entry_t *lfe = va_arg (ap, lisp_gpe_fwd_entry_t *);
1234   lisp_gpe_fwd_entry_format_flag_t flags =
1235     va_arg (ap, lisp_gpe_fwd_entry_format_flag_t);
1236
1237   s = format (s, "VNI:%d VRF:%d EID: %U -> %U  [index:%d]",
1238               lfe->key->vni, lfe->eid_table_id,
1239               format_fid_address, &lfe->key->lcl,
1240               format_fid_address, &lfe->key->rmt,
1241               lfe - lgm->lisp_fwd_entry_pool);
1242
1243   if (LISP_GPE_FWD_ENTRY_TYPE_NEGATIVE == lfe->type)
1244     {
1245       s = format (s, "\n Negative - action:%U",
1246                   format_negative_mapping_action, lfe->action);
1247     }
1248   else
1249     {
1250       lisp_fwd_path_t *path;
1251
1252       s = format (s, "\n via:");
1253       vec_foreach (path, lfe->paths)
1254       {
1255         s = format (s, "\n  %U", format_lisp_fwd_path, path);
1256       }
1257     }
1258
1259   if (flags & LISP_GPE_FWD_ENTRY_FORMAT_DETAIL)
1260     {
1261       switch (fid_addr_type (&lfe->key->rmt))
1262         {
1263         case FID_ADDR_MAC:
1264           s = format (s, " fib-path-list:%d\n", lfe->l2.path_list_index);
1265           s = format (s, " dpo:%U\n", format_dpo_id, &lfe->l2.dpo, 0);
1266           break;
1267         case FID_ADDR_NSH:
1268           s = format (s, " fib-path-list:%d\n", lfe->nsh.path_list_index);
1269           s = format (s, " dpo:%U\n", format_dpo_id, &lfe->nsh.dpo, 0);
1270           break;
1271         case FID_ADDR_IP_PREF:
1272           break;
1273         }
1274     }
1275
1276   return (s);
1277 }
1278
1279 static clib_error_t *
1280 lisp_gpe_fwd_entry_show (vlib_main_t * vm,
1281                          unformat_input_t * input, vlib_cli_command_t * cmd)
1282 {
1283   lisp_gpe_main_t *lgm = &lisp_gpe_main;
1284   lisp_gpe_fwd_entry_t *lfe;
1285   index_t index;
1286   u32 vni = ~0;
1287
1288   if (unformat (input, "vni %d", &vni))
1289     ;
1290   else if (unformat (input, "%d", &index))
1291     {
1292       if (!pool_is_free_index (lgm->lisp_fwd_entry_pool, index))
1293         {
1294           lfe = pool_elt_at_index (lgm->lisp_fwd_entry_pool, index);
1295
1296           vlib_cli_output (vm, "[%d@] %U",
1297                            index,
1298                            format_lisp_gpe_fwd_entry, lfe,
1299                            LISP_GPE_FWD_ENTRY_FORMAT_DETAIL);
1300         }
1301       else
1302         {
1303           vlib_cli_output (vm, "entry %d invalid", index);
1304         }
1305
1306       return (NULL);
1307     }
1308
1309   /* *INDENT-OFF* */
1310   pool_foreach (lfe, lgm->lisp_fwd_entry_pool,
1311   ({
1312     if ((vni == ~0) ||
1313         (lfe->key->vni == vni))
1314       vlib_cli_output (vm, "%U", format_lisp_gpe_fwd_entry, lfe,
1315                        LISP_GPE_FWD_ENTRY_FORMAT_NONE);
1316   }));
1317   /* *INDENT-ON* */
1318
1319   return (NULL);
1320 }
1321
1322 /* *INDENT-OFF* */
1323 VLIB_CLI_COMMAND (lisp_gpe_fwd_entry_show_command, static) = {
1324   .path = "show gpe entry",
1325   .short_help = "show gpe entry vni <vni> vrf <vrf> [leid <leid>] reid <reid>",
1326   .function = lisp_gpe_fwd_entry_show,
1327 };
1328 /* *INDENT-ON* */
1329
1330 clib_error_t *
1331 lisp_gpe_fwd_entry_init (vlib_main_t * vm)
1332 {
1333   lisp_gpe_main_t *lgm = &lisp_gpe_main;
1334   clib_error_t *error = NULL;
1335
1336   if ((error = vlib_call_init_function (vm, lisp_cp_dpo_module_init)))
1337     return (error);
1338
1339   l2_fib_init (lgm);
1340   nsh_fib_init (lgm);
1341
1342   fib_node_register_type (FIB_NODE_TYPE_LISP_GPE_FWD_ENTRY, &lisp_fwd_vft);
1343
1344   return (error);
1345 }
1346
1347 lisp_api_gpe_fwd_entry_t *
1348 vnet_lisp_gpe_fwd_entries_get_by_vni (u32 vni)
1349 {
1350   lisp_gpe_main_t *lgm = &lisp_gpe_main;
1351   lisp_gpe_fwd_entry_t *lfe;
1352   lisp_api_gpe_fwd_entry_t *entries = 0, e;
1353
1354   /* *INDENT-OFF* */
1355   pool_foreach (lfe, lgm->lisp_fwd_entry_pool,
1356   ({
1357     if (lfe->key->vni == vni)
1358       {
1359         memset (&e, 0, sizeof (e));
1360         e.dp_table = lfe->eid_table_id;
1361         e.vni = lfe->key->vni;
1362         e.fwd_entry_index = lfe - lgm->lisp_fwd_entry_pool;
1363         memcpy (&e.reid, &lfe->key->rmt, sizeof (e.reid));
1364         memcpy (&e.leid, &lfe->key->lcl, sizeof (e.leid));
1365         vec_add1 (entries, e);
1366       }
1367   }));
1368   /* *INDENT-ON* */
1369
1370   return entries;
1371 }
1372
1373 VLIB_INIT_FUNCTION (lisp_gpe_fwd_entry_init);
1374
1375 /*
1376  * fd.io coding-style-patch-verification: ON
1377  *
1378  * Local Variables:
1379  * eval: (c-set-style "gnu")
1380  * End:
1381  */