GRE over IPv6
[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 #if (CLIB_DEBUG > 0)
157     ASSERT(FIB_NODE_TYPE_GRE_TUNNEL == node->fn_type);
158 #endif
159     return ((gre_tunnel_t*) (((char*)node) -
160                              STRUCT_OFFSET_OF(gre_tunnel_t, node)));
161 }
162
163 /**
164  * gre_tunnel_stack
165  *
166  * 'stack' (resolve the recursion for) the tunnel's midchain adjacency
167  */
168 void
169 gre_tunnel_stack (adj_index_t ai)
170 {
171     gre_main_t * gm = &gre_main;
172     ip_adjacency_t *adj;
173     gre_tunnel_t *gt;
174     u32 sw_if_index;
175
176     adj = adj_get(ai);
177     sw_if_index = adj->rewrite_header.sw_if_index;
178
179     if ((vec_len(gm->tunnel_index_by_sw_if_index) < sw_if_index) ||
180         (~0 == gm->tunnel_index_by_sw_if_index[sw_if_index]))
181         return;
182
183     gt = pool_elt_at_index(gm->tunnels,
184                            gm->tunnel_index_by_sw_if_index[sw_if_index]);
185
186     /*
187      * find the adjacency that is contributed by the FIB entry
188      * that this tunnel resovles via, and use it as the next adj
189      * in the midchain
190      */
191     if (vnet_hw_interface_get_flags(vnet_get_main(),
192                                     gt->hw_if_index) &
193         VNET_HW_INTERFACE_FLAG_LINK_UP)
194     {
195         adj_nbr_midchain_stack(
196             ai,
197             fib_entry_contribute_ip_forwarding(gt->fib_entry_index));
198     }
199     else
200     {
201         adj_nbr_midchain_unstack(ai);
202     }
203 }
204
205 /**
206  * @brief Call back when restacking all adjacencies on a GRE interface
207  */
208 static adj_walk_rc_t
209 gre_adj_walk_cb (adj_index_t ai,
210                  void *ctx)
211 {
212     gre_tunnel_stack(ai);
213
214     return (ADJ_WALK_RC_CONTINUE);
215 }
216
217 static void
218 gre_tunnel_restack (gre_tunnel_t *gt)
219 {
220     fib_protocol_t proto;
221
222     /*
223      * walk all the adjacencies on th GRE interface and restack them
224      */
225     FOR_EACH_FIB_IP_PROTOCOL(proto)
226     {
227         adj_nbr_walk(gt->sw_if_index,
228                      proto,
229                      gre_adj_walk_cb,
230                      NULL);
231     }
232 }
233
234 /**
235  * Function definition to backwalk a FIB node
236  */
237 static fib_node_back_walk_rc_t
238 gre_tunnel_back_walk (fib_node_t *node,
239                       fib_node_back_walk_ctx_t *ctx)
240 {
241     gre_tunnel_restack(gre_tunnel_from_fib_node(node));
242
243     return (FIB_NODE_BACK_WALK_CONTINUE);
244 }
245
246 /**
247  * Function definition to get a FIB node from its index
248  */
249 static fib_node_t*
250 gre_tunnel_fib_node_get (fib_node_index_t index)
251 {
252     gre_tunnel_t * gt;
253     gre_main_t * gm;
254
255     gm  = &gre_main;
256     gt = pool_elt_at_index(gm->tunnels, index);
257
258     return (&gt->node);
259 }
260
261 /**
262  * Function definition to inform the FIB node that its last lock has gone.
263  */
264 static void
265 gre_tunnel_last_lock_gone (fib_node_t *node)
266 {
267     /*
268      * The MPLS GRE tunnel is a root of the graph. As such
269      * it never has children and thus is never locked.
270      */
271     ASSERT(0);
272 }
273
274 /*
275  * Virtual function table registered by MPLS GRE tunnels
276  * for participation in the FIB object graph.
277  */
278 const static fib_node_vft_t gre_vft = {
279     .fnv_get = gre_tunnel_fib_node_get,
280     .fnv_last_lock = gre_tunnel_last_lock_gone,
281     .fnv_back_walk = gre_tunnel_back_walk,
282 };
283
284 static int
285 vnet_gre_tunnel_add (vnet_gre_add_del_tunnel_args_t *a,
286                      u32 * sw_if_indexp)
287 {
288   gre_main_t * gm = &gre_main;
289   vnet_main_t * vnm = gm->vnet_main;
290   ip4_main_t * im4 = &ip4_main;
291   ip6_main_t * im6 = &ip6_main;
292   gre_tunnel_t * t;
293   vnet_hw_interface_t * hi;
294   u32 hw_if_index, sw_if_index;
295   u32 outer_fib_index;
296   u8 address[6];
297   clib_error_t *error;
298   u8 is_ipv6 = a->is_ipv6;
299
300   if (!is_ipv6)
301     outer_fib_index = ip4_fib_index_from_table_id(a->outer_fib_id);
302   else
303     outer_fib_index = ip6_fib_index_from_table_id(a->outer_fib_id);
304
305   if (~0 == outer_fib_index)
306     return VNET_API_ERROR_NO_SUCH_FIB;
307
308   t = gre_tunnel_db_find(&a->src, &a->dst, a->outer_fib_id, a->is_ipv6);
309
310   if (NULL != t)
311     return VNET_API_ERROR_INVALID_VALUE;
312
313   pool_get_aligned (gm->tunnels, t, CLIB_CACHE_LINE_BYTES);
314   memset (t, 0, sizeof (*t));
315   fib_node_init(&t->node, FIB_NODE_TYPE_GRE_TUNNEL);
316
317   if (a->teb)
318       t->type = GRE_TUNNEL_TYPE_TEB;
319   else
320       t->type = GRE_TUNNEL_TYPE_L3;
321
322   if (vec_len (gm->free_gre_tunnel_hw_if_indices[t->type]) > 0) {
323       vnet_interface_main_t * im = &vnm->interface_main;
324
325       hw_if_index = gm->free_gre_tunnel_hw_if_indices[t->type]
326           [vec_len (gm->free_gre_tunnel_hw_if_indices[t->type])-1];
327       _vec_len (gm->free_gre_tunnel_hw_if_indices[t->type]) -= 1;
328
329       hi = vnet_get_hw_interface (vnm, hw_if_index);
330       hi->dev_instance = t - gm->tunnels;
331       hi->hw_instance = hi->dev_instance;
332
333       /* clear old stats of freed tunnel before reuse */
334       sw_if_index = hi->sw_if_index;
335       vnet_interface_counter_lock(im);
336       vlib_zero_combined_counter
337           (&im->combined_sw_if_counters[VNET_INTERFACE_COUNTER_TX], sw_if_index);
338       vlib_zero_combined_counter
339           (&im->combined_sw_if_counters[VNET_INTERFACE_COUNTER_RX], sw_if_index);
340       vlib_zero_simple_counter
341           (&im->sw_if_counters[VNET_INTERFACE_COUNTER_DROP], sw_if_index);
342         vnet_interface_counter_unlock(im);
343       if (GRE_TUNNEL_TYPE_TEB == t->type)
344       {
345           t->l2_tx_arc = vlib_node_add_named_next(vlib_get_main(),
346                                                   hi->tx_node_index,
347                                                   "adj-l2-midchain");
348       }
349     } else {
350       if (GRE_TUNNEL_TYPE_TEB == t->type)
351       {
352         /* Default MAC address (d00b:eed0:0000 + sw_if_index) */
353         memset (address, 0, sizeof (address));
354         address[0] = 0xd0;
355         address[1] = 0x0b;
356         address[2] = 0xee;
357         address[3] = 0xd0;
358         address[4] = t - gm->tunnels;
359
360         error = ethernet_register_interface(vnm,
361                                             gre_device_teb_class.index,
362                                             t - gm->tunnels, address,
363                                             &hw_if_index,
364                                             0);
365
366         if (error)
367         {
368           clib_error_report (error);
369           return VNET_API_ERROR_INVALID_REGISTRATION;
370         }
371         hi = vnet_get_hw_interface (vnm, hw_if_index);
372
373         t->l2_tx_arc = vlib_node_add_named_next(vlib_get_main(),
374                                                 hi->tx_node_index,
375                                                 "adj-l2-midchain");
376       } else {
377         hw_if_index = vnet_register_interface(vnm,
378                                               gre_device_class.index,
379                                               t - gm->tunnels,
380                                               gre_hw_interface_class.index,
381                                               t - gm->tunnels);
382       }
383       hi = vnet_get_hw_interface (vnm, hw_if_index);
384       sw_if_index = hi->sw_if_index;
385     }
386
387   t->hw_if_index = hw_if_index;
388   t->outer_fib_index = outer_fib_index;
389   t->sw_if_index = sw_if_index;
390   t->l2_adj_index = ADJ_INDEX_INVALID;
391
392   vec_validate_init_empty (gm->tunnel_index_by_sw_if_index, sw_if_index, ~0);
393   gm->tunnel_index_by_sw_if_index[sw_if_index] = t - gm->tunnels;
394
395   if (!is_ipv6)
396     {
397       vec_validate (im4->fib_index_by_sw_if_index, sw_if_index);
398       hi->min_packet_bytes = 64 + sizeof (gre_header_t) + sizeof (ip4_header_t);
399     }
400   else
401     {
402       vec_validate (im6->fib_index_by_sw_if_index, sw_if_index);
403       hi->min_packet_bytes = 64 + sizeof (gre_header_t) + sizeof (ip6_header_t);
404     }
405
406   hi->per_packet_overhead_bytes =
407       /* preamble */ 8 + /* inter frame gap */ 12;
408
409   /* Standard default gre MTU. */
410   hi->max_l3_packet_bytes[VLIB_RX] = hi->max_l3_packet_bytes[VLIB_TX] = 9000;
411
412   /*
413    * source the FIB entry for the tunnel's destination
414    * and become a child thereof. The tunnel will then get poked
415    * when the forwarding for the entry updates, and the tunnel can
416    * re-stack accordingly
417    */
418
419   clib_memcpy (&t->tunnel_src, &a->src, sizeof (t->tunnel_src));
420   t->tunnel_dst.fp_len = !is_ipv6 ? 32 : 128;
421   t->tunnel_dst.fp_proto = !is_ipv6 ? FIB_PROTOCOL_IP4 : FIB_PROTOCOL_IP6;
422   t->tunnel_dst.fp_addr = a->dst;
423
424   gre_tunnel_db_add(t);
425
426   t->fib_entry_index =
427       fib_table_entry_special_add(outer_fib_index,
428                                   &t->tunnel_dst,
429                                   FIB_SOURCE_RR,
430                                   FIB_ENTRY_FLAG_NONE,
431                                   ADJ_INDEX_INVALID);
432   t->sibling_index =
433       fib_entry_child_add(t->fib_entry_index,
434                           FIB_NODE_TYPE_GRE_TUNNEL,
435                           t - gm->tunnels);
436
437   if (GRE_TUNNEL_TYPE_TEB == t->type)
438   {
439       t->l2_adj_index = adj_nbr_add_or_lock(t->tunnel_dst.fp_proto,
440                                             VNET_LINK_ETHERNET,
441                                             &zero_addr,
442                                             sw_if_index);
443       gre_update_adj(vnm, t->sw_if_index, t->l2_adj_index);
444   }
445
446   if (sw_if_indexp)
447     *sw_if_indexp = sw_if_index;
448
449   return 0;
450 }
451
452 static int
453 vnet_gre_tunnel_delete (vnet_gre_add_del_tunnel_args_t *a,
454                         u32 * sw_if_indexp)
455 {
456   gre_main_t * gm = &gre_main;
457   vnet_main_t * vnm = gm->vnet_main;
458   gre_tunnel_t * t;
459   u32 sw_if_index;
460
461   t = gre_tunnel_db_find(&a->src, &a->dst, a->outer_fib_id, a->is_ipv6);
462
463   if (NULL == t)
464     return VNET_API_ERROR_NO_SUCH_ENTRY;
465
466   sw_if_index = t->sw_if_index;
467   vnet_sw_interface_set_flags (vnm, sw_if_index, 0 /* down */);
468   /* make sure tunnel is removed from l2 bd or xconnect */
469   set_int_l2_mode(gm->vlib_main, vnm, MODE_L3, sw_if_index, 0, 0, 0, 0);
470   vec_add1 (gm->free_gre_tunnel_hw_if_indices[t->type], t->hw_if_index);
471   gm->tunnel_index_by_sw_if_index[sw_if_index] = ~0;
472
473   if (GRE_TUNNEL_TYPE_TEB == t->type)
474     adj_unlock(t->l2_adj_index);
475
476   if (t->l2_adj_index != ADJ_INDEX_INVALID)
477       adj_unlock(t->l2_adj_index);
478
479   fib_entry_child_remove(t->fib_entry_index,
480                          t->sibling_index);
481   fib_table_entry_delete_index(t->fib_entry_index,
482                                FIB_SOURCE_RR);
483
484   gre_tunnel_db_remove(t);
485   fib_node_deinit(&t->node);
486   pool_put (gm->tunnels, t);
487
488   if (sw_if_indexp)
489     *sw_if_indexp = sw_if_index;
490
491   return 0;
492 }
493
494 int
495 vnet_gre_add_del_tunnel (vnet_gre_add_del_tunnel_args_t *a,
496                          u32 * sw_if_indexp)
497 {
498   if (a->is_add)
499     return (vnet_gre_tunnel_add(a, sw_if_indexp));
500   else
501     return (vnet_gre_tunnel_delete(a, sw_if_indexp));
502 }
503
504 clib_error_t *
505 gre_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
506 {
507   gre_main_t * gm = &gre_main;
508   vnet_hw_interface_t * hi;
509   gre_tunnel_t *t;
510   u32 ti;
511
512   hi = vnet_get_hw_interface (vnm, hw_if_index);
513
514   if (NULL == gm->tunnel_index_by_sw_if_index ||
515       hi->sw_if_index >= vec_len(gm->tunnel_index_by_sw_if_index))
516       return (NULL);
517
518   ti = gm->tunnel_index_by_sw_if_index[hi->sw_if_index];
519
520   if (~0 == ti)
521       /* not one of ours */
522       return (NULL);
523
524   t = pool_elt_at_index(gm->tunnels, ti);
525
526   if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
527     vnet_hw_interface_set_flags (vnm, hw_if_index, VNET_HW_INTERFACE_FLAG_LINK_UP);
528   else
529     vnet_hw_interface_set_flags (vnm, hw_if_index, 0 /* down */);
530
531   gre_tunnel_restack(t);
532
533   return /* no error */ 0;
534 }
535
536 static clib_error_t *
537 create_gre_tunnel_command_fn (vlib_main_t * vm,
538                  unformat_input_t * input,
539                  vlib_cli_command_t * cmd)
540 {
541   unformat_input_t _line_input, * line_input = &_line_input;
542   vnet_gre_add_del_tunnel_args_t _a, * a = &_a;
543   ip46_address_t src, dst;
544   u32 outer_fib_id = 0;
545   u8 teb = 0;
546   int rv;
547   u32 num_m_args = 0;
548   u8 is_add = 1;
549   u32 sw_if_index;
550   clib_error_t *error = NULL;
551   u8 ipv4_set = 0;
552   u8 ipv6_set = 0;
553
554   /* Get a line of input. */
555   if (! unformat_user (input, unformat_line_input, line_input))
556     return 0;
557
558   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) {
559     if (unformat (line_input, "del"))
560       is_add = 0;
561     else if (unformat (line_input, "src %U", unformat_ip4_address, &src.ip4)) {
562       num_m_args++;
563       ipv4_set = 1;
564     } else if (unformat (line_input, "dst %U", unformat_ip4_address, &dst.ip4)) {
565       num_m_args++;
566       ipv4_set = 1;
567     } else if (unformat (line_input, "src %U", unformat_ip6_address, &src.ip6)) {
568       num_m_args++;
569       ipv6_set = 1;
570     } else if (unformat (line_input, "dst %U", unformat_ip6_address, &dst.ip6)) {
571       num_m_args++;
572       ipv6_set = 1;
573     } else if (unformat (line_input, "outer-fib-id %d", &outer_fib_id))
574       ;
575     else if (unformat (line_input, "teb"))
576       teb = 1;
577     else
578       {
579         error = clib_error_return (0, "unknown input `%U'",
580                                    format_unformat_error, line_input);
581         goto done;
582       }
583   }
584
585   if (num_m_args < 2)
586     {
587       error = clib_error_return (0, "mandatory argument(s) missing");
588       goto done;
589     }
590
591   if ((ipv4_set && memcmp (&src.ip4, &dst.ip4, sizeof(src.ip4)) == 0) ||
592       (ipv6_set && memcmp (&src.ip6, &dst.ip6, sizeof(src.ip6)) == 0))
593     {
594       error = clib_error_return (0, "src and dst are identical");
595       goto done;
596     }
597
598   if (ipv4_set && ipv6_set)
599       return clib_error_return (0, "both IPv4 and IPv6 addresses specified");
600
601   if ((ipv4_set && memcmp (&dst.ip4, &zero_addr.ip4, sizeof(dst.ip4)) == 0) ||
602       (ipv6_set && memcmp (&dst.ip6, &zero_addr.ip6, sizeof(dst.ip6)) == 0))
603     {
604       error = clib_error_return (0, "dst address cannot be zero");
605       goto done;
606     }
607
608   memset (a, 0, sizeof (*a));
609   a->outer_fib_id = outer_fib_id;
610   a->teb = teb;
611   a->is_ipv6 = ipv6_set;
612   if (!ipv6_set)
613     {
614       clib_memcpy(&a->src.ip4, &src.ip4, sizeof(src.ip4));
615       clib_memcpy(&a->dst.ip4, &dst.ip4, sizeof(dst.ip4));
616     }
617   else
618     {
619       clib_memcpy(&a->src.ip6, &src.ip6, sizeof(src.ip6));
620       clib_memcpy(&a->dst.ip6, &dst.ip6, sizeof(dst.ip6));
621     }
622
623   if (is_add)
624     rv = vnet_gre_tunnel_add(a, &sw_if_index);
625   else
626     rv = vnet_gre_tunnel_delete(a, &sw_if_index);
627
628   switch(rv)
629     {
630     case 0:
631       vlib_cli_output(vm, "%U\n", format_vnet_sw_if_index_name, vnet_get_main(), sw_if_index);
632       break;
633     case VNET_API_ERROR_INVALID_VALUE:
634       error = clib_error_return (0, "GRE tunnel already exists...");
635       goto done;
636     case VNET_API_ERROR_NO_SUCH_FIB:
637       error = clib_error_return (0, "outer fib ID %d doesn't exist\n",
638                                  outer_fib_id);
639       goto done;
640     default:
641       error = clib_error_return (0, "vnet_gre_add_del_tunnel returned %d", rv);
642       goto done;
643     }
644
645 done:
646   unformat_free (line_input);
647
648   return error;
649 }
650
651 VLIB_CLI_COMMAND (create_gre_tunnel_command, static) = {
652   .path = "create gre tunnel",
653   .short_help = "create gre tunnel src <addr> dst <addr> "
654                 "[outer-fib-id <fib>] [teb] [del]",
655   .function = create_gre_tunnel_command_fn,
656 };
657
658 static clib_error_t *
659 show_gre_tunnel_command_fn (vlib_main_t * vm,
660                             unformat_input_t * input,
661                             vlib_cli_command_t * cmd)
662 {
663   gre_main_t * gm = &gre_main;
664   gre_tunnel_t * t;
665   u32 ti = ~0;
666
667   if (pool_elts (gm->tunnels) == 0)
668     vlib_cli_output (vm, "No GRE tunnels configured...");
669
670   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
671     {
672       if (unformat (input, "%d", &ti))
673         ;
674       else
675         break;
676     }
677
678   if (~0 == ti)
679     {
680       pool_foreach (t, gm->tunnels,
681       ({
682           vlib_cli_output (vm, "%U", format_gre_tunnel, t);
683       }));
684     }
685   else
686   {
687       t = pool_elt_at_index(gm->tunnels, ti);
688
689       vlib_cli_output (vm, "%U", format_gre_tunnel, t);
690   }
691
692   return 0;
693 }
694
695 VLIB_CLI_COMMAND (show_gre_tunnel_command, static) = {
696     .path = "show gre tunnel",
697     .function = show_gre_tunnel_command_fn,
698 };
699
700 /* force inclusion from application's main.c */
701 clib_error_t *gre_interface_init (vlib_main_t *vm)
702 {
703   fib_node_register_type(FIB_NODE_TYPE_GRE_TUNNEL, &gre_vft);
704
705   return 0;
706 }
707 VLIB_INIT_FUNCTION(gre_interface_init);