2cba596f09733269ed88f7243b0b2267bd4e9954
[vpp.git] / src / 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   clib_error_t *error = NULL;
458
459   /* Get a line of input. */
460   if (! unformat_user (input, unformat_line_input, line_input))
461     return 0;
462
463   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) {
464     if (unformat (line_input, "del"))
465       is_add = 0;
466     else if (unformat (line_input, "local %U",
467                        unformat_ip4_address, &local.ip4))
468     {
469       local_set = 1;
470       ipv4_set = 1;
471     }
472     else if (unformat (line_input, "remote %U",
473                        unformat_ip4_address, &remote.ip4))
474     {
475       remote_set = 1;
476       ipv4_set = 1;
477     }
478     else if (unformat (line_input, "local %U",
479                        unformat_ip6_address, &local.ip6))
480     {
481       local_set = 1;
482       ipv6_set = 1;
483     }
484     else if (unformat (line_input, "remote %U",
485                        unformat_ip6_address, &remote.ip6))
486     {
487       remote_set = 1;
488       ipv6_set = 1;
489     }
490     else if (unformat (line_input, "encap-vrf-id %d", &tmp))
491       {
492         if (ipv6_set)
493           encap_fib_index = ip6_fib_index_from_table_id (tmp);
494         else
495           encap_fib_index =  ip4_fib_index_from_table_id (tmp);
496
497         if (encap_fib_index == ~0)
498           {
499             error = clib_error_return (0, "nonexistent encap fib id %d", tmp);
500             goto done;
501           }
502       }
503     else if (unformat (line_input, "decap-vrf-id %d", &tmp))
504       {
505         if (ipv6_set)
506           decap_fib_index = ip6_fib_index_from_table_id (tmp);
507         else
508           decap_fib_index = ip4_fib_index_from_table_id (tmp);
509
510         if (decap_fib_index == ~0)
511           {
512             error = clib_error_return (0, "nonexistent decap fib id %d", tmp);
513             goto done;
514           }
515       }
516     else if (unformat (line_input, "vni %d", &vni))
517       vni_set = 1;
518     else if (unformat(line_input, "next-ip4"))
519       protocol = VXLAN_GPE_PROTOCOL_IP4;
520     else if (unformat(line_input, "next-ip6"))
521       protocol = VXLAN_GPE_PROTOCOL_IP6;
522     else if (unformat(line_input, "next-ethernet"))
523       protocol = VXLAN_GPE_PROTOCOL_ETHERNET;
524     else if (unformat(line_input, "next-nsh"))
525       protocol = VXLAN_GPE_PROTOCOL_NSH;
526     else
527       {
528         error = clib_error_return (0, "parse error: '%U'",
529                                    format_unformat_error, line_input);
530         goto done;
531       }
532   }
533
534   if (local_set == 0)
535     {
536       error = clib_error_return (0, "tunnel local address not specified");
537       goto done;
538     }
539
540   if (remote_set == 0)
541     {
542       error = clib_error_return (0, "tunnel remote address not specified");
543       goto done;
544     }
545
546   if (ipv4_set && ipv6_set)
547     {
548       error = clib_error_return (0, "both IPv4 and IPv6 addresses specified");
549       goto done;
550     }
551
552   if ((ipv4_set && memcmp(&local.ip4, &remote.ip4, sizeof(local.ip4)) == 0) ||
553       (ipv6_set && memcmp(&local.ip6, &remote.ip6, sizeof(local.ip6)) == 0))
554     {
555       error = clib_error_return (0, "src and dst addresses are identical");
556       goto done;
557     }
558
559   if (vni_set == 0)
560     {
561       error = clib_error_return (0, "vni not specified");
562       goto done;
563     }
564
565   memset (a, 0, sizeof (*a));
566
567   a->is_add = is_add;
568   a->is_ip6 = ipv6_set;
569
570 #define _(x) a->x = x;
571   foreach_gpe_copy_field;
572   if (ipv4_set) foreach_copy_ipv4
573   else          foreach_copy_ipv6
574 #undef _
575
576   rv = vnet_vxlan_gpe_add_del_tunnel (a, &sw_if_index);
577
578   switch(rv)
579     {
580     case 0:
581       vlib_cli_output(vm, "%U\n", format_vnet_sw_if_index_name, vnet_get_main(), sw_if_index);
582       break;
583     case VNET_API_ERROR_INVALID_DECAP_NEXT:
584       error = clib_error_return (0, "invalid decap-next...");
585       goto done;
586
587     case VNET_API_ERROR_TUNNEL_EXIST:
588       error = clib_error_return (0, "tunnel already exists...");
589       goto done;
590
591     case VNET_API_ERROR_NO_SUCH_ENTRY:
592       error = clib_error_return (0, "tunnel does not exist...");
593       goto done;
594
595     default:
596       error = clib_error_return
597         (0, "vnet_vxlan_gpe_add_del_tunnel returned %d", rv);
598       goto done;
599     }
600
601 done:
602   unformat_free (line_input);
603
604   return error;
605 }
606
607 VLIB_CLI_COMMAND (create_vxlan_gpe_tunnel_command, static) = {
608   .path = "create vxlan-gpe tunnel",
609   .short_help =
610   "create vxlan-gpe tunnel local <local-addr> remote <remote-addr>"
611   " vni <nn> [next-ip4][next-ip6][next-ethernet][next-nsh]"
612   " [encap-vrf-id <nn>] [decap-vrf-id <nn>]"
613   " [del]\n",
614   .function = vxlan_gpe_add_del_tunnel_command_fn,
615 };
616
617 /**
618  * @brief CLI function for showing VXLAN GPE tunnels
619  *
620  * @param *vm
621  * @param *input
622  * @param *cmd
623  *
624  * @return error
625  *
626  */
627 static clib_error_t *
628 show_vxlan_gpe_tunnel_command_fn (vlib_main_t * vm,
629                                 unformat_input_t * input,
630                                 vlib_cli_command_t * cmd)
631 {
632   vxlan_gpe_main_t * gm = &vxlan_gpe_main;
633   vxlan_gpe_tunnel_t * t;
634
635   if (pool_elts (gm->tunnels) == 0)
636     vlib_cli_output (vm, "No vxlan-gpe tunnels configured.");
637
638   pool_foreach (t, gm->tunnels,
639   ({
640     vlib_cli_output (vm, "%U", format_vxlan_gpe_tunnel, t);
641   }));
642
643   return 0;
644 }
645
646 VLIB_CLI_COMMAND (show_vxlan_gpe_tunnel_command, static) = {
647     .path = "show vxlan-gpe",
648     .function = show_vxlan_gpe_tunnel_command_fn,
649 };
650
651 /**
652  * @brief Feature init function for VXLAN GPE
653  *
654  * @param *vm
655  *
656  * @return error
657  *
658  */
659 clib_error_t *vxlan_gpe_init (vlib_main_t *vm)
660 {
661   vxlan_gpe_main_t *gm = &vxlan_gpe_main;
662
663   gm->vnet_main = vnet_get_main();
664   gm->vlib_main = vm;
665
666   gm->vxlan4_gpe_tunnel_by_key
667     = hash_create_mem (0, sizeof(vxlan4_gpe_tunnel_key_t), sizeof (uword));
668
669   gm->vxlan6_gpe_tunnel_by_key
670     = hash_create_mem (0, sizeof(vxlan6_gpe_tunnel_key_t), sizeof (uword));
671
672
673   udp_register_dst_port (vm, UDP_DST_PORT_vxlan_gpe,
674                          vxlan4_gpe_input_node.index, 1 /* is_ip4 */);
675   udp_register_dst_port (vm, UDP_DST_PORT_vxlan6_gpe,
676                          vxlan6_gpe_input_node.index, 0 /* is_ip4 */);
677
678   /* Register the list of standard decap protocols supported */
679   vxlan_gpe_register_decap_protocol (VXLAN_GPE_PROTOCOL_IP4,
680                                      VXLAN_GPE_INPUT_NEXT_IP4_INPUT);
681   vxlan_gpe_register_decap_protocol (VXLAN_GPE_PROTOCOL_IP6,
682                                      VXLAN_GPE_INPUT_NEXT_IP6_INPUT);
683   vxlan_gpe_register_decap_protocol (VXLAN_GPE_PROTOCOL_ETHERNET,
684                                      VXLAN_GPE_INPUT_NEXT_ETHERNET_INPUT);
685   return 0;
686 }
687
688 VLIB_INIT_FUNCTION(vxlan_gpe_init);
689