FIB: store the node type not the function pointer.
[vpp.git] / src / vnet / gre / interface.c
1 /*
2  * gre_interface.c: gre interfaces
3  *
4  * Copyright (c) 2012 Cisco and/or its affiliates.
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #include <vnet/vnet.h>
19 #include <vnet/pg/pg.h>
20 #include <vnet/gre/gre.h>
21 #include <vnet/ip/format.h>
22 #include <vnet/fib/ip4_fib.h>
23 #include <vnet/fib/ip6_fib.h>
24 #include <vnet/adj/adj_midchain.h>
25 #include <vnet/adj/adj_nbr.h>
26 #include <vnet/mpls/mpls.h>
27
28 static const char *gre_tunnel_type_names[] = GRE_TUNNEL_TYPE_NAMES;
29
30 static inline u64
31 gre4_mk_key (const ip4_address_t *src,
32             const ip4_address_t *dst,
33             u32 out_fib_index)
34 {
35   // FIXME. the fib index should be part of the key
36   return ((u64)src->as_u32 << 32 | (u64)dst->as_u32);
37 }
38
39 static u8 *
40 format_gre_tunnel_type (u8 * s, va_list * args)
41 {
42   gre_tunnel_type_t type = va_arg (*args, gre_tunnel_type_t);
43
44   return (format(s, "%s", gre_tunnel_type_names[type]));
45 }
46
47 static u8 *
48 format_gre_tunnel (u8 * s, va_list * args)
49 {
50   gre_tunnel_t * t = va_arg (*args, gre_tunnel_t *);
51   gre_main_t * gm = &gre_main;
52   u8 is_ipv6 = t->tunnel_dst.fp_proto == FIB_PROTOCOL_IP6 ? 1 : 0;
53
54   if (!is_ipv6)
55       s = format (s,
56                   "[%d] %U (src) %U (dst) payload %U outer_fib_index %d",
57                   t - gm->tunnels,
58                   format_ip4_address, &t->tunnel_src.ip4,
59                   format_ip4_address, &t->tunnel_dst.fp_addr.ip4,
60                   format_gre_tunnel_type, t->type,
61                   t->outer_fib_index);
62   else
63       s = format (s,
64                   "[%d] %U (src) %U (dst) payload %U outer_fib_index %d",
65                   t - gm->tunnels,
66                   format_ip6_address, &t->tunnel_src.ip6,
67                   format_ip6_address, &t->tunnel_dst.fp_addr.ip6,
68                   format_gre_tunnel_type, t->type,
69                   t->outer_fib_index);
70
71   return s;
72 }
73
74 static gre_tunnel_t *
75 gre_tunnel_db_find (const ip46_address_t *src,
76                     const ip46_address_t *dst,
77                     u32 out_fib_index,
78                     u8 is_ipv6)
79 {
80   gre_main_t * gm = &gre_main;
81   uword * p;
82   u64 key4, key6[4];
83
84   if (!is_ipv6)
85     {
86       key4 = gre4_mk_key(&src->ip4, &dst->ip4, out_fib_index);
87       p = hash_get (gm->tunnel_by_key4, key4);
88     }
89   else
90     {
91       key6[0] = src->ip6.as_u64[0];
92       key6[1] = src->ip6.as_u64[1];
93       key6[2] = dst->ip6.as_u64[0];
94       key6[3] = dst->ip6.as_u64[1];
95       p = hash_get_mem (gm->tunnel_by_key6, key6);
96     }
97
98   if (NULL == p)
99     return (NULL);
100
101   return (pool_elt_at_index (gm->tunnels, p[0]));
102 }
103
104 static void
105 gre_tunnel_db_add (const gre_tunnel_t *t)
106 {
107   gre_main_t * gm = &gre_main;
108   u64 key4, key6[4], *key6_copy;
109   u8 is_ipv6 = t->tunnel_dst.fp_proto == FIB_PROTOCOL_IP6 ? 1 : 0;
110
111   if (!is_ipv6)
112     {
113       key4 = gre4_mk_key(&t->tunnel_src.ip4, &t->tunnel_dst.fp_addr.ip4,
114                        t->outer_fib_index);
115       hash_set (gm->tunnel_by_key4, key4, t - gm->tunnels);
116     }
117   else
118     {
119       key6[0] = t->tunnel_src.ip6.as_u64[0];
120       key6[1] = t->tunnel_src.ip6.as_u64[1];
121       key6[2] = t->tunnel_dst.fp_addr.ip6.as_u64[0];
122       key6[3] = t->tunnel_dst.fp_addr.ip6.as_u64[1];
123       key6_copy = clib_mem_alloc (sizeof (key6));
124       clib_memcpy (key6_copy, key6, sizeof (key6));
125       hash_set_mem (gm->tunnel_by_key6, key6_copy, t - gm->tunnels);
126     }
127 }
128
129 static void
130 gre_tunnel_db_remove (const gre_tunnel_t *t)
131 {
132   gre_main_t * gm = &gre_main;
133   u64 key4, key6[4];
134   u8 is_ipv6 = t->tunnel_dst.fp_proto == FIB_PROTOCOL_IP6 ? 1 : 0;
135
136   if (!is_ipv6)
137     {
138       key4 = gre4_mk_key(&t->tunnel_src.ip4, &t->tunnel_dst.fp_addr.ip4,
139                          t->outer_fib_index);
140       hash_unset (gm->tunnel_by_key4, key4);
141     }
142   else
143     {
144       key6[0] = t->tunnel_src.ip6.as_u64[0];
145       key6[1] = t->tunnel_src.ip6.as_u64[1];
146       key6[2] = t->tunnel_dst.fp_addr.ip6.as_u64[0];
147       key6[3] = t->tunnel_dst.fp_addr.ip6.as_u64[1];
148       hash_unset_mem (gm->tunnel_by_key6, key6);
149     }
150
151 }
152
153 static gre_tunnel_t *
154 gre_tunnel_from_fib_node (fib_node_t *node)
155 {
156     ASSERT(FIB_NODE_TYPE_GRE_TUNNEL == node->fn_type);
157     return ((gre_tunnel_t*) (((char*)node) -
158                              STRUCT_OFFSET_OF(gre_tunnel_t, node)));
159 }
160
161 /**
162  * gre_tunnel_stack
163  *
164  * 'stack' (resolve the recursion for) the tunnel's midchain adjacency
165  */
166 void
167 gre_tunnel_stack (adj_index_t ai)
168 {
169     gre_main_t * gm = &gre_main;
170     ip_adjacency_t *adj;
171     gre_tunnel_t *gt;
172     u32 sw_if_index;
173
174     adj = adj_get(ai);
175     sw_if_index = adj->rewrite_header.sw_if_index;
176
177     if ((vec_len(gm->tunnel_index_by_sw_if_index) < sw_if_index) ||
178         (~0 == gm->tunnel_index_by_sw_if_index[sw_if_index]))
179         return;
180
181     gt = pool_elt_at_index(gm->tunnels,
182                            gm->tunnel_index_by_sw_if_index[sw_if_index]);
183
184     /*
185      * find the adjacency that is contributed by the FIB entry
186      * that this tunnel resovles via, and use it as the next adj
187      * in the midchain
188      */
189     if (vnet_hw_interface_get_flags(vnet_get_main(),
190                                     gt->hw_if_index) &
191         VNET_HW_INTERFACE_FLAG_LINK_UP)
192     {
193         adj_nbr_midchain_stack(
194             ai,
195             fib_entry_contribute_ip_forwarding(gt->fib_entry_index));
196     }
197     else
198     {
199         adj_nbr_midchain_unstack(ai);
200     }
201 }
202
203 /**
204  * @brief Call back when restacking all adjacencies on a GRE interface
205  */
206 static adj_walk_rc_t
207 gre_adj_walk_cb (adj_index_t ai,
208                  void *ctx)
209 {
210     gre_tunnel_stack(ai);
211
212     return (ADJ_WALK_RC_CONTINUE);
213 }
214
215 static void
216 gre_tunnel_restack (gre_tunnel_t *gt)
217 {
218     fib_protocol_t proto;
219
220     /*
221      * walk all the adjacencies on th GRE interface and restack them
222      */
223     FOR_EACH_FIB_IP_PROTOCOL(proto)
224     {
225         adj_nbr_walk(gt->sw_if_index,
226                      proto,
227                      gre_adj_walk_cb,
228                      NULL);
229     }
230 }
231
232 /**
233  * Function definition to backwalk a FIB node
234  */
235 static fib_node_back_walk_rc_t
236 gre_tunnel_back_walk (fib_node_t *node,
237                       fib_node_back_walk_ctx_t *ctx)
238 {
239     gre_tunnel_restack(gre_tunnel_from_fib_node(node));
240
241     return (FIB_NODE_BACK_WALK_CONTINUE);
242 }
243
244 /**
245  * Function definition to get a FIB node from its index
246  */
247 static fib_node_t*
248 gre_tunnel_fib_node_get (fib_node_index_t index)
249 {
250     gre_tunnel_t * gt;
251     gre_main_t * gm;
252
253     gm  = &gre_main;
254     gt = pool_elt_at_index(gm->tunnels, index);
255
256     return (&gt->node);
257 }
258
259 /**
260  * Function definition to inform the FIB node that its last lock has gone.
261  */
262 static void
263 gre_tunnel_last_lock_gone (fib_node_t *node)
264 {
265     /*
266      * The MPLS GRE tunnel is a root of the graph. As such
267      * it never has children and thus is never locked.
268      */
269     ASSERT(0);
270 }
271
272 /*
273  * Virtual function table registered by MPLS GRE tunnels
274  * for participation in the FIB object graph.
275  */
276 const static fib_node_vft_t gre_vft = {
277     .fnv_get = gre_tunnel_fib_node_get,
278     .fnv_last_lock = gre_tunnel_last_lock_gone,
279     .fnv_back_walk = gre_tunnel_back_walk,
280 };
281
282 static int
283 vnet_gre_tunnel_add (vnet_gre_add_del_tunnel_args_t *a,
284                      u32 * sw_if_indexp)
285 {
286   gre_main_t * gm = &gre_main;
287   vnet_main_t * vnm = gm->vnet_main;
288   ip4_main_t * im4 = &ip4_main;
289   ip6_main_t * im6 = &ip6_main;
290   gre_tunnel_t * t;
291   vnet_hw_interface_t * hi;
292   u32 hw_if_index, sw_if_index;
293   u32 outer_fib_index;
294   u8 address[6];
295   clib_error_t *error;
296   u8 is_ipv6 = a->is_ipv6;
297
298   if (!is_ipv6)
299     outer_fib_index = ip4_fib_index_from_table_id(a->outer_fib_id);
300   else
301     outer_fib_index = ip6_fib_index_from_table_id(a->outer_fib_id);
302
303   if (~0 == outer_fib_index)
304     return VNET_API_ERROR_NO_SUCH_FIB;
305
306   t = gre_tunnel_db_find(&a->src, &a->dst, a->outer_fib_id, a->is_ipv6);
307
308   if (NULL != t)
309     return VNET_API_ERROR_INVALID_VALUE;
310
311   pool_get_aligned (gm->tunnels, t, CLIB_CACHE_LINE_BYTES);
312   memset (t, 0, sizeof (*t));
313   fib_node_init(&t->node, FIB_NODE_TYPE_GRE_TUNNEL);
314
315   if (a->teb)
316       t->type = GRE_TUNNEL_TYPE_TEB;
317   else
318       t->type = GRE_TUNNEL_TYPE_L3;
319
320   if (vec_len (gm->free_gre_tunnel_hw_if_indices[t->type]) > 0) {
321       vnet_interface_main_t * im = &vnm->interface_main;
322
323       hw_if_index = gm->free_gre_tunnel_hw_if_indices[t->type]
324           [vec_len (gm->free_gre_tunnel_hw_if_indices[t->type])-1];
325       _vec_len (gm->free_gre_tunnel_hw_if_indices[t->type]) -= 1;
326
327       hi = vnet_get_hw_interface (vnm, hw_if_index);
328       hi->dev_instance = t - gm->tunnels;
329       hi->hw_instance = hi->dev_instance;
330
331       /* clear old stats of freed tunnel before reuse */
332       sw_if_index = hi->sw_if_index;
333       vnet_interface_counter_lock(im);
334       vlib_zero_combined_counter
335           (&im->combined_sw_if_counters[VNET_INTERFACE_COUNTER_TX], sw_if_index);
336       vlib_zero_combined_counter
337           (&im->combined_sw_if_counters[VNET_INTERFACE_COUNTER_RX], sw_if_index);
338       vlib_zero_simple_counter
339           (&im->sw_if_counters[VNET_INTERFACE_COUNTER_DROP], sw_if_index);
340         vnet_interface_counter_unlock(im);
341       if (GRE_TUNNEL_TYPE_TEB == t->type)
342       {
343           t->l2_tx_arc = vlib_node_add_named_next(vlib_get_main(),
344                                                   hi->tx_node_index,
345                                                   "adj-l2-midchain");
346       }
347     } else {
348       if (GRE_TUNNEL_TYPE_TEB == t->type)
349       {
350         /* Default MAC address (d00b:eed0:0000 + sw_if_index) */
351         memset (address, 0, sizeof (address));
352         address[0] = 0xd0;
353         address[1] = 0x0b;
354         address[2] = 0xee;
355         address[3] = 0xd0;
356         address[4] = t - gm->tunnels;
357
358         error = ethernet_register_interface(vnm,
359                                             gre_device_teb_class.index,
360                                             t - gm->tunnels, address,
361                                             &hw_if_index,
362                                             0);
363
364         if (error)
365         {
366           clib_error_report (error);
367           return VNET_API_ERROR_INVALID_REGISTRATION;
368         }
369         hi = vnet_get_hw_interface (vnm, hw_if_index);
370
371         t->l2_tx_arc = vlib_node_add_named_next(vlib_get_main(),
372                                                 hi->tx_node_index,
373                                                 "adj-l2-midchain");
374       } else {
375         hw_if_index = vnet_register_interface(vnm,
376                                               gre_device_class.index,
377                                               t - gm->tunnels,
378                                               gre_hw_interface_class.index,
379                                               t - gm->tunnels);
380       }
381       hi = vnet_get_hw_interface (vnm, hw_if_index);
382       sw_if_index = hi->sw_if_index;
383     }
384
385   t->hw_if_index = hw_if_index;
386   t->outer_fib_index = outer_fib_index;
387   t->sw_if_index = sw_if_index;
388   t->l2_adj_index = ADJ_INDEX_INVALID;
389
390   vec_validate_init_empty (gm->tunnel_index_by_sw_if_index, sw_if_index, ~0);
391   gm->tunnel_index_by_sw_if_index[sw_if_index] = t - gm->tunnels;
392
393   if (!is_ipv6)
394     {
395       vec_validate (im4->fib_index_by_sw_if_index, sw_if_index);
396       hi->min_packet_bytes = 64 + sizeof (gre_header_t) + sizeof (ip4_header_t);
397     }
398   else
399     {
400       vec_validate (im6->fib_index_by_sw_if_index, sw_if_index);
401       hi->min_packet_bytes = 64 + sizeof (gre_header_t) + sizeof (ip6_header_t);
402     }
403
404   hi->per_packet_overhead_bytes =
405       /* preamble */ 8 + /* inter frame gap */ 12;
406
407   /* Standard default gre MTU. */
408   hi->max_l3_packet_bytes[VLIB_RX] = hi->max_l3_packet_bytes[VLIB_TX] = 9000;
409
410   /*
411    * source the FIB entry for the tunnel's destination
412    * and become a child thereof. The tunnel will then get poked
413    * when the forwarding for the entry updates, and the tunnel can
414    * re-stack accordingly
415    */
416
417   clib_memcpy (&t->tunnel_src, &a->src, sizeof (t->tunnel_src));
418   t->tunnel_dst.fp_len = !is_ipv6 ? 32 : 128;
419   t->tunnel_dst.fp_proto = !is_ipv6 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6;
420   t->tunnel_dst.fp_addr = a->dst;
421
422   gre_tunnel_db_add(t);
423
424   t->fib_entry_index =
425       fib_table_entry_special_add(outer_fib_index,
426                                   &t->tunnel_dst,
427                                   FIB_SOURCE_RR,
428                                   FIB_ENTRY_FLAG_NONE);
429   t->sibling_index =
430       fib_entry_child_add(t->fib_entry_index,
431                           FIB_NODE_TYPE_GRE_TUNNEL,
432                           t - gm->tunnels);
433
434   if (GRE_TUNNEL_TYPE_TEB == t->type)
435   {
436       t->l2_adj_index = adj_nbr_add_or_lock(t->tunnel_dst.fp_proto,
437                                             VNET_LINK_ETHERNET,
438                                             &zero_addr,
439                                             sw_if_index);
440       gre_update_adj(vnm, t->sw_if_index, t->l2_adj_index);
441   }
442
443   if (sw_if_indexp)
444     *sw_if_indexp = sw_if_index;
445
446   return 0;
447 }
448
449 static int
450 vnet_gre_tunnel_delete (vnet_gre_add_del_tunnel_args_t *a,
451                         u32 * sw_if_indexp)
452 {
453   gre_main_t * gm = &gre_main;
454   vnet_main_t * vnm = gm->vnet_main;
455   gre_tunnel_t * t;
456   u32 sw_if_index;
457
458   t = gre_tunnel_db_find(&a->src, &a->dst, a->outer_fib_id, a->is_ipv6);
459
460   if (NULL == t)
461     return VNET_API_ERROR_NO_SUCH_ENTRY;
462
463   sw_if_index = t->sw_if_index;
464   vnet_sw_interface_set_flags (vnm, sw_if_index, 0 /* down */);
465   /* make sure tunnel is removed from l2 bd or xconnect */
466   set_int_l2_mode(gm->vlib_main, vnm, MODE_L3, sw_if_index, 0, 0, 0, 0);
467   vec_add1 (gm->free_gre_tunnel_hw_if_indices[t->type], t->hw_if_index);
468   gm->tunnel_index_by_sw_if_index[sw_if_index] = ~0;
469
470   if (GRE_TUNNEL_TYPE_TEB == t->type)
471     adj_unlock(t->l2_adj_index);
472
473   if (t->l2_adj_index != ADJ_INDEX_INVALID)
474       adj_unlock(t->l2_adj_index);
475
476   fib_entry_child_remove(t->fib_entry_index,
477                          t->sibling_index);
478   fib_table_entry_delete_index(t->fib_entry_index,
479                                FIB_SOURCE_RR);
480
481   gre_tunnel_db_remove(t);
482   fib_node_deinit(&t->node);
483   pool_put (gm->tunnels, t);
484
485   if (sw_if_indexp)
486     *sw_if_indexp = sw_if_index;
487
488   return 0;
489 }
490
491 int
492 vnet_gre_add_del_tunnel (vnet_gre_add_del_tunnel_args_t *a,
493                          u32 * sw_if_indexp)
494 {
495   if (a->is_add)
496     return (vnet_gre_tunnel_add(a, sw_if_indexp));
497   else
498     return (vnet_gre_tunnel_delete(a, sw_if_indexp));
499 }
500
501 clib_error_t *
502 gre_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
503 {
504   gre_main_t * gm = &gre_main;
505   vnet_hw_interface_t * hi;
506   gre_tunnel_t *t;
507   u32 ti;
508
509   hi = vnet_get_hw_interface (vnm, hw_if_index);
510
511   if (NULL == gm->tunnel_index_by_sw_if_index ||
512       hi->sw_if_index >= vec_len(gm->tunnel_index_by_sw_if_index))
513       return (NULL);
514
515   ti = gm->tunnel_index_by_sw_if_index[hi->sw_if_index];
516
517   if (~0 == ti)
518       /* not one of ours */
519       return (NULL);
520
521   t = pool_elt_at_index(gm->tunnels, ti);
522
523   if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
524     vnet_hw_interface_set_flags (vnm, hw_if_index, VNET_HW_INTERFACE_FLAG_LINK_UP);
525   else
526     vnet_hw_interface_set_flags (vnm, hw_if_index, 0 /* down */);
527
528   gre_tunnel_restack(t);
529
530   return /* no error */ 0;
531 }
532
533 static clib_error_t *
534 create_gre_tunnel_command_fn (vlib_main_t * vm,
535                  unformat_input_t * input,
536                  vlib_cli_command_t * cmd)
537 {
538   unformat_input_t _line_input, * line_input = &_line_input;
539   vnet_gre_add_del_tunnel_args_t _a, * a = &_a;
540   ip46_address_t src, dst;
541   u32 outer_fib_id = 0;
542   u8 teb = 0;
543   int rv;
544   u32 num_m_args = 0;
545   u8 is_add = 1;
546   u32 sw_if_index;
547   clib_error_t *error = NULL;
548   u8 ipv4_set = 0;
549   u8 ipv6_set = 0;
550
551   /* Get a line of input. */
552   if (! unformat_user (input, unformat_line_input, line_input))
553     return 0;
554
555   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) {
556     if (unformat (line_input, "del"))
557       is_add = 0;
558     else if (unformat (line_input, "src %U", unformat_ip4_address, &src.ip4)) {
559       num_m_args++;
560       ipv4_set = 1;
561     } else if (unformat (line_input, "dst %U", unformat_ip4_address, &dst.ip4)) {
562       num_m_args++;
563       ipv4_set = 1;
564     } else if (unformat (line_input, "src %U", unformat_ip6_address, &src.ip6)) {
565       num_m_args++;
566       ipv6_set = 1;
567     } else if (unformat (line_input, "dst %U", unformat_ip6_address, &dst.ip6)) {
568       num_m_args++;
569       ipv6_set = 1;
570     } else if (unformat (line_input, "outer-fib-id %d", &outer_fib_id))
571       ;
572     else if (unformat (line_input, "teb"))
573       teb = 1;
574     else
575       {
576         error = clib_error_return (0, "unknown input `%U'",
577                                    format_unformat_error, line_input);
578         goto done;
579       }
580   }
581
582   if (num_m_args < 2)
583     {
584       error = clib_error_return (0, "mandatory argument(s) missing");
585       goto done;
586     }
587
588   if ((ipv4_set && memcmp (&src.ip4, &dst.ip4, sizeof(src.ip4)) == 0) ||
589       (ipv6_set && memcmp (&src.ip6, &dst.ip6, sizeof(src.ip6)) == 0))
590     {
591       error = clib_error_return (0, "src and dst are identical");
592       goto done;
593     }
594
595   if (ipv4_set && ipv6_set)
596       return clib_error_return (0, "both IPv4 and IPv6 addresses specified");
597
598   if ((ipv4_set && memcmp (&dst.ip4, &zero_addr.ip4, sizeof(dst.ip4)) == 0) ||
599       (ipv6_set && memcmp (&dst.ip6, &zero_addr.ip6, sizeof(dst.ip6)) == 0))
600     {
601       error = clib_error_return (0, "dst address cannot be zero");
602       goto done;
603     }
604
605   memset (a, 0, sizeof (*a));
606   a->outer_fib_id = outer_fib_id;
607   a->teb = teb;
608   a->is_ipv6 = ipv6_set;
609   if (!ipv6_set)
610     {
611       clib_memcpy(&a->src.ip4, &src.ip4, sizeof(src.ip4));
612       clib_memcpy(&a->dst.ip4, &dst.ip4, sizeof(dst.ip4));
613     }
614   else
615     {
616       clib_memcpy(&a->src.ip6, &src.ip6, sizeof(src.ip6));
617       clib_memcpy(&a->dst.ip6, &dst.ip6, sizeof(dst.ip6));
618     }
619
620   if (is_add)
621     rv = vnet_gre_tunnel_add(a, &sw_if_index);
622   else
623     rv = vnet_gre_tunnel_delete(a, &sw_if_index);
624
625   switch(rv)
626     {
627     case 0:
628       vlib_cli_output(vm, "%U\n", format_vnet_sw_if_index_name, vnet_get_main(), sw_if_index);
629       break;
630     case VNET_API_ERROR_INVALID_VALUE:
631       error = clib_error_return (0, "GRE tunnel already exists...");
632       goto done;
633     case VNET_API_ERROR_NO_SUCH_FIB:
634       error = clib_error_return (0, "outer fib ID %d doesn't exist\n",
635                                  outer_fib_id);
636       goto done;
637     default:
638       error = clib_error_return (0, "vnet_gre_add_del_tunnel returned %d", rv);
639       goto done;
640     }
641
642 done:
643   unformat_free (line_input);
644
645   return error;
646 }
647
648 VLIB_CLI_COMMAND (create_gre_tunnel_command, static) = {
649   .path = "create gre tunnel",
650   .short_help = "create gre tunnel src <addr> dst <addr> "
651                 "[outer-fib-id <fib>] [teb] [del]",
652   .function = create_gre_tunnel_command_fn,
653 };
654
655 static clib_error_t *
656 show_gre_tunnel_command_fn (vlib_main_t * vm,
657                             unformat_input_t * input,
658                             vlib_cli_command_t * cmd)
659 {
660   gre_main_t * gm = &gre_main;
661   gre_tunnel_t * t;
662   u32 ti = ~0;
663
664   if (pool_elts (gm->tunnels) == 0)
665     vlib_cli_output (vm, "No GRE tunnels configured...");
666
667   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
668     {
669       if (unformat (input, "%d", &ti))
670         ;
671       else
672         break;
673     }
674
675   if (~0 == ti)
676     {
677       pool_foreach (t, gm->tunnels,
678       ({
679           vlib_cli_output (vm, "%U", format_gre_tunnel, t);
680       }));
681     }
682   else
683   {
684       t = pool_elt_at_index(gm->tunnels, ti);
685
686       vlib_cli_output (vm, "%U", format_gre_tunnel, t);
687   }
688
689   return 0;
690 }
691
692 VLIB_CLI_COMMAND (show_gre_tunnel_command, static) = {
693     .path = "show gre tunnel",
694     .function = show_gre_tunnel_command_fn,
695 };
696
697 /* force inclusion from application's main.c */
698 clib_error_t *gre_interface_init (vlib_main_t *vm)
699 {
700   fib_node_register_type(FIB_NODE_TYPE_GRE_TUNNEL, &gre_vft);
701
702   return 0;
703 }
704 VLIB_INIT_FUNCTION(gre_interface_init);