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