A Protocol Independent Hierarchical FIB (VPP-352)
[vpp.git] / vnet / vnet / lisp-gpe / lisp_gpe.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  * @file
17  * @brief Common utility functions for IPv4, IPv6 and L2 LISP-GPE tunnels.
18  *
19  */
20
21 #include <vnet/lisp-gpe/lisp_gpe.h>
22 #include <vnet/lisp-gpe/lisp_gpe_adjacency.h>
23 #include <vnet/adj/adj_midchain.h>
24 #include <vnet/fib/fib_table.h>
25 #include <vnet/fib/fib_entry.h>
26 #include <vnet/fib/fib_path_list.h>
27 #include <vnet/dpo/drop_dpo.h>
28 #include <vnet/dpo/load_balance.h>
29
30 /** LISP-GPE global state */
31 lisp_gpe_main_t lisp_gpe_main;
32
33 /**
34  * @brief A Pool of all LISP forwarding entries
35  */
36 static lisp_fwd_entry_t *lisp_fwd_entry_pool;
37
38 /**
39  * DB of all forwarding entries. The Key is:{l-EID,r-EID,vni}
40  * where the EID encodes L2 or L3
41  */
42 static uword *lisp_gpe_fwd_entries;
43
44 static void
45 create_fib_entries (lisp_fwd_entry_t * lfe)
46 {
47   dpo_proto_t dproto;
48
49   dproto = (ip_prefix_version (&lfe->key->rmt.ippref) == IP4 ?
50             FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6);
51
52   lfe->src_fib_index = ip_dst_fib_add_route (lfe->eid_fib_index,
53                                              &lfe->key->rmt.ippref);
54
55   if (LISP_FWD_ENTRY_TYPE_NEGATIVE == lfe->type)
56     {
57       dpo_id_t dpo = DPO_NULL;
58
59       switch (lfe->action)
60         {
61         case LISP_NO_ACTION:
62           /* TODO update timers? */
63         case LISP_FORWARD_NATIVE:
64           /* TODO check if route/next-hop for eid exists in fib and add
65            * more specific for the eid with the next-hop found */
66         case LISP_SEND_MAP_REQUEST:
67           /* insert tunnel that always sends map-request */
68           dpo_set (&dpo, DPO_LISP_CP, 0, dproto);
69           break;
70         case LISP_DROP:
71           /* for drop fwd entries, just add route, no need to add encap tunnel */
72           dpo_copy (&dpo, drop_dpo_get (dproto));
73           break;
74         }
75       ip_src_fib_add_route_w_dpo (lfe->src_fib_index,
76                                   &lfe->key->lcl.ippref, &dpo);
77       dpo_reset (&dpo);
78     }
79   else
80     {
81       ip_src_fib_add_route (lfe->src_fib_index,
82                             &lfe->key->lcl.ippref, lfe->paths);
83     }
84 }
85
86 static void
87 delete_fib_entries (lisp_fwd_entry_t * lfe)
88 {
89   ip_src_dst_fib_del_route (lfe->src_fib_index,
90                             &lfe->key->lcl.ippref,
91                             lfe->eid_fib_index, &lfe->key->rmt.ippref);
92 }
93
94 static void
95 gid_to_dp_address (gid_address_t * g, dp_address_t * d)
96 {
97   switch (gid_address_type (g))
98     {
99     case GID_ADDR_IP_PREFIX:
100     case GID_ADDR_SRC_DST:
101       ip_prefix_copy (&d->ippref, &gid_address_ippref (g));
102       d->type = FID_ADDR_IP_PREF;
103       break;
104     case GID_ADDR_MAC:
105     default:
106       mac_copy (&d->mac, &gid_address_mac (g));
107       d->type = FID_ADDR_MAC;
108       break;
109     }
110 }
111
112 static lisp_fwd_entry_t *
113 find_fwd_entry (lisp_gpe_main_t * lgm,
114                 vnet_lisp_gpe_add_del_fwd_entry_args_t * a,
115                 lisp_gpe_fwd_entry_key_t * key)
116 {
117   uword *p;
118
119   memset (key, 0, sizeof (*key));
120
121   if (GID_ADDR_IP_PREFIX == gid_address_type (&a->rmt_eid))
122     {
123       /*
124        * the ip version of the source is not set to ip6 when the
125        * source is all zeros. force it.
126        */
127       ip_prefix_version (&gid_address_ippref (&a->lcl_eid)) =
128         ip_prefix_version (&gid_address_ippref (&a->rmt_eid));
129     }
130
131   gid_to_dp_address (&a->rmt_eid, &key->rmt);
132   gid_to_dp_address (&a->lcl_eid, &key->lcl);
133   key->vni = a->vni;
134
135   p = hash_get_mem (lisp_gpe_fwd_entries, key);
136
137   if (NULL != p)
138     {
139       return (pool_elt_at_index (lisp_fwd_entry_pool, p[0]));
140     }
141   return (NULL);
142 }
143
144 static int
145 lisp_gpe_fwd_entry_path_sort (void *a1, void *a2)
146 {
147   lisp_fwd_path_t *p1 = a1, *p2 = a2;
148
149   return (p1->priority - p2->priority);
150 }
151
152 /**
153  * @brief Add/Delete LISP IP forwarding entry.
154  *
155  * creation of forwarding entries for IP LISP overlay:
156  *
157  * @param[in]   lgm     Reference to @ref lisp_gpe_main_t.
158  * @param[in]   a       Parameters for building the forwarding entry.
159  *
160  * @return 0 on success.
161  */
162 static int
163 add_ip_fwd_entry (lisp_gpe_main_t * lgm,
164                   vnet_lisp_gpe_add_del_fwd_entry_args_t * a)
165 {
166   lisp_gpe_fwd_entry_key_t key;
167   lisp_fwd_entry_t *lfe;
168   fib_protocol_t fproto;
169
170   lfe = find_fwd_entry (lgm, a, &key);
171
172   if (NULL != lfe)
173     /* don't support updates */
174     return VNET_API_ERROR_INVALID_VALUE;
175
176   pool_get (lisp_fwd_entry_pool, lfe);
177   memset (lfe, 0, sizeof (*lfe));
178   lfe->key = clib_mem_alloc (sizeof (key));
179   memcpy (lfe->key, &key, sizeof (key));
180
181   hash_set_mem (lisp_gpe_fwd_entries, lfe->key, lfe - lisp_fwd_entry_pool);
182
183   fproto = (IP4 == ip_prefix_version (&fid_addr_ippref (&lfe->key->rmt)) ?
184             FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6);
185
186   lfe->type = (a->is_negative ?
187                LISP_FWD_ENTRY_TYPE_NEGATIVE : LISP_FWD_ENTRY_TYPE_NORMAL);
188   lfe->eid_table_id = a->table_id;
189   lfe->eid_fib_index = fib_table_find_or_create_and_lock (fproto,
190                                                           lfe->eid_table_id);
191
192   if (LISP_FWD_ENTRY_TYPE_NEGATIVE != lfe->type)
193     {
194       lisp_fwd_path_t *path;
195       u32 index;
196
197       vec_validate (lfe->paths, vec_len (a->locator_pairs) - 1);
198
199       vec_foreach_index (index, a->locator_pairs)
200       {
201         path = &lfe->paths[index];
202
203         path->priority = a->locator_pairs[index].priority;
204         path->weight = a->locator_pairs[index].weight;
205
206         path->lisp_adj =
207           lisp_gpe_adjacency_find_or_create_and_lock (&a->locator_pairs
208                                                       [index],
209                                                       lfe->eid_table_id,
210                                                       lfe->key->vni);
211       }
212       vec_sort_with_function (lfe->paths, lisp_gpe_fwd_entry_path_sort);
213     }
214
215   create_fib_entries (lfe);
216
217   return (0);
218 }
219
220 static void
221 del_ip_fwd_entry_i (lisp_fwd_entry_t * lfe)
222 {
223   lisp_fwd_path_t *path;
224   fib_protocol_t fproto;
225
226   vec_foreach (path, lfe->paths)
227   {
228     lisp_gpe_adjacency_unlock (path->lisp_adj);
229   }
230
231   delete_fib_entries (lfe);
232
233   fproto = (IP4 == ip_prefix_version (&fid_addr_ippref (&lfe->key->rmt)) ?
234             FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6);
235   fib_table_unlock (lfe->eid_fib_index, fproto);
236
237   hash_unset_mem (lisp_gpe_fwd_entries, lfe->key);
238   clib_mem_free (lfe->key);
239   pool_put (lisp_fwd_entry_pool, lfe);
240 }
241
242 /**
243  * @brief Add/Delete LISP IP forwarding entry.
244  *
245  * removal of forwarding entries for IP LISP overlay:
246  *
247  * @param[in]   lgm     Reference to @ref lisp_gpe_main_t.
248  * @param[in]   a       Parameters for building the forwarding entry.
249  *
250  * @return 0 on success.
251  */
252 static int
253 del_ip_fwd_entry (lisp_gpe_main_t * lgm,
254                   vnet_lisp_gpe_add_del_fwd_entry_args_t * a)
255 {
256   lisp_gpe_fwd_entry_key_t key;
257   lisp_fwd_entry_t *lfe;
258
259   lfe = find_fwd_entry (lgm, a, &key);
260
261   if (NULL == lfe)
262     /* no such entry */
263     return VNET_API_ERROR_INVALID_VALUE;
264
265   del_ip_fwd_entry_i (lfe);
266
267   return (0);
268 }
269
270 static void
271 make_mac_fib_key (BVT (clib_bihash_kv) * kv, u16 bd_index, u8 src_mac[6],
272                   u8 dst_mac[6])
273 {
274   kv->key[0] = (((u64) bd_index) << 48) | mac_to_u64 (dst_mac);
275   kv->key[1] = mac_to_u64 (src_mac);
276   kv->key[2] = 0;
277 }
278
279 /**
280  * @brief Lookup L2 SD FIB entry
281  *
282  * Does a vni + dest + source lookup in the L2 LISP FIB. If the lookup fails
283  * it tries a second time with source set to 0 (i.e., a simple dest lookup).
284  *
285  * @param[in]   lgm             Reference to @ref lisp_gpe_main_t.
286  * @param[in]   bd_index        Bridge domain index.
287  * @param[in]   src_mac         Source mac address.
288  * @param[in]   dst_mac         Destination mac address.
289  *
290  * @return index of mapping matching the lookup key.
291  */
292 index_t
293 lisp_l2_fib_lookup (lisp_gpe_main_t * lgm, u16 bd_index, u8 src_mac[6],
294                     u8 dst_mac[6])
295 {
296   int rv;
297   BVT (clib_bihash_kv) kv, value;
298
299   make_mac_fib_key (&kv, bd_index, src_mac, dst_mac);
300   rv = BV (clib_bihash_search_inline_2) (&lgm->l2_fib, &kv, &value);
301
302   /* no match, try with src 0, catch all for dst */
303   if (rv != 0)
304     {
305       kv.key[1] = 0;
306       rv = BV (clib_bihash_search_inline_2) (&lgm->l2_fib, &kv, &value);
307       if (rv == 0)
308         return value.value;
309     }
310
311   return lisp_gpe_main.l2_lb_miss;
312 }
313
314 /**
315  * @brief Add/del L2 SD FIB entry
316  *
317  * Inserts value in L2 FIB keyed by vni + dest + source. If entry is
318  * overwritten the associated value is returned.
319  *
320  * @param[in]   lgm             Reference to @ref lisp_gpe_main_t.
321  * @param[in]   bd_index        Bridge domain index.
322  * @param[in]   src_mac         Source mac address.
323  * @param[in]   dst_mac         Destination mac address.
324  * @param[in]   val             Value to add.
325  * @param[in]   is_add          Add/del flag.
326  *
327  * @return ~0 or value of overwritten entry.
328  */
329 u32
330 lisp_l2_fib_add_del_entry (lisp_gpe_main_t * lgm, u16 bd_index, u8 src_mac[6],
331                            u8 dst_mac[6], u32 val, u8 is_add)
332 {
333   BVT (clib_bihash_kv) kv, value;
334   u32 old_val = ~0;
335
336   make_mac_fib_key (&kv, bd_index, src_mac, dst_mac);
337
338   if (BV (clib_bihash_search) (&lgm->l2_fib, &kv, &value) == 0)
339     old_val = value.value;
340
341   if (!is_add)
342     BV (clib_bihash_add_del) (&lgm->l2_fib, &kv, 0 /* is_add */ );
343   else
344     {
345       kv.value = val;
346       BV (clib_bihash_add_del) (&lgm->l2_fib, &kv, 1 /* is_add */ );
347     }
348   return old_val;
349 }
350
351 static void
352 l2_fib_init (lisp_gpe_main_t * lgm)
353 {
354   BV (clib_bihash_init) (&lgm->l2_fib, "l2 fib",
355                          1 << max_log2 (L2_FIB_DEFAULT_HASH_NUM_BUCKETS),
356                          L2_FIB_DEFAULT_HASH_MEMORY_SIZE);
357
358   /*
359    * the result from a 'miss' in a L2 Table
360    */
361   lgm->l2_lb_miss = load_balance_create (1, DPO_PROTO_IP4, 0);
362   load_balance_set_bucket (lgm->l2_lb_miss, 0, drop_dpo_get (DPO_PROTO_IP4));
363 }
364
365 /**
366  * @brief Add/Delete LISP L2 forwarding entry.
367  *
368  * Coordinates the creation/removal of forwarding entries for L2 LISP overlay:
369  * creates lisp-gpe tunnel and injects new entry in Source/Dest L2 FIB.
370  *
371  * @param[in]   lgm     Reference to @ref lisp_gpe_main_t.
372  * @param[in]   a       Parameters for building the forwarding entry.
373  *
374  * @return 0 on success.
375  */
376 static int
377 add_del_l2_fwd_entry (lisp_gpe_main_t * lgm,
378                       vnet_lisp_gpe_add_del_fwd_entry_args_t * a)
379 {
380   /* lisp_gpe_fwd_entry_key_t key; */
381   /* lisp_fwd_entry_t *lfe; */
382   /* fib_protocol_t fproto; */
383   /* uword *bd_indexp; */
384
385   /* bd_indexp = hash_get (bdm->bd_index_by_bd_id, a->bd_id); */
386   /* if (!bd_indexp) */
387   /*   { */
388   /*     clib_warning ("bridge domain %d doesn't exist", a->bd_id); */
389   /*     return -1; */
390   /*   } */
391
392   /* lfe = find_fwd_entry(lgm, a, &key); */
393
394   /* if (NULL != lfe) */
395   /*   /\* don't support updates *\/ */
396   /*   return VNET_API_ERROR_INVALID_VALUE; */
397
398   /* int rv; */
399   /* u32 tun_index; */
400   /* fib_node_index_t old_path_list; */
401   /* bd_main_t *bdm = &bd_main; */
402   /* fib_route_path_t *rpaths; */
403   /* lisp_gpe_tunnel_t *t; */
404   /* const dpo_id_t *dpo; */
405   /* index_t lbi; */
406
407   /* /\* create tunnel *\/ */
408   /* rv = add_del_ip_tunnel (a, 1 /\* is_l2 *\/ , &tun_index, NULL); */
409   /* if (rv) */
410   /*   return rv; */
411
412   /* bd_indexp = hash_get (bdm->bd_index_by_bd_id, a->bd_id); */
413   /* if (!bd_indexp) */
414   /*   { */
415   /*     clib_warning ("bridge domain %d doesn't exist", a->bd_id); */
416   /*     return -1; */
417   /*   } */
418
419   /* t = pool_elt_at_index (lgm->tunnels, tun_index); */
420   /* old_path_list = t->l2_path_list; */
421
422   /* if (LISP_NO_ACTION == t->action) */
423   /*   { */
424   /*     rpaths = lisp_gpe_mk_paths_for_sub_tunnels (t); */
425
426   /*     t->l2_path_list = fib_path_list_create (FIB_PATH_LIST_FLAG_NONE, */
427   /*                                          rpaths); */
428
429   /*     vec_free (rpaths); */
430   /*     fib_path_list_lock (t->l2_path_list); */
431
432   /*     dpo = fib_path_list_contribute_forwarding (t->l2_path_list, */
433   /*                                             FIB_FORW_CHAIN_TYPE_UNICAST_IP); */
434   /*     lbi = dpo->dpoi_index; */
435   /*   } */
436   /* else if (LISP_SEND_MAP_REQUEST == t->action) */
437   /*   { */
438   /*     lbi = lgm->l2_lb_cp_lkup; */
439   /*   } */
440   /* else */
441   /*   { */
442   /*     lbi = lgm->l2_lb_miss; */
443   /*   } */
444   /* fib_path_list_unlock (old_path_list); */
445
446   /* /\* add entry to l2 lisp fib *\/ */
447   /* lisp_l2_fib_add_del_entry (lgm, bd_indexp[0], gid_address_mac (&a->lcl_eid), */
448   /*                         gid_address_mac (&a->rmt_eid), lbi, a->is_add); */
449   return 0;
450 }
451
452 /**
453  * @brief Forwarding entry create/remove dispatcher.
454  *
455  * Calls l2 or l3 forwarding entry add/del function based on input data.
456  *
457  * @param[in]   a       Forwarding entry parameters.
458  * @param[out]  hw_if_indexp    NOT USED
459  *
460  * @return 0 on success.
461  */
462 int
463 vnet_lisp_gpe_add_del_fwd_entry (vnet_lisp_gpe_add_del_fwd_entry_args_t * a,
464                                  u32 * hw_if_indexp)
465 {
466   lisp_gpe_main_t *lgm = &lisp_gpe_main;
467   u8 type;
468
469   if (vnet_lisp_gpe_enable_disable_status () == 0)
470     {
471       clib_warning ("LISP is disabled!");
472       return VNET_API_ERROR_LISP_DISABLED;
473     }
474
475   type = gid_address_type (&a->rmt_eid);
476   switch (type)
477     {
478     case GID_ADDR_IP_PREFIX:
479       if (a->is_add)
480         return add_ip_fwd_entry (lgm, a);
481       else
482         return del_ip_fwd_entry (lgm, a);
483       break;
484     case GID_ADDR_MAC:
485       return add_del_l2_fwd_entry (lgm, a);
486     default:
487       clib_warning ("Forwarding entries for type %d not supported!", type);
488       return -1;
489     }
490 }
491
492 /** CLI command to add/del forwarding entry. */
493 static clib_error_t *
494 lisp_gpe_add_del_fwd_entry_command_fn (vlib_main_t * vm,
495                                        unformat_input_t * input,
496                                        vlib_cli_command_t * cmd)
497 {
498   unformat_input_t _line_input, *line_input = &_line_input;
499   u8 is_add = 1;
500   ip_address_t lloc, rloc;
501   clib_error_t *error = 0;
502   gid_address_t _reid, *reid = &_reid, _leid, *leid = &_leid;
503   u8 reid_set = 0, leid_set = 0, is_negative = 0, vrf_set = 0, vni_set = 0;
504   u32 vni, vrf, action = ~0, p, w;
505   locator_pair_t pair, *pairs = 0;
506   int rv;
507
508   /* Get a line of input. */
509   if (!unformat_user (input, unformat_line_input, line_input))
510     return 0;
511
512   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
513     {
514       if (unformat (line_input, "del"))
515         is_add = 0;
516       else if (unformat (line_input, "add"))
517         is_add = 1;
518       else if (unformat (line_input, "leid %U", unformat_gid_address, leid))
519         {
520           leid_set = 1;
521         }
522       else if (unformat (line_input, "reid %U", unformat_gid_address, reid))
523         {
524           reid_set = 1;
525         }
526       else if (unformat (line_input, "vni %u", &vni))
527         {
528           gid_address_vni (leid) = vni;
529           gid_address_vni (reid) = vni;
530           vni_set = 1;
531         }
532       else if (unformat (line_input, "vrf %u", &vrf))
533         {
534           vrf_set = 1;
535         }
536       else if (unformat (line_input, "negative action %U",
537                          unformat_negative_mapping_action, &action))
538         {
539           is_negative = 1;
540         }
541       else if (unformat (line_input, "loc-pair %U %U p %d w %d",
542                          unformat_ip_address, &lloc,
543                          unformat_ip_address, &rloc, &p, &w))
544         {
545           pair.lcl_loc = lloc;
546           pair.rmt_loc = rloc;
547           pair.priority = p;
548           pair.weight = w;
549           vec_add1 (pairs, pair);
550         }
551       else
552         {
553           error = unformat_parse_error (line_input);
554           goto done;
555         }
556     }
557   unformat_free (line_input);
558
559   if (!vni_set || !vrf_set)
560     {
561       error = clib_error_return (0, "vni and vrf must be set!");
562       goto done;
563     }
564
565   if (!reid_set)
566     {
567       error = clib_error_return (0, "remote eid must be set!");
568       goto done;
569     }
570
571   if (is_negative)
572     {
573       if (~0 == action)
574         {
575           error = clib_error_return (0, "no action set for negative tunnel!");
576           goto done;
577         }
578     }
579   else
580     {
581       if (vec_len (pairs) == 0)
582         {
583           error = clib_error_return (0, "expected ip4/ip6 locators.");
584           goto done;
585         }
586     }
587
588   if (!leid_set)
589     {
590       /* if leid not set, make sure it's the same AFI like reid */
591       gid_address_type (leid) = gid_address_type (reid);
592       if (GID_ADDR_IP_PREFIX == gid_address_type (reid))
593         gid_address_ip_version (leid) = gid_address_ip_version (reid);
594     }
595
596   /* add fwd entry */
597   vnet_lisp_gpe_add_del_fwd_entry_args_t _a, *a = &_a;
598   memset (a, 0, sizeof (a[0]));
599
600   a->is_add = is_add;
601   a->vni = vni;
602   a->table_id = vrf;
603   gid_address_copy (&a->lcl_eid, leid);
604   gid_address_copy (&a->rmt_eid, reid);
605   a->locator_pairs = pairs;
606
607   rv = vnet_lisp_gpe_add_del_fwd_entry (a, 0);
608   if (0 != rv)
609     {
610       error = clib_error_return (0, "failed to %s gpe tunnel!",
611                                  is_add ? "add" : "delete");
612     }
613
614 done:
615   vec_free (pairs);
616   return error;
617 }
618
619 /* *INDENT-OFF* */
620 VLIB_CLI_COMMAND (lisp_gpe_add_del_fwd_entry_command, static) = {
621   .path = "lisp gpe entry",
622   .short_help = "lisp gpe entry add/del vni <vni> vrf <vrf> [leid <leid>]"
623       "reid <reid> [loc-pair <lloc> <rloc> p <priority> w <weight>] "
624       "[negative action <action>]",
625   .function = lisp_gpe_add_del_fwd_entry_command_fn,
626 };
627 /* *INDENT-ON* */
628
629 static u8 *
630 format_lisp_fwd_path (u8 * s, va_list ap)
631 {
632   lisp_fwd_path_t *lfp = va_arg (ap, lisp_fwd_path_t *);
633
634   s = format (s, "pirority:%d weight:%d ", lfp->priority, lfp->weight);
635   s = format (s, "adj:[%U]\n",
636               format_lisp_gpe_adjacency,
637               lisp_gpe_adjacency_get (lfp->lisp_adj),
638               LISP_GPE_ADJ_FORMAT_FLAG_NONE);
639
640   return (s);
641 }
642
643 static u8 *
644 format_lisp_gpe_fwd_entry (u8 * s, va_list ap)
645 {
646   lisp_fwd_entry_t *lfe = va_arg (ap, lisp_fwd_entry_t *);
647
648   s = format (s, "VNI:%d VRF:%d EID: %U -> %U",
649               lfe->key->vni, lfe->eid_table_id,
650               format_fid_address, &lfe->key->lcl,
651               format_fid_address, &lfe->key->rmt);
652   if (LISP_FWD_ENTRY_TYPE_NEGATIVE == lfe->type)
653     {
654       s = format (s, "\n Negative - action:%U",
655                   format_negative_mapping_action, lfe->action);
656     }
657   else
658     {
659       lisp_fwd_path_t *path;
660
661       s = format (s, "\n via:");
662       vec_foreach (path, lfe->paths)
663       {
664         s = format (s, "\n  %U", format_lisp_fwd_path, path);
665       }
666     }
667
668   return (s);
669 }
670
671 static clib_error_t *
672 lisp_gpe_fwd_entry_show (vlib_main_t * vm,
673                          unformat_input_t * input, vlib_cli_command_t * cmd)
674 {
675   lisp_fwd_entry_t *lfe;
676
677 /* *INDENT-OFF* */
678   pool_foreach (lfe, lisp_fwd_entry_pool,
679   ({
680     vlib_cli_output (vm, "%U", format_lisp_gpe_fwd_entry, lfe);
681   }));
682 /* *INDENT-ON* */
683
684   return (NULL);
685 }
686
687 /* *INDENT-OFF* */
688 VLIB_CLI_COMMAND (lisp_gpe_fwd_entry_show_command, static) = {
689   .path = "show lisp gpe entry",
690   .short_help = "show lisp gpe entry vni <vni> vrf <vrf> [leid <leid>] reid <reid>",
691   .function = lisp_gpe_fwd_entry_show,
692 };
693 /* *INDENT-ON* */
694
695 /** Check if LISP-GPE is enabled. */
696 u8
697 vnet_lisp_gpe_enable_disable_status (void)
698 {
699   lisp_gpe_main_t *lgm = &lisp_gpe_main;
700
701   return lgm->is_en;
702 }
703
704 /** Enable/disable LISP-GPE. */
705 clib_error_t *
706 vnet_lisp_gpe_enable_disable (vnet_lisp_gpe_enable_disable_args_t * a)
707 {
708   lisp_gpe_main_t *lgm = &lisp_gpe_main;
709
710   if (a->is_en)
711     {
712       lgm->is_en = 1;
713     }
714   else
715     {
716       CLIB_UNUSED (uword * val);
717       hash_pair_t *p;
718       u32 *dp_tables = 0, *dp_table;
719       vnet_lisp_gpe_add_del_iface_args_t _ai, *ai = &_ai;
720       lisp_fwd_entry_t *lfe;
721
722       /* remove all entries */
723       /* *INDENT-OFF* */
724       pool_foreach (lfe, lisp_fwd_entry_pool,
725       ({
726         del_ip_fwd_entry_i (lfe);
727       }));
728       /* *INDENT-ON* */
729
730       /* disable all l3 ifaces */
731
732       /* *INDENT-OFF* */
733       hash_foreach_pair(p, lgm->l3_ifaces.hw_if_index_by_dp_table, ({
734         vec_add1(dp_tables, p->key);
735       }));
736       /* *INDENT-ON* */
737
738       vec_foreach (dp_table, dp_tables)
739       {
740         ai->is_add = 0;
741         ai->table_id = dp_table[0];
742         ai->is_l2 = 0;
743
744         /* disables interface and removes defaults */
745         vnet_lisp_gpe_add_del_iface (ai, 0);
746       }
747
748       /* disable all l2 ifaces */
749       _vec_len (dp_tables) = 0;
750
751       /* *INDENT-OFF* */
752       hash_foreach_pair(p, lgm->l2_ifaces.hw_if_index_by_dp_table, ({
753         vec_add1(dp_tables, p->key);
754       }));
755       /* *INDENT-ON* */
756
757       vec_foreach (dp_table, dp_tables)
758       {
759         ai->is_add = 0;
760         ai->bd_id = dp_table[0];
761         ai->is_l2 = 1;
762
763         /* disables interface and removes defaults */
764         vnet_lisp_gpe_add_del_iface (ai, 0);
765       }
766
767       vec_free (dp_tables);
768       lgm->is_en = 0;
769     }
770
771   return 0;
772 }
773
774 /** CLI command to enable/disable LISP-GPE. */
775 static clib_error_t *
776 lisp_gpe_enable_disable_command_fn (vlib_main_t * vm,
777                                     unformat_input_t * input,
778                                     vlib_cli_command_t * cmd)
779 {
780   unformat_input_t _line_input, *line_input = &_line_input;
781   u8 is_en = 1;
782   vnet_lisp_gpe_enable_disable_args_t _a, *a = &_a;
783
784   /* Get a line of input. */
785   if (!unformat_user (input, unformat_line_input, line_input))
786     return 0;
787
788   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
789     {
790       if (unformat (line_input, "enable"))
791         is_en = 1;
792       else if (unformat (line_input, "disable"))
793         is_en = 0;
794       else
795         {
796           return clib_error_return (0, "parse error: '%U'",
797                                     format_unformat_error, line_input);
798         }
799     }
800   a->is_en = is_en;
801   return vnet_lisp_gpe_enable_disable (a);
802 }
803
804 /* *INDENT-OFF* */
805 VLIB_CLI_COMMAND (enable_disable_lisp_gpe_command, static) = {
806   .path = "lisp gpe",
807   .short_help = "lisp gpe [enable|disable]",
808   .function = lisp_gpe_enable_disable_command_fn,
809 };
810 /* *INDENT-ON* */
811
812 /** CLI command to show LISP-GPE interfaces. */
813 static clib_error_t *
814 lisp_show_iface_command_fn (vlib_main_t * vm,
815                             unformat_input_t * input,
816                             vlib_cli_command_t * cmd)
817 {
818   lisp_gpe_main_t *lgm = &lisp_gpe_main;
819   hash_pair_t *p;
820
821   vlib_cli_output (vm, "%=10s%=12s", "vrf", "hw_if_index");
822
823   /* *INDENT-OFF* */
824   hash_foreach_pair (p, lgm->l3_ifaces.hw_if_index_by_dp_table, ({
825     vlib_cli_output (vm, "%=10d%=10d", p->key, p->value[0]);
826   }));
827   /* *INDENT-ON* */
828
829   if (0 != lgm->l2_ifaces.hw_if_index_by_dp_table)
830     {
831       vlib_cli_output (vm, "%=10s%=12s", "bd_id", "hw_if_index");
832       /* *INDENT-OFF* */
833       hash_foreach_pair (p, lgm->l2_ifaces.hw_if_index_by_dp_table, ({
834         vlib_cli_output (vm, "%=10d%=10d", p->key, p->value[0]);
835       }));
836       /* *INDENT-ON* */
837     }
838   return 0;
839 }
840
841 /* *INDENT-OFF* */
842 VLIB_CLI_COMMAND (lisp_show_iface_command) = {
843     .path = "show lisp gpe interface",
844     .short_help = "show lisp gpe interface",
845     .function = lisp_show_iface_command_fn,
846 };
847 /* *INDENT-ON* */
848
849 /** Format LISP-GPE status. */
850 u8 *
851 format_vnet_lisp_gpe_status (u8 * s, va_list * args)
852 {
853   lisp_gpe_main_t *lgm = &lisp_gpe_main;
854   return format (s, "%s", lgm->is_en ? "enabled" : "disabled");
855 }
856
857
858 /** LISP-GPE init function. */
859 clib_error_t *
860 lisp_gpe_init (vlib_main_t * vm)
861 {
862   lisp_gpe_main_t *lgm = &lisp_gpe_main;
863   clib_error_t *error = 0;
864
865   if ((error = vlib_call_init_function (vm, ip_main_init)))
866     return error;
867
868   if ((error = vlib_call_init_function (vm, ip4_lookup_init)))
869     return error;
870
871   lgm->vnet_main = vnet_get_main ();
872   lgm->vlib_main = vm;
873   lgm->im4 = &ip4_main;
874   lgm->im6 = &ip6_main;
875   lgm->lm4 = &ip4_main.lookup_main;
876   lgm->lm6 = &ip6_main.lookup_main;
877
878   lisp_gpe_fwd_entries = hash_create_mem (0,
879                                           sizeof (lisp_gpe_fwd_entry_key_t),
880                                           sizeof (uword));
881
882   l2_fib_init (lgm);
883
884   udp_register_dst_port (vm, UDP_DST_PORT_lisp_gpe,
885                          lisp_gpe_ip4_input_node.index, 1 /* is_ip4 */ );
886   udp_register_dst_port (vm, UDP_DST_PORT_lisp_gpe6,
887                          lisp_gpe_ip6_input_node.index, 0 /* is_ip4 */ );
888   return 0;
889 }
890
891 VLIB_INIT_FUNCTION (lisp_gpe_init);
892
893 /*
894  * fd.io coding-style-patch-verification: ON
895  *
896  * Local Variables:
897  * eval: (c-set-style "gnu")
898  * End:
899  */