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