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