blob: dc5c689f5d308773850fbb2e11ff189a6df25bb8 [file] [log] [blame]
Hongjun Nief486b12017-04-12 19:21:16 +08001/*
2 *------------------------------------------------------------------
Hongjun Niccd4a652017-06-01 00:24:12 +08003 * Copyright (c) 2017 Intel and/or its affiliates.
Hongjun Nief486b12017-04-12 19:21:16 +08004 * 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/mfib/mfib_table.h>
28#include <vnet/adj/adj_mcast.h>
29#include <vnet/dpo/dpo.h>
30#include <vnet/plugin/plugin.h>
31#include <vpp/app/version.h>
32#include <gtpu/gtpu.h>
33
34
35gtpu_main_t gtpu_main;
36
jerryiana825c8b2017-08-03 10:17:01 +080037/* *INDENT-OFF* */
38VNET_FEATURE_INIT (ip4_gtpu_bypass, static) = {
39 .arc_name = "ip4-unicast",
40 .node_name = "ip4-gtpu-bypass",
41 .runs_before = VNET_FEATURES ("ip4-lookup"),
42};
43
44VNET_FEATURE_INIT (ip6_gtpu_bypass, static) = {
45 .arc_name = "ip6-unicast",
46 .node_name = "ip6-gtpu-bypass",
47 .runs_before = VNET_FEATURES ("ip6-lookup"),
48};
49/* *INDENT-on* */
50
Filip Tehlar03331212019-02-26 04:53:38 -080051u8 * format_gtpu_encap_trace (u8 * s, va_list * args)
52{
53 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
54 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
55 gtpu_encap_trace_t * t
56 = va_arg (*args, gtpu_encap_trace_t *);
57
58 s = format (s, "GTPU encap to gtpu_tunnel%d teid %d",
59 t->tunnel_index, t->teid);
60 return s;
61}
62
Hongjun Nief486b12017-04-12 19:21:16 +080063static u8 *
64format_decap_next (u8 * s, va_list * args)
65{
66 u32 next_index = va_arg (*args, u32);
67
68 switch (next_index)
69 {
70 case GTPU_INPUT_NEXT_DROP:
71 return format (s, "drop");
72 case GTPU_INPUT_NEXT_L2_INPUT:
73 return format (s, "l2");
Hongjun Niccd4a652017-06-01 00:24:12 +080074 case GTPU_INPUT_NEXT_IP4_INPUT:
75 return format (s, "ip4");
76 case GTPU_INPUT_NEXT_IP6_INPUT:
77 return format (s, "ip6");
Hongjun Nief486b12017-04-12 19:21:16 +080078 default:
79 return format (s, "index %d", next_index);
80 }
81 return s;
82}
83
84u8 *
85format_gtpu_tunnel (u8 * s, va_list * args)
86{
87 gtpu_tunnel_t *t = va_arg (*args, gtpu_tunnel_t *);
88 gtpu_main_t *ngm = &gtpu_main;
89
John Lo4478d8e2018-01-12 17:15:25 -050090 s = format (s, "[%d] src %U dst %U teid %d fib-idx %d sw-if-idx %d ",
Hongjun Nief486b12017-04-12 19:21:16 +080091 t - ngm->tunnels,
92 format_ip46_address, &t->src, IP46_TYPE_ANY,
93 format_ip46_address, &t->dst, IP46_TYPE_ANY,
John Lo4478d8e2018-01-12 17:15:25 -050094 t->teid, t->encap_fib_index, t->sw_if_index);
Hongjun Nief486b12017-04-12 19:21:16 +080095
John Lo4478d8e2018-01-12 17:15:25 -050096 s = format (s, "encap-dpo-idx %d ", t->next_dpo.dpoi_index);
97 s = format (s, "decap-next-%U ", format_decap_next, t->decap_next_index);
Hongjun Nief486b12017-04-12 19:21:16 +080098
John Lo4478d8e2018-01-12 17:15:25 -050099 if (PREDICT_FALSE (ip46_address_is_multicast (&t->dst)))
100 s = format (s, "mcast-sw-if-idx %d ", t->mcast_sw_if_index);
101
Hongjun Nief486b12017-04-12 19:21:16 +0800102 return s;
103}
104
105static u8 *
106format_gtpu_name (u8 * s, va_list * args)
107{
108 u32 dev_instance = va_arg (*args, u32);
109 return format (s, "gtpu_tunnel%d", dev_instance);
110}
111
Hongjun Nief486b12017-04-12 19:21:16 +0800112static clib_error_t *
113gtpu_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
114{
115 u32 hw_flags = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ?
116 VNET_HW_INTERFACE_FLAG_LINK_UP : 0;
117 vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags);
118
119 return /* no error */ 0;
120}
121
122/* *INDENT-OFF* */
123VNET_DEVICE_CLASS (gtpu_device_class,static) = {
124 .name = "GTPU",
125 .format_device_name = format_gtpu_name,
126 .format_tx_trace = format_gtpu_encap_trace,
Hongjun Nief486b12017-04-12 19:21:16 +0800127 .admin_up_down_function = gtpu_interface_admin_up_down,
128};
129/* *INDENT-ON* */
130
131static u8 *
132format_gtpu_header_with_length (u8 * s, va_list * args)
133{
134 u32 dev_instance = va_arg (*args, u32);
135 s = format (s, "unimplemented dev %u", dev_instance);
136 return s;
137}
138
139/* *INDENT-OFF* */
140VNET_HW_INTERFACE_CLASS (gtpu_hw_class) =
141{
142 .name = "GTPU",
143 .format_header = format_gtpu_header_with_length,
144 .build_rewrite = default_build_rewrite,
Hongjun Ni77eb3a92017-11-03 18:28:10 +0800145 .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,
Hongjun Nief486b12017-04-12 19:21:16 +0800146};
147/* *INDENT-ON* */
148
149static void
150gtpu_tunnel_restack_dpo (gtpu_tunnel_t * t)
151{
152 dpo_id_t dpo = DPO_INVALID;
153 u32 encap_index = ip46_address_is_ip4 (&t->dst) ?
154 gtpu4_encap_node.index : gtpu6_encap_node.index;
155 fib_forward_chain_type_t forw_type = ip46_address_is_ip4 (&t->dst) ?
156 FIB_FORW_CHAIN_TYPE_UNICAST_IP4 : FIB_FORW_CHAIN_TYPE_UNICAST_IP6;
157
158 fib_entry_contribute_forwarding (t->fib_entry_index, forw_type, &dpo);
159 dpo_stack_from_node (encap_index, &t->next_dpo, &dpo);
160 dpo_reset (&dpo);
161}
162
163static gtpu_tunnel_t *
164gtpu_tunnel_from_fib_node (fib_node_t * node)
165{
166 return ((gtpu_tunnel_t *) (((char *) node) -
167 STRUCT_OFFSET_OF (gtpu_tunnel_t, node)));
168}
169
170/**
171 * Function definition to backwalk a FIB node -
172 * Here we will restack the new dpo of GTPU DIP to encap node.
173 */
174static fib_node_back_walk_rc_t
175gtpu_tunnel_back_walk (fib_node_t * node, fib_node_back_walk_ctx_t * ctx)
176{
177 gtpu_tunnel_restack_dpo (gtpu_tunnel_from_fib_node (node));
178 return (FIB_NODE_BACK_WALK_CONTINUE);
179}
180
181/**
182 * Function definition to get a FIB node from its index
183 */
184static fib_node_t *
185gtpu_tunnel_fib_node_get (fib_node_index_t index)
186{
187 gtpu_tunnel_t *t;
188 gtpu_main_t *gtm = &gtpu_main;
189
190 t = pool_elt_at_index (gtm->tunnels, index);
191
192 return (&t->node);
193}
194
195/**
196 * Function definition to inform the FIB node that its last lock has gone.
197 */
198static void
199gtpu_tunnel_last_lock_gone (fib_node_t * node)
200{
201 /*
202 * The GTPU tunnel is a root of the graph. As such
203 * it never has children and thus is never locked.
204 */
205 ASSERT (0);
206}
207
208/*
209 * Virtual function table registered by GTPU tunnels
210 * for participation in the FIB object graph.
211 */
212const static fib_node_vft_t gtpu_vft = {
213 .fnv_get = gtpu_tunnel_fib_node_get,
214 .fnv_last_lock = gtpu_tunnel_last_lock_gone,
215 .fnv_back_walk = gtpu_tunnel_back_walk,
216};
217
218
219#define foreach_copy_field \
220_(teid) \
221_(mcast_sw_if_index) \
222_(encap_fib_index) \
223_(decap_next_index) \
224_(src) \
225_(dst)
226
227static void
228ip_udp_gtpu_rewrite (gtpu_tunnel_t * t, bool is_ip6)
229{
230 union
231 {
232 ip4_gtpu_header_t *h4;
233 ip6_gtpu_header_t *h6;
234 u8 *rw;
235 } r =
236 {
237 .rw = 0};
238 int len = is_ip6 ? sizeof *r.h6 : sizeof *r.h4;
239
240 vec_validate_aligned (r.rw, len - 1, CLIB_CACHE_LINE_BYTES);
241
242 udp_header_t *udp;
243 gtpu_header_t *gtpu;
244 /* Fixed portion of the (outer) ip header */
245 if (!is_ip6)
246 {
247 ip4_header_t *ip = &r.h4->ip4;
248 udp = &r.h4->udp;
249 gtpu = &r.h4->gtpu;
250 ip->ip_version_and_header_length = 0x45;
251 ip->ttl = 254;
252 ip->protocol = IP_PROTOCOL_UDP;
253
254 ip->src_address = t->src.ip4;
255 ip->dst_address = t->dst.ip4;
256
257 /* we fix up the ip4 header length and checksum after-the-fact */
258 ip->checksum = ip4_header_checksum (ip);
259 }
260 else
261 {
262 ip6_header_t *ip = &r.h6->ip6;
263 udp = &r.h6->udp;
264 gtpu = &r.h6->gtpu;
265 ip->ip_version_traffic_class_and_flow_label =
266 clib_host_to_net_u32 (6 << 28);
267 ip->hop_limit = 255;
268 ip->protocol = IP_PROTOCOL_UDP;
269
270 ip->src_address = t->src.ip6;
271 ip->dst_address = t->dst.ip6;
272 }
273
274 /* UDP header, randomize src port on something, maybe? */
275 udp->src_port = clib_host_to_net_u16 (2152);
276 udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_GTPU);
277
278 /* GTPU header */
279 gtpu->ver_flags = GTPU_V1_VER | GTPU_PT_GTP;
280 gtpu->type = GTPU_TYPE_GTPU;
281 gtpu->teid = clib_host_to_net_u32 (t->teid);
282
283 t->rewrite = r.rw;
284 /* Now only support 8-byte gtpu header. TBD */
285 _vec_len (t->rewrite) = sizeof (ip4_gtpu_header_t) - 4;
286
287 return;
288}
289
290static bool
291gtpu_decap_next_is_valid (gtpu_main_t * gtm, u32 is_ip6, u32 decap_next_index)
292{
293 vlib_main_t *vm = gtm->vlib_main;
294 u32 input_idx = (!is_ip6) ? gtpu4_input_node.index : gtpu6_input_node.index;
295 vlib_node_runtime_t *r = vlib_node_get_runtime (vm, input_idx);
296
297 return decap_next_index < r->n_next_nodes;
298}
299
Hongjun Nief486b12017-04-12 19:21:16 +0800300static uword
301vtep_addr_ref (ip46_address_t * ip)
302{
303 uword *vtep = ip46_address_is_ip4 (ip) ?
304 hash_get (gtpu_main.vtep4, ip->ip4.as_u32) :
305 hash_get_mem (gtpu_main.vtep6, &ip->ip6);
306 if (vtep)
307 return ++(*vtep);
308 ip46_address_is_ip4 (ip) ?
309 hash_set (gtpu_main.vtep4, ip->ip4.as_u32, 1) :
John Loe6bfeab2018-01-04 16:39:42 -0500310 hash_set_mem_alloc (&gtpu_main.vtep6, &ip->ip6, 1);
Hongjun Nief486b12017-04-12 19:21:16 +0800311 return 1;
312}
313
314static uword
315vtep_addr_unref (ip46_address_t * ip)
316{
317 uword *vtep = ip46_address_is_ip4 (ip) ?
318 hash_get (gtpu_main.vtep4, ip->ip4.as_u32) :
319 hash_get_mem (gtpu_main.vtep6, &ip->ip6);
320 ASSERT (vtep);
321 if (--(*vtep) != 0)
322 return *vtep;
323 ip46_address_is_ip4 (ip) ?
324 hash_unset (gtpu_main.vtep4, ip->ip4.as_u32) :
John Loe6bfeab2018-01-04 16:39:42 -0500325 hash_unset_mem_free (&gtpu_main.vtep6, &ip->ip6);
Hongjun Nief486b12017-04-12 19:21:16 +0800326 return 0;
327}
328
329typedef CLIB_PACKED (union
330 {
331 struct
332 {
333 fib_node_index_t mfib_entry_index;
334 adj_index_t mcast_adj_index;
335 }; u64 as_u64;
336 }) mcast_shared_t;
337
338static inline mcast_shared_t
339mcast_shared_get (ip46_address_t * ip)
340{
341 ASSERT (ip46_address_is_multicast (ip));
342 uword *p = hash_get_mem (gtpu_main.mcast_shared, ip);
343 ASSERT (p);
344 return (mcast_shared_t)
345 {
346 .as_u64 = *p};
347}
348
349static inline void
350mcast_shared_add (ip46_address_t * dst, fib_node_index_t mfei, adj_index_t ai)
351{
352 mcast_shared_t new_ep = {
353 .mcast_adj_index = ai,
354 .mfib_entry_index = mfei,
355 };
356
John Loe6bfeab2018-01-04 16:39:42 -0500357 hash_set_mem_alloc (&gtpu_main.mcast_shared, dst, new_ep.as_u64);
Hongjun Nief486b12017-04-12 19:21:16 +0800358}
359
360static inline void
361mcast_shared_remove (ip46_address_t * dst)
362{
363 mcast_shared_t ep = mcast_shared_get (dst);
364
365 adj_unlock (ep.mcast_adj_index);
366 mfib_table_entry_delete_index (ep.mfib_entry_index, MFIB_SOURCE_GTPU);
367
John Loe6bfeab2018-01-04 16:39:42 -0500368 hash_unset_mem_free (&gtpu_main.mcast_shared, dst);
Hongjun Nief486b12017-04-12 19:21:16 +0800369}
370
Hongjun Nief486b12017-04-12 19:21:16 +0800371int vnet_gtpu_add_del_tunnel
372 (vnet_gtpu_add_del_tunnel_args_t * a, u32 * sw_if_indexp)
373{
374 gtpu_main_t *gtm = &gtpu_main;
375 gtpu_tunnel_t *t = 0;
376 vnet_main_t *vnm = gtm->vnet_main;
377 uword *p;
378 u32 hw_if_index = ~0;
379 u32 sw_if_index = ~0;
380 gtpu4_tunnel_key_t key4;
381 gtpu6_tunnel_key_t key6;
382 u32 is_ip6 = a->is_ip6;
383
384 if (!is_ip6)
385 {
386 key4.src = a->dst.ip4.as_u32; /* decap src in key is encap dst in config */
387 key4.teid = clib_host_to_net_u32 (a->teid);
388 p = hash_get (gtm->gtpu4_tunnel_by_key, key4.as_u64);
389 }
390 else
391 {
392 key6.src = a->dst.ip6;
393 key6.teid = clib_host_to_net_u32 (a->teid);
394 p = hash_get_mem (gtm->gtpu6_tunnel_by_key, &key6);
395 }
396
397 if (a->is_add)
398 {
399 l2input_main_t *l2im = &l2input_main;
400
401 /* adding a tunnel: tunnel must not already exist */
402 if (p)
403 return VNET_API_ERROR_TUNNEL_EXIST;
404
405 /*if not set explicitly, default to l2 */
406 if (a->decap_next_index == ~0)
407 a->decap_next_index = GTPU_INPUT_NEXT_L2_INPUT;
408 if (!gtpu_decap_next_is_valid (gtm, is_ip6, a->decap_next_index))
409 return VNET_API_ERROR_INVALID_DECAP_NEXT;
410
411 pool_get_aligned (gtm->tunnels, t, CLIB_CACHE_LINE_BYTES);
Dave Barachb7b92992018-10-17 10:38:51 -0400412 clib_memset (t, 0, sizeof (*t));
Hongjun Nief486b12017-04-12 19:21:16 +0800413
414 /* copy from arg structure */
415#define _(x) t->x = a->x;
416 foreach_copy_field;
417#undef _
418
419 ip_udp_gtpu_rewrite (t, is_ip6);
420
421 /* copy the key */
422 if (is_ip6)
John Loe6bfeab2018-01-04 16:39:42 -0500423 hash_set_mem_alloc (&gtm->gtpu6_tunnel_by_key, &key6,
424 t - gtm->tunnels);
Hongjun Nief486b12017-04-12 19:21:16 +0800425 else
426 hash_set (gtm->gtpu4_tunnel_by_key, key4.as_u64, t - gtm->tunnels);
427
428 vnet_hw_interface_t *hi;
429 if (vec_len (gtm->free_gtpu_tunnel_hw_if_indices) > 0)
430 {
431 vnet_interface_main_t *im = &vnm->interface_main;
432 hw_if_index = gtm->free_gtpu_tunnel_hw_if_indices
433 [vec_len (gtm->free_gtpu_tunnel_hw_if_indices) - 1];
434 _vec_len (gtm->free_gtpu_tunnel_hw_if_indices) -= 1;
435
436 hi = vnet_get_hw_interface (vnm, hw_if_index);
437 hi->dev_instance = t - gtm->tunnels;
438 hi->hw_instance = hi->dev_instance;
439
440 /* clear old stats of freed tunnel before reuse */
441 sw_if_index = hi->sw_if_index;
442 vnet_interface_counter_lock (im);
443 vlib_zero_combined_counter
444 (&im->combined_sw_if_counters[VNET_INTERFACE_COUNTER_TX],
445 sw_if_index);
446 vlib_zero_combined_counter (&im->combined_sw_if_counters
447 [VNET_INTERFACE_COUNTER_RX],
448 sw_if_index);
449 vlib_zero_simple_counter (&im->sw_if_counters
450 [VNET_INTERFACE_COUNTER_DROP],
451 sw_if_index);
452 vnet_interface_counter_unlock (im);
453 }
454 else
455 {
456 hw_if_index = vnet_register_interface
457 (vnm, gtpu_device_class.index, t - gtm->tunnels,
458 gtpu_hw_class.index, t - gtm->tunnels);
459 hi = vnet_get_hw_interface (vnm, hw_if_index);
460 }
461
John Loe5453d02018-01-23 19:21:34 -0500462 /* Set gtpu tunnel output node */
463 u32 encap_index = !is_ip6 ?
464 gtpu4_encap_node.index : gtpu6_encap_node.index;
465 vnet_set_interface_output_node (vnm, hw_if_index, encap_index);
466
Hongjun Nief486b12017-04-12 19:21:16 +0800467 t->hw_if_index = hw_if_index;
468 t->sw_if_index = sw_if_index = hi->sw_if_index;
469
470 vec_validate_init_empty (gtm->tunnel_index_by_sw_if_index, sw_if_index,
471 ~0);
472 gtm->tunnel_index_by_sw_if_index[sw_if_index] = t - gtm->tunnels;
473
474 /* setup l2 input config with l2 feature and bd 0 to drop packet */
475 vec_validate (l2im->configs, sw_if_index);
476 l2im->configs[sw_if_index].feature_bitmap = L2INPUT_FEAT_DROP;
477 l2im->configs[sw_if_index].bd_index = 0;
478
479 vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, sw_if_index);
480 si->flags &= ~VNET_SW_INTERFACE_FLAG_HIDDEN;
481 vnet_sw_interface_set_flags (vnm, sw_if_index,
482 VNET_SW_INTERFACE_FLAG_ADMIN_UP);
483
484 fib_node_init (&t->node, gtm->fib_node_type);
485 fib_prefix_t tun_dst_pfx;
Hongjun Nief486b12017-04-12 19:21:16 +0800486 vnet_flood_class_t flood_class = VNET_FLOOD_CLASS_TUNNEL_NORMAL;
487
488 fib_prefix_from_ip46_addr (&t->dst, &tun_dst_pfx);
489 if (!ip46_address_is_multicast (&t->dst))
490 {
491 /* Unicast tunnel -
492 * source the FIB entry for the tunnel's destination
493 * and become a child thereof. The tunnel will then get poked
494 * when the forwarding for the entry updates, and the tunnel can
495 * re-stack accordingly
496 */
497 vtep_addr_ref (&t->src);
498 t->fib_entry_index = fib_table_entry_special_add
499 (t->encap_fib_index, &tun_dst_pfx, FIB_SOURCE_RR,
500 FIB_ENTRY_FLAG_NONE);
501 t->sibling_index = fib_entry_child_add
502 (t->fib_entry_index, gtm->fib_node_type, t - gtm->tunnels);
503 gtpu_tunnel_restack_dpo (t);
504 }
505 else
506 {
507 /* Multicast tunnel -
Paul Vinciguerrabdc0e6b2018-09-22 05:32:50 -0700508 * as the same mcast group can be used for multiple mcast tunnels
509 * with different VNIs, create the output adjacency only if
Hongjun Nief486b12017-04-12 19:21:16 +0800510 * it does not already exist
511 */
512 fib_protocol_t fp = fib_ip_proto (is_ip6);
513
514 if (vtep_addr_ref (&t->dst) == 1)
515 {
516 fib_node_index_t mfei;
517 adj_index_t ai;
518 fib_route_path_t path = {
Neale Rannsda78f952017-05-24 09:15:43 -0700519 .frp_proto = fib_proto_to_dpo (fp),
Hongjun Nief486b12017-04-12 19:21:16 +0800520 .frp_addr = zero_addr,
521 .frp_sw_if_index = 0xffffffff,
522 .frp_fib_index = ~0,
Neale Ranns097fa662018-05-01 05:17:55 -0700523 .frp_weight = 1,
Hongjun Nief486b12017-04-12 19:21:16 +0800524 .frp_flags = FIB_ROUTE_PATH_LOCAL,
Neale Ranns097fa662018-05-01 05:17:55 -0700525 .frp_mitf_flags = MFIB_ITF_FLAG_FORWARD,
Hongjun Nief486b12017-04-12 19:21:16 +0800526 };
527 const mfib_prefix_t mpfx = {
528 .fp_proto = fp,
529 .fp_len = (is_ip6 ? 128 : 32),
530 .fp_grp_addr = tun_dst_pfx.fp_addr,
531 };
532
533 /*
534 * Setup the (*,G) to receive traffic on the mcast group
535 * - the forwarding interface is for-us
536 * - the accepting interface is that from the API
537 */
538 mfib_table_entry_path_update (t->encap_fib_index,
Neale Ranns097fa662018-05-01 05:17:55 -0700539 &mpfx, MFIB_SOURCE_GTPU, &path);
Hongjun Nief486b12017-04-12 19:21:16 +0800540
541 path.frp_sw_if_index = a->mcast_sw_if_index;
542 path.frp_flags = FIB_ROUTE_PATH_FLAG_NONE;
Neale Ranns097fa662018-05-01 05:17:55 -0700543 path.frp_mitf_flags = MFIB_ITF_FLAG_ACCEPT;
Hongjun Nief486b12017-04-12 19:21:16 +0800544 mfei = mfib_table_entry_path_update (t->encap_fib_index,
545 &mpfx,
Neale Ranns097fa662018-05-01 05:17:55 -0700546 MFIB_SOURCE_GTPU, &path);
Hongjun Nief486b12017-04-12 19:21:16 +0800547
548 /*
549 * Create the mcast adjacency to send traffic to the group
550 */
551 ai = adj_mcast_add_or_lock (fp,
552 fib_proto_to_link (fp),
553 a->mcast_sw_if_index);
554
555 /*
556 * create a new end-point
557 */
558 mcast_shared_add (&t->dst, mfei, ai);
559 }
560
561 dpo_id_t dpo = DPO_INVALID;
562 mcast_shared_t ep = mcast_shared_get (&t->dst);
563
564 /* Stack shared mcast dst mac addr rewrite on encap */
565 dpo_set (&dpo, DPO_ADJACENCY_MCAST,
566 fib_proto_to_dpo (fp), ep.mcast_adj_index);
567
568 dpo_stack_from_node (encap_index, &t->next_dpo, &dpo);
569
570 dpo_reset (&dpo);
571 flood_class = VNET_FLOOD_CLASS_TUNNEL_MASTER;
572 }
573
Hongjun Nief486b12017-04-12 19:21:16 +0800574 vnet_get_sw_interface (vnet_get_main (), sw_if_index)->flood_class =
575 flood_class;
576 }
577 else
578 {
579 /* deleting a tunnel: tunnel must exist */
580 if (!p)
581 return VNET_API_ERROR_NO_SUCH_ENTRY;
582
583 t = pool_elt_at_index (gtm->tunnels, p[0]);
584 sw_if_index = t->sw_if_index;
585
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 */
Neale Rannsb4743802018-09-05 09:13:57 -0700591 set_int_l2_mode (gtm->vlib_main, vnm, MODE_L3, t->sw_if_index, 0,
592 L2_BD_PORT_TYPE_NORMAL, 0, 0);
Hongjun Nief486b12017-04-12 19:21:16 +0800593 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
John Loe6bfeab2018-01-04 16:39:42 -0500600 hash_unset_mem_free (&gtm->gtpu6_tunnel_by_key, &key6);
Hongjun Nief486b12017-04-12 19:21:16 +0800601
602 if (!ip46_address_is_multicast (&t->dst))
603 {
604 vtep_addr_unref (&t->src);
605 fib_entry_child_remove (t->fib_entry_index, t->sibling_index);
606 fib_table_entry_delete_index (t->fib_entry_index, FIB_SOURCE_RR);
607 }
608 else if (vtep_addr_unref (&t->dst) == 0)
609 {
610 mcast_shared_remove (&t->dst);
611 }
612
613 fib_node_deinit (&t->node);
614 vec_free (t->rewrite);
615 pool_put (gtm->tunnels, t);
616 }
617
618 if (sw_if_indexp)
619 *sw_if_indexp = sw_if_index;
620
Jakub Grajciare8964fe2019-05-22 11:51:47 +0200621 if (a->is_add)
622 {
623 /* register udp ports */
624 if (!is_ip6 && !udp_is_valid_dst_port (UDP_DST_PORT_GTPU, 1))
625 udp_register_dst_port (gtm->vlib_main, UDP_DST_PORT_GTPU,
626 gtpu4_input_node.index, /* is_ip4 */ 1);
627 if (is_ip6 && !udp_is_valid_dst_port (UDP_DST_PORT_GTPU6, 0))
628 udp_register_dst_port (gtm->vlib_main, UDP_DST_PORT_GTPU6,
629 gtpu6_input_node.index, /* is_ip4 */ 0);
630 }
631
Hongjun Nief486b12017-04-12 19:21:16 +0800632 return 0;
633}
634
635static uword
636get_decap_next_for_node (u32 node_index, u32 ipv4_set)
637{
638 gtpu_main_t *gtm = &gtpu_main;
639 vlib_main_t *vm = gtm->vlib_main;
640 uword input_node = (ipv4_set) ? gtpu4_input_node.index :
641 gtpu6_input_node.index;
642
643 return vlib_node_add_next (vm, input_node, node_index);
644}
645
646static uword
647unformat_decap_next (unformat_input_t * input, va_list * args)
648{
649 u32 *result = va_arg (*args, u32 *);
650 u32 ipv4_set = va_arg (*args, int);
651 gtpu_main_t *gtm = &gtpu_main;
652 vlib_main_t *vm = gtm->vlib_main;
653 u32 node_index;
654 u32 tmp;
655
656 if (unformat (input, "l2"))
657 *result = GTPU_INPUT_NEXT_L2_INPUT;
Hongjun Niccd4a652017-06-01 00:24:12 +0800658 else if (unformat (input, "ip4"))
659 *result = GTPU_INPUT_NEXT_IP4_INPUT;
660 else if (unformat (input, "ip6"))
661 *result = GTPU_INPUT_NEXT_IP6_INPUT;
Hongjun Nief486b12017-04-12 19:21:16 +0800662 else if (unformat (input, "node %U", unformat_vlib_node, vm, &node_index))
663 *result = get_decap_next_for_node (node_index, ipv4_set);
664 else if (unformat (input, "%d", &tmp))
665 *result = tmp;
666 else
667 return 0;
Hongjun Niccd4a652017-06-01 00:24:12 +0800668
Hongjun Nief486b12017-04-12 19:21:16 +0800669 return 1;
670}
671
672static clib_error_t *
673gtpu_add_del_tunnel_command_fn (vlib_main_t * vm,
674 unformat_input_t * input,
675 vlib_cli_command_t * cmd)
676{
677 unformat_input_t _line_input, *line_input = &_line_input;
678 ip46_address_t src, dst;
679 u8 is_add = 1;
680 u8 src_set = 0;
681 u8 dst_set = 0;
682 u8 grp_set = 0;
683 u8 ipv4_set = 0;
684 u8 ipv6_set = 0;
685 u32 encap_fib_index = 0;
686 u32 mcast_sw_if_index = ~0;
687 u32 decap_next_index = GTPU_INPUT_NEXT_L2_INPUT;
688 u32 teid = 0;
689 u32 tmp;
690 int rv;
691 vnet_gtpu_add_del_tunnel_args_t _a, *a = &_a;
692 u32 tunnel_sw_if_index;
693 clib_error_t *error = NULL;
694
695 /* Cant "universally zero init" (={0}) due to GCC bug 53119 */
Dave Barachb7b92992018-10-17 10:38:51 -0400696 clib_memset (&src, 0, sizeof src);
697 clib_memset (&dst, 0, sizeof dst);
Hongjun Nief486b12017-04-12 19:21:16 +0800698
699 /* Get a line of input. */
700 if (!unformat_user (input, unformat_line_input, line_input))
701 return 0;
702
703 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
704 {
705 if (unformat (line_input, "del"))
706 {
707 is_add = 0;
708 }
709 else if (unformat (line_input, "src %U",
710 unformat_ip4_address, &src.ip4))
711 {
712 src_set = 1;
713 ipv4_set = 1;
714 }
715 else if (unformat (line_input, "dst %U",
716 unformat_ip4_address, &dst.ip4))
717 {
718 dst_set = 1;
719 ipv4_set = 1;
720 }
721 else if (unformat (line_input, "src %U",
722 unformat_ip6_address, &src.ip6))
723 {
724 src_set = 1;
725 ipv6_set = 1;
726 }
727 else if (unformat (line_input, "dst %U",
728 unformat_ip6_address, &dst.ip6))
729 {
730 dst_set = 1;
731 ipv6_set = 1;
732 }
733 else if (unformat (line_input, "group %U %U",
734 unformat_ip4_address, &dst.ip4,
735 unformat_vnet_sw_interface,
736 vnet_get_main (), &mcast_sw_if_index))
737 {
738 grp_set = dst_set = 1;
739 ipv4_set = 1;
740 }
741 else if (unformat (line_input, "group %U %U",
742 unformat_ip6_address, &dst.ip6,
743 unformat_vnet_sw_interface,
744 vnet_get_main (), &mcast_sw_if_index))
745 {
746 grp_set = dst_set = 1;
747 ipv6_set = 1;
748 }
749 else if (unformat (line_input, "encap-vrf-id %d", &tmp))
750 {
751 encap_fib_index = fib_table_find (fib_ip_proto (ipv6_set), tmp);
752 if (encap_fib_index == ~0)
753 {
754 error =
755 clib_error_return (0, "nonexistent encap-vrf-id %d", tmp);
756 goto done;
757 }
758 }
759 else if (unformat (line_input, "decap-next %U", unformat_decap_next,
760 &decap_next_index, ipv4_set))
761 ;
762 else if (unformat (line_input, "teid %d", &teid))
763 ;
764 else
765 {
766 error = clib_error_return (0, "parse error: '%U'",
767 format_unformat_error, line_input);
768 goto done;
769 }
770 }
771
772 if (src_set == 0)
773 {
774 error = clib_error_return (0, "tunnel src address not specified");
775 goto done;
776 }
777
778 if (dst_set == 0)
779 {
780 error = clib_error_return (0, "tunnel dst address not specified");
781 goto done;
782 }
783
784 if (grp_set && !ip46_address_is_multicast (&dst))
785 {
786 error = clib_error_return (0, "tunnel group address not multicast");
787 goto done;
788 }
789
790 if (grp_set == 0 && ip46_address_is_multicast (&dst))
791 {
792 error = clib_error_return (0, "dst address must be unicast");
793 goto done;
794 }
795
796 if (grp_set && mcast_sw_if_index == ~0)
797 {
798 error = clib_error_return (0, "tunnel nonexistent multicast device");
799 goto done;
800 }
801
802 if (ipv4_set && ipv6_set)
803 {
804 error = clib_error_return (0, "both IPv4 and IPv6 addresses specified");
805 goto done;
806 }
807
808 if (ip46_address_cmp (&src, &dst) == 0)
809 {
810 error = clib_error_return (0, "src and dst addresses are identical");
811 goto done;
812 }
813
814 if (decap_next_index == ~0)
815 {
816 error = clib_error_return (0, "next node not found");
817 goto done;
818 }
819
Dave Barachb7b92992018-10-17 10:38:51 -0400820 clib_memset (a, 0, sizeof (*a));
Hongjun Nief486b12017-04-12 19:21:16 +0800821
822 a->is_add = is_add;
823 a->is_ip6 = ipv6_set;
824
825#define _(x) a->x = x;
826 foreach_copy_field;
827#undef _
828
829 rv = vnet_gtpu_add_del_tunnel (a, &tunnel_sw_if_index);
830
831 switch (rv)
832 {
833 case 0:
834 if (is_add)
835 vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name,
836 vnet_get_main (), tunnel_sw_if_index);
837 break;
838
839 case VNET_API_ERROR_TUNNEL_EXIST:
840 error = clib_error_return (0, "tunnel already exists...");
841 goto done;
842
843 case VNET_API_ERROR_NO_SUCH_ENTRY:
844 error = clib_error_return (0, "tunnel does not exist...");
845 goto done;
846
847 default:
848 error = clib_error_return
849 (0, "vnet_gtpu_add_del_tunnel returned %d", rv);
850 goto done;
851 }
852
853done:
854 unformat_free (line_input);
855
856 return error;
857}
858
859/*?
860 * Add or delete a GTPU Tunnel.
861 *
862 * GTPU provides the features needed to allow L2 bridge domains (BDs)
863 * to span multiple servers. This is done by building an L2 overlay on
864 * top of an L3 network underlay using GTPU tunnels.
865 *
866 * This makes it possible for servers to be co-located in the same data
867 * center or be separated geographically as long as they are reachable
868 * through the underlay L3 network.
869 *
870 * You can refer to this kind of L2 overlay bridge domain as a GTPU
871 * (Virtual eXtensible VLAN) segment.
872 *
873 * @cliexpar
874 * Example of how to create a GTPU Tunnel:
875 * @cliexcmd{create gtpu tunnel src 10.0.3.1 dst 10.0.3.3 teid 13 encap-vrf-id 7}
876 * Example of how to delete a GTPU Tunnel:
877 * @cliexcmd{create gtpu tunnel src 10.0.3.1 dst 10.0.3.3 teid 13 del}
878 ?*/
879/* *INDENT-OFF* */
880VLIB_CLI_COMMAND (create_gtpu_tunnel_command, static) = {
881 .path = "create gtpu tunnel",
882 .short_help =
883 "create gtpu tunnel src <local-vtep-addr>"
884 " {dst <remote-vtep-addr>|group <mcast-vtep-addr> <intf-name>} teid <nn>"
Hongjun Niccd4a652017-06-01 00:24:12 +0800885 " [encap-vrf-id <nn>] [decap-next [l2|ip4|ip6|node <name>]] [del]",
Hongjun Nief486b12017-04-12 19:21:16 +0800886 .function = gtpu_add_del_tunnel_command_fn,
887};
888/* *INDENT-ON* */
889
890static clib_error_t *
891show_gtpu_tunnel_command_fn (vlib_main_t * vm,
892 unformat_input_t * input,
893 vlib_cli_command_t * cmd)
894{
895 gtpu_main_t *gtm = &gtpu_main;
896 gtpu_tunnel_t *t;
897
898 if (pool_elts (gtm->tunnels) == 0)
899 vlib_cli_output (vm, "No gtpu tunnels configured...");
900
901 pool_foreach (t, gtm->tunnels, (
902 {
903 vlib_cli_output (vm, "%U",
904 format_gtpu_tunnel, t);
905 }
906 ));
907
908 return 0;
909}
910
911/*?
912 * Display all the GTPU Tunnel entries.
913 *
914 * @cliexpar
915 * Example of how to display the GTPU Tunnel entries:
916 * @cliexstart{show gtpu tunnel}
917 * [0] src 10.0.3.1 dst 10.0.3.3 teid 13 encap_fib_index 0 sw_if_index 5 decap_next l2
918 * @cliexend
919 ?*/
920/* *INDENT-OFF* */
921VLIB_CLI_COMMAND (show_gtpu_tunnel_command, static) = {
922 .path = "show gtpu tunnel",
923 .short_help = "show gtpu tunnel",
924 .function = show_gtpu_tunnel_command_fn,
925};
926/* *INDENT-ON* */
927
928void
929vnet_int_gtpu_bypass_mode (u32 sw_if_index, u8 is_ip6, u8 is_enable)
930{
931 if (is_ip6)
932 vnet_feature_enable_disable ("ip6-unicast", "ip6-gtpu-bypass",
933 sw_if_index, is_enable, 0, 0);
934 else
935 vnet_feature_enable_disable ("ip4-unicast", "ip4-gtpu-bypass",
936 sw_if_index, is_enable, 0, 0);
937}
938
939static clib_error_t *
940set_ip_gtpu_bypass (u32 is_ip6,
941 unformat_input_t * input, vlib_cli_command_t * cmd)
942{
943 unformat_input_t _line_input, *line_input = &_line_input;
944 vnet_main_t *vnm = vnet_get_main ();
945 clib_error_t *error = 0;
946 u32 sw_if_index, is_enable;
947
948 sw_if_index = ~0;
949 is_enable = 1;
950
951 if (!unformat_user (input, unformat_line_input, line_input))
952 return 0;
953
954 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
955 {
956 if (unformat_user
957 (line_input, unformat_vnet_sw_interface, vnm, &sw_if_index))
958 ;
959 else if (unformat (line_input, "del"))
960 is_enable = 0;
961 else
962 {
963 error = unformat_parse_error (line_input);
964 goto done;
965 }
966 }
967
968 if (~0 == sw_if_index)
969 {
970 error = clib_error_return (0, "unknown interface `%U'",
971 format_unformat_error, line_input);
972 goto done;
973 }
974
975 vnet_int_gtpu_bypass_mode (sw_if_index, is_ip6, is_enable);
976
977done:
978 unformat_free (line_input);
979
980 return error;
981}
982
983static clib_error_t *
984set_ip4_gtpu_bypass (vlib_main_t * vm,
985 unformat_input_t * input, vlib_cli_command_t * cmd)
986{
987 return set_ip_gtpu_bypass (0, input, cmd);
988}
989
990/*?
991 * This command adds the 'ip4-gtpu-bypass' graph node for a given interface.
992 * By adding the IPv4 gtpu-bypass graph node to an interface, the node checks
993 * for and validate input gtpu packet and bypass ip4-lookup, ip4-local,
994 * ip4-udp-lookup nodes to speedup gtpu packet forwarding. This node will
995 * cause extra overhead to for non-gtpu packets which is kept at a minimum.
996 *
997 * @cliexpar
998 * @parblock
999 * Example of graph node before ip4-gtpu-bypass is enabled:
1000 * @cliexstart{show vlib graph ip4-gtpu-bypass}
1001 * Name Next Previous
1002 * ip4-gtpu-bypass error-drop [0]
1003 * gtpu4-input [1]
1004 * ip4-lookup [2]
1005 * @cliexend
1006 *
1007 * Example of how to enable ip4-gtpu-bypass on an interface:
1008 * @cliexcmd{set interface ip gtpu-bypass GigabitEthernet2/0/0}
1009 *
1010 * Example of graph node after ip4-gtpu-bypass is enabled:
1011 * @cliexstart{show vlib graph ip4-gtpu-bypass}
1012 * Name Next Previous
1013 * ip4-gtpu-bypass error-drop [0] ip4-input
1014 * gtpu4-input [1] ip4-input-no-checksum
1015 * ip4-lookup [2]
1016 * @cliexend
1017 *
Paul Vinciguerrabdc0e6b2018-09-22 05:32:50 -07001018 * Example of how to display the feature enabled on an interface:
Hongjun Nief486b12017-04-12 19:21:16 +08001019 * @cliexstart{show ip interface features GigabitEthernet2/0/0}
1020 * IP feature paths configured on GigabitEthernet2/0/0...
1021 * ...
1022 * ipv4 unicast:
1023 * ip4-gtpu-bypass
1024 * ip4-lookup
1025 * ...
1026 * @cliexend
1027 *
1028 * Example of how to disable ip4-gtpu-bypass on an interface:
1029 * @cliexcmd{set interface ip gtpu-bypass GigabitEthernet2/0/0 del}
1030 * @endparblock
1031?*/
1032/* *INDENT-OFF* */
1033VLIB_CLI_COMMAND (set_interface_ip_gtpu_bypass_command, static) = {
1034 .path = "set interface ip gtpu-bypass",
1035 .function = set_ip4_gtpu_bypass,
1036 .short_help = "set interface ip gtpu-bypass <interface> [del]",
1037};
1038/* *INDENT-ON* */
1039
1040static clib_error_t *
1041set_ip6_gtpu_bypass (vlib_main_t * vm,
1042 unformat_input_t * input, vlib_cli_command_t * cmd)
1043{
1044 return set_ip_gtpu_bypass (1, input, cmd);
1045}
1046
1047/*?
1048 * This command adds the 'ip6-gtpu-bypass' graph node for a given interface.
1049 * By adding the IPv6 gtpu-bypass graph node to an interface, the node checks
1050 * for and validate input gtpu packet and bypass ip6-lookup, ip6-local,
1051 * ip6-udp-lookup nodes to speedup gtpu packet forwarding. This node will
1052 * cause extra overhead to for non-gtpu packets which is kept at a minimum.
1053 *
1054 * @cliexpar
1055 * @parblock
1056 * Example of graph node before ip6-gtpu-bypass is enabled:
1057 * @cliexstart{show vlib graph ip6-gtpu-bypass}
1058 * Name Next Previous
1059 * ip6-gtpu-bypass error-drop [0]
1060 * gtpu6-input [1]
1061 * ip6-lookup [2]
1062 * @cliexend
1063 *
1064 * Example of how to enable ip6-gtpu-bypass on an interface:
1065 * @cliexcmd{set interface ip6 gtpu-bypass GigabitEthernet2/0/0}
1066 *
1067 * Example of graph node after ip6-gtpu-bypass is enabled:
1068 * @cliexstart{show vlib graph ip6-gtpu-bypass}
1069 * Name Next Previous
1070 * ip6-gtpu-bypass error-drop [0] ip6-input
1071 * gtpu6-input [1] ip4-input-no-checksum
1072 * ip6-lookup [2]
1073 * @cliexend
1074 *
Paul Vinciguerrabdc0e6b2018-09-22 05:32:50 -07001075 * Example of how to display the feature enabled on an interface:
Hongjun Nief486b12017-04-12 19:21:16 +08001076 * @cliexstart{show ip interface features GigabitEthernet2/0/0}
1077 * IP feature paths configured on GigabitEthernet2/0/0...
1078 * ...
1079 * ipv6 unicast:
1080 * ip6-gtpu-bypass
1081 * ip6-lookup
1082 * ...
1083 * @cliexend
1084 *
1085 * Example of how to disable ip6-gtpu-bypass on an interface:
1086 * @cliexcmd{set interface ip6 gtpu-bypass GigabitEthernet2/0/0 del}
1087 * @endparblock
1088?*/
1089/* *INDENT-OFF* */
1090VLIB_CLI_COMMAND (set_interface_ip6_gtpu_bypass_command, static) = {
1091 .path = "set interface ip6 gtpu-bypass",
1092 .function = set_ip6_gtpu_bypass,
1093 .short_help = "set interface ip gtpu-bypass <interface> [del]",
1094};
1095/* *INDENT-ON* */
1096
1097clib_error_t *
1098gtpu_init (vlib_main_t * vm)
1099{
1100 gtpu_main_t *gtm = &gtpu_main;
1101
1102 gtm->vnet_main = vnet_get_main ();
1103 gtm->vlib_main = vm;
1104
1105 /* initialize the ip6 hash */
1106 gtm->gtpu6_tunnel_by_key = hash_create_mem (0,
1107 sizeof (gtpu6_tunnel_key_t),
1108 sizeof (uword));
1109 gtm->vtep6 = hash_create_mem (0, sizeof (ip6_address_t), sizeof (uword));
1110 gtm->mcast_shared = hash_create_mem (0,
1111 sizeof (ip46_address_t),
1112 sizeof (mcast_shared_t));
1113
Hongjun Nief486b12017-04-12 19:21:16 +08001114 gtm->fib_node_type = fib_node_register_new_type (&gtpu_vft);
1115
1116 return 0;
1117}
1118
1119VLIB_INIT_FUNCTION (gtpu_init);
1120
1121/* *INDENT-OFF* */
1122VLIB_PLUGIN_REGISTER () = {
1123 .version = VPP_BUILD_VER,
Dave Wallace1d1985d2019-04-23 15:29:50 -04001124 .description = "GPRS Tunnelling Protocol, User Data (GTPv1-U)",
Hongjun Nief486b12017-04-12 19:21:16 +08001125};
1126/* *INDENT-ON* */
1127
1128/*
1129 * fd.io coding-style-patch-verification: ON
1130 *
1131 * Local Variables:
1132 * eval: (c-set-style "gnu")
1133 * End:
1134 */