L2 over LISP and GRE (VPP-457)
[vpp.git] / vnet / vnet / lisp-gpe / interface.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 /**
17  * @file
18  * @brief Common utility functions for LISP-GPE interfaces.
19  *
20  */
21
22 #include <vppinfra/error.h>
23 #include <vppinfra/hash.h>
24 #include <vnet/vnet.h>
25 #include <vnet/ip/ip.h>
26 #include <vnet/ip/udp.h>
27 #include <vnet/ethernet/ethernet.h>
28 #include <vnet/lisp-gpe/lisp_gpe.h>
29 #include <vnet/lisp-gpe/lisp_gpe_fwd_entry.h>
30 #include <vnet/lisp-gpe/lisp_gpe_tenant.h>
31 #include <vnet/adj/adj.h>
32 #include <vnet/fib/fib_table.h>
33 #include <vnet/fib/ip4_fib.h>
34 #include <vnet/fib/ip6_fib.h>
35 #include <vnet/lisp-cp/lisp_cp_dpo.h>
36
37 /**
38  * @brief The VLIB node arc/edge from the interface's TX node, to the L2
39  * load-balanceing node. Which is where all packets go
40  */
41 static uword l2_arc_to_lb;
42
43 #define foreach_lisp_gpe_tx_next        \
44   _(DROP, "error-drop")                 \
45   _(IP4_LOOKUP, "ip4-lookup")           \
46   _(IP6_LOOKUP, "ip6-lookup")
47
48 typedef enum
49 {
50 #define _(sym,str) LISP_GPE_TX_NEXT_##sym,
51   foreach_lisp_gpe_tx_next
52 #undef _
53     LISP_GPE_TX_N_NEXT,
54 } lisp_gpe_tx_next_t;
55
56 typedef struct
57 {
58   u32 tunnel_index;
59 } lisp_gpe_tx_trace_t;
60
61 u8 *
62 format_lisp_gpe_tx_trace (u8 * s, va_list * args)
63 {
64   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
65   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
66   lisp_gpe_tx_trace_t *t = va_arg (*args, lisp_gpe_tx_trace_t *);
67
68   s = format (s, "LISP-GPE-TX: tunnel %d", t->tunnel_index);
69   return s;
70 }
71
72 #define is_v4_packet(_h) ((*(u8*) _h) & 0xF0) == 0x40
73
74 /**
75  * @brief LISP-GPE interface TX (encap) function.
76  * @node lisp_gpe_interface_tx
77  *
78  * The LISP-GPE interface TX (encap) function.
79  *
80  * Looks up the associated tunnel based on the adjacency hit in the SD FIB
81  * and if the tunnel is multihomed it uses the flow hash to determine
82  * sub-tunnel, and rewrite string, to be used to encapsulate the packet.
83  *
84  * @param[in]   vm      vlib_main_t corresponding to the current thread.
85  * @param[in]   node    vlib_node_runtime_t data for this node.
86  * @param[in]   frame   vlib_frame_t whose contents should be dispatched.
87  *
88  * @return number of vectors in frame.
89  */
90 static uword
91 lisp_gpe_interface_tx (vlib_main_t * vm, vlib_node_runtime_t * node,
92                        vlib_frame_t * from_frame)
93 {
94   u32 n_left_from, next_index, *from, *to_next;
95   lisp_gpe_main_t *lgm = &lisp_gpe_main;
96
97   from = vlib_frame_vector_args (from_frame);
98   n_left_from = from_frame->n_vectors;
99
100   next_index = node->cached_next_index;
101
102   while (n_left_from > 0)
103     {
104       u32 n_left_to_next;
105
106       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
107
108       while (n_left_from > 0 && n_left_to_next > 0)
109         {
110           u32 bi0, adj_index0, next0;
111           const ip_adjacency_t *adj0;
112           const dpo_id_t *dpo0;
113           vlib_buffer_t *b0;
114           u8 is_v4_0;
115
116           bi0 = from[0];
117           to_next[0] = bi0;
118           from += 1;
119           to_next += 1;
120           n_left_from -= 1;
121           n_left_to_next -= 1;
122
123           b0 = vlib_get_buffer (vm, bi0);
124
125           /* Fixup the checksum and len fields in the LISP tunnel encap
126            * that was applied at the midchain node */
127           is_v4_0 = is_v4_packet (vlib_buffer_get_current (b0));
128           ip_udp_fixup_one (lgm->vlib_main, b0, is_v4_0);
129
130           /* Follow the DPO on which the midchain is stacked */
131           adj_index0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
132           adj0 = adj_get (adj_index0);
133           dpo0 = &adj0->sub_type.midchain.next_dpo;
134           next0 = dpo0->dpoi_next_node;
135           vnet_buffer (b0)->ip.adj_index[VLIB_TX] = dpo0->dpoi_index;
136
137           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
138             {
139               lisp_gpe_tx_trace_t *tr = vlib_add_trace (vm, node, b0,
140                                                         sizeof (*tr));
141               tr->tunnel_index = adj_index0;
142             }
143           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
144                                            n_left_to_next, bi0, next0);
145         }
146
147       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
148     }
149
150   return from_frame->n_vectors;
151 }
152
153 static u8 *
154 format_lisp_gpe_name (u8 * s, va_list * args)
155 {
156   u32 dev_instance = va_arg (*args, u32);
157   return format (s, "lisp_gpe%d", dev_instance);
158 }
159
160 /* *INDENT-OFF* */
161 VNET_DEVICE_CLASS (lisp_gpe_device_class) = {
162   .name = "LISP_GPE",
163   .format_device_name = format_lisp_gpe_name,
164   .format_tx_trace = format_lisp_gpe_tx_trace,
165   .tx_function = lisp_gpe_interface_tx,
166   .no_flatten_output_chains = 1,
167 };
168 /* *INDENT-ON* */
169
170 static uword
171 dummy_set_rewrite (vnet_main_t * vnm, u32 sw_if_index, u32 l3_type,
172                    void *dst_address, void *rewrite, uword max_rewrite_bytes)
173 {
174   return 0;
175 }
176
177 u8 *
178 format_lisp_gpe_header_with_length (u8 * s, va_list * args)
179 {
180   lisp_gpe_header_t *h = va_arg (*args, lisp_gpe_header_t *);
181   u32 max_header_bytes = va_arg (*args, u32);
182   u32 header_bytes;
183
184   header_bytes = sizeof (h[0]);
185   if (max_header_bytes != 0 && header_bytes > max_header_bytes)
186     return format (s, "lisp-gpe header truncated");
187
188   s = format (s, "flags: ");
189 #define _(n,v) if (h->flags & v) s = format (s, "%s ", #n);
190   foreach_lisp_gpe_flag_bit;
191 #undef _
192
193   s = format (s, "\n  ver_res %d res %d next_protocol %d iid %d(%x)",
194               h->ver_res, h->res, h->next_protocol,
195               clib_net_to_host_u32 (h->iid), clib_net_to_host_u32 (h->iid));
196   return s;
197 }
198
199 /* *INDENT-OFF* */
200 VNET_HW_INTERFACE_CLASS (lisp_gpe_hw_class) = {
201   .name = "LISP_GPE",
202   .format_header = format_lisp_gpe_header_with_length,
203   .set_rewrite = dummy_set_rewrite,
204 };
205 /* *INDENT-ON* */
206
207
208 typedef struct
209 {
210   u32 lb_index;
211 } l2_lisp_gpe_tx_trace_t;
212
213 static u8 *
214 format_l2_lisp_gpe_tx_trace (u8 * s, va_list * args)
215 {
216   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
217   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
218   l2_lisp_gpe_tx_trace_t *t = va_arg (*args, l2_lisp_gpe_tx_trace_t *);
219
220   s = format (s, "L2-LISP-GPE-TX: load-balance %d", t->lb_index);
221   return s;
222 }
223
224 /**
225  * @brief LISP-GPE interface TX (encap) function for L2 overlays.
226  * @node l2_lisp_gpe_interface_tx
227  *
228  * The L2 LISP-GPE interface TX (encap) function.
229  *
230  * Uses bridge domain index, source and destination ethernet addresses to
231  * lookup tunnel. If the tunnel is multihomed a flow has is used to determine
232  * the sub-tunnel and therefore the rewrite string to be used to encapsulate
233  * the packets.
234  *
235  * @param[in]   vm        vlib_main_t corresponding to the current thread.
236  * @param[in]   node      vlib_node_runtime_t data for this node.
237  * @param[in]   frame     vlib_frame_t whose contents should be dispatched.
238  *
239  * @return number of vectors in frame.
240  */
241 static uword
242 l2_lisp_gpe_interface_tx (vlib_main_t * vm, vlib_node_runtime_t * node,
243                           vlib_frame_t * from_frame)
244 {
245   u32 n_left_from, next_index, *from, *to_next;
246   lisp_gpe_main_t *lgm = &lisp_gpe_main;
247
248   from = vlib_frame_vector_args (from_frame);
249   n_left_from = from_frame->n_vectors;
250
251   next_index = node->cached_next_index;
252
253   while (n_left_from > 0)
254     {
255       u32 n_left_to_next;
256
257       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
258
259       while (n_left_from > 0 && n_left_to_next > 0)
260         {
261           vlib_buffer_t *b0;
262           u32 bi0, lbi0;
263           ethernet_header_t *e0;
264
265           bi0 = from[0];
266           to_next[0] = bi0;
267           from += 1;
268           to_next += 1;
269           n_left_from -= 1;
270           n_left_to_next -= 1;
271
272           b0 = vlib_get_buffer (vm, bi0);
273           e0 = vlib_buffer_get_current (b0);
274
275           vnet_buffer (b0)->lisp.overlay_afi = LISP_AFI_MAC;
276
277           /* lookup dst + src mac */
278           lbi0 = lisp_l2_fib_lookup (lgm, vnet_buffer (b0)->l2.bd_index,
279                                      e0->src_address, e0->dst_address);
280           vnet_buffer (b0)->ip.adj_index[VLIB_TX] = lbi0;
281
282
283           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
284             {
285               l2_lisp_gpe_tx_trace_t *tr = vlib_add_trace (vm, node, b0,
286                                                            sizeof (*tr));
287               tr->lb_index = lbi0;
288             }
289           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
290                                            n_left_to_next, bi0, l2_arc_to_lb);
291         }
292
293       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
294     }
295
296   return from_frame->n_vectors;
297 }
298
299 static u8 *
300 format_l2_lisp_gpe_name (u8 * s, va_list * args)
301 {
302   u32 dev_instance = va_arg (*args, u32);
303   return format (s, "l2_lisp_gpe%d", dev_instance);
304 }
305
306 /* *INDENT-OFF* */
307 VNET_DEVICE_CLASS (l2_lisp_gpe_device_class,static) = {
308   .name = "L2_LISP_GPE",
309   .format_device_name = format_l2_lisp_gpe_name,
310   .format_tx_trace = format_l2_lisp_gpe_tx_trace,
311   .tx_function = l2_lisp_gpe_interface_tx,
312   .no_flatten_output_chains = 1,
313 };
314 /* *INDENT-ON* */
315
316 static vnet_hw_interface_t *
317 lisp_gpe_create_iface (lisp_gpe_main_t * lgm, u32 vni, u32 dp_table,
318                        vnet_device_class_t * dev_class,
319                        tunnel_lookup_t * tuns)
320 {
321   u32 flen;
322   u32 hw_if_index = ~0;
323   u8 *new_name;
324   vnet_hw_interface_t *hi;
325   vnet_main_t *vnm = lgm->vnet_main;
326
327   /* create hw lisp_gpeX iface if needed, otherwise reuse existing */
328   flen = vec_len (lgm->free_tunnel_hw_if_indices);
329   if (flen > 0)
330     {
331       hw_if_index = lgm->free_tunnel_hw_if_indices[flen - 1];
332       _vec_len (lgm->free_tunnel_hw_if_indices) -= 1;
333
334       hi = vnet_get_hw_interface (vnm, hw_if_index);
335
336       /* rename interface */
337       new_name = format (0, "%U", dev_class->format_device_name, vni);
338
339       vec_add1 (new_name, 0);
340       vnet_rename_interface (vnm, hw_if_index, (char *) new_name);
341       vec_free (new_name);
342
343       /* clear old stats of freed interface before reuse */
344       vnet_interface_main_t *im = &vnm->interface_main;
345       vnet_interface_counter_lock (im);
346       vlib_zero_combined_counter (&im->combined_sw_if_counters
347                                   [VNET_INTERFACE_COUNTER_TX],
348                                   hi->sw_if_index);
349       vlib_zero_combined_counter (&im->combined_sw_if_counters
350                                   [VNET_INTERFACE_COUNTER_RX],
351                                   hi->sw_if_index);
352       vlib_zero_simple_counter (&im->sw_if_counters
353                                 [VNET_INTERFACE_COUNTER_DROP],
354                                 hi->sw_if_index);
355       vnet_interface_counter_unlock (im);
356     }
357   else
358     {
359       hw_if_index = vnet_register_interface (vnm, dev_class->index, vni,
360                                              lisp_gpe_hw_class.index, 0);
361       hi = vnet_get_hw_interface (vnm, hw_if_index);
362     }
363
364   hash_set (tuns->hw_if_index_by_dp_table, dp_table, hw_if_index);
365
366   /* set tunnel termination: post decap, packets are tagged as having been
367    * originated by lisp-gpe interface */
368   hash_set (tuns->sw_if_index_by_vni, vni, hi->sw_if_index);
369   hash_set (tuns->vni_by_sw_if_index, hi->sw_if_index, vni);
370
371   return hi;
372 }
373
374 static void
375 lisp_gpe_remove_iface (lisp_gpe_main_t * lgm, u32 hi_index, u32 dp_table,
376                        tunnel_lookup_t * tuns)
377 {
378   vnet_main_t *vnm = lgm->vnet_main;
379   vnet_hw_interface_t *hi;
380   uword *vnip;
381
382   hi = vnet_get_hw_interface (vnm, hi_index);
383
384   /* disable interface */
385   vnet_sw_interface_set_flags (vnm, hi->sw_if_index, 0 /* down */ );
386   vnet_hw_interface_set_flags (vnm, hi->hw_if_index, 0 /* down */ );
387   hash_unset (tuns->hw_if_index_by_dp_table, dp_table);
388   vec_add1 (lgm->free_tunnel_hw_if_indices, hi->hw_if_index);
389
390   /* clean tunnel termination and vni to sw_if_index binding */
391   vnip = hash_get (tuns->vni_by_sw_if_index, hi->sw_if_index);
392   if (0 == vnip)
393     {
394       clib_warning ("No vni associated to interface %d", hi->sw_if_index);
395       return;
396     }
397   hash_unset (tuns->sw_if_index_by_vni, vnip[0]);
398   hash_unset (tuns->vni_by_sw_if_index, hi->sw_if_index);
399 }
400
401 static void
402 lisp_gpe_iface_set_table (u32 sw_if_index, u32 table_id)
403 {
404   fib_node_index_t fib_index;
405
406   fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, table_id);
407   vec_validate (ip4_main.fib_index_by_sw_if_index, sw_if_index);
408   ip4_main.fib_index_by_sw_if_index[sw_if_index] = fib_index;
409   ip4_sw_interface_enable_disable (sw_if_index, 1);
410
411   fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, table_id);
412   vec_validate (ip6_main.fib_index_by_sw_if_index, sw_if_index);
413   ip6_main.fib_index_by_sw_if_index[sw_if_index] = fib_index;
414   ip6_sw_interface_enable_disable (sw_if_index, 1);
415 }
416
417 static void
418 lisp_gpe_tenant_del_default_routes (u32 table_id)
419 {
420   fib_protocol_t proto;
421
422   FOR_EACH_FIB_IP_PROTOCOL (proto)
423   {
424     fib_prefix_t prefix = {
425       .fp_proto = proto,
426     };
427     u32 fib_index;
428
429     fib_index = fib_table_find (prefix.fp_proto, table_id);
430     fib_table_entry_special_remove (fib_index, &prefix, FIB_SOURCE_LISP);
431     fib_table_unlock (fib_index, prefix.fp_proto);
432   }
433 }
434
435 static void
436 lisp_gpe_tenant_add_default_routes (u32 table_id)
437 {
438   fib_protocol_t proto;
439
440   FOR_EACH_FIB_IP_PROTOCOL (proto)
441   {
442     fib_prefix_t prefix = {
443       .fp_proto = proto,
444     };
445     u32 fib_index;
446
447     /*
448      * Add a deafult route that results in a control plane punt DPO
449      */
450     fib_index = fib_table_find_or_create_and_lock (prefix.fp_proto, table_id);
451     fib_table_entry_special_dpo_add (fib_index, &prefix, FIB_SOURCE_LISP,
452                                      FIB_ENTRY_FLAG_EXCLUSIVE,
453                                      lisp_cp_dpo_get (fib_proto_to_dpo
454                                                       (proto)));
455   }
456 }
457
458
459 /**
460  * @brief Add/del LISP-GPE L3 interface.
461  *
462  * Creates LISP-GPE interface, sets ingress arcs from lisp_gpeX_lookup,
463  * installs default routes that attract all traffic with no more specific
464  * routes to lgpe-ipx-lookup, set egress arcs to ipx-lookup, sets
465  * the interface in the right vrf and enables it.
466  *
467  * @param[in]   lgm     Reference to @ref lisp_gpe_main_t.
468  * @param[in]   a       Parameters to create interface.
469  *
470  * @return number of vectors in frame.
471  */
472 u32
473 lisp_gpe_add_l3_iface (lisp_gpe_main_t * lgm, u32 vni, u32 table_id)
474 {
475   vnet_main_t *vnm = lgm->vnet_main;
476   tunnel_lookup_t *l3_ifaces = &lgm->l3_ifaces;
477   vnet_hw_interface_t *hi;
478   uword *hip, *si;
479
480   hip = hash_get (l3_ifaces->hw_if_index_by_dp_table, table_id);
481
482   if (hip)
483     {
484       clib_warning ("vrf %d already mapped to a vni", table_id);
485       return ~0;
486     }
487
488   si = hash_get (l3_ifaces->sw_if_index_by_vni, vni);
489
490   if (si)
491     {
492       clib_warning ("Interface for vni %d already exists", vni);
493     }
494
495   /* create lisp iface and populate tunnel tables */
496   hi = lisp_gpe_create_iface (lgm, vni, table_id,
497                               &lisp_gpe_device_class, l3_ifaces);
498
499   /* insert default routes that point to lisp-cp lookup */
500   lisp_gpe_iface_set_table (hi->sw_if_index, table_id);
501   lisp_gpe_tenant_add_default_routes (table_id);
502
503   /* enable interface */
504   vnet_sw_interface_set_flags (vnm, hi->sw_if_index,
505                                VNET_SW_INTERFACE_FLAG_ADMIN_UP);
506   vnet_hw_interface_set_flags (vnm, hi->hw_if_index,
507                                VNET_HW_INTERFACE_FLAG_LINK_UP);
508
509   return (hi->sw_if_index);
510 }
511
512 void
513 lisp_gpe_del_l3_iface (lisp_gpe_main_t * lgm, u32 vni, u32 table_id)
514 {
515   vnet_main_t *vnm = lgm->vnet_main;
516   tunnel_lookup_t *l3_ifaces = &lgm->l3_ifaces;
517   vnet_hw_interface_t *hi;
518   uword *hip;
519
520   hip = hash_get (l3_ifaces->hw_if_index_by_dp_table, table_id);
521
522   if (hip == 0)
523     {
524       clib_warning ("The interface for vrf %d doesn't exist", table_id);
525       return;
526     }
527
528   hi = vnet_get_hw_interface (vnm, hip[0]);
529
530   lisp_gpe_remove_iface (lgm, hip[0], table_id, &lgm->l3_ifaces);
531
532   /* unset default routes */
533   ip4_sw_interface_enable_disable (hi->sw_if_index, 0);
534   ip6_sw_interface_enable_disable (hi->sw_if_index, 0);
535   lisp_gpe_tenant_del_default_routes (table_id);
536 }
537
538 /**
539  * @brief Add/del LISP-GPE L2 interface.
540  *
541  * Creates LISP-GPE interface, sets it in L2 mode in the appropriate
542  * bridge domain, sets egress arcs and enables it.
543  *
544  * @param[in]   lgm     Reference to @ref lisp_gpe_main_t.
545  * @param[in]   a       Parameters to create interface.
546  *
547  * @return number of vectors in frame.
548  */
549 u32
550 lisp_gpe_add_l2_iface (lisp_gpe_main_t * lgm, u32 vni, u32 bd_id)
551 {
552   vnet_main_t *vnm = lgm->vnet_main;
553   tunnel_lookup_t *l2_ifaces = &lgm->l2_ifaces;
554   vnet_hw_interface_t *hi;
555   uword *hip, *si;
556   u16 bd_index;
557
558   bd_index = bd_find_or_add_bd_index (&bd_main, bd_id);
559   hip = hash_get (l2_ifaces->hw_if_index_by_dp_table, bd_index);
560
561   if (hip)
562     {
563       clib_warning ("bridge domain %d already mapped to a vni", bd_id);
564       return ~0;
565     }
566
567   si = hash_get (l2_ifaces->sw_if_index_by_vni, vni);
568   if (si)
569     {
570       clib_warning ("Interface for vni %d already exists", vni);
571       return ~0;
572     }
573
574   /* create lisp iface and populate tunnel tables */
575   hi = lisp_gpe_create_iface (lgm, vni, bd_index,
576                               &l2_lisp_gpe_device_class, &lgm->l2_ifaces);
577
578   /* enable interface */
579   vnet_sw_interface_set_flags (vnm, hi->sw_if_index,
580                                VNET_SW_INTERFACE_FLAG_ADMIN_UP);
581   vnet_hw_interface_set_flags (vnm, hi->hw_if_index,
582                                VNET_HW_INTERFACE_FLAG_LINK_UP);
583
584   l2_arc_to_lb = vlib_node_add_named_next (vlib_get_main (),
585                                            hi->tx_node_index,
586                                            "l2-load-balance");
587
588   /* we're ready. add iface to l2 bridge domain */
589   set_int_l2_mode (lgm->vlib_main, vnm, MODE_L2_BRIDGE, hi->sw_if_index,
590                    bd_index, 0, 0, 0);
591
592   return (hi->sw_if_index);
593 }
594
595 /**
596  * @brief Add/del LISP-GPE L2 interface.
597  *
598  * Creates LISP-GPE interface, sets it in L2 mode in the appropriate
599  * bridge domain, sets egress arcs and enables it.
600  *
601  * @param[in]   lgm     Reference to @ref lisp_gpe_main_t.
602  * @param[in]   a       Parameters to create interface.
603  *
604  * @return number of vectors in frame.
605  */
606 void
607 lisp_gpe_del_l2_iface (lisp_gpe_main_t * lgm, u32 vni, u32 bd_id)
608 {
609   tunnel_lookup_t *l2_ifaces = &lgm->l2_ifaces;
610   u16 bd_index;
611   uword *hip;
612
613   bd_index = bd_find_or_add_bd_index (&bd_main, bd_id);
614   hip = hash_get (l2_ifaces->hw_if_index_by_dp_table, bd_index);
615
616   if (hip == 0)
617     {
618       clib_warning ("The interface for bridge domain %d doesn't exist",
619                     bd_id);
620       return;
621     }
622   lisp_gpe_remove_iface (lgm, hip[0], bd_index, &lgm->l2_ifaces);
623 }
624
625 static clib_error_t *
626 lisp_gpe_add_del_iface_command_fn (vlib_main_t * vm, unformat_input_t * input,
627                                    vlib_cli_command_t * cmd)
628 {
629   unformat_input_t _line_input, *line_input = &_line_input;
630   u8 is_add = 1;
631   u32 table_id, vni, bd_id;
632   u8 vni_is_set = 0, vrf_is_set = 0, bd_index_is_set = 0;
633
634   if (vnet_lisp_gpe_enable_disable_status () == 0)
635     {
636       return clib_error_return (0, "LISP is disabled");
637     }
638
639   /* Get a line of input. */
640   if (!unformat_user (input, unformat_line_input, line_input))
641     return 0;
642
643   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
644     {
645       if (unformat (line_input, "add"))
646         is_add = 1;
647       else if (unformat (line_input, "del"))
648         is_add = 0;
649       else if (unformat (line_input, "vrf %d", &table_id))
650         {
651           vrf_is_set = 1;
652         }
653       else if (unformat (line_input, "vni %d", &vni))
654         {
655           vni_is_set = 1;
656         }
657       else if (unformat (line_input, "bd %d", &bd_id))
658         {
659           bd_index_is_set = 1;
660         }
661       else
662         {
663           return clib_error_return (0, "parse error: '%U'",
664                                     format_unformat_error, line_input);
665         }
666     }
667
668   if (vrf_is_set && bd_index_is_set)
669     return clib_error_return (0,
670                               "Cannot set both vrf and brdige domain index!");
671
672   if (!vni_is_set)
673     return clib_error_return (0, "vni must be set!");
674
675   if (!vrf_is_set && !bd_index_is_set)
676     return clib_error_return (0, "vrf or bridge domain index must be set!");
677
678   if (bd_index_is_set)
679     {
680       if (is_add)
681         {
682           if (~0 == lisp_gpe_tenant_l2_iface_add_or_lock (vni, bd_id))
683             return clib_error_return (0, "L2 interface not created");
684         }
685       else
686         lisp_gpe_tenant_l2_iface_unlock (vni);
687     }
688   else
689     {
690       if (is_add)
691         {
692           if (~0 == lisp_gpe_tenant_l3_iface_add_or_lock (vni, table_id))
693             return clib_error_return (0, "L3 interface not created");
694         }
695       else
696         lisp_gpe_tenant_l3_iface_unlock (vni);
697     }
698
699   return (NULL);
700 }
701
702 /* *INDENT-OFF* */
703 VLIB_CLI_COMMAND (add_del_lisp_gpe_iface_command, static) = {
704   .path = "lisp gpe iface",
705   .short_help = "lisp gpe iface add/del vni <vni> vrf <vrf>",
706   .function = lisp_gpe_add_del_iface_command_fn,
707 };
708 /* *INDENT-ON* */
709
710 /*
711  * fd.io coding-style-patch-verification: ON
712  *
713  * Local Variables:
714  * eval: (c-set-style "gnu")
715  * End:
716  */