dpdk: Add support for Mellanox ConnectX-4 devices
[vpp.git] / vnet / vnet / vxlan-gpe / vxlan_gpe.c
1 /*
2  * Copyright (c) 2015 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 and IPv6 VXLAN GPE tunnels
18  *
19 */
20 #include <vnet/vxlan-gpe/vxlan_gpe.h>
21 #include <vnet/fib/fib.h>
22 #include <vnet/ip/format.h>
23
24 vxlan_gpe_main_t vxlan_gpe_main;
25
26 /**
27  * @brief Tracing function for VXLAN GPE tunnel packets
28  *
29  * @param *s formatting string
30  * @param *args
31  *
32  * @return *s formatted string
33  *
34  */
35 u8 * format_vxlan_gpe_tunnel (u8 * s, va_list * args)
36 {
37   vxlan_gpe_tunnel_t * t = va_arg (*args, vxlan_gpe_tunnel_t *);
38   vxlan_gpe_main_t * gm = &vxlan_gpe_main;
39
40   s = format (s, "[%d] local: %U remote: %U ",
41               t - gm->tunnels,
42               format_ip46_address, &t->local, IP46_TYPE_ANY,
43               format_ip46_address, &t->remote, IP46_TYPE_ANY);
44
45   s = format (s, "  vxlan VNI %d ", t->vni);
46
47   switch (t->protocol)
48     {
49     case VXLAN_GPE_PROTOCOL_IP4:
50       s = format (s, "next-protocol ip4");
51       break;
52     case VXLAN_GPE_PROTOCOL_IP6:
53       s = format (s, "next-protocol ip6");
54       break;
55     case VXLAN_GPE_PROTOCOL_ETHERNET:
56       s = format (s, "next-protocol ethernet");
57       break;
58     case VXLAN_GPE_PROTOCOL_NSH:
59       s = format (s, "next-protocol nsh");
60       break;
61     default:
62       s = format (s, "next-protocol unknown %d", t->protocol);
63     }
64
65   s = format (s, " fibs: (encap %d, decap %d)",
66               t->encap_fib_index,
67               t->decap_fib_index);
68
69   return s;
70 }
71
72 /**
73  * @brief Naming for VXLAN GPE tunnel
74  *
75  * @param *s formatting string
76  * @param *args
77  *
78  * @return *s formatted string
79  *
80  */
81 static u8 * format_vxlan_gpe_name (u8 * s, va_list * args)
82 {
83   u32 dev_instance = va_arg (*args, u32);
84   return format (s, "vxlan_gpe_tunnel%d", dev_instance);
85 }
86
87 static uword dummy_interface_tx (vlib_main_t * vm,
88                                  vlib_node_runtime_t * node,
89                                  vlib_frame_t * frame)
90 {
91   clib_warning ("you shouldn't be here, leaking buffers...");
92   return frame->n_vectors;
93 }
94
95 /**
96  * @brief CLI function for VXLAN GPE admin up/down
97  *
98  * @param *vnm
99  * @param hw_if_index
100  * @param flag
101  *
102  * @return *rc
103  *
104  */
105 static clib_error_t *
106 vxlan_gpe_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
107 {
108   if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
109     vnet_hw_interface_set_flags (vnm, hw_if_index, VNET_HW_INTERFACE_FLAG_LINK_UP);
110   else
111     vnet_hw_interface_set_flags (vnm, hw_if_index, 0);
112
113   return 0;
114 }
115
116 VNET_DEVICE_CLASS (vxlan_gpe_device_class,static) = {
117   .name = "VXLAN_GPE",
118   .format_device_name = format_vxlan_gpe_name,
119   .format_tx_trace = format_vxlan_gpe_encap_trace,
120   .tx_function = dummy_interface_tx,
121   .admin_up_down_function = vxlan_gpe_interface_admin_up_down,
122 };
123
124
125 /**
126  * @brief Formatting function for tracing VXLAN GPE with length
127  *
128  * @param *s
129  * @param *args
130  *
131  * @return *s
132  *
133  */
134 static u8 * format_vxlan_gpe_header_with_length (u8 * s, va_list * args)
135 {
136   u32 dev_instance = va_arg (*args, u32);
137   s = format (s, "unimplemented dev %u", dev_instance);
138   return s;
139 }
140
141 VNET_HW_INTERFACE_CLASS (vxlan_gpe_hw_class) = {
142   .name = "VXLAN_GPE",
143   .format_header = format_vxlan_gpe_header_with_length,
144   .build_rewrite = default_build_rewrite,
145   .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,
146 };
147
148
149 #define foreach_gpe_copy_field                  \
150 _(vni)                                          \
151 _(protocol)                                \
152 _(encap_fib_index)                              \
153 _(decap_fib_index)
154
155 #define foreach_copy_ipv4 {                     \
156   _(local.ip4.as_u32)                           \
157   _(remote.ip4.as_u32)                          \
158 }
159
160 #define foreach_copy_ipv6 {                     \
161   _(local.ip6.as_u64[0])                        \
162   _(local.ip6.as_u64[1])                        \
163   _(remote.ip6.as_u64[0])                       \
164   _(remote.ip6.as_u64[1])                       \
165 }
166
167
168 /**
169  * @brief Calculate IPv4 VXLAN GPE rewrite header
170  *
171  * @param *t
172  *
173  * @return rc
174  *
175  */
176 int vxlan4_gpe_rewrite (vxlan_gpe_tunnel_t * t, u32 extension_size, 
177                         u8 protocol_override, uword encap_next_node)
178 {
179   u8 *rw = 0;
180   ip4_header_t * ip0;
181   ip4_vxlan_gpe_header_t * h0;
182   int len;
183
184   len = sizeof (*h0) + extension_size;
185
186   vec_free(t->rewrite);
187   vec_validate_aligned (rw, len-1, CLIB_CACHE_LINE_BYTES);
188
189   h0 = (ip4_vxlan_gpe_header_t *) rw;
190
191   /* Fixed portion of the (outer) ip4 header */
192   ip0 = &h0->ip4;
193   ip0->ip_version_and_header_length = 0x45;
194   ip0->ttl = 254;
195   ip0->protocol = IP_PROTOCOL_UDP;
196
197   /* we fix up the ip4 header length and checksum after-the-fact */
198   ip0->src_address.as_u32 = t->local.ip4.as_u32;
199   ip0->dst_address.as_u32 = t->remote.ip4.as_u32;
200   ip0->checksum = ip4_header_checksum (ip0);
201
202   /* UDP header, randomize src port on something, maybe? */
203   h0->udp.src_port = clib_host_to_net_u16 (4790);
204   h0->udp.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_vxlan_gpe);
205
206   /* VXLAN header. Are we having fun yet? */
207   h0->vxlan.flags = VXLAN_GPE_FLAGS_I | VXLAN_GPE_FLAGS_P;
208   h0->vxlan.ver_res = VXLAN_GPE_VERSION;
209   if (protocol_override)
210   {
211       h0->vxlan.protocol = protocol_override;
212   }
213   else
214   {
215       h0->vxlan.protocol = t->protocol;
216   }
217   t->rewrite_size = sizeof(ip4_vxlan_gpe_header_t) +  extension_size;
218   h0->vxlan.vni_res = clib_host_to_net_u32 (t->vni<<8);
219
220   t->rewrite = rw;
221   t->encap_next_node = encap_next_node;
222   return (0);
223 }
224
225 /**
226  * @brief Calculate IPv6 VXLAN GPE rewrite header
227  *
228  * @param *t
229  *
230  * @return rc
231  *
232  */
233 int vxlan6_gpe_rewrite (vxlan_gpe_tunnel_t * t, u32 extension_size, 
234                         u8 protocol_override, uword encap_next_node)
235 {
236   u8 *rw = 0;
237   ip6_header_t * ip0;
238   ip6_vxlan_gpe_header_t * h0;
239   int len;
240
241   len = sizeof (*h0) + extension_size;
242
243   vec_free(t->rewrite);
244   vec_validate_aligned (rw, len-1, CLIB_CACHE_LINE_BYTES);
245
246   h0 = (ip6_vxlan_gpe_header_t *) rw;
247
248   /* Fixed portion of the (outer) ip4 header */
249   ip0 = &h0->ip6;
250   ip0->ip_version_traffic_class_and_flow_label = clib_host_to_net_u32(6 << 28);
251   ip0->hop_limit = 255;
252   ip0->protocol = IP_PROTOCOL_UDP;
253
254   ip0->src_address.as_u64[0] = t->local.ip6.as_u64[0];
255   ip0->src_address.as_u64[1] = t->local.ip6.as_u64[1];
256   ip0->dst_address.as_u64[0] = t->remote.ip6.as_u64[0];
257   ip0->dst_address.as_u64[1] = t->remote.ip6.as_u64[1];
258
259   /* UDP header, randomize src port on something, maybe? */
260   h0->udp.src_port = clib_host_to_net_u16 (4790);
261   h0->udp.dst_port = clib_host_to_net_u16 (UDP_DST_PORT_vxlan_gpe);
262
263   /* VXLAN header. Are we having fun yet? */
264   h0->vxlan.flags = VXLAN_GPE_FLAGS_I | VXLAN_GPE_FLAGS_P;
265   h0->vxlan.ver_res = VXLAN_GPE_VERSION;
266   if (protocol_override)
267   {
268       h0->vxlan.protocol = t->protocol;
269   }
270   else
271   {
272       h0->vxlan.protocol = protocol_override;
273   }
274   t->rewrite_size = sizeof(ip4_vxlan_gpe_header_t) +  extension_size;
275   h0->vxlan.vni_res = clib_host_to_net_u32 (t->vni<<8);
276
277   t->rewrite = rw;
278   t->encap_next_node = encap_next_node;
279   return (0);
280 }
281
282 /**
283  * @brief Add or Del a VXLAN GPE tunnel
284  *
285  * @param *a
286  * @param *sw_if_index
287  *
288  * @return rc
289  *
290  */
291 int vnet_vxlan_gpe_add_del_tunnel
292 (vnet_vxlan_gpe_add_del_tunnel_args_t *a, u32 * sw_if_indexp)
293 {
294   vxlan_gpe_main_t * gm = &vxlan_gpe_main;
295   vxlan_gpe_tunnel_t *t = 0;
296   vnet_main_t * vnm = gm->vnet_main;
297   vnet_hw_interface_t * hi;
298   uword * p;
299   u32 hw_if_index = ~0;
300   u32 sw_if_index = ~0;
301   int rv;
302   vxlan4_gpe_tunnel_key_t key4, *key4_copy;
303   vxlan6_gpe_tunnel_key_t key6, *key6_copy;
304   hash_pair_t *hp;
305
306   if (!a->is_ip6)
307   {
308     key4.local = a->local.ip4.as_u32;
309     key4.remote = a->remote.ip4.as_u32;
310     key4.vni = clib_host_to_net_u32 (a->vni << 8);
311     key4.pad = 0;
312
313     p = hash_get_mem(gm->vxlan4_gpe_tunnel_by_key, &key4);
314   }
315   else
316   {
317     key6.local.as_u64[0] = a->local.ip6.as_u64[0];
318     key6.local.as_u64[1] = a->local.ip6.as_u64[1];
319     key6.remote.as_u64[0] = a->remote.ip6.as_u64[0];
320     key6.remote.as_u64[1] = a->remote.ip6.as_u64[1];
321     key6.vni = clib_host_to_net_u32 (a->vni << 8);
322
323     p = hash_get_mem(gm->vxlan6_gpe_tunnel_by_key, &key6);
324   }
325
326   if (a->is_add)
327     {
328       /* adding a tunnel: tunnel must not already exist */
329       if (p)
330         return VNET_API_ERROR_INVALID_VALUE;
331
332       pool_get_aligned (gm->tunnels, t, CLIB_CACHE_LINE_BYTES);
333       memset (t, 0, sizeof (*t));
334
335       /* copy from arg structure */
336 #define _(x) t->x = a->x;
337       foreach_gpe_copy_field;
338       if (!a->is_ip6) foreach_copy_ipv4
339       else            foreach_copy_ipv6
340 #undef _
341
342       if (!a->is_ip6) t->flags |= VXLAN_GPE_TUNNEL_IS_IPV4;
343
344       if (!a->is_ip6) {
345         rv = vxlan4_gpe_rewrite (t, 0, 0, VXLAN_GPE_ENCAP_NEXT_IP4_LOOKUP);
346       } else {
347         rv = vxlan6_gpe_rewrite (t, 0, 0, VXLAN_GPE_ENCAP_NEXT_IP6_LOOKUP);
348       }
349
350       if (rv)
351       {
352           pool_put (gm->tunnels, t);
353           return rv;
354       }
355
356       if (!a->is_ip6)
357       {
358         key4_copy = clib_mem_alloc (sizeof (*key4_copy));
359         clib_memcpy (key4_copy, &key4, sizeof (*key4_copy));
360         hash_set_mem (gm->vxlan4_gpe_tunnel_by_key, key4_copy,
361                       t - gm->tunnels);
362       }
363       else
364       {
365           key6_copy = clib_mem_alloc (sizeof (*key6_copy));
366           clib_memcpy (key6_copy, &key6, sizeof (*key6_copy));
367           hash_set_mem (gm->vxlan6_gpe_tunnel_by_key, key6_copy,
368                         t - gm->tunnels);
369       }
370
371       if (vec_len (gm->free_vxlan_gpe_tunnel_hw_if_indices) > 0)
372         {
373           hw_if_index = gm->free_vxlan_gpe_tunnel_hw_if_indices
374             [vec_len (gm->free_vxlan_gpe_tunnel_hw_if_indices)-1];
375           _vec_len (gm->free_vxlan_gpe_tunnel_hw_if_indices) -= 1;
376
377           hi = vnet_get_hw_interface (vnm, hw_if_index);
378           hi->dev_instance = t - gm->tunnels;
379           hi->hw_instance = hi->dev_instance;
380         }
381       else
382         {
383           hw_if_index = vnet_register_interface
384             (vnm, vxlan_gpe_device_class.index, t - gm->tunnels,
385              vxlan_gpe_hw_class.index, t - gm->tunnels);
386           hi = vnet_get_hw_interface (vnm, hw_if_index);
387           hi->output_node_index = vxlan_gpe_encap_node.index;
388         }
389
390       t->hw_if_index = hw_if_index;
391       t->sw_if_index = sw_if_index = hi->sw_if_index;
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       vnet_sw_interface_set_flags (vnm, hi->sw_if_index,
396                                    VNET_SW_INTERFACE_FLAG_ADMIN_UP);
397     }
398   else
399     {
400       /* deleting a tunnel: tunnel must exist */
401       if (!p)
402         return VNET_API_ERROR_NO_SUCH_ENTRY;
403
404       t = pool_elt_at_index (gm->tunnels, p[0]);
405
406       vnet_sw_interface_set_flags (vnm, t->sw_if_index, 0 /* down */);
407       vec_add1 (gm->free_vxlan_gpe_tunnel_hw_if_indices, t->hw_if_index);
408
409       gm->tunnel_index_by_sw_if_index[t->sw_if_index] = ~0;
410
411       if (!a->is_ip6)
412       {
413         hp = hash_get_pair (gm->vxlan4_gpe_tunnel_by_key, &key4);
414         key4_copy = (void *)(hp->key);
415         hash_unset_mem (gm->vxlan4_gpe_tunnel_by_key, &key4);
416         clib_mem_free (key4_copy);
417       }
418       else
419       {
420         hp = hash_get_pair (gm->vxlan6_gpe_tunnel_by_key, &key6);
421         key6_copy = (void *)(hp->key);
422         hash_unset_mem (gm->vxlan4_gpe_tunnel_by_key, &key6);
423         clib_mem_free (key6_copy);
424       }
425
426       vec_free (t->rewrite);
427       pool_put (gm->tunnels, t);
428     }
429
430   if (sw_if_indexp)
431       *sw_if_indexp = sw_if_index;
432
433   return 0;
434 }
435
436 static clib_error_t *
437 vxlan_gpe_add_del_tunnel_command_fn (vlib_main_t * vm,
438                                    unformat_input_t * input,
439                                    vlib_cli_command_t * cmd)
440 {
441   unformat_input_t _line_input, * line_input = &_line_input;
442   u8 is_add = 1;
443   ip46_address_t local, remote;
444   u8 local_set = 0;
445   u8 remote_set = 0;
446   u8 ipv4_set = 0;
447   u8 ipv6_set = 0;
448   u32 encap_fib_index = 0;
449   u32 decap_fib_index = 0;
450   u8 protocol = VXLAN_GPE_PROTOCOL_IP4;
451   u32 vni;
452   u8 vni_set = 0;
453   int rv;
454   u32 tmp;
455   vnet_vxlan_gpe_add_del_tunnel_args_t _a, * a = &_a;
456   u32 sw_if_index;
457
458   /* Get a line of input. */
459   if (! unformat_user (input, unformat_line_input, line_input))
460     return 0;
461
462   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) {
463     if (unformat (line_input, "del"))
464       is_add = 0;
465     else if (unformat (line_input, "local %U",
466                        unformat_ip4_address, &local.ip4))
467     {
468       local_set = 1;
469       ipv4_set = 1;
470     }
471     else if (unformat (line_input, "remote %U",
472                        unformat_ip4_address, &remote.ip4))
473     {
474       remote_set = 1;
475       ipv4_set = 1;
476     }
477     else if (unformat (line_input, "local %U",
478                        unformat_ip6_address, &local.ip6))
479     {
480       local_set = 1;
481       ipv6_set = 1;
482     }
483     else if (unformat (line_input, "remote %U",
484                        unformat_ip6_address, &remote.ip6))
485     {
486       remote_set = 1;
487       ipv6_set = 1;
488     }
489     else if (unformat (line_input, "encap-vrf-id %d", &tmp))
490       {
491         if (ipv6_set)
492           encap_fib_index = ip6_fib_index_from_table_id (tmp);
493         else
494           encap_fib_index =  ip4_fib_index_from_table_id (tmp);
495
496         if (encap_fib_index == ~0)
497           return clib_error_return (0, "nonexistent encap fib id %d", tmp);
498       }
499     else if (unformat (line_input, "decap-vrf-id %d", &tmp))
500       {
501         if (ipv6_set)
502           decap_fib_index = ip6_fib_index_from_table_id (tmp);
503         else
504           decap_fib_index = ip4_fib_index_from_table_id (tmp);
505
506         if (decap_fib_index == ~0)
507           return clib_error_return (0, "nonexistent decap fib id %d", tmp);
508       }
509     else if (unformat (line_input, "vni %d", &vni))
510       vni_set = 1;
511     else if (unformat(line_input, "next-ip4"))
512       protocol = VXLAN_GPE_PROTOCOL_IP4;
513     else if (unformat(line_input, "next-ip6"))
514       protocol = VXLAN_GPE_PROTOCOL_IP6;
515     else if (unformat(line_input, "next-ethernet"))
516       protocol = VXLAN_GPE_PROTOCOL_ETHERNET;
517     else if (unformat(line_input, "next-nsh"))
518       protocol = VXLAN_GPE_PROTOCOL_NSH;
519     else
520       return clib_error_return (0, "parse error: '%U'",
521                                 format_unformat_error, line_input);
522   }
523
524   unformat_free (line_input);
525
526   if (local_set == 0)
527     return clib_error_return (0, "tunnel local address not specified");
528
529   if (remote_set == 0)
530     return clib_error_return (0, "tunnel remote address not specified");
531
532   if (ipv4_set && ipv6_set)
533     return clib_error_return (0, "both IPv4 and IPv6 addresses specified");
534
535   if ((ipv4_set && memcmp(&local.ip4, &remote.ip4, sizeof(local.ip4)) == 0) ||
536       (ipv6_set && memcmp(&local.ip6, &remote.ip6, sizeof(local.ip6)) == 0))
537     return clib_error_return (0, "src and dst addresses are identical");
538
539   if (vni_set == 0)
540     return clib_error_return (0, "vni not specified");
541
542   memset (a, 0, sizeof (*a));
543
544   a->is_add = is_add;
545   a->is_ip6 = ipv6_set;
546
547 #define _(x) a->x = x;
548   foreach_gpe_copy_field;
549   if (ipv4_set) foreach_copy_ipv4
550   else          foreach_copy_ipv6
551 #undef _
552
553   rv = vnet_vxlan_gpe_add_del_tunnel (a, &sw_if_index);
554
555   switch(rv)
556     {
557     case 0:
558       vlib_cli_output(vm, "%U\n", format_vnet_sw_if_index_name, vnet_get_main(), sw_if_index);
559       break;
560     case VNET_API_ERROR_INVALID_DECAP_NEXT:
561       return clib_error_return (0, "invalid decap-next...");
562
563     case VNET_API_ERROR_TUNNEL_EXIST:
564       return clib_error_return (0, "tunnel already exists...");
565
566     case VNET_API_ERROR_NO_SUCH_ENTRY:
567       return clib_error_return (0, "tunnel does not exist...");
568
569     default:
570       return clib_error_return
571         (0, "vnet_vxlan_gpe_add_del_tunnel returned %d", rv);
572     }
573
574   return 0;
575 }
576
577 VLIB_CLI_COMMAND (create_vxlan_gpe_tunnel_command, static) = {
578   .path = "create vxlan-gpe tunnel",
579   .short_help =
580   "create vxlan-gpe tunnel local <local-addr> remote <remote-addr>"
581   " vni <nn> [next-ip4][next-ip6][next-ethernet][next-nsh]"
582   " [encap-vrf-id <nn>] [decap-vrf-id <nn>]"
583   " [del]\n",
584   .function = vxlan_gpe_add_del_tunnel_command_fn,
585 };
586
587 /**
588  * @brief CLI function for showing VXLAN GPE tunnels
589  *
590  * @param *vm
591  * @param *input
592  * @param *cmd
593  *
594  * @return error
595  *
596  */
597 static clib_error_t *
598 show_vxlan_gpe_tunnel_command_fn (vlib_main_t * vm,
599                                 unformat_input_t * input,
600                                 vlib_cli_command_t * cmd)
601 {
602   vxlan_gpe_main_t * gm = &vxlan_gpe_main;
603   vxlan_gpe_tunnel_t * t;
604
605   if (pool_elts (gm->tunnels) == 0)
606     vlib_cli_output (vm, "No vxlan-gpe tunnels configured.");
607
608   pool_foreach (t, gm->tunnels,
609   ({
610     vlib_cli_output (vm, "%U", format_vxlan_gpe_tunnel, t);
611   }));
612
613   return 0;
614 }
615
616 VLIB_CLI_COMMAND (show_vxlan_gpe_tunnel_command, static) = {
617     .path = "show vxlan-gpe",
618     .function = show_vxlan_gpe_tunnel_command_fn,
619 };
620
621 /**
622  * @brief Feature init function for VXLAN GPE
623  *
624  * @param *vm
625  *
626  * @return error
627  *
628  */
629 clib_error_t *vxlan_gpe_init (vlib_main_t *vm)
630 {
631   vxlan_gpe_main_t *gm = &vxlan_gpe_main;
632
633   gm->vnet_main = vnet_get_main();
634   gm->vlib_main = vm;
635
636   gm->vxlan4_gpe_tunnel_by_key
637     = hash_create_mem (0, sizeof(vxlan4_gpe_tunnel_key_t), sizeof (uword));
638
639   gm->vxlan6_gpe_tunnel_by_key
640     = hash_create_mem (0, sizeof(vxlan6_gpe_tunnel_key_t), sizeof (uword));
641
642
643   udp_register_dst_port (vm, UDP_DST_PORT_vxlan_gpe,
644                          vxlan4_gpe_input_node.index, 1 /* is_ip4 */);
645   udp_register_dst_port (vm, UDP_DST_PORT_vxlan6_gpe,
646                          vxlan6_gpe_input_node.index, 0 /* is_ip4 */);
647
648   /* Register the list of standard decap protocols supported */
649   vxlan_gpe_register_decap_protocol (VXLAN_GPE_PROTOCOL_IP4,
650                                      VXLAN_GPE_INPUT_NEXT_IP4_INPUT);
651   vxlan_gpe_register_decap_protocol (VXLAN_GPE_PROTOCOL_IP6,
652                                      VXLAN_GPE_INPUT_NEXT_IP6_INPUT);
653   vxlan_gpe_register_decap_protocol (VXLAN_GPE_PROTOCOL_ETHERNET,
654                                      VXLAN_GPE_INPUT_NEXT_ETHERNET_INPUT);
655   return 0;
656 }
657
658 VLIB_INIT_FUNCTION(vxlan_gpe_init);
659