086a4b55f63725b3ae58a8d1a276d4d216487e4d
[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_set_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_dec_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, &mpfx,
528                                             MFIB_SOURCE_GTPU,
529                                             MFIB_ENTRY_FLAG_NONE, &path);
530
531               path.frp_sw_if_index = a->mcast_sw_if_index;
532               path.frp_flags = FIB_ROUTE_PATH_FLAG_NONE;
533               path.frp_mitf_flags = MFIB_ITF_FLAG_ACCEPT;
534               mfei = mfib_table_entry_path_update (
535                 t->encap_fib_index, &mpfx, MFIB_SOURCE_GTPU,
536                 MFIB_ENTRY_FLAG_NONE, &path);
537
538               /*
539                * Create the mcast adjacency to send traffic to the group
540                */
541               ai = adj_mcast_add_or_lock (fp,
542                                           fib_proto_to_link (fp),
543                                           a->mcast_sw_if_index);
544
545               /*
546                * create a new end-point
547                */
548               mcast_shared_add (&t->dst, mfei, ai);
549             }
550
551           dpo_id_t dpo = DPO_INVALID;
552           mcast_shared_t ep = mcast_shared_get (&t->dst);
553
554           /* Stack shared mcast dst mac addr rewrite on encap */
555           dpo_set (&dpo, DPO_ADJACENCY_MCAST,
556                    fib_proto_to_dpo (fp), ep.mcast_adj_index);
557
558           dpo_stack_from_node (encap_index, &t->next_dpo, &dpo);
559
560           dpo_reset (&dpo);
561           flood_class = VNET_FLOOD_CLASS_TUNNEL_MASTER;
562         }
563
564       vnet_get_sw_interface (vnet_get_main (), sw_if_index)->flood_class =
565         flood_class;
566     }
567   else
568     {
569       /* mod-tteid or deleting a tunnel: tunnel must exist */
570       if (!p)
571         return VNET_API_ERROR_NO_SUCH_ENTRY;
572
573       t = pool_elt_at_index (gtm->tunnels, p[0]);
574       sw_if_index = t->sw_if_index;
575
576       if (a->opn == GTPU_UPD_TTEID)
577         {
578           if (a->tteid == 0)
579             return VNET_API_ERROR_INVALID_VALUE;
580           t->tteid = a->tteid;
581           vec_free (t->rewrite);
582           ip_udp_gtpu_rewrite (t, is_ip6);
583           return 0;
584         }
585
586       /* delete tunnel */
587       vnet_sw_interface_set_flags (vnm, t->sw_if_index, 0 /* down */ );
588       vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, t->sw_if_index);
589       si->flags |= VNET_SW_INTERFACE_FLAG_HIDDEN;
590
591       /* make sure tunnel is removed from l2 bd or xconnect */
592       set_int_l2_mode (gtm->vlib_main, vnm, MODE_L3, t->sw_if_index, 0,
593                        L2_BD_PORT_TYPE_NORMAL, 0, 0);
594       vec_add1 (gtm->free_gtpu_tunnel_hw_if_indices, t->hw_if_index);
595
596       gtm->tunnel_index_by_sw_if_index[t->sw_if_index] = ~0;
597
598       if (!is_ip6)
599         hash_unset (gtm->gtpu4_tunnel_by_key, key4.as_u64);
600       else
601         hash_unset_mem_free (&gtm->gtpu6_tunnel_by_key, &key6);
602
603       if (!ip46_address_is_multicast (&t->dst))
604         {
605           if (t->flow_index != ~0)
606             vnet_flow_del (vnm, t->flow_index);
607
608           vtep_addr_unref (&gtm->vtep_table, t->encap_fib_index, &t->src);
609           fib_entry_untrack (t->fib_entry_index, t->sibling_index);
610         }
611       else if (vtep_addr_unref (&gtm->vtep_table,
612                                 t->encap_fib_index, &t->dst) == 0)
613         {
614           mcast_shared_remove (&t->dst);
615         }
616
617       fib_node_deinit (&t->node);
618       vec_free (t->rewrite);
619       pool_put (gtm->tunnels, t);
620     }
621
622   if (sw_if_indexp)
623     *sw_if_indexp = sw_if_index;
624
625   if (a->opn == GTPU_ADD_TUNNEL)
626     {
627       /* register udp ports */
628       if (!is_ip6 && !udp_is_valid_dst_port (UDP_DST_PORT_GTPU, 1))
629         udp_register_dst_port (gtm->vlib_main, UDP_DST_PORT_GTPU,
630                                gtpu4_input_node.index, /* is_ip4 */ 1);
631       if (is_ip6 && !udp_is_valid_dst_port (UDP_DST_PORT_GTPU6, 0))
632         udp_register_dst_port (gtm->vlib_main, UDP_DST_PORT_GTPU6,
633                                gtpu6_input_node.index, /* is_ip4 */ 0);
634     }
635
636   return 0;
637 }
638
639 static uword
640 get_decap_next_for_node (u32 node_index, u32 ipv4_set)
641 {
642   gtpu_main_t *gtm = &gtpu_main;
643   vlib_main_t *vm = gtm->vlib_main;
644   uword input_node = (ipv4_set) ? gtpu4_input_node.index :
645     gtpu6_input_node.index;
646
647   return vlib_node_add_next (vm, input_node, node_index);
648 }
649
650 static uword
651 unformat_decap_next (unformat_input_t * input, va_list * args)
652 {
653   u32 *result = va_arg (*args, u32 *);
654   u32 ipv4_set = va_arg (*args, int);
655   gtpu_main_t *gtm = &gtpu_main;
656   vlib_main_t *vm = gtm->vlib_main;
657   u32 node_index;
658   u32 tmp;
659
660   if (unformat (input, "l2"))
661     *result = GTPU_INPUT_NEXT_L2_INPUT;
662   else if (unformat (input, "ip4"))
663     *result = GTPU_INPUT_NEXT_IP4_INPUT;
664   else if (unformat (input, "ip6"))
665     *result = GTPU_INPUT_NEXT_IP6_INPUT;
666   else if (unformat (input, "node %U", unformat_vlib_node, vm, &node_index))
667     *result = get_decap_next_for_node (node_index, ipv4_set);
668   else if (unformat (input, "%d", &tmp))
669     *result = tmp;
670   else
671     return 0;
672
673   return 1;
674 }
675
676 static clib_error_t *
677 gtpu_add_del_tunnel_command_fn (vlib_main_t * vm,
678                                 unformat_input_t * input,
679                                 vlib_cli_command_t * cmd)
680 {
681   unformat_input_t _line_input, *line_input = &_line_input;
682   ip46_address_t src, dst;
683   u8 opn = GTPU_ADD_TUNNEL;
684   u8 src_set = 0;
685   u8 dst_set = 0;
686   u8 grp_set = 0;
687   u8 ipv4_set = 0;
688   u8 ipv6_set = 0;
689   u32 encap_fib_index = 0;
690   u32 mcast_sw_if_index = ~0;
691   u32 decap_next_index = GTPU_INPUT_NEXT_L2_INPUT;
692   u32 teid = 0, tteid = 0;
693   u32 tmp;
694   int rv;
695   vnet_gtpu_add_mod_del_tunnel_args_t _a, *a = &_a;
696   u32 tunnel_sw_if_index;
697   clib_error_t *error = NULL;
698
699   /* Cant "universally zero init" (={0}) due to GCC bug 53119 */
700   clib_memset (&src, 0, sizeof src);
701   clib_memset (&dst, 0, sizeof dst);
702
703   /* Get a line of input. */
704   if (!unformat_user (input, unformat_line_input, line_input))
705     return 0;
706
707   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
708     {
709       if (unformat (line_input, "del"))
710         {
711           opn = GTPU_DEL_TUNNEL;
712         }
713       else if (unformat (line_input, "src %U",
714                          unformat_ip4_address, &src.ip4))
715         {
716           src_set = 1;
717           ipv4_set = 1;
718         }
719       else if (unformat (line_input, "dst %U",
720                          unformat_ip4_address, &dst.ip4))
721         {
722           dst_set = 1;
723           ipv4_set = 1;
724         }
725       else if (unformat (line_input, "src %U",
726                          unformat_ip6_address, &src.ip6))
727         {
728           src_set = 1;
729           ipv6_set = 1;
730         }
731       else if (unformat (line_input, "dst %U",
732                          unformat_ip6_address, &dst.ip6))
733         {
734           dst_set = 1;
735           ipv6_set = 1;
736         }
737       else if (unformat (line_input, "group %U %U",
738                          unformat_ip4_address, &dst.ip4,
739                          unformat_vnet_sw_interface,
740                          vnet_get_main (), &mcast_sw_if_index))
741         {
742           grp_set = dst_set = 1;
743           ipv4_set = 1;
744         }
745       else if (unformat (line_input, "group %U %U",
746                          unformat_ip6_address, &dst.ip6,
747                          unformat_vnet_sw_interface,
748                          vnet_get_main (), &mcast_sw_if_index))
749         {
750           grp_set = dst_set = 1;
751           ipv6_set = 1;
752         }
753       else if (unformat (line_input, "encap-vrf-id %d", &tmp))
754         {
755           encap_fib_index = fib_table_find (fib_ip_proto (ipv6_set), tmp);
756           if (encap_fib_index == ~0)
757             {
758               error =
759                 clib_error_return (0, "nonexistent encap-vrf-id %d", tmp);
760               goto done;
761             }
762         }
763       else if (unformat (line_input, "decap-next %U", unformat_decap_next,
764                          &decap_next_index, ipv4_set))
765         ;
766       else if (unformat (line_input, "teid %d", &teid))
767         ;
768       else if (unformat (line_input, "tteid %d", &tteid))
769         ;
770       else if (unformat (line_input, "upd-tteid %d", &tteid))
771         opn = GTPU_UPD_TTEID;
772       else
773         {
774           error = clib_error_return (0, "parse error: '%U'",
775                                      format_unformat_error, line_input);
776           goto done;
777         }
778     }
779
780   if (teid == 0)
781     {
782       error = clib_error_return (0, "tunnel teid specified");
783       goto done;
784     }
785
786   if (src_set == 0 && opn == GTPU_ADD_TUNNEL)
787     {
788       error = clib_error_return (0, "tunnel src address not specified");
789       goto done;
790     }
791
792   if (dst_set == 0)
793     {
794       error = clib_error_return (0, "tunnel dst address not specified");
795       goto done;
796     }
797
798   if (grp_set && !ip46_address_is_multicast (&dst))
799     {
800       error = clib_error_return (0, "tunnel group address not multicast");
801       goto done;
802     }
803
804   if (grp_set == 0 && ip46_address_is_multicast (&dst))
805     {
806       error = clib_error_return (0, "dst address must be unicast");
807       goto done;
808     }
809
810   if (grp_set && mcast_sw_if_index == ~0)
811     {
812       error = clib_error_return (0, "tunnel nonexistent multicast device");
813       goto done;
814     }
815
816   if (ipv4_set && ipv6_set)
817     {
818       error = clib_error_return (0, "both IPv4 and IPv6 addresses specified");
819       goto done;
820     }
821
822   if (ip46_address_cmp (&src, &dst) == 0)
823     {
824       error = clib_error_return (0, "src and dst addresses are identical");
825       goto done;
826     }
827
828   if (decap_next_index == ~0)
829     {
830       error = clib_error_return (0, "next node not found");
831       goto done;
832     }
833
834   clib_memset (a, 0, sizeof (*a));
835
836   a->opn = opn;
837
838 #define _(x) a->x = x;
839   foreach_copy_field;
840 #undef _
841
842   rv = vnet_gtpu_add_mod_del_tunnel (a, &tunnel_sw_if_index);
843
844   switch (rv)
845     {
846     case 0:
847       if (opn == GTPU_ADD_TUNNEL)
848         vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name,
849                          vnet_get_main (), tunnel_sw_if_index);
850       break;
851
852     case VNET_API_ERROR_TUNNEL_EXIST:
853       error = clib_error_return (0, "tunnel already exists...");
854       goto done;
855
856     case VNET_API_ERROR_NO_SUCH_ENTRY:
857       error = clib_error_return (0, "tunnel does not exist...");
858       goto done;
859
860     case VNET_API_ERROR_INVALID_VALUE:
861       error = clib_error_return (0, "tx teid not specified...");
862       goto done;
863
864     default:
865       error = clib_error_return
866         (0, "vnet_gtpu_add_del_tunnel returned %d", rv);
867       goto done;
868     }
869
870 done:
871   unformat_free (line_input);
872
873   return error;
874 }
875
876 /*?
877  * Add or delete a GTPU Tunnel.
878  *
879  * GTPU can be used to transport Ethernet packets as its PDU type to
880  * provides allow L2 network or bridge domains (BDs)
881  * to span multiple servers. This is done by building an L2 overlay on
882  * top of an L3 network underlay using GTPU tunnels.
883  *
884  * GTPU can also be used to transport IP packets as its PDU type to
885  * allow IP forwarding over underlay network, e.g. between RAN and UPF
886  * for mobility deployments.
887  *
888  * @cliexpar
889  * Example of how to create a GTPU Tunnel:
890  * @cliexcmd{create gtpu tunnel src 10.0.3.1 dst 10.0.3.3 teid 13 tteid 55
891  * encap-vrf-id 7}
892  * Example of how to delete a GTPU Tunnel:
893  * @cliexcmd{create gtpu tunnel src 10.0.3.1 dst 10.0.3.3 teid 13 encap-vrf-id
894  * 7 del}
895  * Example of how to update tx TEID of a GTPU Tunnel:
896  * @cliexcmd{create gtpu tunnel src 10.0.3.1 dst 10.0.3.3 encap-vrf-id 7
897  * upd-tteid 55}
898  ?*/
899 /* *INDENT-OFF* */
900 VLIB_CLI_COMMAND (create_gtpu_tunnel_command, static) = {
901   .path = "create gtpu tunnel",
902   .short_help =
903   "create gtpu tunnel src <local-tep-addr>"
904   " {dst <remote-tep-addr>|group <mcast-addr> <intf-name>}"
905   " teid <nn> [tteid <nn>] [encap-vrf-id <nn>]"
906   " [decap-next [l2|ip4|ip6|node <name>]] [del | upd-tteid <nn>]",
907   .function = gtpu_add_del_tunnel_command_fn,
908 };
909 /* *INDENT-ON* */
910
911 static clib_error_t *
912 show_gtpu_tunnel_command_fn (vlib_main_t * vm,
913                              unformat_input_t * input,
914                              vlib_cli_command_t * cmd)
915 {
916   gtpu_main_t *gtm = &gtpu_main;
917   gtpu_tunnel_t *t;
918
919   if (pool_elts (gtm->tunnels) == 0)
920     vlib_cli_output (vm, "No gtpu tunnels configured...");
921
922   pool_foreach (t, gtm->tunnels)
923   {
924     vlib_cli_output (vm, "%U", format_gtpu_tunnel, t);
925   }
926
927   return 0;
928 }
929
930 /*?
931  * Display all the GTPU Tunnel entries.
932  *
933  * @cliexpar
934  * Example of how to display the GTPU Tunnel entries:
935  * @cliexstart{show gtpu tunnel}
936  * [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
937  * @cliexend
938  ?*/
939 /* *INDENT-OFF* */
940 VLIB_CLI_COMMAND (show_gtpu_tunnel_command, static) = {
941     .path = "show gtpu tunnel",
942     .short_help = "show gtpu tunnel",
943     .function = show_gtpu_tunnel_command_fn,
944 };
945 /* *INDENT-ON* */
946
947 void
948 vnet_int_gtpu_bypass_mode (u32 sw_if_index, u8 is_ip6, u8 is_enable)
949 {
950   if (is_ip6)
951     vnet_feature_enable_disable ("ip6-unicast", "ip6-gtpu-bypass",
952                                  sw_if_index, is_enable, 0, 0);
953   else
954     vnet_feature_enable_disable ("ip4-unicast", "ip4-gtpu-bypass",
955                                  sw_if_index, is_enable, 0, 0);
956 }
957
958 static clib_error_t *
959 set_ip_gtpu_bypass (u32 is_ip6,
960                     unformat_input_t * input, vlib_cli_command_t * cmd)
961 {
962   unformat_input_t _line_input, *line_input = &_line_input;
963   vnet_main_t *vnm = vnet_get_main ();
964   clib_error_t *error = 0;
965   u32 sw_if_index, is_enable;
966
967   sw_if_index = ~0;
968   is_enable = 1;
969
970   if (!unformat_user (input, unformat_line_input, line_input))
971     return 0;
972
973   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
974     {
975       if (unformat_user
976           (line_input, unformat_vnet_sw_interface, vnm, &sw_if_index))
977         ;
978       else if (unformat (line_input, "del"))
979         is_enable = 0;
980       else
981         {
982           error = unformat_parse_error (line_input);
983           goto done;
984         }
985     }
986
987   if (~0 == sw_if_index)
988     {
989       error = clib_error_return (0, "unknown interface `%U'",
990                                  format_unformat_error, line_input);
991       goto done;
992     }
993
994   vnet_int_gtpu_bypass_mode (sw_if_index, is_ip6, is_enable);
995
996 done:
997   unformat_free (line_input);
998
999   return error;
1000 }
1001
1002 static clib_error_t *
1003 set_ip4_gtpu_bypass (vlib_main_t * vm,
1004                      unformat_input_t * input, vlib_cli_command_t * cmd)
1005 {
1006   return set_ip_gtpu_bypass (0, input, cmd);
1007 }
1008
1009 /*?
1010  * This command adds the 'ip4-gtpu-bypass' graph node for a given interface.
1011  * By adding the IPv4 gtpu-bypass graph node to an interface, the node checks
1012  * for and validate input gtpu packet and bypass ip4-lookup, ip4-local,
1013  * ip4-udp-lookup nodes to speedup gtpu packet forwarding. This node will
1014  * cause extra overhead to for non-gtpu packets which is kept at a minimum.
1015  *
1016  * @cliexpar
1017  * @parblock
1018  * Example of graph node before ip4-gtpu-bypass is enabled:
1019  * @cliexstart{show vlib graph ip4-gtpu-bypass}
1020  *            Name                      Next                    Previous
1021  * ip4-gtpu-bypass                error-drop [0]
1022  *                                gtpu4-input [1]
1023  *                                 ip4-lookup [2]
1024  * @cliexend
1025  *
1026  * Example of how to enable ip4-gtpu-bypass on an interface:
1027  * @cliexcmd{set interface ip gtpu-bypass GigabitEthernet2/0/0}
1028  *
1029  * Example of graph node after ip4-gtpu-bypass is enabled:
1030  * @cliexstart{show vlib graph ip4-gtpu-bypass}
1031  *            Name                      Next                    Previous
1032  * ip4-gtpu-bypass                error-drop [0]               ip4-input
1033  *                                gtpu4-input [1]        ip4-input-no-checksum
1034  *                                 ip4-lookup [2]
1035  * @cliexend
1036  *
1037  * Example of how to display the feature enabled on an interface:
1038  * @cliexstart{show ip interface features GigabitEthernet2/0/0}
1039  * IP feature paths configured on GigabitEthernet2/0/0...
1040  * ...
1041  * ipv4 unicast:
1042  *   ip4-gtpu-bypass
1043  *   ip4-lookup
1044  * ...
1045  * @cliexend
1046  *
1047  * Example of how to disable ip4-gtpu-bypass on an interface:
1048  * @cliexcmd{set interface ip gtpu-bypass GigabitEthernet2/0/0 del}
1049  * @endparblock
1050 ?*/
1051 /* *INDENT-OFF* */
1052 VLIB_CLI_COMMAND (set_interface_ip_gtpu_bypass_command, static) = {
1053   .path = "set interface ip gtpu-bypass",
1054   .function = set_ip4_gtpu_bypass,
1055   .short_help = "set interface ip gtpu-bypass <interface> [del]",
1056 };
1057 /* *INDENT-ON* */
1058
1059 static clib_error_t *
1060 set_ip6_gtpu_bypass (vlib_main_t * vm,
1061                      unformat_input_t * input, vlib_cli_command_t * cmd)
1062 {
1063   return set_ip_gtpu_bypass (1, input, cmd);
1064 }
1065
1066 /*?
1067  * This command adds the 'ip6-gtpu-bypass' graph node for a given interface.
1068  * By adding the IPv6 gtpu-bypass graph node to an interface, the node checks
1069  * for and validate input gtpu packet and bypass ip6-lookup, ip6-local,
1070  * ip6-udp-lookup nodes to speedup gtpu packet forwarding. This node will
1071  * cause extra overhead to for non-gtpu packets which is kept at a minimum.
1072  *
1073  * @cliexpar
1074  * @parblock
1075  * Example of graph node before ip6-gtpu-bypass is enabled:
1076  * @cliexstart{show vlib graph ip6-gtpu-bypass}
1077  *            Name                      Next                    Previous
1078  * ip6-gtpu-bypass                error-drop [0]
1079  *                                gtpu6-input [1]
1080  *                                 ip6-lookup [2]
1081  * @cliexend
1082  *
1083  * Example of how to enable ip6-gtpu-bypass on an interface:
1084  * @cliexcmd{set interface ip6 gtpu-bypass GigabitEthernet2/0/0}
1085  *
1086  * Example of graph node after ip6-gtpu-bypass is enabled:
1087  * @cliexstart{show vlib graph ip6-gtpu-bypass}
1088  *            Name                      Next                    Previous
1089  * ip6-gtpu-bypass                error-drop [0]               ip6-input
1090  *                                gtpu6-input [1]        ip4-input-no-checksum
1091  *                                 ip6-lookup [2]
1092  * @cliexend
1093  *
1094  * Example of how to display the feature enabled on an interface:
1095  * @cliexstart{show ip interface features GigabitEthernet2/0/0}
1096  * IP feature paths configured on GigabitEthernet2/0/0...
1097  * ...
1098  * ipv6 unicast:
1099  *   ip6-gtpu-bypass
1100  *   ip6-lookup
1101  * ...
1102  * @cliexend
1103  *
1104  * Example of how to disable ip6-gtpu-bypass on an interface:
1105  * @cliexcmd{set interface ip6 gtpu-bypass GigabitEthernet2/0/0 del}
1106  * @endparblock
1107 ?*/
1108 /* *INDENT-OFF* */
1109 VLIB_CLI_COMMAND (set_interface_ip6_gtpu_bypass_command, static) = {
1110   .path = "set interface ip6 gtpu-bypass",
1111   .function = set_ip6_gtpu_bypass,
1112   .short_help = "set interface ip6 gtpu-bypass <interface> [del]",
1113 };
1114 /* *INDENT-ON* */
1115
1116 int
1117 vnet_gtpu_add_del_rx_flow (u32 hw_if_index, u32 t_index, int is_add)
1118 {
1119   gtpu_main_t *gtm = &gtpu_main;
1120   gtpu_tunnel_t *t = pool_elt_at_index (gtm->tunnels, t_index);
1121   vnet_main_t *vnm = vnet_get_main ();
1122   if (is_add)
1123     {
1124       if (t->flow_index == ~0)
1125         {
1126           vnet_flow_t flow = {
1127             .actions =
1128               VNET_FLOW_ACTION_REDIRECT_TO_NODE | VNET_FLOW_ACTION_MARK |
1129               VNET_FLOW_ACTION_BUFFER_ADVANCE,
1130             .mark_flow_id = t_index + gtm->flow_id_start,
1131             .redirect_node_index = gtpu4_flow_input_node.index,
1132             .buffer_advance = sizeof (ethernet_header_t)
1133               + sizeof (ip4_header_t) + sizeof (udp_header_t),
1134             .type = VNET_FLOW_TYPE_IP4_GTPU,
1135             .ip4_gtpu = {
1136                          .protocol.prot = IP_PROTOCOL_UDP,
1137                          .src_addr.addr = t->dst.ip4,
1138                          .src_addr.mask.as_u32 = ~0,
1139                          .dst_addr.addr = t->src.ip4,
1140                          .dst_addr.mask.as_u32 = ~0,
1141                          .teid = t->teid,
1142                          }
1143             ,
1144           };
1145           vnet_flow_add (vnm, &flow, &t->flow_index);
1146         }
1147
1148       return vnet_flow_enable (vnm, t->flow_index, hw_if_index);
1149     }
1150
1151   /* flow index is removed when the tunnel is deleted */
1152   return vnet_flow_disable (vnm, t->flow_index, hw_if_index);
1153 }
1154
1155 u32
1156 vnet_gtpu_get_tunnel_index (u32 sw_if_index)
1157 {
1158   gtpu_main_t *gtm = &gtpu_main;
1159
1160   if (sw_if_index >= vec_len (gtm->tunnel_index_by_sw_if_index))
1161     return ~0;
1162   return gtm->tunnel_index_by_sw_if_index[sw_if_index];
1163 }
1164
1165 static clib_error_t *
1166 gtpu_offload_command_fn (vlib_main_t * vm,
1167                          unformat_input_t * input, vlib_cli_command_t * cmd)
1168 {
1169   unformat_input_t _line_input, *line_input = &_line_input;
1170
1171   /* Get a line of input. */
1172   if (!unformat_user (input, unformat_line_input, line_input))
1173     return 0;
1174
1175   vnet_main_t *vnm = vnet_get_main ();
1176   u32 rx_sw_if_index = ~0;
1177   u32 hw_if_index = ~0;
1178   int is_add = 1;
1179
1180   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1181     {
1182       if (unformat (line_input, "hw %U", unformat_vnet_hw_interface, vnm,
1183                     &hw_if_index))
1184         continue;
1185       if (unformat (line_input, "rx %U", unformat_vnet_sw_interface, vnm,
1186                     &rx_sw_if_index))
1187         continue;
1188       if (unformat (line_input, "del"))
1189         {
1190           is_add = 0;
1191           continue;
1192         }
1193       return clib_error_return (0, "unknown input `%U'",
1194                                 format_unformat_error, line_input);
1195     }
1196
1197   if (rx_sw_if_index == ~0)
1198     return clib_error_return (0, "missing rx interface");
1199   if (hw_if_index == ~0)
1200     return clib_error_return (0, "missing hw interface");
1201
1202   u32 t_index = vnet_gtpu_get_tunnel_index (rx_sw_if_index);;
1203   if (t_index == ~0)
1204     return clib_error_return (0, "%U is not a gtpu tunnel",
1205                               format_vnet_sw_if_index_name, vnm,
1206                               rx_sw_if_index);
1207
1208   gtpu_main_t *gtm = &gtpu_main;
1209   gtpu_tunnel_t *t = pool_elt_at_index (gtm->tunnels, t_index);
1210
1211   /* first support ipv4 hw offload */
1212   if (!ip46_address_is_ip4 (&t->dst))
1213     return clib_error_return (0, "currently only IPV4 tunnels are supported");
1214
1215   /* inner protocol should be IPv4/IPv6 */
1216   if ((t->decap_next_index != GTPU_INPUT_NEXT_IP4_INPUT) &&
1217       (t->decap_next_index != GTPU_INPUT_NEXT_IP6_INPUT))
1218     return clib_error_return (0,
1219                               "currently only inner IPv4/IPv6 protocol is supported");
1220
1221   vnet_hw_interface_t *hw_if = vnet_get_hw_interface (vnm, hw_if_index);
1222   ip4_main_t *im = &ip4_main;
1223   u32 rx_fib_index =
1224     vec_elt (im->fib_index_by_sw_if_index, hw_if->sw_if_index);
1225
1226   if (t->encap_fib_index != rx_fib_index)
1227     return clib_error_return (0, "interface/tunnel fib mismatch");
1228
1229   if (vnet_gtpu_add_del_rx_flow (hw_if_index, t_index, is_add))
1230     return clib_error_return (0, "error %s flow",
1231                               is_add ? "enabling" : "disabling");
1232
1233   return 0;
1234 }
1235
1236
1237 /* *INDENT-OFF* */
1238 VLIB_CLI_COMMAND (gtpu_offload_command, static) = {
1239     .path = "set flow-offload gtpu",
1240     .short_help =
1241     "set flow-offload gtpu hw <inerface-name> rx <tunnel-name> [del]",
1242     .function = gtpu_offload_command_fn,
1243 };
1244 /* *INDENT-ON* */
1245
1246 clib_error_t *
1247 gtpu_init (vlib_main_t * vm)
1248 {
1249   gtpu_main_t *gtm = &gtpu_main;
1250
1251   gtm->vnet_main = vnet_get_main ();
1252   gtm->vlib_main = vm;
1253
1254   vnet_flow_get_range (gtm->vnet_main, "gtpu", 1024 * 1024,
1255                        &gtm->flow_id_start);
1256
1257   /* initialize the ip6 hash */
1258   gtm->gtpu6_tunnel_by_key = hash_create_mem (0,
1259                                               sizeof (gtpu6_tunnel_key_t),
1260                                               sizeof (uword));
1261   gtm->vtep_table = vtep_table_create ();
1262   gtm->mcast_shared = hash_create_mem (0,
1263                                        sizeof (ip46_address_t),
1264                                        sizeof (mcast_shared_t));
1265
1266   gtm->fib_node_type = fib_node_register_new_type ("gtpu", &gtpu_vft);
1267
1268   return 0;
1269 }
1270
1271 VLIB_INIT_FUNCTION (gtpu_init);
1272
1273 /* *INDENT-OFF* */
1274 VLIB_PLUGIN_REGISTER () = {
1275     .version = VPP_BUILD_VER,
1276     .description = "GPRS Tunnelling Protocol, User Data (GTPv1-U)",
1277 };
1278 /* *INDENT-ON* */
1279
1280 /*
1281  * fd.io coding-style-patch-verification: ON
1282  *
1283  * Local Variables:
1284  * eval: (c-set-style "gnu")
1285  * End:
1286  */