Line data Source code
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 50423 : 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 50423 : 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 24 : u8 * format_gtpu_encap_trace (u8 * s, va_list * args)
53 : {
54 24 : CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
55 24 : CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
56 24 : gtpu_encap_trace_t * t
57 : = va_arg (*args, gtpu_encap_trace_t *);
58 :
59 24 : s = format (s, "GTPU encap to gtpu_tunnel%d tteid %d",
60 : t->tunnel_index, t->tteid);
61 24 : return s;
62 : }
63 :
64 : static u8 *
65 110 : format_decap_next (u8 * s, va_list * args)
66 : {
67 110 : u32 next_index = va_arg (*args, u32);
68 :
69 110 : switch (next_index)
70 : {
71 0 : case GTPU_INPUT_NEXT_DROP:
72 0 : return format (s, "drop");
73 110 : case GTPU_INPUT_NEXT_L2_INPUT:
74 110 : return format (s, "l2");
75 0 : case GTPU_INPUT_NEXT_IP4_INPUT:
76 0 : return format (s, "ip4");
77 0 : case GTPU_INPUT_NEXT_IP6_INPUT:
78 0 : return format (s, "ip6");
79 0 : default:
80 0 : return format (s, "index %d", next_index);
81 : }
82 : return s;
83 : }
84 :
85 : u8 *
86 110 : format_gtpu_tunnel (u8 * s, va_list * args)
87 : {
88 110 : gtpu_tunnel_t *t = va_arg (*args, gtpu_tunnel_t *);
89 110 : gtpu_main_t *ngm = >pu_main;
90 110 : ip4_main_t *im4 = &ip4_main;
91 110 : ip6_main_t *im6 = &ip6_main;
92 110 : u8 is_ipv6 = !ip46_address_is_ip4 (&t->dst);
93 :
94 110 : u32 encap_vrf_id =
95 110 : is_ipv6 ? im6->fibs[t->encap_fib_index].ft_table_id :
96 110 : im4->fibs[t->encap_fib_index].ft_table_id;
97 :
98 110 : s = format (s, "[%d] src %U dst %U teid %d tteid %d "
99 : "encap-vrf-id %d sw-if-idx %d ",
100 110 : 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 110 : s = format (s, "encap-dpo-idx %d ", t->next_dpo.dpoi_index);
106 110 : s = format (s, "decap-next-%U ", format_decap_next, t->decap_next_index);
107 :
108 110 : if (PREDICT_FALSE (ip46_address_is_multicast (&t->dst)))
109 5 : s = format (s, "mcast-sw-if-idx %d ", t->mcast_sw_if_index);
110 :
111 110 : return s;
112 : }
113 :
114 : static u8 *
115 316 : format_gtpu_name (u8 * s, va_list * args)
116 : {
117 316 : u32 dev_instance = va_arg (*args, u32);
118 316 : return format (s, "gtpu_tunnel%d", dev_instance);
119 : }
120 :
121 : static clib_error_t *
122 106 : gtpu_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
123 : {
124 106 : u32 hw_flags = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ?
125 : VNET_HW_INTERFACE_FLAG_LINK_UP : 0;
126 106 : vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags);
127 :
128 106 : return /* no error */ 0;
129 : }
130 :
131 : /* *INDENT-OFF* */
132 7839 : 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 0 : format_gtpu_header_with_length (u8 * s, va_list * args)
142 : {
143 0 : u32 dev_instance = va_arg (*args, u32);
144 0 : s = format (s, "unimplemented dev %u", dev_instance);
145 0 : return s;
146 : }
147 :
148 : /* *INDENT-OFF* */
149 4479 : 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 33 : gtpu_tunnel_restack_dpo (gtpu_tunnel_t * t)
160 : {
161 33 : dpo_id_t dpo = DPO_INVALID;
162 33 : u32 encap_index = ip46_address_is_ip4 (&t->dst) ?
163 33 : gtpu4_encap_node.index : gtpu6_encap_node.index;
164 33 : fib_forward_chain_type_t forw_type = ip46_address_is_ip4 (&t->dst) ?
165 33 : FIB_FORW_CHAIN_TYPE_UNICAST_IP4 : FIB_FORW_CHAIN_TYPE_UNICAST_IP6;
166 :
167 33 : fib_entry_contribute_forwarding (t->fib_entry_index, forw_type, &dpo);
168 33 : dpo_stack_from_node (encap_index, &t->next_dpo, &dpo);
169 33 : dpo_reset (&dpo);
170 33 : }
171 :
172 : static gtpu_tunnel_t *
173 10 : gtpu_tunnel_from_fib_node (fib_node_t * node)
174 : {
175 10 : 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 10 : gtpu_tunnel_back_walk (fib_node_t * node, fib_node_back_walk_ctx_t * ctx)
185 : {
186 10 : gtpu_tunnel_restack_dpo (gtpu_tunnel_from_fib_node (node));
187 10 : 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 10 : gtpu_tunnel_fib_node_get (fib_node_index_t index)
195 : {
196 : gtpu_tunnel_t *t;
197 10 : gtpu_main_t *gtm = >pu_main;
198 :
199 10 : t = pool_elt_at_index (gtm->tunnels, index);
200 :
201 10 : 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 0 : 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 0 : ASSERT (0);
215 0 : }
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 64 : 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 64 : } r =
246 : {
247 : .rw = 0};
248 64 : int len = is_ip6 ? sizeof *r.h6 : sizeof *r.h4;
249 :
250 64 : 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 64 : if (!is_ip6)
256 : {
257 63 : ip4_header_t *ip = &r.h4->ip4;
258 63 : udp = &r.h4->udp;
259 63 : gtpu = &r.h4->gtpu;
260 63 : ip->ip_version_and_header_length = 0x45;
261 63 : ip->ttl = 254;
262 63 : ip->protocol = IP_PROTOCOL_UDP;
263 :
264 63 : ip->src_address = t->src.ip4;
265 63 : ip->dst_address = t->dst.ip4;
266 :
267 : /* we fix up the ip4 header length and checksum after-the-fact */
268 63 : ip->checksum = ip4_header_checksum (ip);
269 : }
270 : else
271 : {
272 1 : ip6_header_t *ip = &r.h6->ip6;
273 1 : udp = &r.h6->udp;
274 1 : gtpu = &r.h6->gtpu;
275 1 : ip->ip_version_traffic_class_and_flow_label =
276 1 : clib_host_to_net_u32 (6 << 28);
277 1 : ip->hop_limit = 255;
278 1 : ip->protocol = IP_PROTOCOL_UDP;
279 :
280 1 : ip->src_address = t->src.ip6;
281 1 : ip->dst_address = t->dst.ip6;
282 : }
283 :
284 : /* UDP header, randomize src port on something, maybe? */
285 64 : udp->src_port = clib_host_to_net_u16 (2152);
286 64 : udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_GTPU);
287 :
288 : /* GTPU header */
289 64 : gtpu->ver_flags = GTPU_V1_VER | GTPU_PT_GTP;
290 64 : gtpu->type = GTPU_TYPE_GTPU;
291 64 : gtpu->teid = clib_host_to_net_u32 (t->tteid);
292 :
293 64 : t->rewrite = r.rw;
294 : /* Now only support 8-byte gtpu header. TBD */
295 64 : vec_set_len (t->rewrite, sizeof (ip4_gtpu_header_t) - 4);
296 :
297 64 : return;
298 : }
299 :
300 : static bool
301 64 : gtpu_decap_next_is_valid (gtpu_main_t * gtm, u32 is_ip6, u32 decap_next_index)
302 : {
303 64 : vlib_main_t *vm = gtm->vlib_main;
304 64 : u32 input_idx = (!is_ip6) ? gtpu4_input_node.index : gtpu6_input_node.index;
305 64 : vlib_node_runtime_t *r = vlib_node_get_runtime (vm, input_idx);
306 :
307 64 : 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 61 : mcast_shared_get (ip46_address_t * ip)
321 : {
322 61 : ASSERT (ip46_address_is_multicast (ip));
323 61 : uword *p = hash_get_mem (gtpu_main.mcast_shared, ip);
324 61 : ALWAYS_ASSERT (p);
325 122 : return (mcast_shared_t)
326 : {
327 61 : .as_u64 = *p};
328 : }
329 :
330 : static inline void
331 21 : mcast_shared_add (ip46_address_t * dst, fib_node_index_t mfei, adj_index_t ai)
332 : {
333 21 : mcast_shared_t new_ep = {
334 : .mcast_adj_index = ai,
335 : .mfib_entry_index = mfei,
336 : };
337 :
338 21 : hash_set_mem_alloc (>pu_main.mcast_shared, dst, new_ep.as_u64);
339 21 : }
340 :
341 : static inline void
342 20 : mcast_shared_remove (ip46_address_t * dst)
343 : {
344 20 : mcast_shared_t ep = mcast_shared_get (dst);
345 :
346 20 : adj_unlock (ep.mcast_adj_index);
347 20 : mfib_table_entry_delete_index (ep.mfib_entry_index, MFIB_SOURCE_GTPU);
348 :
349 20 : hash_unset_mem_free (>pu_main.mcast_shared, dst);
350 20 : }
351 :
352 106 : int vnet_gtpu_add_mod_del_tunnel
353 : (vnet_gtpu_add_mod_del_tunnel_args_t * a, u32 * sw_if_indexp)
354 : {
355 106 : gtpu_main_t *gtm = >pu_main;
356 106 : gtpu_tunnel_t *t = 0;
357 106 : vnet_main_t *vnm = gtm->vnet_main;
358 : uword *p;
359 106 : u32 hw_if_index = ~0;
360 106 : u32 sw_if_index = ~0;
361 : gtpu4_tunnel_key_t key4;
362 : gtpu6_tunnel_key_t key6;
363 106 : bool is_ip6 = !ip46_address_is_ip4 (&a->dst);
364 :
365 106 : if (!is_ip6)
366 : {
367 104 : key4.src = a->dst.ip4.as_u32; /* decap src in key is encap dst in config */
368 104 : key4.teid = clib_host_to_net_u32 (a->teid);
369 104 : p = hash_get (gtm->gtpu4_tunnel_by_key, key4.as_u64);
370 : }
371 : else
372 : {
373 2 : key6.src = a->dst.ip6;
374 2 : key6.teid = clib_host_to_net_u32 (a->teid);
375 2 : p = hash_get_mem (gtm->gtpu6_tunnel_by_key, &key6);
376 : }
377 :
378 106 : if (a->opn == GTPU_ADD_TUNNEL)
379 : {
380 64 : l2input_main_t *l2im = &l2input_main;
381 :
382 : /* adding a tunnel: tunnel must not already exist */
383 64 : if (p)
384 0 : return VNET_API_ERROR_TUNNEL_EXIST;
385 :
386 : /*if not set explicitly, default to l2 */
387 64 : if (a->decap_next_index == ~0)
388 64 : a->decap_next_index = GTPU_INPUT_NEXT_L2_INPUT;
389 64 : if (!gtpu_decap_next_is_valid (gtm, is_ip6, a->decap_next_index))
390 0 : return VNET_API_ERROR_INVALID_DECAP_NEXT;
391 :
392 64 : pool_get_aligned (gtm->tunnels, t, CLIB_CACHE_LINE_BYTES);
393 64 : clib_memset (t, 0, sizeof (*t));
394 :
395 : /* copy from arg structure */
396 : #define _(x) t->x = a->x;
397 64 : foreach_copy_field;
398 : #undef _
399 :
400 : /* default to same as local rx teid */
401 64 : if (t->tteid == 0)
402 64 : t->tteid = t->teid;
403 :
404 64 : ip_udp_gtpu_rewrite (t, is_ip6);
405 :
406 : /* clear the flow index */
407 64 : t->flow_index = ~0;
408 :
409 : /* copy the key */
410 64 : if (is_ip6)
411 1 : hash_set_mem_alloc (>m->gtpu6_tunnel_by_key, &key6,
412 1 : t - gtm->tunnels);
413 : else
414 63 : hash_set (gtm->gtpu4_tunnel_by_key, key4.as_u64, t - gtm->tunnels);
415 :
416 : vnet_hw_interface_t *hi;
417 64 : if (vec_len (gtm->free_gtpu_tunnel_hw_if_indices) > 0)
418 10 : {
419 10 : vnet_interface_main_t *im = &vnm->interface_main;
420 20 : hw_if_index = gtm->free_gtpu_tunnel_hw_if_indices
421 10 : [vec_len (gtm->free_gtpu_tunnel_hw_if_indices) - 1];
422 10 : vec_dec_len (gtm->free_gtpu_tunnel_hw_if_indices, 1);
423 :
424 10 : hi = vnet_get_hw_interface (vnm, hw_if_index);
425 10 : hi->dev_instance = t - gtm->tunnels;
426 10 : hi->hw_instance = hi->dev_instance;
427 :
428 : /* clear old stats of freed tunnel before reuse */
429 10 : sw_if_index = hi->sw_if_index;
430 10 : vnet_interface_counter_lock (im);
431 10 : vlib_zero_combined_counter
432 10 : (&im->combined_sw_if_counters[VNET_INTERFACE_COUNTER_TX],
433 : sw_if_index);
434 10 : vlib_zero_combined_counter (&im->combined_sw_if_counters
435 : [VNET_INTERFACE_COUNTER_RX],
436 : sw_if_index);
437 10 : vlib_zero_simple_counter (&im->sw_if_counters
438 : [VNET_INTERFACE_COUNTER_DROP],
439 : sw_if_index);
440 10 : vnet_interface_counter_unlock (im);
441 : }
442 : else
443 : {
444 54 : hw_if_index = vnet_register_interface
445 54 : (vnm, gtpu_device_class.index, t - gtm->tunnels,
446 54 : gtpu_hw_class.index, t - gtm->tunnels);
447 54 : hi = vnet_get_hw_interface (vnm, hw_if_index);
448 : }
449 :
450 : /* Set gtpu tunnel output node */
451 128 : u32 encap_index = !is_ip6 ?
452 64 : gtpu4_encap_node.index : gtpu6_encap_node.index;
453 64 : vnet_set_interface_output_node (vnm, hw_if_index, encap_index);
454 :
455 64 : t->hw_if_index = hw_if_index;
456 64 : t->sw_if_index = sw_if_index = hi->sw_if_index;
457 :
458 125 : vec_validate_init_empty (gtm->tunnel_index_by_sw_if_index, sw_if_index,
459 : ~0);
460 64 : 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 64 : vec_validate (l2im->configs, sw_if_index);
464 64 : l2im->configs[sw_if_index].feature_bitmap = L2INPUT_FEAT_DROP;
465 64 : l2im->configs[sw_if_index].bd_index = 0;
466 :
467 64 : vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, sw_if_index);
468 64 : si->flags &= ~VNET_SW_INTERFACE_FLAG_HIDDEN;
469 64 : vnet_sw_interface_set_flags (vnm, sw_if_index,
470 : VNET_SW_INTERFACE_FLAG_ADMIN_UP);
471 :
472 64 : fib_node_init (&t->node, gtm->fib_node_type);
473 : fib_prefix_t tun_dst_pfx;
474 64 : vnet_flood_class_t flood_class = VNET_FLOOD_CLASS_TUNNEL_NORMAL;
475 :
476 64 : fib_protocol_t fp = fib_ip_proto (is_ip6);
477 64 : fib_prefix_from_ip46_addr (fp, &t->dst, &tun_dst_pfx);
478 64 : 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 23 : vtep_addr_ref (>m->vtep_table, t->encap_fib_index, &t->src);
487 46 : t->fib_entry_index = fib_entry_track (t->encap_fib_index,
488 : &tun_dst_pfx,
489 23 : gtm->fib_node_type,
490 23 : t - gtm->tunnels,
491 23 : &t->sibling_index);
492 23 : 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 41 : if (vtep_addr_ref (>m->vtep_table,
502 41 : t->encap_fib_index, &t->dst) == 1)
503 : {
504 : fib_node_index_t mfei;
505 : adj_index_t ai;
506 42 : fib_route_path_t path = {
507 21 : .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 21 : 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 21 : mfib_table_entry_path_update (t->encap_fib_index, &mpfx,
527 : MFIB_SOURCE_GTPU,
528 : MFIB_ENTRY_FLAG_NONE, &path);
529 :
530 21 : path.frp_sw_if_index = a->mcast_sw_if_index;
531 21 : path.frp_flags = FIB_ROUTE_PATH_FLAG_NONE;
532 21 : path.frp_mitf_flags = MFIB_ITF_FLAG_ACCEPT;
533 21 : mfei = mfib_table_entry_path_update (
534 21 : 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 21 : ai = adj_mcast_add_or_lock (fp,
541 21 : fib_proto_to_link (fp),
542 : a->mcast_sw_if_index);
543 :
544 : /*
545 : * create a new end-point
546 : */
547 21 : mcast_shared_add (&t->dst, mfei, ai);
548 : }
549 :
550 41 : dpo_id_t dpo = DPO_INVALID;
551 41 : mcast_shared_t ep = mcast_shared_get (&t->dst);
552 :
553 : /* Stack shared mcast dst mac addr rewrite on encap */
554 41 : dpo_set (&dpo, DPO_ADJACENCY_MCAST,
555 41 : fib_proto_to_dpo (fp), ep.mcast_adj_index);
556 :
557 41 : dpo_stack_from_node (encap_index, &t->next_dpo, &dpo);
558 :
559 41 : dpo_reset (&dpo);
560 41 : flood_class = VNET_FLOOD_CLASS_TUNNEL_MASTER;
561 : }
562 :
563 64 : 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 42 : if (!p)
570 0 : return VNET_API_ERROR_NO_SUCH_ENTRY;
571 :
572 42 : t = pool_elt_at_index (gtm->tunnels, p[0]);
573 42 : sw_if_index = t->sw_if_index;
574 :
575 42 : if (a->opn == GTPU_UPD_TTEID)
576 : {
577 0 : if (a->tteid == 0)
578 0 : return VNET_API_ERROR_INVALID_VALUE;
579 0 : t->tteid = a->tteid;
580 0 : vec_free (t->rewrite);
581 0 : ip_udp_gtpu_rewrite (t, is_ip6);
582 0 : return 0;
583 : }
584 :
585 : /* delete tunnel */
586 42 : vnet_sw_interface_set_flags (vnm, t->sw_if_index, 0 /* down */ );
587 42 : vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, t->sw_if_index);
588 42 : si->flags |= VNET_SW_INTERFACE_FLAG_HIDDEN;
589 :
590 : /* make sure tunnel is removed from l2 bd or xconnect */
591 42 : set_int_l2_mode (gtm->vlib_main, vnm, MODE_L3, t->sw_if_index, 0,
592 : L2_BD_PORT_TYPE_NORMAL, 0, 0);
593 42 : vec_add1 (gtm->free_gtpu_tunnel_hw_if_indices, t->hw_if_index);
594 :
595 42 : gtm->tunnel_index_by_sw_if_index[t->sw_if_index] = ~0;
596 :
597 42 : if (!is_ip6)
598 41 : hash_unset (gtm->gtpu4_tunnel_by_key, key4.as_u64);
599 : else
600 1 : hash_unset_mem_free (>m->gtpu6_tunnel_by_key, &key6);
601 :
602 42 : if (!ip46_address_is_multicast (&t->dst))
603 : {
604 2 : if (t->flow_index != ~0)
605 0 : vnet_flow_del (vnm, t->flow_index);
606 :
607 2 : vtep_addr_unref (>m->vtep_table, t->encap_fib_index, &t->src);
608 2 : fib_entry_untrack (t->fib_entry_index, t->sibling_index);
609 : }
610 40 : else if (vtep_addr_unref (>m->vtep_table,
611 40 : t->encap_fib_index, &t->dst) == 0)
612 : {
613 20 : mcast_shared_remove (&t->dst);
614 : }
615 :
616 42 : fib_node_deinit (&t->node);
617 42 : vec_free (t->rewrite);
618 42 : pool_put (gtm->tunnels, t);
619 : }
620 :
621 106 : if (sw_if_indexp)
622 106 : *sw_if_indexp = sw_if_index;
623 :
624 106 : if (a->opn == GTPU_ADD_TUNNEL)
625 : {
626 : /* register udp ports */
627 64 : if (!is_ip6 && !udp_is_valid_dst_port (UDP_DST_PORT_GTPU, 1))
628 2 : udp_register_dst_port (gtm->vlib_main, UDP_DST_PORT_GTPU,
629 : gtpu4_input_node.index, /* is_ip4 */ 1);
630 64 : if (is_ip6 && !udp_is_valid_dst_port (UDP_DST_PORT_GTPU6, 0))
631 1 : udp_register_dst_port (gtm->vlib_main, UDP_DST_PORT_GTPU6,
632 : gtpu6_input_node.index, /* is_ip4 */ 0);
633 : }
634 :
635 106 : return 0;
636 : }
637 :
638 : static uword
639 0 : get_decap_next_for_node (u32 node_index, u32 ipv4_set)
640 : {
641 0 : gtpu_main_t *gtm = >pu_main;
642 0 : vlib_main_t *vm = gtm->vlib_main;
643 0 : uword input_node = (ipv4_set) ? gtpu4_input_node.index :
644 0 : gtpu6_input_node.index;
645 :
646 0 : return vlib_node_add_next (vm, input_node, node_index);
647 : }
648 :
649 : static uword
650 0 : unformat_decap_next (unformat_input_t * input, va_list * args)
651 : {
652 0 : u32 *result = va_arg (*args, u32 *);
653 0 : u32 ipv4_set = va_arg (*args, int);
654 0 : gtpu_main_t *gtm = >pu_main;
655 0 : vlib_main_t *vm = gtm->vlib_main;
656 : u32 node_index;
657 : u32 tmp;
658 :
659 0 : if (unformat (input, "l2"))
660 0 : *result = GTPU_INPUT_NEXT_L2_INPUT;
661 0 : else if (unformat (input, "ip4"))
662 0 : *result = GTPU_INPUT_NEXT_IP4_INPUT;
663 0 : else if (unformat (input, "ip6"))
664 0 : *result = GTPU_INPUT_NEXT_IP6_INPUT;
665 0 : else if (unformat (input, "node %U", unformat_vlib_node, vm, &node_index))
666 0 : *result = get_decap_next_for_node (node_index, ipv4_set);
667 0 : else if (unformat (input, "%d", &tmp))
668 0 : *result = tmp;
669 : else
670 0 : return 0;
671 :
672 0 : return 1;
673 : }
674 :
675 : static clib_error_t *
676 0 : gtpu_add_del_tunnel_command_fn (vlib_main_t * vm,
677 : unformat_input_t * input,
678 : vlib_cli_command_t * cmd)
679 : {
680 0 : unformat_input_t _line_input, *line_input = &_line_input;
681 : ip46_address_t src, dst;
682 0 : u8 opn = GTPU_ADD_TUNNEL;
683 0 : u8 src_set = 0;
684 0 : u8 dst_set = 0;
685 0 : u8 grp_set = 0;
686 0 : u8 ipv4_set = 0;
687 0 : u8 ipv6_set = 0;
688 0 : u32 encap_fib_index = 0;
689 0 : u32 mcast_sw_if_index = ~0;
690 0 : u32 decap_next_index = GTPU_INPUT_NEXT_L2_INPUT;
691 0 : u32 teid = 0, tteid = 0;
692 : u32 tmp;
693 : int rv;
694 0 : vnet_gtpu_add_mod_del_tunnel_args_t _a, *a = &_a;
695 : u32 tunnel_sw_if_index;
696 0 : clib_error_t *error = NULL;
697 :
698 : /* Cant "universally zero init" (={0}) due to GCC bug 53119 */
699 0 : clib_memset (&src, 0, sizeof src);
700 0 : clib_memset (&dst, 0, sizeof dst);
701 :
702 : /* Get a line of input. */
703 0 : if (!unformat_user (input, unformat_line_input, line_input))
704 0 : return 0;
705 :
706 0 : while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
707 : {
708 0 : if (unformat (line_input, "del"))
709 : {
710 0 : opn = GTPU_DEL_TUNNEL;
711 : }
712 0 : else if (unformat (line_input, "src %U",
713 : unformat_ip4_address, &src.ip4))
714 : {
715 0 : src_set = 1;
716 0 : ipv4_set = 1;
717 : }
718 0 : else if (unformat (line_input, "dst %U",
719 : unformat_ip4_address, &dst.ip4))
720 : {
721 0 : dst_set = 1;
722 0 : ipv4_set = 1;
723 : }
724 0 : else if (unformat (line_input, "src %U",
725 : unformat_ip6_address, &src.ip6))
726 : {
727 0 : src_set = 1;
728 0 : ipv6_set = 1;
729 : }
730 0 : else if (unformat (line_input, "dst %U",
731 : unformat_ip6_address, &dst.ip6))
732 : {
733 0 : dst_set = 1;
734 0 : ipv6_set = 1;
735 : }
736 0 : 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 0 : grp_set = dst_set = 1;
742 0 : ipv4_set = 1;
743 : }
744 0 : 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 0 : grp_set = dst_set = 1;
750 0 : ipv6_set = 1;
751 : }
752 0 : else if (unformat (line_input, "encap-vrf-id %d", &tmp))
753 : {
754 0 : encap_fib_index = fib_table_find (fib_ip_proto (ipv6_set), tmp);
755 0 : if (encap_fib_index == ~0)
756 : {
757 : error =
758 0 : clib_error_return (0, "nonexistent encap-vrf-id %d", tmp);
759 0 : goto done;
760 : }
761 : }
762 0 : else if (unformat (line_input, "decap-next %U", unformat_decap_next,
763 : &decap_next_index, ipv4_set))
764 : ;
765 0 : else if (unformat (line_input, "teid %d", &teid))
766 : ;
767 0 : else if (unformat (line_input, "tteid %d", &tteid))
768 : ;
769 0 : else if (unformat (line_input, "upd-tteid %d", &tteid))
770 0 : opn = GTPU_UPD_TTEID;
771 : else
772 : {
773 0 : error = clib_error_return (0, "parse error: '%U'",
774 : format_unformat_error, line_input);
775 0 : goto done;
776 : }
777 : }
778 :
779 0 : if (teid == 0)
780 : {
781 0 : error = clib_error_return (0, "tunnel teid specified");
782 0 : goto done;
783 : }
784 :
785 0 : if (src_set == 0 && opn == GTPU_ADD_TUNNEL)
786 : {
787 0 : error = clib_error_return (0, "tunnel src address not specified");
788 0 : goto done;
789 : }
790 :
791 0 : if (dst_set == 0)
792 : {
793 0 : error = clib_error_return (0, "tunnel dst address not specified");
794 0 : goto done;
795 : }
796 :
797 0 : if (grp_set && !ip46_address_is_multicast (&dst))
798 : {
799 0 : error = clib_error_return (0, "tunnel group address not multicast");
800 0 : goto done;
801 : }
802 :
803 0 : if (grp_set == 0 && ip46_address_is_multicast (&dst))
804 : {
805 0 : error = clib_error_return (0, "dst address must be unicast");
806 0 : goto done;
807 : }
808 :
809 0 : if (grp_set && mcast_sw_if_index == ~0)
810 : {
811 0 : error = clib_error_return (0, "tunnel nonexistent multicast device");
812 0 : goto done;
813 : }
814 :
815 0 : if (ipv4_set && ipv6_set)
816 : {
817 0 : error = clib_error_return (0, "both IPv4 and IPv6 addresses specified");
818 0 : goto done;
819 : }
820 :
821 0 : if (ip46_address_cmp (&src, &dst) == 0)
822 : {
823 0 : error = clib_error_return (0, "src and dst addresses are identical");
824 0 : goto done;
825 : }
826 :
827 0 : if (decap_next_index == ~0)
828 : {
829 0 : error = clib_error_return (0, "next node not found");
830 0 : goto done;
831 : }
832 :
833 0 : clib_memset (a, 0, sizeof (*a));
834 :
835 0 : a->opn = opn;
836 :
837 : #define _(x) a->x = x;
838 0 : foreach_copy_field;
839 : #undef _
840 :
841 0 : rv = vnet_gtpu_add_mod_del_tunnel (a, &tunnel_sw_if_index);
842 :
843 0 : switch (rv)
844 : {
845 0 : case 0:
846 0 : if (opn == GTPU_ADD_TUNNEL)
847 0 : vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name,
848 : vnet_get_main (), tunnel_sw_if_index);
849 0 : break;
850 :
851 0 : case VNET_API_ERROR_TUNNEL_EXIST:
852 0 : error = clib_error_return (0, "tunnel already exists...");
853 0 : goto done;
854 :
855 0 : case VNET_API_ERROR_NO_SUCH_ENTRY:
856 0 : error = clib_error_return (0, "tunnel does not exist...");
857 0 : goto done;
858 :
859 0 : case VNET_API_ERROR_INVALID_VALUE:
860 0 : error = clib_error_return (0, "tx teid not specified...");
861 0 : goto done;
862 :
863 0 : default:
864 0 : error = clib_error_return
865 : (0, "vnet_gtpu_add_del_tunnel returned %d", rv);
866 0 : goto done;
867 : }
868 :
869 0 : done:
870 0 : unformat_free (line_input);
871 :
872 0 : 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 207367 : 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 5 : show_gtpu_tunnel_command_fn (vlib_main_t * vm,
912 : unformat_input_t * input,
913 : vlib_cli_command_t * cmd)
914 : {
915 5 : gtpu_main_t *gtm = >pu_main;
916 : gtpu_tunnel_t *t;
917 :
918 5 : if (pool_elts (gtm->tunnels) == 0)
919 0 : vlib_cli_output (vm, "No gtpu tunnels configured...");
920 :
921 115 : pool_foreach (t, gtm->tunnels)
922 : {
923 110 : vlib_cli_output (vm, "%U", format_gtpu_tunnel, t);
924 : }
925 :
926 5 : 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 207367 : 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 0 : vnet_int_gtpu_bypass_mode (u32 sw_if_index, u8 is_ip6, u8 is_enable)
948 : {
949 0 : if (is_ip6)
950 0 : vnet_feature_enable_disable ("ip6-unicast", "ip6-gtpu-bypass",
951 : sw_if_index, is_enable, 0, 0);
952 : else
953 0 : vnet_feature_enable_disable ("ip4-unicast", "ip4-gtpu-bypass",
954 : sw_if_index, is_enable, 0, 0);
955 0 : }
956 :
957 : static clib_error_t *
958 0 : set_ip_gtpu_bypass (u32 is_ip6,
959 : unformat_input_t * input, vlib_cli_command_t * cmd)
960 : {
961 0 : unformat_input_t _line_input, *line_input = &_line_input;
962 0 : vnet_main_t *vnm = vnet_get_main ();
963 0 : clib_error_t *error = 0;
964 : u32 sw_if_index, is_enable;
965 :
966 0 : sw_if_index = ~0;
967 0 : is_enable = 1;
968 :
969 0 : if (!unformat_user (input, unformat_line_input, line_input))
970 0 : return 0;
971 :
972 0 : while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
973 : {
974 0 : if (unformat_user
975 : (line_input, unformat_vnet_sw_interface, vnm, &sw_if_index))
976 : ;
977 0 : else if (unformat (line_input, "del"))
978 0 : is_enable = 0;
979 : else
980 : {
981 0 : error = unformat_parse_error (line_input);
982 0 : goto done;
983 : }
984 : }
985 :
986 0 : if (~0 == sw_if_index)
987 : {
988 0 : error = clib_error_return (0, "unknown interface `%U'",
989 : format_unformat_error, line_input);
990 0 : goto done;
991 : }
992 :
993 0 : vnet_int_gtpu_bypass_mode (sw_if_index, is_ip6, is_enable);
994 :
995 0 : done:
996 0 : unformat_free (line_input);
997 :
998 0 : return error;
999 : }
1000 :
1001 : static clib_error_t *
1002 0 : set_ip4_gtpu_bypass (vlib_main_t * vm,
1003 : unformat_input_t * input, vlib_cli_command_t * cmd)
1004 : {
1005 0 : 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 207367 : 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 0 : set_ip6_gtpu_bypass (vlib_main_t * vm,
1060 : unformat_input_t * input, vlib_cli_command_t * cmd)
1061 : {
1062 0 : 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 207367 : 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 0 : vnet_gtpu_add_del_rx_flow (u32 hw_if_index, u32 t_index, int is_add)
1117 : {
1118 0 : gtpu_main_t *gtm = >pu_main;
1119 0 : gtpu_tunnel_t *t = pool_elt_at_index (gtm->tunnels, t_index);
1120 0 : vnet_main_t *vnm = vnet_get_main ();
1121 0 : if (is_add)
1122 : {
1123 0 : if (t->flow_index == ~0)
1124 : {
1125 0 : vnet_flow_t flow = {
1126 : .actions =
1127 : VNET_FLOW_ACTION_REDIRECT_TO_NODE | VNET_FLOW_ACTION_MARK |
1128 : VNET_FLOW_ACTION_BUFFER_ADVANCE,
1129 0 : .mark_flow_id = t_index + gtm->flow_id_start,
1130 0 : .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 0 : .teid = t->teid,
1141 : }
1142 : ,
1143 : };
1144 0 : vnet_flow_add (vnm, &flow, &t->flow_index);
1145 : }
1146 :
1147 0 : return vnet_flow_enable (vnm, t->flow_index, hw_if_index);
1148 : }
1149 :
1150 : /* flow index is removed when the tunnel is deleted */
1151 0 : return vnet_flow_disable (vnm, t->flow_index, hw_if_index);
1152 : }
1153 :
1154 : u32
1155 0 : vnet_gtpu_get_tunnel_index (u32 sw_if_index)
1156 : {
1157 0 : gtpu_main_t *gtm = >pu_main;
1158 :
1159 0 : if (sw_if_index >= vec_len (gtm->tunnel_index_by_sw_if_index))
1160 0 : return ~0;
1161 0 : return gtm->tunnel_index_by_sw_if_index[sw_if_index];
1162 : }
1163 :
1164 : static clib_error_t *
1165 0 : gtpu_offload_command_fn (vlib_main_t * vm,
1166 : unformat_input_t * input, vlib_cli_command_t * cmd)
1167 : {
1168 0 : unformat_input_t _line_input, *line_input = &_line_input;
1169 :
1170 : /* Get a line of input. */
1171 0 : if (!unformat_user (input, unformat_line_input, line_input))
1172 0 : return 0;
1173 :
1174 0 : vnet_main_t *vnm = vnet_get_main ();
1175 0 : u32 rx_sw_if_index = ~0;
1176 0 : u32 hw_if_index = ~0;
1177 0 : int is_add = 1;
1178 :
1179 0 : while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
1180 : {
1181 0 : if (unformat (line_input, "hw %U", unformat_vnet_hw_interface, vnm,
1182 : &hw_if_index))
1183 0 : continue;
1184 0 : if (unformat (line_input, "rx %U", unformat_vnet_sw_interface, vnm,
1185 : &rx_sw_if_index))
1186 0 : continue;
1187 0 : if (unformat (line_input, "del"))
1188 : {
1189 0 : is_add = 0;
1190 0 : continue;
1191 : }
1192 0 : return clib_error_return (0, "unknown input `%U'",
1193 : format_unformat_error, line_input);
1194 : }
1195 :
1196 0 : if (rx_sw_if_index == ~0)
1197 0 : return clib_error_return (0, "missing rx interface");
1198 0 : if (hw_if_index == ~0)
1199 0 : return clib_error_return (0, "missing hw interface");
1200 :
1201 0 : u32 t_index = vnet_gtpu_get_tunnel_index (rx_sw_if_index);;
1202 0 : if (t_index == ~0)
1203 0 : 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 0 : gtpu_main_t *gtm = >pu_main;
1208 0 : gtpu_tunnel_t *t = pool_elt_at_index (gtm->tunnels, t_index);
1209 :
1210 : /* first support ipv4 hw offload */
1211 0 : if (!ip46_address_is_ip4 (&t->dst))
1212 0 : return clib_error_return (0, "currently only IPV4 tunnels are supported");
1213 :
1214 : /* inner protocol should be IPv4/IPv6 */
1215 0 : if ((t->decap_next_index != GTPU_INPUT_NEXT_IP4_INPUT) &&
1216 0 : (t->decap_next_index != GTPU_INPUT_NEXT_IP6_INPUT))
1217 0 : return clib_error_return (0,
1218 : "currently only inner IPv4/IPv6 protocol is supported");
1219 :
1220 0 : vnet_hw_interface_t *hw_if = vnet_get_hw_interface (vnm, hw_if_index);
1221 0 : ip4_main_t *im = &ip4_main;
1222 0 : u32 rx_fib_index =
1223 0 : vec_elt (im->fib_index_by_sw_if_index, hw_if->sw_if_index);
1224 :
1225 0 : if (t->encap_fib_index != rx_fib_index)
1226 0 : return clib_error_return (0, "interface/tunnel fib mismatch");
1227 :
1228 0 : if (vnet_gtpu_add_del_rx_flow (hw_if_index, t_index, is_add))
1229 0 : return clib_error_return (0, "error %s flow",
1230 : is_add ? "enabling" : "disabling");
1231 :
1232 0 : return 0;
1233 : }
1234 :
1235 :
1236 : /* *INDENT-OFF* */
1237 207367 : 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 559 : gtpu_init (vlib_main_t * vm)
1247 : {
1248 559 : gtpu_main_t *gtm = >pu_main;
1249 :
1250 559 : gtm->vnet_main = vnet_get_main ();
1251 559 : gtm->vlib_main = vm;
1252 :
1253 559 : vnet_flow_get_range (gtm->vnet_main, "gtpu", 1024 * 1024,
1254 : >m->flow_id_start);
1255 :
1256 : /* initialize the ip6 hash */
1257 559 : gtm->gtpu6_tunnel_by_key = hash_create_mem (0,
1258 : sizeof (gtpu6_tunnel_key_t),
1259 : sizeof (uword));
1260 559 : gtm->vtep_table = vtep_table_create ();
1261 559 : gtm->mcast_shared = hash_create_mem (0,
1262 : sizeof (ip46_address_t),
1263 : sizeof (mcast_shared_t));
1264 :
1265 559 : gtm->fib_node_type = fib_node_register_new_type ("gtpu", >pu_vft);
1266 :
1267 559 : return 0;
1268 : }
1269 :
1270 1119 : 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 : */
|