gtpu: offload RX flow
[vpp.git] / src / plugins / gtpu / gtpu.c
1 /*
2  *------------------------------------------------------------------
3  * Copyright (c) 2017 Intel and/or its affiliates.
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *------------------------------------------------------------------
16  */
17 #include <stdint.h>
18 #include <net/if.h>
19 #include <sys/ioctl.h>
20 #include <inttypes.h>
21
22 #include <vlib/vlib.h>
23 #include <vlib/unix/unix.h>
24 #include <vnet/ethernet/ethernet.h>
25 #include <vnet/fib/fib_entry.h>
26 #include <vnet/fib/fib_table.h>
27 #include <vnet/fib/fib_entry_track.h>
28 #include <vnet/mfib/mfib_table.h>
29 #include <vnet/adj/adj_mcast.h>
30 #include <vnet/dpo/dpo.h>
31 #include <vnet/plugin/plugin.h>
32 #include <vpp/app/version.h>
33 #include <gtpu/gtpu.h>
34 #include <vnet/flow/flow.h>
35
36 gtpu_main_t gtpu_main;
37
38 /* *INDENT-OFF* */
39 VNET_FEATURE_INIT (ip4_gtpu_bypass, static) = {
40   .arc_name = "ip4-unicast",
41   .node_name = "ip4-gtpu-bypass",
42   .runs_before = VNET_FEATURES ("ip4-lookup"),
43 };
44
45 VNET_FEATURE_INIT (ip6_gtpu_bypass, static) = {
46   .arc_name = "ip6-unicast",
47   .node_name = "ip6-gtpu-bypass",
48   .runs_before = VNET_FEATURES ("ip6-lookup"),
49 };
50 /* *INDENT-on* */
51
52 u8 * format_gtpu_encap_trace (u8 * s, va_list * args)
53 {
54   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
55   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
56   gtpu_encap_trace_t * t
57       = va_arg (*args, gtpu_encap_trace_t *);
58
59   s = format (s, "GTPU encap to gtpu_tunnel%d teid %d",
60               t->tunnel_index, t->teid);
61   return s;
62 }
63
64 static u8 *
65 format_decap_next (u8 * s, va_list * args)
66 {
67   u32 next_index = va_arg (*args, u32);
68
69   switch (next_index)
70     {
71     case GTPU_INPUT_NEXT_DROP:
72       return format (s, "drop");
73     case GTPU_INPUT_NEXT_L2_INPUT:
74       return format (s, "l2");
75     case GTPU_INPUT_NEXT_IP4_INPUT:
76       return format (s, "ip4");
77     case GTPU_INPUT_NEXT_IP6_INPUT:
78       return format (s, "ip6");
79     default:
80       return format (s, "index %d", next_index);
81     }
82   return s;
83 }
84
85 u8 *
86 format_gtpu_tunnel (u8 * s, va_list * args)
87 {
88   gtpu_tunnel_t *t = va_arg (*args, gtpu_tunnel_t *);
89   gtpu_main_t *ngm = &gtpu_main;
90
91   s = format (s, "[%d] src %U dst %U teid %d fib-idx %d sw-if-idx %d ",
92               t - ngm->tunnels,
93               format_ip46_address, &t->src, IP46_TYPE_ANY,
94               format_ip46_address, &t->dst, IP46_TYPE_ANY,
95               t->teid,  t->encap_fib_index, t->sw_if_index);
96
97   s = format (s, "encap-dpo-idx %d ", t->next_dpo.dpoi_index);
98   s = format (s, "decap-next-%U ", format_decap_next, t->decap_next_index);
99
100   if (PREDICT_FALSE (ip46_address_is_multicast (&t->dst)))
101     s = format (s, "mcast-sw-if-idx %d ", t->mcast_sw_if_index);
102
103   return s;
104 }
105
106 static u8 *
107 format_gtpu_name (u8 * s, va_list * args)
108 {
109   u32 dev_instance = va_arg (*args, u32);
110   return format (s, "gtpu_tunnel%d", dev_instance);
111 }
112
113 static clib_error_t *
114 gtpu_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
115 {
116   u32 hw_flags = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ?
117     VNET_HW_INTERFACE_FLAG_LINK_UP : 0;
118   vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags);
119
120   return /* no error */ 0;
121 }
122
123 /* *INDENT-OFF* */
124 VNET_DEVICE_CLASS (gtpu_device_class,static) = {
125   .name = "GTPU",
126   .format_device_name = format_gtpu_name,
127   .format_tx_trace = format_gtpu_encap_trace,
128   .admin_up_down_function = gtpu_interface_admin_up_down,
129 };
130 /* *INDENT-ON* */
131
132 static u8 *
133 format_gtpu_header_with_length (u8 * s, va_list * args)
134 {
135   u32 dev_instance = va_arg (*args, u32);
136   s = format (s, "unimplemented dev %u", dev_instance);
137   return s;
138 }
139
140 /* *INDENT-OFF* */
141 VNET_HW_INTERFACE_CLASS (gtpu_hw_class) =
142 {
143   .name = "GTPU",
144   .format_header = format_gtpu_header_with_length,
145   .build_rewrite = default_build_rewrite,
146   .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,
147 };
148 /* *INDENT-ON* */
149
150 static void
151 gtpu_tunnel_restack_dpo (gtpu_tunnel_t * t)
152 {
153   dpo_id_t dpo = DPO_INVALID;
154   u32 encap_index = ip46_address_is_ip4 (&t->dst) ?
155     gtpu4_encap_node.index : gtpu6_encap_node.index;
156   fib_forward_chain_type_t forw_type = ip46_address_is_ip4 (&t->dst) ?
157     FIB_FORW_CHAIN_TYPE_UNICAST_IP4 : FIB_FORW_CHAIN_TYPE_UNICAST_IP6;
158
159   fib_entry_contribute_forwarding (t->fib_entry_index, forw_type, &dpo);
160   dpo_stack_from_node (encap_index, &t->next_dpo, &dpo);
161   dpo_reset (&dpo);
162 }
163
164 static gtpu_tunnel_t *
165 gtpu_tunnel_from_fib_node (fib_node_t * node)
166 {
167   return ((gtpu_tunnel_t *) (((char *) node) -
168                              STRUCT_OFFSET_OF (gtpu_tunnel_t, node)));
169 }
170
171 /**
172  * Function definition to backwalk a FIB node -
173  * Here we will restack the new dpo of GTPU DIP to encap node.
174  */
175 static fib_node_back_walk_rc_t
176 gtpu_tunnel_back_walk (fib_node_t * node, fib_node_back_walk_ctx_t * ctx)
177 {
178   gtpu_tunnel_restack_dpo (gtpu_tunnel_from_fib_node (node));
179   return (FIB_NODE_BACK_WALK_CONTINUE);
180 }
181
182 /**
183  * Function definition to get a FIB node from its index
184  */
185 static fib_node_t *
186 gtpu_tunnel_fib_node_get (fib_node_index_t index)
187 {
188   gtpu_tunnel_t *t;
189   gtpu_main_t *gtm = &gtpu_main;
190
191   t = pool_elt_at_index (gtm->tunnels, index);
192
193   return (&t->node);
194 }
195
196 /**
197  * Function definition to inform the FIB node that its last lock has gone.
198  */
199 static void
200 gtpu_tunnel_last_lock_gone (fib_node_t * node)
201 {
202   /*
203    * The GTPU tunnel is a root of the graph. As such
204    * it never has children and thus is never locked.
205    */
206   ASSERT (0);
207 }
208
209 /*
210  * Virtual function table registered by GTPU tunnels
211  * for participation in the FIB object graph.
212  */
213 const static fib_node_vft_t gtpu_vft = {
214   .fnv_get = gtpu_tunnel_fib_node_get,
215   .fnv_last_lock = gtpu_tunnel_last_lock_gone,
216   .fnv_back_walk = gtpu_tunnel_back_walk,
217 };
218
219
220 #define foreach_copy_field                      \
221 _(teid)                                          \
222 _(mcast_sw_if_index)                            \
223 _(encap_fib_index)                              \
224 _(decap_next_index)                             \
225 _(src)                                          \
226 _(dst)
227
228 static void
229 ip_udp_gtpu_rewrite (gtpu_tunnel_t * t, bool is_ip6)
230 {
231   union
232   {
233     ip4_gtpu_header_t *h4;
234     ip6_gtpu_header_t *h6;
235     u8 *rw;
236   } r =
237   {
238   .rw = 0};
239   int len = is_ip6 ? sizeof *r.h6 : sizeof *r.h4;
240
241   vec_validate_aligned (r.rw, len - 1, CLIB_CACHE_LINE_BYTES);
242
243   udp_header_t *udp;
244   gtpu_header_t *gtpu;
245   /* Fixed portion of the (outer) ip header */
246   if (!is_ip6)
247     {
248       ip4_header_t *ip = &r.h4->ip4;
249       udp = &r.h4->udp;
250       gtpu = &r.h4->gtpu;
251       ip->ip_version_and_header_length = 0x45;
252       ip->ttl = 254;
253       ip->protocol = IP_PROTOCOL_UDP;
254
255       ip->src_address = t->src.ip4;
256       ip->dst_address = t->dst.ip4;
257
258       /* we fix up the ip4 header length and checksum after-the-fact */
259       ip->checksum = ip4_header_checksum (ip);
260     }
261   else
262     {
263       ip6_header_t *ip = &r.h6->ip6;
264       udp = &r.h6->udp;
265       gtpu = &r.h6->gtpu;
266       ip->ip_version_traffic_class_and_flow_label =
267         clib_host_to_net_u32 (6 << 28);
268       ip->hop_limit = 255;
269       ip->protocol = IP_PROTOCOL_UDP;
270
271       ip->src_address = t->src.ip6;
272       ip->dst_address = t->dst.ip6;
273     }
274
275   /* UDP header, randomize src port on something, maybe? */
276   udp->src_port = clib_host_to_net_u16 (2152);
277   udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_GTPU);
278
279   /* GTPU header */
280   gtpu->ver_flags = GTPU_V1_VER | GTPU_PT_GTP;
281   gtpu->type = GTPU_TYPE_GTPU;
282   gtpu->teid = clib_host_to_net_u32 (t->teid);
283
284   t->rewrite = r.rw;
285   /* Now only support 8-byte gtpu header. TBD */
286   _vec_len (t->rewrite) = sizeof (ip4_gtpu_header_t) - 4;
287
288   return;
289 }
290
291 static bool
292 gtpu_decap_next_is_valid (gtpu_main_t * gtm, u32 is_ip6, u32 decap_next_index)
293 {
294   vlib_main_t *vm = gtm->vlib_main;
295   u32 input_idx = (!is_ip6) ? gtpu4_input_node.index : gtpu6_input_node.index;
296   vlib_node_runtime_t *r = vlib_node_get_runtime (vm, input_idx);
297
298   return decap_next_index < r->n_next_nodes;
299 }
300
301 static uword
302 vtep_addr_ref (ip46_address_t * ip)
303 {
304   uword *vtep = ip46_address_is_ip4 (ip) ?
305     hash_get (gtpu_main.vtep4, ip->ip4.as_u32) :
306     hash_get_mem (gtpu_main.vtep6, &ip->ip6);
307   if (vtep)
308     return ++(*vtep);
309   ip46_address_is_ip4 (ip) ?
310     hash_set (gtpu_main.vtep4, ip->ip4.as_u32, 1) :
311     hash_set_mem_alloc (&gtpu_main.vtep6, &ip->ip6, 1);
312   return 1;
313 }
314
315 static uword
316 vtep_addr_unref (ip46_address_t * ip)
317 {
318   uword *vtep = ip46_address_is_ip4 (ip) ?
319     hash_get (gtpu_main.vtep4, ip->ip4.as_u32) :
320     hash_get_mem (gtpu_main.vtep6, &ip->ip6);
321   ASSERT (vtep);
322   if (--(*vtep) != 0)
323     return *vtep;
324   ip46_address_is_ip4 (ip) ?
325     hash_unset (gtpu_main.vtep4, ip->ip4.as_u32) :
326     hash_unset_mem_free (&gtpu_main.vtep6, &ip->ip6);
327   return 0;
328 }
329
330 typedef CLIB_PACKED (union
331                      {
332                      struct
333                      {
334                      fib_node_index_t mfib_entry_index;
335                      adj_index_t mcast_adj_index;
336                      }; u64 as_u64;
337                      }) mcast_shared_t;
338
339 static inline mcast_shared_t
340 mcast_shared_get (ip46_address_t * ip)
341 {
342   ASSERT (ip46_address_is_multicast (ip));
343   uword *p = hash_get_mem (gtpu_main.mcast_shared, ip);
344   ASSERT (p);
345   return (mcast_shared_t)
346   {
347   .as_u64 = *p};
348 }
349
350 static inline void
351 mcast_shared_add (ip46_address_t * dst, fib_node_index_t mfei, adj_index_t ai)
352 {
353   mcast_shared_t new_ep = {
354     .mcast_adj_index = ai,
355     .mfib_entry_index = mfei,
356   };
357
358   hash_set_mem_alloc (&gtpu_main.mcast_shared, dst, new_ep.as_u64);
359 }
360
361 static inline void
362 mcast_shared_remove (ip46_address_t * dst)
363 {
364   mcast_shared_t ep = mcast_shared_get (dst);
365
366   adj_unlock (ep.mcast_adj_index);
367   mfib_table_entry_delete_index (ep.mfib_entry_index, MFIB_SOURCE_GTPU);
368
369   hash_unset_mem_free (&gtpu_main.mcast_shared, dst);
370 }
371
372 int vnet_gtpu_add_del_tunnel
373   (vnet_gtpu_add_del_tunnel_args_t * a, u32 * sw_if_indexp)
374 {
375   gtpu_main_t *gtm = &gtpu_main;
376   gtpu_tunnel_t *t = 0;
377   vnet_main_t *vnm = gtm->vnet_main;
378   uword *p;
379   u32 hw_if_index = ~0;
380   u32 sw_if_index = ~0;
381   gtpu4_tunnel_key_t key4;
382   gtpu6_tunnel_key_t key6;
383   bool is_ip6 = !ip46_address_is_ip4 (&a->dst);
384
385   if (!is_ip6)
386     {
387       key4.src = a->dst.ip4.as_u32;     /* decap src in key is encap dst in config */
388       key4.teid = clib_host_to_net_u32 (a->teid);
389       p = hash_get (gtm->gtpu4_tunnel_by_key, key4.as_u64);
390     }
391   else
392     {
393       key6.src = a->dst.ip6;
394       key6.teid = clib_host_to_net_u32 (a->teid);
395       p = hash_get_mem (gtm->gtpu6_tunnel_by_key, &key6);
396     }
397
398   if (a->is_add)
399     {
400       l2input_main_t *l2im = &l2input_main;
401
402       /* adding a tunnel: tunnel must not already exist */
403       if (p)
404         return VNET_API_ERROR_TUNNEL_EXIST;
405
406       /*if not set explicitly, default to l2 */
407       if (a->decap_next_index == ~0)
408         a->decap_next_index = GTPU_INPUT_NEXT_L2_INPUT;
409       if (!gtpu_decap_next_is_valid (gtm, is_ip6, a->decap_next_index))
410         return VNET_API_ERROR_INVALID_DECAP_NEXT;
411
412       pool_get_aligned (gtm->tunnels, t, CLIB_CACHE_LINE_BYTES);
413       clib_memset (t, 0, sizeof (*t));
414
415       /* copy from arg structure */
416 #define _(x) t->x = a->x;
417       foreach_copy_field;
418 #undef _
419
420       ip_udp_gtpu_rewrite (t, is_ip6);
421
422       /* clear the flow index */
423       t->flow_index = ~0;
424
425       /* copy the key */
426       if (is_ip6)
427         hash_set_mem_alloc (&gtm->gtpu6_tunnel_by_key, &key6,
428                             t - gtm->tunnels);
429       else
430         hash_set (gtm->gtpu4_tunnel_by_key, key4.as_u64, t - gtm->tunnels);
431
432       vnet_hw_interface_t *hi;
433       if (vec_len (gtm->free_gtpu_tunnel_hw_if_indices) > 0)
434         {
435           vnet_interface_main_t *im = &vnm->interface_main;
436           hw_if_index = gtm->free_gtpu_tunnel_hw_if_indices
437             [vec_len (gtm->free_gtpu_tunnel_hw_if_indices) - 1];
438           _vec_len (gtm->free_gtpu_tunnel_hw_if_indices) -= 1;
439
440           hi = vnet_get_hw_interface (vnm, hw_if_index);
441           hi->dev_instance = t - gtm->tunnels;
442           hi->hw_instance = hi->dev_instance;
443
444           /* clear old stats of freed tunnel before reuse */
445           sw_if_index = hi->sw_if_index;
446           vnet_interface_counter_lock (im);
447           vlib_zero_combined_counter
448             (&im->combined_sw_if_counters[VNET_INTERFACE_COUNTER_TX],
449              sw_if_index);
450           vlib_zero_combined_counter (&im->combined_sw_if_counters
451                                       [VNET_INTERFACE_COUNTER_RX],
452                                       sw_if_index);
453           vlib_zero_simple_counter (&im->sw_if_counters
454                                     [VNET_INTERFACE_COUNTER_DROP],
455                                     sw_if_index);
456           vnet_interface_counter_unlock (im);
457         }
458       else
459         {
460           hw_if_index = vnet_register_interface
461             (vnm, gtpu_device_class.index, t - gtm->tunnels,
462              gtpu_hw_class.index, t - gtm->tunnels);
463           hi = vnet_get_hw_interface (vnm, hw_if_index);
464         }
465
466       /* Set gtpu tunnel output node */
467       u32 encap_index = !is_ip6 ?
468         gtpu4_encap_node.index : gtpu6_encap_node.index;
469       vnet_set_interface_output_node (vnm, hw_if_index, encap_index);
470
471       t->hw_if_index = hw_if_index;
472       t->sw_if_index = sw_if_index = hi->sw_if_index;
473
474       vec_validate_init_empty (gtm->tunnel_index_by_sw_if_index, sw_if_index,
475                                ~0);
476       gtm->tunnel_index_by_sw_if_index[sw_if_index] = t - gtm->tunnels;
477
478       /* setup l2 input config with l2 feature and bd 0 to drop packet */
479       vec_validate (l2im->configs, sw_if_index);
480       l2im->configs[sw_if_index].feature_bitmap = L2INPUT_FEAT_DROP;
481       l2im->configs[sw_if_index].bd_index = 0;
482
483       vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, sw_if_index);
484       si->flags &= ~VNET_SW_INTERFACE_FLAG_HIDDEN;
485       vnet_sw_interface_set_flags (vnm, sw_if_index,
486                                    VNET_SW_INTERFACE_FLAG_ADMIN_UP);
487
488       fib_node_init (&t->node, gtm->fib_node_type);
489       fib_prefix_t tun_dst_pfx;
490       vnet_flood_class_t flood_class = VNET_FLOOD_CLASS_TUNNEL_NORMAL;
491
492       fib_prefix_from_ip46_addr (&t->dst, &tun_dst_pfx);
493       if (!ip46_address_is_multicast (&t->dst))
494         {
495           /* Unicast tunnel -
496            * Track the FIB entry for the tunnel's destination.
497            * The tunnel will then get poked
498            * when the forwarding for the entry updates, and the tunnel can
499            * re-stack accordingly
500            */
501           vtep_addr_ref (&t->src);
502           t->fib_entry_index = fib_entry_track (t->encap_fib_index,
503                                                 &tun_dst_pfx,
504                                                 gtm->fib_node_type,
505                                                 t - gtm->tunnels,
506                                                 &t->sibling_index);
507           gtpu_tunnel_restack_dpo (t);
508         }
509       else
510         {
511           /* Multicast tunnel -
512            * as the same mcast group can be used for multiple mcast tunnels
513            * with different VNIs, create the output adjacency only if
514            * it does not already exist
515            */
516           fib_protocol_t fp = fib_ip_proto (is_ip6);
517
518           if (vtep_addr_ref (&t->dst) == 1)
519             {
520               fib_node_index_t mfei;
521               adj_index_t ai;
522               fib_route_path_t path = {
523                 .frp_proto = fib_proto_to_dpo (fp),
524                 .frp_addr = zero_addr,
525                 .frp_sw_if_index = 0xffffffff,
526                 .frp_fib_index = ~0,
527                 .frp_weight = 1,
528                 .frp_flags = FIB_ROUTE_PATH_LOCAL,
529                 .frp_mitf_flags = MFIB_ITF_FLAG_FORWARD,
530               };
531               const mfib_prefix_t mpfx = {
532                 .fp_proto = fp,
533                 .fp_len = (is_ip6 ? 128 : 32),
534                 .fp_grp_addr = tun_dst_pfx.fp_addr,
535               };
536
537               /*
538                * Setup the (*,G) to receive traffic on the mcast group
539                *  - the forwarding interface is for-us
540                *  - the accepting interface is that from the API
541                */
542               mfib_table_entry_path_update (t->encap_fib_index,
543                                             &mpfx, MFIB_SOURCE_GTPU, &path);
544
545               path.frp_sw_if_index = a->mcast_sw_if_index;
546               path.frp_flags = FIB_ROUTE_PATH_FLAG_NONE;
547               path.frp_mitf_flags = MFIB_ITF_FLAG_ACCEPT;
548               mfei = mfib_table_entry_path_update (t->encap_fib_index,
549                                                    &mpfx,
550                                                    MFIB_SOURCE_GTPU, &path);
551
552               /*
553                * Create the mcast adjacency to send traffic to the group
554                */
555               ai = adj_mcast_add_or_lock (fp,
556                                           fib_proto_to_link (fp),
557                                           a->mcast_sw_if_index);
558
559               /*
560                * create a new end-point
561                */
562               mcast_shared_add (&t->dst, mfei, ai);
563             }
564
565           dpo_id_t dpo = DPO_INVALID;
566           mcast_shared_t ep = mcast_shared_get (&t->dst);
567
568           /* Stack shared mcast dst mac addr rewrite on encap */
569           dpo_set (&dpo, DPO_ADJACENCY_MCAST,
570                    fib_proto_to_dpo (fp), ep.mcast_adj_index);
571
572           dpo_stack_from_node (encap_index, &t->next_dpo, &dpo);
573
574           dpo_reset (&dpo);
575           flood_class = VNET_FLOOD_CLASS_TUNNEL_MASTER;
576         }
577
578       vnet_get_sw_interface (vnet_get_main (), sw_if_index)->flood_class =
579         flood_class;
580     }
581   else
582     {
583       /* deleting a tunnel: tunnel must exist */
584       if (!p)
585         return VNET_API_ERROR_NO_SUCH_ENTRY;
586
587       t = pool_elt_at_index (gtm->tunnels, p[0]);
588       sw_if_index = t->sw_if_index;
589
590       vnet_sw_interface_set_flags (vnm, t->sw_if_index, 0 /* down */ );
591       vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, t->sw_if_index);
592       si->flags |= VNET_SW_INTERFACE_FLAG_HIDDEN;
593
594       /* make sure tunnel is removed from l2 bd or xconnect */
595       set_int_l2_mode (gtm->vlib_main, vnm, MODE_L3, t->sw_if_index, 0,
596                        L2_BD_PORT_TYPE_NORMAL, 0, 0);
597       vec_add1 (gtm->free_gtpu_tunnel_hw_if_indices, t->hw_if_index);
598
599       gtm->tunnel_index_by_sw_if_index[t->sw_if_index] = ~0;
600
601       if (!is_ip6)
602         hash_unset (gtm->gtpu4_tunnel_by_key, key4.as_u64);
603       else
604         hash_unset_mem_free (&gtm->gtpu6_tunnel_by_key, &key6);
605
606       if (!ip46_address_is_multicast (&t->dst))
607         {
608           if (t->flow_index != ~0)
609             vnet_flow_del (vnm, t->flow_index);
610
611           vtep_addr_unref (&t->src);
612           fib_entry_untrack (t->fib_entry_index, t->sibling_index);
613         }
614       else if (vtep_addr_unref (&t->dst) == 0)
615         {
616           mcast_shared_remove (&t->dst);
617         }
618
619       fib_node_deinit (&t->node);
620       vec_free (t->rewrite);
621       pool_put (gtm->tunnels, t);
622     }
623
624   if (sw_if_indexp)
625     *sw_if_indexp = sw_if_index;
626
627   if (a->is_add)
628     {
629       /* register udp ports */
630       if (!is_ip6 && !udp_is_valid_dst_port (UDP_DST_PORT_GTPU, 1))
631         udp_register_dst_port (gtm->vlib_main, UDP_DST_PORT_GTPU,
632                                gtpu4_input_node.index, /* is_ip4 */ 1);
633       if (is_ip6 && !udp_is_valid_dst_port (UDP_DST_PORT_GTPU6, 0))
634         udp_register_dst_port (gtm->vlib_main, UDP_DST_PORT_GTPU6,
635                                gtpu6_input_node.index, /* is_ip4 */ 0);
636     }
637
638   return 0;
639 }
640
641 static uword
642 get_decap_next_for_node (u32 node_index, u32 ipv4_set)
643 {
644   gtpu_main_t *gtm = &gtpu_main;
645   vlib_main_t *vm = gtm->vlib_main;
646   uword input_node = (ipv4_set) ? gtpu4_input_node.index :
647     gtpu6_input_node.index;
648
649   return vlib_node_add_next (vm, input_node, node_index);
650 }
651
652 static uword
653 unformat_decap_next (unformat_input_t * input, va_list * args)
654 {
655   u32 *result = va_arg (*args, u32 *);
656   u32 ipv4_set = va_arg (*args, int);
657   gtpu_main_t *gtm = &gtpu_main;
658   vlib_main_t *vm = gtm->vlib_main;
659   u32 node_index;
660   u32 tmp;
661
662   if (unformat (input, "l2"))
663     *result = GTPU_INPUT_NEXT_L2_INPUT;
664   else if (unformat (input, "ip4"))
665     *result = GTPU_INPUT_NEXT_IP4_INPUT;
666   else if (unformat (input, "ip6"))
667     *result = GTPU_INPUT_NEXT_IP6_INPUT;
668   else if (unformat (input, "node %U", unformat_vlib_node, vm, &node_index))
669     *result = get_decap_next_for_node (node_index, ipv4_set);
670   else if (unformat (input, "%d", &tmp))
671     *result = tmp;
672   else
673     return 0;
674
675   return 1;
676 }
677
678 static clib_error_t *
679 gtpu_add_del_tunnel_command_fn (vlib_main_t * vm,
680                                 unformat_input_t * input,
681                                 vlib_cli_command_t * cmd)
682 {
683   unformat_input_t _line_input, *line_input = &_line_input;
684   ip46_address_t src, dst;
685   u8 is_add = 1;
686   u8 src_set = 0;
687   u8 dst_set = 0;
688   u8 grp_set = 0;
689   u8 ipv4_set = 0;
690   u8 ipv6_set = 0;
691   u32 encap_fib_index = 0;
692   u32 mcast_sw_if_index = ~0;
693   u32 decap_next_index = GTPU_INPUT_NEXT_L2_INPUT;
694   u32 teid = 0;
695   u32 tmp;
696   int rv;
697   vnet_gtpu_add_del_tunnel_args_t _a, *a = &_a;
698   u32 tunnel_sw_if_index;
699   clib_error_t *error = NULL;
700
701   /* Cant "universally zero init" (={0}) due to GCC bug 53119 */
702   clib_memset (&src, 0, sizeof src);
703   clib_memset (&dst, 0, sizeof dst);
704
705   /* Get a line of input. */
706   if (!unformat_user (input, unformat_line_input, line_input))
707     return 0;
708
709   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
710     {
711       if (unformat (line_input, "del"))
712         {
713           is_add = 0;
714         }
715       else if (unformat (line_input, "src %U",
716                          unformat_ip4_address, &src.ip4))
717         {
718           src_set = 1;
719           ipv4_set = 1;
720         }
721       else if (unformat (line_input, "dst %U",
722                          unformat_ip4_address, &dst.ip4))
723         {
724           dst_set = 1;
725           ipv4_set = 1;
726         }
727       else if (unformat (line_input, "src %U",
728                          unformat_ip6_address, &src.ip6))
729         {
730           src_set = 1;
731           ipv6_set = 1;
732         }
733       else if (unformat (line_input, "dst %U",
734                          unformat_ip6_address, &dst.ip6))
735         {
736           dst_set = 1;
737           ipv6_set = 1;
738         }
739       else if (unformat (line_input, "group %U %U",
740                          unformat_ip4_address, &dst.ip4,
741                          unformat_vnet_sw_interface,
742                          vnet_get_main (), &mcast_sw_if_index))
743         {
744           grp_set = dst_set = 1;
745           ipv4_set = 1;
746         }
747       else if (unformat (line_input, "group %U %U",
748                          unformat_ip6_address, &dst.ip6,
749                          unformat_vnet_sw_interface,
750                          vnet_get_main (), &mcast_sw_if_index))
751         {
752           grp_set = dst_set = 1;
753           ipv6_set = 1;
754         }
755       else if (unformat (line_input, "encap-vrf-id %d", &tmp))
756         {
757           encap_fib_index = fib_table_find (fib_ip_proto (ipv6_set), tmp);
758           if (encap_fib_index == ~0)
759             {
760               error =
761                 clib_error_return (0, "nonexistent encap-vrf-id %d", tmp);
762               goto done;
763             }
764         }
765       else if (unformat (line_input, "decap-next %U", unformat_decap_next,
766                          &decap_next_index, ipv4_set))
767         ;
768       else if (unformat (line_input, "teid %d", &teid))
769         ;
770       else
771         {
772           error = clib_error_return (0, "parse error: '%U'",
773                                      format_unformat_error, line_input);
774           goto done;
775         }
776     }
777
778   if (src_set == 0)
779     {
780       error = clib_error_return (0, "tunnel src address not specified");
781       goto done;
782     }
783
784   if (dst_set == 0)
785     {
786       error = clib_error_return (0, "tunnel dst address not specified");
787       goto done;
788     }
789
790   if (grp_set && !ip46_address_is_multicast (&dst))
791     {
792       error = clib_error_return (0, "tunnel group address not multicast");
793       goto done;
794     }
795
796   if (grp_set == 0 && ip46_address_is_multicast (&dst))
797     {
798       error = clib_error_return (0, "dst address must be unicast");
799       goto done;
800     }
801
802   if (grp_set && mcast_sw_if_index == ~0)
803     {
804       error = clib_error_return (0, "tunnel nonexistent multicast device");
805       goto done;
806     }
807
808   if (ipv4_set && ipv6_set)
809     {
810       error = clib_error_return (0, "both IPv4 and IPv6 addresses specified");
811       goto done;
812     }
813
814   if (ip46_address_cmp (&src, &dst) == 0)
815     {
816       error = clib_error_return (0, "src and dst addresses are identical");
817       goto done;
818     }
819
820   if (decap_next_index == ~0)
821     {
822       error = clib_error_return (0, "next node not found");
823       goto done;
824     }
825
826   clib_memset (a, 0, sizeof (*a));
827
828   a->is_add = is_add;
829
830 #define _(x) a->x = x;
831   foreach_copy_field;
832 #undef _
833
834   rv = vnet_gtpu_add_del_tunnel (a, &tunnel_sw_if_index);
835
836   switch (rv)
837     {
838     case 0:
839       if (is_add)
840         vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name,
841                          vnet_get_main (), tunnel_sw_if_index);
842       break;
843
844     case VNET_API_ERROR_TUNNEL_EXIST:
845       error = clib_error_return (0, "tunnel already exists...");
846       goto done;
847
848     case VNET_API_ERROR_NO_SUCH_ENTRY:
849       error = clib_error_return (0, "tunnel does not exist...");
850       goto done;
851
852     default:
853       error = clib_error_return
854         (0, "vnet_gtpu_add_del_tunnel returned %d", rv);
855       goto done;
856     }
857
858 done:
859   unformat_free (line_input);
860
861   return error;
862 }
863
864 /*?
865  * Add or delete a GTPU Tunnel.
866  *
867  * GTPU provides the features needed to allow L2 bridge domains (BDs)
868  * to span multiple servers. This is done by building an L2 overlay on
869  * top of an L3 network underlay using GTPU tunnels.
870  *
871  * This makes it possible for servers to be co-located in the same data
872  * center or be separated geographically as long as they are reachable
873  * through the underlay L3 network.
874  *
875  * You can refer to this kind of L2 overlay bridge domain as a GTPU
876  * (Virtual eXtensible VLAN) segment.
877  *
878  * @cliexpar
879  * Example of how to create a GTPU Tunnel:
880  * @cliexcmd{create gtpu tunnel src 10.0.3.1 dst 10.0.3.3 teid 13 encap-vrf-id 7}
881  * Example of how to delete a GTPU Tunnel:
882  * @cliexcmd{create gtpu tunnel src 10.0.3.1 dst 10.0.3.3 teid 13 del}
883  ?*/
884 /* *INDENT-OFF* */
885 VLIB_CLI_COMMAND (create_gtpu_tunnel_command, static) = {
886   .path = "create gtpu tunnel",
887   .short_help =
888   "create gtpu tunnel src <local-vtep-addr>"
889   " {dst <remote-vtep-addr>|group <mcast-vtep-addr> <intf-name>} teid <nn>"
890   " [encap-vrf-id <nn>] [decap-next [l2|ip4|ip6|node <name>]] [del]",
891   .function = gtpu_add_del_tunnel_command_fn,
892 };
893 /* *INDENT-ON* */
894
895 static clib_error_t *
896 show_gtpu_tunnel_command_fn (vlib_main_t * vm,
897                              unformat_input_t * input,
898                              vlib_cli_command_t * cmd)
899 {
900   gtpu_main_t *gtm = &gtpu_main;
901   gtpu_tunnel_t *t;
902
903   if (pool_elts (gtm->tunnels) == 0)
904     vlib_cli_output (vm, "No gtpu tunnels configured...");
905
906   pool_foreach (t, gtm->tunnels, (
907                                    {
908                                    vlib_cli_output (vm, "%U",
909                                                     format_gtpu_tunnel, t);
910                                    }
911                 ));
912
913   return 0;
914 }
915
916 /*?
917  * Display all the GTPU Tunnel entries.
918  *
919  * @cliexpar
920  * Example of how to display the GTPU Tunnel entries:
921  * @cliexstart{show gtpu tunnel}
922  * [0] src 10.0.3.1 dst 10.0.3.3 teid 13 encap_fib_index 0 sw_if_index 5 decap_next l2
923  * @cliexend
924  ?*/
925 /* *INDENT-OFF* */
926 VLIB_CLI_COMMAND (show_gtpu_tunnel_command, static) = {
927     .path = "show gtpu tunnel",
928     .short_help = "show gtpu tunnel",
929     .function = show_gtpu_tunnel_command_fn,
930 };
931 /* *INDENT-ON* */
932
933 void
934 vnet_int_gtpu_bypass_mode (u32 sw_if_index, u8 is_ip6, u8 is_enable)
935 {
936   if (is_ip6)
937     vnet_feature_enable_disable ("ip6-unicast", "ip6-gtpu-bypass",
938                                  sw_if_index, is_enable, 0, 0);
939   else
940     vnet_feature_enable_disable ("ip4-unicast", "ip4-gtpu-bypass",
941                                  sw_if_index, is_enable, 0, 0);
942 }
943
944 static clib_error_t *
945 set_ip_gtpu_bypass (u32 is_ip6,
946                     unformat_input_t * input, vlib_cli_command_t * cmd)
947 {
948   unformat_input_t _line_input, *line_input = &_line_input;
949   vnet_main_t *vnm = vnet_get_main ();
950   clib_error_t *error = 0;
951   u32 sw_if_index, is_enable;
952
953   sw_if_index = ~0;
954   is_enable = 1;
955
956   if (!unformat_user (input, unformat_line_input, line_input))
957     return 0;
958
959   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
960     {
961       if (unformat_user
962           (line_input, unformat_vnet_sw_interface, vnm, &sw_if_index))
963         ;
964       else if (unformat (line_input, "del"))
965         is_enable = 0;
966       else
967         {
968           error = unformat_parse_error (line_input);
969           goto done;
970         }
971     }
972
973   if (~0 == sw_if_index)
974     {
975       error = clib_error_return (0, "unknown interface `%U'",
976                                  format_unformat_error, line_input);
977       goto done;
978     }
979
980   vnet_int_gtpu_bypass_mode (sw_if_index, is_ip6, is_enable);
981
982 done:
983   unformat_free (line_input);
984
985   return error;
986 }
987
988 static clib_error_t *
989 set_ip4_gtpu_bypass (vlib_main_t * vm,
990                      unformat_input_t * input, vlib_cli_command_t * cmd)
991 {
992   return set_ip_gtpu_bypass (0, input, cmd);
993 }
994
995 /*?
996  * This command adds the 'ip4-gtpu-bypass' graph node for a given interface.
997  * By adding the IPv4 gtpu-bypass graph node to an interface, the node checks
998  *  for and validate input gtpu packet and bypass ip4-lookup, ip4-local,
999  * ip4-udp-lookup nodes to speedup gtpu packet forwarding. This node will
1000  * cause extra overhead to for non-gtpu packets which is kept at a minimum.
1001  *
1002  * @cliexpar
1003  * @parblock
1004  * Example of graph node before ip4-gtpu-bypass is enabled:
1005  * @cliexstart{show vlib graph ip4-gtpu-bypass}
1006  *            Name                      Next                    Previous
1007  * ip4-gtpu-bypass                error-drop [0]
1008  *                                gtpu4-input [1]
1009  *                                 ip4-lookup [2]
1010  * @cliexend
1011  *
1012  * Example of how to enable ip4-gtpu-bypass on an interface:
1013  * @cliexcmd{set interface ip gtpu-bypass GigabitEthernet2/0/0}
1014  *
1015  * Example of graph node after ip4-gtpu-bypass is enabled:
1016  * @cliexstart{show vlib graph ip4-gtpu-bypass}
1017  *            Name                      Next                    Previous
1018  * ip4-gtpu-bypass                error-drop [0]               ip4-input
1019  *                                gtpu4-input [1]        ip4-input-no-checksum
1020  *                                 ip4-lookup [2]
1021  * @cliexend
1022  *
1023  * Example of how to display the feature enabled on an interface:
1024  * @cliexstart{show ip interface features GigabitEthernet2/0/0}
1025  * IP feature paths configured on GigabitEthernet2/0/0...
1026  * ...
1027  * ipv4 unicast:
1028  *   ip4-gtpu-bypass
1029  *   ip4-lookup
1030  * ...
1031  * @cliexend
1032  *
1033  * Example of how to disable ip4-gtpu-bypass on an interface:
1034  * @cliexcmd{set interface ip gtpu-bypass GigabitEthernet2/0/0 del}
1035  * @endparblock
1036 ?*/
1037 /* *INDENT-OFF* */
1038 VLIB_CLI_COMMAND (set_interface_ip_gtpu_bypass_command, static) = {
1039   .path = "set interface ip gtpu-bypass",
1040   .function = set_ip4_gtpu_bypass,
1041   .short_help = "set interface ip gtpu-bypass <interface> [del]",
1042 };
1043 /* *INDENT-ON* */
1044
1045 static clib_error_t *
1046 set_ip6_gtpu_bypass (vlib_main_t * vm,
1047                      unformat_input_t * input, vlib_cli_command_t * cmd)
1048 {
1049   return set_ip_gtpu_bypass (1, input, cmd);
1050 }
1051
1052 /*?
1053  * This command adds the 'ip6-gtpu-bypass' graph node for a given interface.
1054  * By adding the IPv6 gtpu-bypass graph node to an interface, the node checks
1055  *  for and validate input gtpu packet and bypass ip6-lookup, ip6-local,
1056  * ip6-udp-lookup nodes to speedup gtpu packet forwarding. This node will
1057  * cause extra overhead to for non-gtpu packets which is kept at a minimum.
1058  *
1059  * @cliexpar
1060  * @parblock
1061  * Example of graph node before ip6-gtpu-bypass is enabled:
1062  * @cliexstart{show vlib graph ip6-gtpu-bypass}
1063  *            Name                      Next                    Previous
1064  * ip6-gtpu-bypass                error-drop [0]
1065  *                                gtpu6-input [1]
1066  *                                 ip6-lookup [2]
1067  * @cliexend
1068  *
1069  * Example of how to enable ip6-gtpu-bypass on an interface:
1070  * @cliexcmd{set interface ip6 gtpu-bypass GigabitEthernet2/0/0}
1071  *
1072  * Example of graph node after ip6-gtpu-bypass is enabled:
1073  * @cliexstart{show vlib graph ip6-gtpu-bypass}
1074  *            Name                      Next                    Previous
1075  * ip6-gtpu-bypass                error-drop [0]               ip6-input
1076  *                                gtpu6-input [1]        ip4-input-no-checksum
1077  *                                 ip6-lookup [2]
1078  * @cliexend
1079  *
1080  * Example of how to display the feature enabled on an interface:
1081  * @cliexstart{show ip interface features GigabitEthernet2/0/0}
1082  * IP feature paths configured on GigabitEthernet2/0/0...
1083  * ...
1084  * ipv6 unicast:
1085  *   ip6-gtpu-bypass
1086  *   ip6-lookup
1087  * ...
1088  * @cliexend
1089  *
1090  * Example of how to disable ip6-gtpu-bypass on an interface:
1091  * @cliexcmd{set interface ip6 gtpu-bypass GigabitEthernet2/0/0 del}
1092  * @endparblock
1093 ?*/
1094 /* *INDENT-OFF* */
1095 VLIB_CLI_COMMAND (set_interface_ip6_gtpu_bypass_command, static) = {
1096   .path = "set interface ip6 gtpu-bypass",
1097   .function = set_ip6_gtpu_bypass,
1098   .short_help = "set interface ip6 gtpu-bypass <interface> [del]",
1099 };
1100 /* *INDENT-ON* */
1101
1102 int
1103 vnet_gtpu_add_del_rx_flow (u32 hw_if_index, u32 t_index, int is_add)
1104 {
1105   gtpu_main_t *gtm = &gtpu_main;
1106   gtpu_tunnel_t *t = pool_elt_at_index (gtm->tunnels, t_index);
1107   vnet_main_t *vnm = vnet_get_main ();
1108   if (is_add)
1109     {
1110       if (t->flow_index == ~0)
1111         {
1112           vnet_flow_t flow = {
1113             .actions =
1114               VNET_FLOW_ACTION_REDIRECT_TO_NODE | VNET_FLOW_ACTION_MARK |
1115               VNET_FLOW_ACTION_BUFFER_ADVANCE,
1116             .mark_flow_id = t_index + gtm->flow_id_start,
1117             .redirect_node_index = gtpu4_flow_input_node.index,
1118             .buffer_advance = sizeof (ethernet_header_t)
1119               + sizeof (ip4_header_t) + sizeof (udp_header_t),
1120             .type = VNET_FLOW_TYPE_IP4_GTPU_IP4,
1121             .ip4_gtpu = {
1122                          .protocol = IP_PROTOCOL_UDP,
1123                          .src_addr.addr = t->dst.ip4,
1124                          .src_addr.mask.as_u32 = ~0,
1125                          .dst_addr.addr = t->src.ip4,
1126                          .dst_addr.mask.as_u32 = ~0,
1127                          .teid = t->teid,
1128                          }
1129             ,
1130           };
1131           vnet_flow_add (vnm, &flow, &t->flow_index);
1132         }
1133
1134       return vnet_flow_enable (vnm, t->flow_index, hw_if_index);
1135     }
1136
1137   /* flow index is removed when the tunnel is deleted */
1138   return vnet_flow_disable (vnm, t->flow_index, hw_if_index);
1139 }
1140
1141 u32
1142 vnet_gtpu_get_tunnel_index (u32 sw_if_index)
1143 {
1144   gtpu_main_t *gtm = &gtpu_main;
1145
1146   if (sw_if_index >= vec_len (gtm->tunnel_index_by_sw_if_index))
1147     return ~0;
1148   return gtm->tunnel_index_by_sw_if_index[sw_if_index];
1149 }
1150
1151 static clib_error_t *
1152 gtpu_offload_command_fn (vlib_main_t * vm,
1153                          unformat_input_t * input, vlib_cli_command_t * cmd)
1154 {
1155   unformat_input_t _line_input, *line_input = &_line_input;
1156
1157   /* Get a line of input. */
1158   if (!unformat_user (input, unformat_line_input, line_input))
1159     return 0;
1160
1161   vnet_main_t *vnm = vnet_get_main ();
1162   u32 rx_sw_if_index = ~0;
1163   u32 hw_if_index = ~0;
1164   int is_add = 1;
1165
1166   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1167     {
1168       if (unformat (line_input, "hw %U", unformat_vnet_hw_interface, vnm,
1169                     &hw_if_index))
1170         continue;
1171       if (unformat (line_input, "rx %U", unformat_vnet_sw_interface, vnm,
1172                     &rx_sw_if_index))
1173         continue;
1174       if (unformat (line_input, "del"))
1175         {
1176           is_add = 0;
1177           continue;
1178         }
1179       return clib_error_return (0, "unknown input `%U'",
1180                                 format_unformat_error, line_input);
1181     }
1182
1183   if (rx_sw_if_index == ~0)
1184     return clib_error_return (0, "missing rx interface");
1185   if (hw_if_index == ~0)
1186     return clib_error_return (0, "missing hw interface");
1187
1188   u32 t_index = vnet_gtpu_get_tunnel_index (rx_sw_if_index);;
1189   if (t_index == ~0)
1190     return clib_error_return (0, "%U is not a gtpu tunnel",
1191                               format_vnet_sw_if_index_name, vnm,
1192                               rx_sw_if_index);
1193
1194   gtpu_main_t *gtm = &gtpu_main;
1195   gtpu_tunnel_t *t = pool_elt_at_index (gtm->tunnels, t_index);
1196
1197   /* first support ipv4 hw offload */
1198   if (!ip46_address_is_ip4 (&t->dst))
1199     return clib_error_return (0, "currently only IPV4 tunnels are supported");
1200
1201   /* inner protocol should be IPv4 */
1202   if (t->decap_next_index != GTPU_INPUT_NEXT_IP4_INPUT)
1203     return clib_error_return (0,
1204                               "currently only inner IPV4 protocol is supported");
1205
1206   vnet_hw_interface_t *hw_if = vnet_get_hw_interface (vnm, hw_if_index);
1207   ip4_main_t *im = &ip4_main;
1208   u32 rx_fib_index =
1209     vec_elt (im->fib_index_by_sw_if_index, hw_if->sw_if_index);
1210
1211   if (t->encap_fib_index != rx_fib_index)
1212     return clib_error_return (0, "interface/tunnel fib mismatch");
1213
1214   if (vnet_gtpu_add_del_rx_flow (hw_if_index, t_index, is_add))
1215     return clib_error_return (0, "error %s flow",
1216                               is_add ? "enabling" : "disabling");
1217
1218   return 0;
1219 }
1220
1221
1222 /* *INDENT-OFF* */
1223 VLIB_CLI_COMMAND (gtpu_offload_command, static) = {
1224     .path = "set flow-offload gtpu",
1225     .short_help =
1226     "set flow-offload gtpu hw <inerface-name> rx <tunnel-name> [del]",
1227     .function = gtpu_offload_command_fn,
1228 };
1229 /* *INDENT-ON* */
1230
1231 clib_error_t *
1232 gtpu_init (vlib_main_t * vm)
1233 {
1234   gtpu_main_t *gtm = &gtpu_main;
1235
1236   gtm->vnet_main = vnet_get_main ();
1237   gtm->vlib_main = vm;
1238
1239   vnet_flow_get_range (gtm->vnet_main, "gtpu", 1024 * 1024,
1240                        &gtm->flow_id_start);
1241
1242   /* initialize the ip6 hash */
1243   gtm->gtpu6_tunnel_by_key = hash_create_mem (0,
1244                                               sizeof (gtpu6_tunnel_key_t),
1245                                               sizeof (uword));
1246   gtm->vtep6 = hash_create_mem (0, sizeof (ip6_address_t), sizeof (uword));
1247   gtm->mcast_shared = hash_create_mem (0,
1248                                        sizeof (ip46_address_t),
1249                                        sizeof (mcast_shared_t));
1250
1251   gtm->fib_node_type = fib_node_register_new_type (&gtpu_vft);
1252
1253   return 0;
1254 }
1255
1256 VLIB_INIT_FUNCTION (gtpu_init);
1257
1258 /* *INDENT-OFF* */
1259 VLIB_PLUGIN_REGISTER () = {
1260     .version = VPP_BUILD_VER,
1261     .description = "GPRS Tunnelling Protocol, User Data (GTPv1-U)",
1262 };
1263 /* *INDENT-ON* */
1264
1265 /*
1266  * fd.io coding-style-patch-verification: ON
1267  *
1268  * Local Variables:
1269  * eval: (c-set-style "gnu")
1270  * End:
1271  */