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