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